Artık std :: array'e sahip olduğumuza göre, C tarzı diziler için hangi kullanımlar kaldı?


89

std::arrayC dizilerinden çok daha üstündür. Eski kodla birlikte çalışmak istesem bile, kullanabilirim std::array::data(). Eski tarz bir dizi istememin bir nedeni var mı?

Yanıtlar:


60

Bir şeyi gözden kaçırmadıysam (standarttaki en son değişiklikleri çok yakından takip etmedim), C tarzı dizilerin çoğu kullanımı hala devam ediyor. std::arraystatik başlatmaya izin verir, ancak yine de sizin için başlatıcıları saymaz. Ve daha önce C tarzı dizilerin tek gerçek kullanımı std::arrayşu satırlar boyunca statik olarak başlatılmış tablolar içindi:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

bunların üzerinde yinelemek için olağan beginve endşablon işlevlerini (C ++ 11'de uyarlanmıştır) kullanarak Derleyicinin başlatıcı sayısından belirlediği boyuttan hiç bahsetmeden.

DÜZENLEME: Unuttuğum başka bir şey: dizge değişmezleri hala C tarzı dizilerdir; yani tür ile char[]. Sadece sahip olduğumuz için hiç kimsenin dize değişmezlerini dışlayacağını düşünmüyorum std::array.


7
Uzunluğunu belirtmek zorunda kalmadan dizileri oluşturan değişken bir işlev şablonu yazabilirsiniz.
sağ taraf

2
C ++ 17 Sınıf Şablon Kesintisi ile, başlatıcı sayısının otomatik olarak düşürülmesi desteklenir. Örneğin, "auto a = std :: array {1, 2, 3};"
Ricky65

Nitpick: dizi değişmezlerinin türüconst char[]
Bulletmagnet

31

Hayır. Açıkça söylemek gerekirse. Ve 30 karakterde.

Elbette, uygulamak için C dizilerine ihtiyacınız var std::array, ancak bu, bir kullanıcının C dizilerini istemesi için bir neden değil. Ek olarak, hayır, std::arraybir C dizisinden daha az performansa sahip değildir ve sınır denetimli bir erişim seçeneği vardır. Ve son olarak, herhangi bir C ++ programının Standart kitaplığa bağlı olması tamamen mantıklıdır - bu, standart olma amacının bir çeşididir - ve bir Standart kitaplığa erişiminiz yoksa, derleyiciniz uyumlu değildir ve soru "C ++" olarak etiketlenir, "C ++ ve C ++ olmayanlar, uygunsuz olduğunu düşündükleri için spesifikasyonun yarısını kaçıran şeyler."


1
Hm. Ya başka bir dilden çağrılan ve parametre olarak bir şeyin geçirilmesi gereken C ++ kodu yazıyorsanız?
asveikau

3
Bağımsız uygulamaların neredeyse tüm standart kitaplığı dışarıda bırakmasına ve yine de uyumlu olmasına izin verilir. std::arrayYine de bağımsız bir C ++ 11 uygulamasında uygulanamayan bir derleyici hakkında ciddi şüphelerim olurdu .
Dennis Zickefoose

11
C ++ 0x Nihai Taslak (Belge N3092) § 1.4.7 "İki tür uygulama tanımlanmıştır: barındırılan ve bağımsız. Barındırılan bir uygulama için, bu Uluslararası Standart, kullanılabilir kitaplıklar kümesini tanımlar. Bağımsız bir uygulama, yürütmenin yapılabileceği bir bir işletim sisteminin yararı olmadan gerçekleşir ve belirli dil desteği kitaplıklarını içeren uygulama tanımlı bir kitaplık kümesine sahiptir ".. STL, bağımsız bir derleyicide" dil desteği "kitaplığı olarak dahil edilmez
Earlz

24

Görünüşe göre çok boyutlu diziler kullanmak C dizilerinden daha kolaydır std::array. Örneğin,

char c_arr[5][6][7];

aksine

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Ayrıca, C dizilerinin otomatik bozunma özelliğinden dolayı c_arr[i], yukarıdaki örnekte bir işaretçi olarak bozulur ve kalan boyutları iki parametre olarak daha geçirmeniz yeterlidir. Demek istediğim o c_arrkopyalamak için pahalı değil. Ancak cpp_arr[i]kopyalamak çok maliyetli olacaktır.


1
Bununla birlikte, arrayboyutları kaybetmeden çok boyutlu bir işlevi bir işleve geçirebilirsiniz . Ve bunu bir işlev şablonuna aktarırsanız, bu işlev her boyutun hem boyutunu hem de boyutunu veya bunlardan yalnızca birini çıkarabilir. Bu, esas olarak keyfi boyutlar üzerinde çalışan bilimsel şablon kitaplıkları için ilginç olabilir.
Sebastian Mach

31
bir basit template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;bu sorunlardan herhangi birini çözmelidir.
Miles Rout

7
Sizin örnek c_arrolduğunu çok kopyalamak için pahalı! Bunu kendiniz yapmak için kodu sağlamalısınız. Bozulacağı işaretçi, bir kopyadan çok referansa daha yakın bir eşdeğerdir ve std::arrayeğer istediğiniz buysa, bir referansı iletmek için kullanabilirsiniz .
ChetS

1
@MilesRout teknik olarak std::size_tbunun yerine olması gerekmez intmi? nitpicking için özür dilerim, ama bu onu evrensel yapar.
robbie

1
@ robbie0630 Evet, isterseniz yapabilirsiniz size_t, ancak 4 milyardan fazla satır veya sütuna sahip dizilerin gerekli olduğu birçok senaryo olduğunu hayal edemiyorum.
Miles Rout

14

Sumant'ın dediği gibi, çok boyutlu dizilerin kullanımı yerleşik C dizileriyle kullanmaktan çok daha kolaydır std::array.

İç içe yerleştirildiğinde, std::arrayokunması çok zor olabilir ve gereksiz yere ayrıntılı hale gelebilir.

Örneğin:

std::array<std::array<int, 3>, 3> arr1; 

nazaran

char c_arr[3][3]; 

Ayrıca, not olduğunu begin(), end()ve size()tüm dönüş anlamsız değerler zaman iç içe std::array.

Bu nedenlerle kendi sabit boyutlu çok boyutlu dizi kaplarımı yarattım array_2dve array_3d. Bunlar benzer olan std::array, ancak 2 ve 3 boyutlu çok boyutlu diziler. Daha güvenlidirler ve yerleşik çok boyutlu dizilerden daha kötü performansları yoktur. Nadir oldukları için boyutları 3'ten büyük olan çok boyutlu diziler için bir kap eklemedim. C ++ 0x'de, keyfi sayıda boyutu destekleyen değişken bir şablon sürümü yapılabilir.

İki boyutlu varyantın bir örneği:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

Tam dokümantasyon burada mevcuttur:

http://fsma.googlecode.com/files/fsma.html

Kitaplığı buradan indirebilirsiniz:

http://fsma.googlecode.com/files/fsma.zip


4
Sabit boyutlu C tarzı diziler kolaydır, ancak boyutları değiştirmek isterseniz işler karmaşıklaşır. Örneğin, verildiğinde arr[x][y], arrbir dizi dizisi mi , bir dizi işaretçi mi, bir diziye işaretçi mi yoksa bir işaretçi mi olduğunu söyleyemezsiniz ; tüm uygulamalar, ihtiyaçlarınıza bağlı olarak yasaldır. Ve muhtemelen çok boyutlu diziler için gerçek dünyadaki kullanım durumlarının çoğu, boyutun çalışma zamanında belirlenmesini gerektirir.
Keith Thompson

N boyutlu dizilerin değişken şablon uygulamasını görmeyi çok isterim! En iyi meta programlama!
steffen

3
@steffen Birkaç yıl önce bir girişimde bulundum. Bunu burada görüntüleyebilirsiniz: code.google.com/p/fsma/source/browse/trunk/… . Örneği burada test edin: code.google.com/p/fsma/source/browse/trunk/… . Eminim çok daha iyi yapılabilir.
Ricky65

5

C ++ 'da bulunan C tarzı diziler, gerçekte gerçek C dizilerinden çok daha az çok yönlüdür. Aradaki fark, C'de dizi türlerinin çalışma zamanı boyutlarına sahip olabilmesidir . Aşağıdakiler geçerli C kodudur, ancak ne C ++ C tarzı dizilerle ne de C ++ array<>türleriyle ifade edilemez :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

C ++ 'da, geçici diziyi öbek üzerinde ayırmanız gerekir:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Bu ile başarılamaz std::array<>çünkübar derleme zamanında bilinmemektedir, bu C ++ veya her iki C tarzı diziler kullanımını gerektirir std::vector<>.


İlk örnek C ++ 'da göreceli olarak kolayca ifade edilebilirken ( new[]ve gerektirse de delete[]), aşağıdakiler C ++' da olmadan elde edilemez std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Buradaki nokta, çizgi dizilerinin işaretçilerinin int (*)[width] C ++ 'da bir çalışma zamanı genişliği kullanamamasıdır, bu da herhangi bir görüntü işleme kodunu C ++' da C 'de olduğundan çok daha karmaşık hale getirir: Görüntü işleme örneğinin tipik bir C ++ uygulaması şu şekilde görünür:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Bu kod, yukarıdaki C koduyla tam olarak aynı hesaplamaları yapar, ancak dizin hesaplamasını elle yapması gerekir. indekslerin kullanıldığı her yerde . 2B durum için, bu yine de mümkündür (endeks hesaplamasını yanlış yapmak için birçok fırsatla birlikte gelse de). Yine de, 3B durumunda gerçekten kötü oluyor.

C ++ ile kod yazmayı seviyorum. Ancak ne zaman çok boyutlu verileri değiştirmem gerekse, gerçekten kendime kodun bu bölümünü C'ye taşımam gerekip gerekmediğini soruyorum.


7
En azından Clang ve GCC'nin C ++ 'da VLA'ları desteklediğine dikkat edilmelidir.
Janus Troelsen

@ JanusTroelsen ve ayrıca hangi öğe türlerini destekledikleri konusunda korkunç derecede sınırlı olduklarını.
rightfold

C11, VLA'yı isteğe bağlı kılmaz mı? Eğer öyleyse, cevabınızın yanıltıcı olduğunu düşünüyorum. C99 standart olduğunda ancak C11 olmadığında doğru olurdu.
Z bozonu

1
@Zboson C99 bir C standardıdır ve VLA özelliklerini uygulayan derleyiciler vardır ( gccörneğin). C11, isteğe bağlı olarak oldukça ilginç şeyler yaptı ve bunun, özelliği yasaklamak istedikleri için olduğunu düşünmüyorum. Bunu, tamamen standart uyumlu bir derleyici yazma düzeyini düşürmek istediklerinin bir işareti olarak görme eğilimindeyim: VLA'ları uygulamak oldukça zor bir canavar ve çoğu kod olmadan da yapılabilir, bu nedenle yeni bir derleyici için yeni bir derleyici için mantıklı geliyor VLA'ları hemen uygulamak zorunda kalmamak için platform.
cmaster

-1

Olabilir std::arrayyavaş değil. Ama basit depoyu kullanarak bazı kıyaslamalar yaptım ve std :: array; Aşağıdaki karşılaştırma sonuçlarına bakın (W8.1, VS2013 Güncelleme 4'te):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Negatif işaretlere göre kullandığım kod pastebin'de ( link )

Kıyaslama sınıf kodu burada ;

Kıyaslamalar hakkında pek bir şey bilmiyorum ... Kodum hatalı olabilir


6
Karşılaştırma kodu veya derleme bayrakları olmadan karşılaştırma sonuçları? Hadi, daha iyisini yapabilirsin.
R. Martinho Fernandes

FWIW, sadece bu küçük kod parçası, kıyaslamanın ciddi şekilde kusurlu olduğunu gösteriyor. Yeterince akıllı bir derleyici her şeyi long test_arr_without_init() { return ARR_SIZE; }
şuna

Bu sadece bir örnekti. Ben bunun önemli olmadığını düşündüm. Void döndürmek için kodu değiştirdim, VS 2013'te / O2 / Ot / Gl.
K'Prime

Dönüş değerini kaldırmak, derleyicinin her şeyi void test_arr_without_init() {}şimdiye çevirebileceği anlamına gelir . Ölçtüğünüz kodun ölçmek istediğiniz kod olduğundan emin olmak için gerçekten çemberlerden geçmeniz gerekir.
R. Martinho Fernandes

-5
  1. gibi bir şey uygulamak std::array
  2. STL'yi kullanmak istemiyorsanız veya kullanamıyorsanız
  3. Performans için

27
std::arrayBir C dizisinden nasıl daha az performanslı olacağını söyle .
Xeo 24:11

2
Wikipedia'dan : "Dizi uygulaması, bağlı denetim yapmak için gerekli değildir. Ancak, artırmadaki uygulama bunu [] işleci için yapar, ancak yineleyiciler için yapmaz." - dolayısıyla operatör [] daha yavaştır. Uygulamalara bakmadım, ancak uygulamadaki herhangi bir kod optimize edicinin önüne geçebilir.
Lou Franco

19
@Aaron McDaid: Bu sadece içeride, içeride at()değil operator[], tıpkı gibi std::vector. Performans düşüşü veya kod bloğu yoktur std::array, derleyici bu tür şeyleri optimize etmek için tasarlanmıştır. Ve tabii ki, kontrol edilen fonksiyonun eklenmesi mükemmel bir hata ayıklama aracı ve büyük bir avantajdır. @Lou Franco: Tüm C ++ kodları Standart kitaplığa bağlı olabilir - bunun için olduğu gibi. @Earlz: Kullanılabilir STL'niz yoksa, o zaman C ++ değildir ve bunun sonu budur.
Puppy

6
@Earlz: C ++ Standardı, Standart kitaplığı içerir. Kitaplığı kullanamıyorsanız, uyumlu değildir. İkincisi, std::arrayeşdeğer C dizisi kullanımından daha büyük olması için çok boktan bir derleyiciye sahip olmalısınız .
Puppy

5
@Earlz: "Tam uyumlu olmayan" ve "spesifikasyondaki yüzlerce sayfa olan eksik özellikler" arasında büyük bir fark var.
Puppy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.