C / C ++ 'da 0 boyutlu bir dizi tanımlarsam ne olur?


127

Merak ediyorum, int array[0];kodda sıfır uzunluklu bir dizi tanımlarsam aslında ne olur ? GCC hiç şikayet etmiyor.

Örnek Program

#include <stdio.h>

int main() {
    int arr[0];
    return 0;
}

açıklama

Aslında Darhazer'ın yorumlarındaki değişken uzunluk gibi işaret edilmek yerine sıfır uzunluklu dizilerin bu şekilde başlatılıp başlatılmadığını anlamaya çalışıyorum.

Bunun nedeni, bazı kodları vahşi doğada yayınlamam gerektiğidir, bu nedenle , statik olarak tanımlanmış bir kodla bazı kodlarda meydana gelen SIZE, olarak tanımlandığı durumlarla ilgilenmem gerekip gerekmediğini anlamaya çalışıyorum.0int array[SIZE];

Aslında GCC'nin şikayet etmemesine şaşırmıştım, bu da soruma yol açtı. Aldığım cevaplara göre, bir uyarının olmamasının büyük ölçüde yeni [] sözdizimi ile güncellenmemiş eski kodu desteklemesinden kaynaklandığına inanıyorum.

Esas olarak hatayı merak ettiğim için, Lundin'in cevabını doğru olarak etiketliyorum (Nawaz'ınki ilkti, ancak o kadar eksiksiz değildi) - diğerleri kuyruk dolgulu yapılar için gerçek kullanımına işaret ediyordu, ancak alakalı olsa da, değil '' t tam olarak aradığım şey.


51
@AlexanderCorwin: Maalesef C ++ 'da, tanımlanmamış davranışlar, standart olmayan uzantılar ve diğer anormalliklerle, kendi kendinize bir şeyler denemek genellikle bilgiye giden bir yol değildir.
Benjamin Lindley

5
@JustinKirk Ben de test ederek ve işe yaradığını görerek tuzağa düştüm. Ve gönderimde aldığım eleştirilerden dolayı, test etmenin ve çalıştığını görmenin geçerli ve yasal olduğu anlamına gelmediğini öğrendim. Bu nedenle bazen kendi kendine test geçerli değildir.
StormByte

2
@JustinKirk, nerede kullanacağına dair bir örnek için Matthieu'nun cevabına bakın . Ayrıca, dizi boyutunun bir şablon parametresi olduğu bir şablonda kullanışlı olabilir. Sorudaki örnek açıkça bağlam dışı.
Mark Ransom

2
@JustinKirk: []Python'da veya hatta ""C'de amaç nedir ? Bazen bir dizi gerektiren bir işlev veya makronuz vardır, ancak içine koyacak hiçbir veriniz yoktur.
dan04

15
"C / C ++" nedir? Bunlar iki ayrı dil
Yörüngede Hafiflik Yarışları

Yanıtlar:


86

Bir dizinin boyutu sıfır olamaz.

ISO 9899: 2011 6.7.6.2:

İfade sabit bir ifade ise, sıfırdan büyük bir değere sahip olmalıdır.

Yukarıdaki metin hem düz bir dizi için geçerlidir (1. paragraf). VLA (değişken uzunluk dizisi) için, ifadenin değeri sıfırdan küçük veya sıfıra eşitse davranış tanımsızdır (paragraf 5). Bu, C standardındaki normatif metindir. Bir derleyicinin onu farklı şekilde uygulamasına izin verilmez.

gcc -std=c99 -pedantic VLA olmayan durum için uyarı verir.


34
"aslında bir hata vermelidir" - "uyarılar" ve "hatalar" arasındaki ayrım standartta tanınmaz (yalnızca "tanılamadan" bahseder) ve derlemenin durması gereken tek durum [ör. gerçek dünya farkı uyarı ve hata arasında] bir #errordirektifle karşılaşmak üzeredir.
Random832

12
Bilginize, genel bir kural, (C veya C ++) standartlarına derleyiciler gerekenleri tek devlet olarak izin değil, ne onlar gerekir izin vermeyecek . Bazı durumlarda, derleyicinin bir "tanılama" yayınlaması gerektiğini ancak bu aldıkları kadar spesifik olduğunu belirteceklerdir. Gerisi derleyici satıcısına bırakılır. EDIT: Random832'nin de söylediği.
mcmcc

