C dizilerinin neden 0 uzunluğu olamaz?


13

C11 standardı, hem boyutsal hem de değişken uzunluktaki dizilerin "sıfırdan büyük bir değere sahip olacağını" söylüyor. 0 uzunluğuna izin vermemenin gerekçesi nedir?

Özellikle değişken uzunluklu diziler için her seferinde sıfır büyüklüğüne sahip olmak mükemmel bir anlam ifade eder. Boyutları bir makro veya derleme yapılandırma seçeneğinden olduğunda statik diziler için de yararlıdır.

İlginç bir şekilde GCC (ve clang), sıfır uzunluklu dizilere izin veren uzantılar sağlar. Java ayrıca sıfır uzunluklu dizilere de izin verir.


7
stackoverflow.com/q/8625572 ... "Sıfır uzunluklu bir dizi, her nesnenin benzersiz bir adrese sahip olması şartıyla uzlaşmak zor ve kafa karıştırıcı olurdu."
Robert Harvey

3
@RobertHarvey: Verilen struct { int p[1],q[1]; } foo; int *pp = p+1;, ppmeşru bir işaretçi *ppolacaktır , ancak benzersiz bir adresi olmayacaktır. Neden aynı mantık sıfır uzunluklu bir diziyle tutulamıyor? Verilen ki int q[0]; bir yapı içinde , qgeçerliliği olduğu gibi olur bir adres bakın olur p+1yukarıda örnek.
supercat

@DocBrown 6.7.11.2.5 C11 standardından bir VLA'nın boyutunu belirlemek için kullanılan ifade hakkında konuşmak "her değerlendirildiğinde sıfırdan büyük bir değere sahip olacaktır." C99'u bilmiyorum (ve bunu değiştirecekleri garip görünüyor) ama sıfır uzunluğa sahip olamayacaksınız gibi görünüyor.
Kevin Cox

@KevinCox: C11 standardının (veya söz konusu parçanın) ücretsiz bir çevrimiçi sürümü var mı?
Doc Brown

Son sürüm ücretsiz (ne yazık) mevcut değil, ancak taslakları indirebilirsiniz. Son taslak open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf şeklindedir .
Kevin Cox

Yanıtlar:


11

Bahse girerim ki C dizileri, ayrılan bir bellek yığınının başlangıcına işaret ediyor. 0 boyuta sahip olmak, hiçbir şey göstermeyeceğiniz anlamına gelir. Hiçbir şeye sahip olamazsınız, bu yüzden seçilmiş bir şey olması gerekecekti. Kullanamazsınız null, çünkü o zaman 0 uzunluk dizileriniz boş işaretçiler gibi görünür. Ve bu noktada her farklı uygulama kaosa yol açan farklı keyfi davranışlar seçecek.



8
@delnan: Bu konuda bilgiçlikçi olmak istiyorsanız, dizi ve işaretçi aritmetiği, bir işaretleyicinin bir diziye erişmek veya bir diziyi simüle etmek için uygun şekilde kullanılabileceği şekilde tanımlanır. Başka bir deyişle, C'de eşdeğer olan işaretçi aritmetiği ve dizi indeksleme. Ama sonuç yine de aynı ... dizinin uzunluğu sıfırsa, hala hiçbir şeye işaret etmiyorsunuz.
Robert Harvey

3
@RobertHarvey Her şey doğru, ama kapanış kelimeleriniz (ve geriye dönük olarak tüm cevap) sadece böyle bir diziyi açıklamak için karışık ve kafa karıştırıcı bir yol gibi görünüyor ( Bence bu cevap "ayrı bir bellek yığını" olarak adlandırılır?) sizeof0 ve bunun nasıl sorun yaratacağı. Bütün bunlar, kısalık veya netlik kaybı olmadan uygun kavramlar ve terminoloji kullanılarak açıklanabilir. Dizileri ve işaretçileri karıştırmak yalnızca dizileri yayma riski taşır = işaretçi yanılgısı (diğer bağlamlarda daha önemlidir) yararsızdır.