8
@Lundin "Bir derleyicinin sıfır uzunluklu diziler içeren bir ikili oluşturmasına izin verilmez." Standart kesinlikle bu türden hiçbir şey söylemiyor. Yalnızca boyutu için sıfır uzunluklu sabit ifadeye sahip bir dizi içeren kaynak kodu verildiğinde en az bir tanılama mesajı üretmesi gerektiğini söyler. Standardın, bir derleyicinin bir ikili dosya oluşturmasını yasakladığı tek durum, bir #errorönişlemci yönergesiyle karşılaşmasıdır .
Random832

5
@Lundin Tüm doğru durumlar için bir ikili dosya oluşturmak # 1'i tatmin eder ve yanlış durumlar için bir tane oluşturmak veya üretmemek onu etkilemez. # 3 için bir uyarı yazdırmak yeterlidir. Standart bu kaynak kodun davranışını tanımlamadığından, bu davranışın # 2 ile hiçbir ilgisi yoktur.
Random832

13
@Lundin: Önemli olan, ifadenizin yanlış olmasıdır; uyumlu derleyicilerin , bir tanılama yayınlandığı sürece, sıfır uzunluklu diziler içeren bir ikili oluşturmalarına izin verilir.
Keith Thompson

85

Standart gereği buna izin verilmez.

Bununla birlikte, bu bildirimleri esnek bir dizi üyesi ( FAM ) bildirimi olarak ele almak C derleyicilerinde mevcut uygulama olmuştur :

C99 6.7.2.1, §16 : Özel bir durum olarak, birden fazla adlandırılmış üyesi olan bir yapının son elemanı tamamlanmamış bir dizi türüne sahip olabilir; buna esnek dizi üyesi denir.

Bir FAM'in standart sözdizimi şöyledir:

struct Array {
  size_t size;
  int content[];
};

Fikir, daha sonra onu şu şekilde tahsis etmenizdir:

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

Ayrıca statik olarak da kullanabilirsiniz (gcc uzantısı):

Array a = { 3, { 1, 2, 3 } };

Bu aynı zamanda kuyruklu yapılar (bu terim C99 Standardının yayınlanmasından önce gelir) veya struct hack (bunu işaret ettiği için Joe Wreschnig'e teşekkürler ) olarak da bilinir .

Ancak bu sözdizimi yalnızca son zamanlarda C99'da standartlaştırıldı (ve etkiler garanti edildi). Önce sabit bir boyut gerekliydi.

  • 1 oldukça tuhaf olmasına rağmen, taşınabilir bir yoldu.
  • 0 amacı belirtmede daha iyiydi, ancak Standart söz konusu olduğunda yasal değildi ve bazı derleyiciler (gcc dahil) tarafından bir uzantı olarak desteklendi.

Bununla birlikte, kuyruk doldurma uygulaması, depolamanın mevcut olduğu gerçeğine dayanır (dikkatli malloc), bu nedenle genel olarak istif kullanımı için uygun değildir .


@Lundin: Burada herhangi bir VLA görmedim, tüm boyutlar derleme sırasında biliniyor. Esnek dizi terim geliyor gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html ve hak doe int content[];bildiğim kadarıyla anladığım kadarıyla burada. Sanatın C terimleri hakkında çok bilgili olmadığıma göre ... mantığımın doğru görünüp görünmediğini onaylayabilir misiniz?
Matthieu M.

@MatthieuM .: C99 6.7.2.1, §16: Özel bir durum olarak, birden fazla isimlendirilmiş üyeye sahip bir yapının son elemanı eksik bir dizi tipine sahip olabilir; buna esnek dizi üyesi denir.
Christoph

Bu deyim aynı zamanda "struct hack" adıyla da bilinir ve bu isme aşina olan "kuyruk yastıklı yapı" dan daha fazla insanla tanıştım (belki de gelecekteki ABI uyumluluğu için bir yapıyı doldurmaya genel bir referans olması dışında bunu daha önce hiç duymadım ) veya ilk olarak C99'da duyduğum "esnek dizi üyesi".

1
Yapısal hack için 1 dizi boyutunu kullanmak, derleyicilerin ciyaklamasını engelleyecekti, ancak yalnızca "taşınabilirdi" çünkü derleyici yazarları, fiili bir standart olarak bu tür bir kullanımı kabul edecek kadar iyiydi. Sıfır boyutlu diziler üzerindeki yasak, programcının tek öğeli dizileri sonuçta kötü bir ikame olarak kullanması ve derleyici yazarlarının, Standart tarafından talep edilmese bile programcıların ihtiyaçlarına hizmet etmeleri gerektiği konusundaki tarihsel tutumu olmasaydı, derleyici yazarları tek öğeli bir dizi olduğunda kolayca ve yararlı bir şekilde optimize foo[x]edilmiştir . foo[0]foo
supercat

1
@RobertSsupportsMonicaCellio: Yanıtta açıkça gösterildiği gibi, ancak sonunda . En başından daha net hale getirmek için açıklamayı da ön yükledim.
Matthieu M.

58

Standart C ve C ++, sıfır boyutlu dizi olduğu değil izin ..

GCC kullanıyorsanız, -pedanticseçeneği ile derleyin . Şunları söyleyerek uyarı verecektir :

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

C ++ olması durumunda benzer uyarı verir.


9
Visual C ++ 2010'da:error C2466: cannot allocate an array of constant size 0
Mark Ransom

4
-Werror basitçe tüm uyarıları hatalara dönüştürür, bu da GCC derleyicisinin yanlış davranışını düzeltmez.
Lundin

C ++ Builder 2009 da doğru bir şekilde bir hata veriyor:[BCC32 Error] test.c(3): E2021 Array must have at least one element
Lundin

1
Bunun yerine, şunu -pedantic -Werrorda yapabilirsiniz-pedantic-errors
Stephan Dollberg

3
Sıfır boyutlu bir dizi, sıfır boyutlu bir dizi ile tamamen aynı şey değildir std::array. (Bir kenara: VLA'ların dikkate alındığı ve C ++ 'da olmasının açıkça reddedildiği kaynağı bulamıyorum ama hatırlıyorum.)

27

Bu tamamen yasa dışıdır ve her zaman öyledir, ancak birçok derleyici hatayı işaret etmeyi ihmal eder. Bunu neden yapmak istediğinden emin değilim. Bildiğim tek kullanım, bir boolean'dan bir derleme zamanı hatasını tetiklemektir:

char someCondition[ condition ];

Eğer conditionbir yanlış, o zaman ben bir derleme zamanı hatası alıyorum. Derleyiciler buna izin verdiğinden, ancak şunu kullanmaya başladım:

char someCondition[ 2 * condition - 1 ];

Bu 1 veya -1 boyutunu verir ve -1 boyutunu kabul edecek bir derleyici bulamadım.


Bu, onu kullanmak için ilginç bir hack.
Alex Koay

10
Metaprogramlamada yaygın bir numara olduğunu düşünüyorum. Uygulamaları kullansa şaşırmam STATIC_ASSERT.
James Kanze

Neden sadece:#if condition \n #error whatever \n #endif
Jerfov2

1
@ Jerfov2, çünkü durum ön işleme zamanında bilinmeyebilir, yalnızca derleme zamanı
rmeador

9

Bu argümanda gcc'nin çevrimiçi dokümantasyonunun tam bir sayfası olduğunu ekleyeceğim .

Bazı alıntılar:

GNU C'de sıfır uzunluklu dizilere izin verilir.

ISO C90'da içeriklere 1 uzunluk vermeniz gerekir.

ve

3.0'dan önceki GCC sürümleri, sıfır uzunluklu dizilerin, esnek dizilermiş gibi statik olarak başlatılmasına izin verdi. Yararlı olan bu durumlara ek olarak, daha sonraki verileri bozacak durumlarda başlatmaya da izin verdi

yani yapabilirsin

int arr[0] = { 1 };

ve patlama :-)


Beğenebilir miyim int a[0]o zaman a[0] = 1 a[1] = 2?
Suraj Jain

2
@SurajJain Yığınınızın üzerine yazmak istiyorsanız :-) C dizini yazdığınız dizinin boyutuna göre kontrol etmez, bu nedenle yapabilirsiniz, a[100000] = 5ancak şanslıysanız uygulamanızı çökertirsiniz, eğer şanslıysanız: -)
xanatos