2
" Null kullanamazsınız, çünkü o zaman 0 uzunluk dizileriniz null işaretçiler gibi görünür " - aslında Delphi'nin yaptığı da budur. Boş hanedanlar ve boş uzun diziler teknik olarak boş göstericilerdir.
JensG

3
-1, burada @ delnan ile doluyum. Bu, özellikle OP'nin sıfır uzunluklu diziler kavramını destekleyen bazı büyük derleyiciler hakkında yazdıkları bağlamda hiçbir şeyi açıklamaz. Eminim ki sıfır uzunluklu diziler C'de "kaosa yol açmaz" değil, uygulama-bağımsız bir şekilde de sağlanabilir.
Doc Brown

6

Bir dizinin genellikle bellekte nasıl düzenlendiğine bakalım:

         +----+
arr[0] : |    |
         +----+
arr[1] : |    |
         +----+
arr[2] : |    |
         +----+
          ...
         +----+
arr[n] : |    |
         +----+

arrİlk öğenin adresini depolayan ayrı bir nesne olmadığını unutmayın ; ifadede bir dizi göründüğünde, C ilk öğenin adresini gerektiği gibi hesaplar .

Yani, hadi düşün: 0 öğeli bir dizi olurdu hiçbir depolama bunun için bir kenara seti, dizi adresi hesaplamak için bir şey yok anlamına gelen (başka bir deyişle, tanımlayıcı için hiçbir nesne haritalama alınmamaktadır). Bu, " intHafızada yer almayan bir değişken oluşturmak istiyorum " demek gibidir. Bu saçma bir operasyon.

Düzenle

Java dizileri C ve C ++ dizilerinden tamamen farklı hayvanlardır; ilkel bir tür değil, türetilmiş bir referans türüdür Object.

Düzenle 2

Aşağıdaki yorumlarda ortaya çıkan bir nokta - "0'dan büyük" kısıtlaması, yalnızca boyutun sabit bir ifade ile belirtildiği diziler için geçerlidir ; bir VLA'nın 0 uzunluğuna sahip olmasına izin verilir. 0 değerli sabit olmayan bir ifadeye sahip bir VLA'nın bildirilmesi bir sınırlama ihlali değildir, ancak tanımlanmamış davranışları çağırır.

VLA'ların normal dizilerden farklı hayvanlar olduğu ve bunların uygulanmasının 0 boyuta izin verebileceği açıktır . Bu statictür nesnelerin boyutunun program başlamadan önce bilinmesi gerektiğinden, bildirilemez veya dosya kapsamında olamaz.

Ayrıca, C11'den itibaren, VLA'ları desteklemek için uygulamaların gerekli olmadığı hiçbir şeye değmez.


3
Üzgünüm, ama IMHO, Telastyn gibi bu noktayı kaçırıyorsun. Sıfır uzunluklu diziler çok mantıklı olabilir ve OP'nin bize anlattıkları gibi mevcut uygulamalar bunun yapılabileceğini gösterir.
Doc Brown

@DocBrown: İlk olarak, dil standardının neden büyük olasılıkla izin vermediğini ele alıyordum. İkincisi, 0 uzunluklu bir dizinin mantıklı olduğu bir örnek istiyorum, çünkü dürüstçe bir tane düşünemiyorum. Büyük olasılıkla uygulama tedavi etmektir T a[0]olarak T *a, ama sonra neden sadece kullanmayın T *a?
John Bode

Üzgünüm, ama standardın bunu neden yasakladığını "teorik akıl yürütme" almıyorum. Cevabımı adresin gerçekten nasıl hesaplanabileceğini okuyun. Ve ben Robert Harveys'deki sorunun ilk yorumunda soruyu takip etmenizi ve ikinci cevabı okumanızı tavsiye ederim, faydalı bir örnek var.
Doc Brown