Int a [0]; değişken bir dizi anlamına gelir (sıfır boyutlu dizi), Şimdi Nasıl
Atayabilirim

@SurajJain "C dizini ve yazdığınız dizinin boyutunu kontrol etmiyor" un hangi kısmı net değil? C'de indeks denetimi yoktur, dizinin sonundan sonra yazabilir ve bilgisayarı çökertebilir veya belleğinizin değerli parçalarının üzerine yazabilirsiniz. Yani 0 elemanlı bir diziniz varsa, 0 elemanlarının sonundan sonra yazabilirsiniz.
xanatos

Buna bakın quora.com/…
Suraj Jain

9

Sıfır uzunluklu dizilerin başka bir kullanımı, değişken uzunluklu nesneler (C99 öncesi) yapmak içindir. Sıfır uzunlukta diziler olan farklı gelen esnek diziler [] 0 olmadan sahiptir.

Gcc doc'tan alıntı :

GNU C'de sıfır uzunluklu dizilere izin verilir. Değişken uzunluklu bir nesne için gerçekten başlık olan bir yapının son öğesi olarak çok kullanışlıdırlar:

 struct line {
   int length;
   char contents[0];
 };
 
 struct line *thisline = (struct line *)
   malloc (sizeof (struct line) + this_length);
 thisline->length = this_length;

ISO C99'da, sözdizimi ve anlambilim açısından biraz farklı olan esnek bir dizi üyesi kullanırsınız:

  • Esnek dizi üyeleri, 0 olmadan içerik olarak [] yazılır.
  • Esnek dizi üyeleri eksik türe sahip ve bu nedenle sizeof operatörü uygulanamayabilir.

Bir gerçek dünya örneği sıfır uzunlukta diziler ise struct kdbus_itemde kdbus.h (Linux kernel modülü).