@DocBrown: Ah. structKesmek. Hiç şahsen kullanmadım; Değişken boyutta bir structtüre ihtiyaç duyan bir problem üzerinde asla çalışmadı .
John Bode

2
C99'dan beri AFAIK'i unutmamak için C değişken uzunluklu dizilere izin verir. Dizi boyutu bir parametre olduğunda, 0 değerini özel bir durum olarak ele almak zorunda kalmamak birçok programı basitleştirebilir.
Doc Brown

2

Genellikle sıfır (aslında değişken) boyut dizinizin çalışma zamanında boyutunu bilmesini istersiniz. Daha sonra bunu bir pakete koyun structve aşağıdaki gibi esnek dizi üyeleri kullanın :

struct my_st {
   unsigned len;
   double flexarray[]; // of size len
};

Açıkçası esnek dizi üyesi kendi son structolmalı ve daha önce bir şey olması gerekir. Genellikle bu esnek dizi üyesinin gerçek çalışma zamanı tarafından kullanılan uzunluğu ile ilgili bir şey olurdu.

Tabii ki tahsis edersiniz:

 unsigned len = some_length_computation();
 struct my_st*p = malloc(sizeof(struct my_st)+len*sizeof(double));
 if (!p) { perror("malloc my_st"); exit(EXIT_FAILURE); };
 p->len = len;
 for (unsigned ix=0; ix<len; ix++)
    p->flexarray[ix] = log(3.0+(double)ix);

AFAIK, bu C99'da zaten mümkün ve çok faydalı.

BTW, esnek dizi üyeleri C ++ 'da mevcut değildir (çünkü ne zaman ve nasıl yapılandırılmaları ve yok edilmeleri gerektiğini tanımlamak zor olacaktır). Ancak geleceğe bakın std :: dynarray


Bilirsiniz, sadece önemsiz türlerle kısıtlanabilirlerdi ve hiçbir zorluk olmazdı.
Deduplicator

2

İfade bir type name[count]işlevde yazılırsa, C derleyicisine yığın çerçeve sizeof(type)*countbaytlarına tahsis etmesini ve dizideki ilk öğenin adresini hesaplamasını söylersiniz .

İfade type name[count]tüm işlev ve yapı tanımlarının dışında yazılırsa, C derleyicisine veri segmenti sizeof(type)*countbaytlarına tahsis etmesini ve dizideki ilk öğenin adresini hesaplamasını söylersiniz .

nameaslında dizideki ilk öğenin adresini saklayan sabit bir nesnedir ve bir hafızanın adresini saklayan her nesneye işaretçi denir, bu yüzden namedizi yerine işaretçi olarak davranmanızın nedeni budur . C'deki dizilere yalnızca işaretçilerle erişilebildiğini unutmayın.

Eğer countsıfır değerlendirir daha sonra yığın çerçevesi ya da veri bölümü ya da sıfır bayt tahsis ve dizideki ilk elemanın adresini geri C derleyici anlatmak bir sabit ifadesidir, ancak bu durumu ile ilgili sorun, ilk eleman uzunlukta bir dizi mevcut değildir ve var olmayan bir şeyin adresini hesaplayamazsınız.

Bu rasyoneldir, o element no. -length dizisinde count+1mevcut değil count, bu nedenle C derleyicisinin sıfır uzunluklu diziyi bir işlevin içinde ve dışında değişken olarak tanımlamasını yasaklamasının nedeni budur, çünkü nameo zaman içeriği nedir? nameTam olarak hangi adres saklanıyor?

Eğer pbir işaretçidir daha sonra ifade p[n]eşdeğerdir*(p + n)

Sağ ifadede * yıldızı vasıtaları tarafından işaret belleğe erişim ibrenin KQUEUE operasyonu olduğunda p + nAdresinde depolanan bellek veya erişim p + n, p + nişaretçi ifadesidir, bu adresini alır pve bu adrese sayı ekler nçarpın işaretçinin türünün boyutu p.