2
IMHO, Standardın sıfır uzunluklu dizileri yasaklaması için iyi bir neden yoktu; sıfır boyutlu nesneler bir yapının üyeleri olarak gayet iyi olabilir ve onları void*aritmetik amaçları olarak kabul edebilir (bu nedenle sıfır boyutlu nesnelere işaretçi eklemek veya çıkarmak yasaktır). Esnek Dizi Üyeleri çoğunlukla sıfır boyutlu dizilerden daha iyi olsalar da, aşağıdakilere fazladan bir "sözdizimsel" yönlendirme eklemeden, şeyleri takma ad vermek için bir tür "birleşim" işlevi görebilirler (örneğin struct foo {unsigned char as_bytes[0]; int x,y; float z;}, üyelere erişebilir x. z...
supercat

... doğrudan örneğin myStruct.asFoo.x, vb. söylemek zorunda kalmadan . Ayrıca, IIRC, C bir yapı içine esnek bir dizi elemanını dahil etmek için herhangi bir çabada ciyaklıyor, böylece bilinen uzunlukta birden fazla diğer esnek dizi elemanını içeren bir yapıya sahip olmayı imkansız kılıyor. içeriği.
supercat

@supercat iyi bir neden, dış dizi sınırlarına erişme kuralının bütünlüğünü korumaktır. Bir yapının son üyesi olarak, C99 esnek dizi üyesi , GCC sıfır boyutlu diziyle tamamen aynı etkiye ulaşır, ancak diğer kurallara özel durumlar eklemeye gerek yoktur. IMHO sizeof x->contents, gcc'de 0 döndürmek yerine ISO C'de bir hata olan bir gelişmedir . Yapı üyesi olmayan sıfır boyutlu diziler bir sürü başka problemi beraberinde getirir.
MM

@MM: İki eşit işaretçiyi sıfır boyutlu bir nesneye çıkarmak sıfır vermek olarak tanımlansaydı (herhangi bir boyuttaki nesneye eşit işaretçileri çıkarmak gibi) ve eşit olmayan işaretçileri sıfır boyutlu nesnelere çıkarmak, verim olarak tanımlansaydı ne gibi sorunlara neden olurlardı? Belirtilmemiş Değer? Standart, bir uygulamanın, bir FAM içeren bir yapının başka bir yapının içine gömülmesine izin verebileceğini belirtmişse, ikinci yapıdaki sonraki elemanın, FAM ile aynı eleman tipine sahip bir dizi veya böyle bir diziyle başlayan bir yapı olması koşuluyla, , ve şartıyla ...
supercat

... FAM'ı diziyi takma ad olarak tanır (hizalama kuralları dizilerin farklı ofsetlere inmesine neden olacaksa, bir teşhis gerekli olacaktır), bu çok faydalı olurdu. Olduğu gibi, genel formattaki yapılara işaretçileri kabul eden struct {int n; THING dat[];}ve statik veya otomatik süreli şeylerle çalışabilen bir yönteme sahip olmanın iyi bir yolu yoktur .
supercat

6

Yapılar içindeki sıfır boyutlu dizi bildirimlerine izin verildiyse ve anlambilim böyle olsaydı (1) hizalamayı zorlayacaklar ancak başka türlü herhangi bir alan ayırmayacaklarsa ve (2) diziyi indekslemek, sonuçta ortaya çıkan göstericinin struct ile aynı bellek bloğu içinde olacağı durum. Bu tür bir davranışa hiçbir C standardı tarafından asla izin verilmedi, ancak bazı eski derleyiciler, derleyiciler için boş parantezlerle eksik dizi bildirimlerine izin vermesi için standart hale gelmeden önce buna izin verdi.

Genellikle boyut 1 dizisi kullanılarak uygulanan struct hack işlemi tehlikelidir ve derleyicilerin onu kırmaktan kaçınmaları için herhangi bir gereksinim olduğunu düşünmüyorum. Örneğin, ben bir derleyici görürse bu beklenir int a[1], bu konuda kendi hakları içinde olacaktır a[i]olarak a[0]. Birisi struct hack'in hizalama sorunları gibi bir şeyle çözmeye çalışırsa

typedef struct {
  uint32_t boyutu;
  uint8_t verileri [4]; // Dolgunun yapının boyutunu düşürmesini önlemek için dört kullanın
}

bir derleyici zeki olabilir ve dizi boyutunun gerçekten dört olduğunu varsayabilir:

; Yazıldığı gibi
  foo = myStruct-> veri [i];
; Yorumlandığı gibi (küçük endian donanımı varsayarsak)
  foo = ((* (uint32_t *) myStruct-> veri) >> (i << 3)) & 0xFF;

Böyle bir optimizasyon, özellikle myStruct->dataaynı işlemde bir kayıt listesine yüklenebiliyorsa makul olabilir myStruct->size. Standartta böyle bir optimizasyonu yasaklayacak hiçbir şey bilmiyorum, elbette dördüncü öğenin ötesindeki şeylere erişmeyi bekleyebilecek herhangi bir kodu kırabilir.


1
Esnek dizi elemanı yapı kesmek meşru bir versiyonu olarak C99 ilave edilmiş
AA

Standart, farklı dizi üyelerine erişimin çakışmadığını söyler, bu da optimizasyonu imkansız kılma eğilimindedir.
Ben Voigt

@BenVoigt: C dili standardı, bir bayt yazmanın ve aynı anda bir kelimeyi içeren kelimeyi okumanın etkisini belirtmez, ancak işlemcilerin% 99,9'u yazmanın başarılı olacağını ve kelimenin yeni veya eski sürümünü içereceğini belirtir. diğer baytların değiştirilmemiş içeriği ile birlikte bayt. Bir derleyici bu tür işlemcileri hedeflerse, çatışma ne olur?
süper araba

@supercat: C dili standardı, iki farklı dizi öğesine aynı anda yazmanın çakışmayacağını garanti eder. Yani (yazarken oku) argümanınız iyi çalışıyor, yeterli değil.
Ben Voigt

@BenVoigt: Eğer bir kod parçası örneğin 0, 1 ve 2 dizi elemanlarına bir sırayla yazacak olsaydı, dört elemanın tümünü uzun bir şekilde okumasına, üçünü değiştirmesine ve dördünü de geri yazmasına izin verilmezdi, ama ben dördünün tümünü uzun olarak okumaya, üçü değiştirmeye, alt 16 biti kısa olarak ve 16-23 bitlerini bayt olarak geri yazmaya izin verileceğini düşünüyorum. Buna katılmıyor musun? Ve sadece dizinin öğelerini okuması gereken kodun, onları basitçe uzun bir süre okumasına ve kullanmasına izin verilir.
supercat
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.