Adres ve numara eklemek mümkün mü?

Evet mümkündür, çünkü adres onaltılık gösterimde yaygın olarak temsil edilen işaretsiz tam sayıdır.


Birçok derleyici, Standard'dan önce sıfır boyutlu dizi bildirimlerine izin vermek için kullanılır ve birçoğu bu tür bildirimlere uzantı olarak izin vermeye devam eder. Tek boyutlu bir nesne olduğunu tanırsa Böyle beyanlar hiçbir soruna neden olur Ngelmiştir N+1ilk adresleri, ilişkili Nbenzersiz bayt ve son tanımlamak hangi Nhangi her noktada bu bayt sadece geçmiş biri. Böyle bir tanım N, 0 olduğu dejenere durumda bile iyi çalışır.
supercat

1

Bellek adresine bir işaretçi istiyorsanız, bir tane bildiriniz. Bir dizi aslında ayırdığınız bellek yığınını gösterir. Diziler işlevlere iletildiğinde işaretçilere bozunur, ancak işaret ettikleri bellek yığın üzerindeyse sorun olmaz. Sıfır boyutlu bir dizi bildirmek için bir neden yoktur.


2
Genellikle bunu doğrudan yapmazsınız, ancak bir makronun sonucu olarak veya dinamik veri içeren değişken uzunluklu bir dizi bildirirken.
Kevin Cox

Bir dizi asla işaret etmez. İşaretçiler içerebilir ve çoğu bağlamda aslında ilk öğeye bir işaretçi kullanırsınız, ancak bu farklı bir hikaye.
Tekilleştirici

1
Dizi adı, dizide bulunan belleğin sabit bir göstergesidir.
ncmathsadist

1
Hayır, dizi adı , çoğu bağlamda, ilk öğenin işaretçisine bozulur . Fark genellikle çok önemlidir.
Deduplicator

1

Orijinal C89 günlerinden itibaren, bir C Standardı bir şeyin Tanımsız Davranış olduğunu belirttiğinde, bunun anlamı "Belirli bir hedef platformda bir uygulamayı amaçlanan amaç için en uygun hale getirecek her şeyi yapın" idi. Standardın yazarları, herhangi bir belirli amaç için hangi davranışların en uygun olabileceğini tahmin etmek istememişlerdir. VLA uzantılarına sahip mevcut C89 uygulamaları, sıfır boyutu verildiğinde farklı, ancak mantıklı davranışlara sahip olabilir (örneğin, bazıları diziye NULL veren bir adres ifadesi olarak davranmışken, diğerleri adrese eşit olabilecek bir adres ifadesi olarak davranmış olabilir başka bir rasgele değişken olabilir, ancak güvenli bir şekilde sıfır olmadan ona sıfır eklenebilir). Herhangi bir kod böyle farklı davranışlara güvenmiş olsaydı, Standardın yazarları

Standardın yazarları, hangi uygulamaların ne yapabileceğini tahmin etmeye çalışmaktan veya herhangi bir davranışın diğerinden daha üstün görülmesi gerektiğini öne sürmek yerine, uygulayıcıların bu davayı uygun gördükleri en iyi şekilde ele almak için karar vermelerine izin verdiler. Sahne arkasında malloc () kullanan uygulamalar, dizinin adresini NULL (sıfır büyüklüğünde malloc boşsa), yığın-adres hesaplamaları kullanan uygulamalar ise diğer bazı değişkenlerin adresiyle eşleşen bir işaretçi verebilir ve diğer bazı uygulamalar da yapabilir diğer şeyler. Derleyici yazarlarının sıfır boyutlu köşe kasasının kasıtlı olarak işe yaramaz bir şekilde davranmasını sağlamak için kendi yollarından çıkacaklarını sanmıyorum.

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.