C dizileri neden uzunluklarını izlemiyor?


77

Bir dizinin uzunluğunu bir dizinin içindeyken açıkça depolamamanın arkasındaki neden Cneydi?

Gördüğüm gibi, bunu yapmanın çok zor nedenleri var ama standardı desteklemiyor (C89). Örneğin:

  1. Tamponda uzunluğun mevcut olması, tamponun taşmasını önleyebilir.
  2. Bir Java stili arr.lengthhem açıktır hem de programcının intbirkaç dizi ile ilgileniyorsa yığın üzerinde birçok s bulundurma zorunluluğunu önler
  3. Fonksiyon parametreleri daha uyumlu hale gelir.

Ancak, belki de en motive edici sebep, bence, genellikle, uzunluğunu muhafaza etmeden yer kazanmamasıdır. Dizilerin çoğu kullanımının dinamik tahsisat içerdiğini söylemeye teşebbüs ediyorum. Doğru, insanların yığında tahsis edilen bir diziyi kullandığı bazı durumlar olabilir, ancak bu yalnızca bir işlev çağrısı * olabilir - yığın 4 veya 8 bayt fazlalık işleyebilir.

Yığın yöneticisi, dinamik olarak tahsis edilen dizinin kullandığı boş blok büyüklüğünü yine de izlemek zorunda olduğundan, neden bu bilgiyi kullanılabilir kılmayalım (ve derleme zamanında kontrol edilen ek kuralı ekleyelim, bir tane uzunluğa kadar uzunluğu değiştiremez) kendini ayağından vurmak gibi).

Diğer tarafta düşünebildiğim tek şey, hiçbir uzunluk izlemenin derleyicileri daha basit hale getirmemiş olabileceği , ancak bu kadar basit hale getiremeyeceğidir .

* Teknik olarak, otomatik depolama dizisine sahip bir dizi özyinelemeli işlev yazılabilir ve bu (çok ayrıntılı) durumda, uzunluğu depolamak gerçekten de daha fazla alan kullanımıyla sonuçlanabilir.


6
Sanırım, C yapılara parametre ve dönüş değeri türleri olarak dahil edildiğinde C'nin "vektörler" (ya da herhangi bir isim) için sözdizimsel şeker içermesi gerektiği, altında uzunluk ve ya diziliş dizisi ya da işaretçi olacağı söylenebilirdi. . Bu ortak yapı için dil düzeyinde destek (aynı zamanda tek bir yapı değil ayrı argümanlar olarak iletildiğinde) sayısız hata ve basitleştirilmiş standart kütüphaneyi de kurtarmış olacaktır.
hyde

3
Ayrıca Pascal'ın Neden Favori Programlama Dili Değil Olduğunu Bölüm 2.1'in kavrayıcı olduğunu da öğrenebilirsiniz.

34
Diğer tüm cevapların bazı ilginç noktaları olsa da, alt satırında C'nin C dilinde yazılmış olması, böylece meclis dili programcılarının kodları daha kolay yazabilmesi ve taşınabilir olması gerektiğini düşünüyorum. Bunu göz önünde bulundurarak, bir dizi uzunluğunu otomatik olarak bir dizi İLE saklamak, bir sıkıntıya neden olmazdı (diğer hoş şekerleme arzuları gibi). Bu özellikler bugünlerde güzel görünüyor, ancak o zamanlar çoğu zaman program veya verilerden birinin bir baytını sisteminize sıkıştırmak gerçekten zordu. Hafızanın boşa harcanması, C'nin benimsenmesini ciddi ölçüde kısıtlardı.
Dunk

6
Cevabınızın asıl kısmı, benim istediğim gibi çoktan cevaplandı, ancak farklı bir nokta ortaya koyabiliyorum: "Neden bir malloc()alanın boyutu taşınabilir bir şekilde istenemiyor?" Bu beni birkaç kez merak ettiren bir şey.
glglgl

5
Yeniden açmak için oy kullanma. Bir yerde bazı sebepler var, basitçe "K&R düşünmedi" olsa bile.
Telastyn

Yanıtlar:


106

Dizi uzunluğu statik bir özellik olduğundan C dizileri uzunluklarını izler:

int xs[42];  /* a 42-element array */

Bu uzunluğu genellikle sorgulayamazsınız, ancak yine de statik olması gerekmemek zorunda değilsiniz - sadece XS_LENGTHuzunluk için bir makro belirtin ve bitirdiniz.

Daha önemli olan konu, C dizilerinin, örneğin bir işleve geçildiğinde, işaretçilere dolaylı olarak indirgenmesidir. Bu mantıklı geliyor ve bazı düşük seviyeli numaralara izin veriyor, ancak dizinin uzunluğu hakkındaki bilgileri kaybediyor. Bu nedenle, daha iyi bir soru, C'nin işaretçilere bu dolaylı indirgeme ile neden tasarlandığıdır.

Diğer bir husus, işaretçilerin, bellek adresinin kendisinden başka bir depolamaya ihtiyaç duymamasıdır. C işaretçilere tamsayılar, diğer işaretçilere işaretçiler koymamıza ve işaretçileri dizilermiş gibi işlememize olanak tanır. Bunu yaparken, C dizi uzunluğunu varoluş için üretecek kadar delice değil, ama Örümcek Adam sloganına güveniyor gibi görünüyor: büyük güçle programcı uzunlukları ve taşmaları takip etme sorumluluğunu üstlenecek.


13
Yanılmıyorsam, C derleyicilerinin statik dizi uzunluklarını takip ettiğini söylemek istediğinizi düşünüyorum . Ancak bu sadece bir işaretçi alan fonksiyonlar için iyi değildir.
VF1

25
@ VF1 evet. Ancak önemli olan, dizilerin ve işaretçilerin C'deki farklı şeyler olmasıdır . Derleyici uzantıları kullanmadığınızı varsayarsak, genellikle bir dizinin kendisini bir işleve geçiremezsiniz, ancak bir işaretçiyi iletebilir ve bir işaretçiyi bir dizimiş gibi indeksleyebilirsiniz. İşaretçilerin uzunluğunun bağlı olmadığından etkili bir şekilde şikayet ediyorsunuz. Dizilerin, işlev argümanları olarak iletilemediğinden veya dizilerin işaretçilere dolaylı olarak indirgendiğinden şikayetçi olmalısınız.
amon

37
"Genellikle bu uzunluğu sorgulayamazsın" - aslında yapabilirsin, bu operatörün büyüklüğü - sizeof (xs), int'nin dört bayt uzunluğunda olduğunu varsayarak 168 döndürürdü. 42'yi almak için: sizeof (xs) / sizeof (int)
tcrosley

15
@tcrosley Bu sadece dizi bildirimi kapsamında çalışır - xs'i başka bir işleve param olarak geçirmeyi deneyin, daha sonra sizeeof (xs) 'in size ne verdiğini görün ...
Gwyn Evans

26
@GwynEvans yine: işaretçiler diziler değildir. Bu nedenle, “bir diziyi başka bir işleve param olarak geçirirseniz”, bir diziyi değil bir işaretçiyi geçemezsiniz. İddia eden sizeof(xs)nerede xsbir dizi başka kapsamında farklı bir şey olurdu C tasarım diziler onların kapsamını bırakmak izin vermediğinden, pervasızca yanlıştır. Eğer sizeof(xs)nerede xsbir dizi farklı sizeof(xs)yerlerde xsbir işaretçi size, çünkü o hiç şaşırtıcı portakal ile elma karşılaştırarak .
amon,

38

Bunların çoğu, o sırada mevcut bilgisayarlarla ilgiliydi. Derlenmiş programın sınırlı bir kaynak bilgisayarında çalışması gerekmekle kalmadı, belki de daha önemlisi, derleyicinin bu makinelerde çalışması gerekiyordu. Thompson C geliştirdiği sırada, 8k RAM ile bir PDP-7 kullanıyordu. Gerçek makine kodunda ani bir analogu bulunmayan karmaşık dil özellikleri dile dahil edilmedi.

C'nin geçmişini dikkatlice okuduğunuzda , yukarıdakileri daha iyi anlarsınız, ancak bu tamamen onların sahip olduğu makine kısıtlamalarının sonucu değildi:

Dahası, dil (C) önemli kavramları tanımlamak için önemli bir güç göstermektedir; örneğin, uzunluğu çalışma zamanında değişen vektörler, yalnızca birkaç temel kural ve kuralla. ... C'nin yaklaşımını neredeyse iki eşzamanlı dilin Algol 68 ve Pascal [Jensen 74] ile karşılaştırması ilginçtir. Algol 68'deki diziler ya sabit sınırlara sahiptir ya da “esnektir:” esnek dizileri barındırmak için hem dil tanımında hem de derleyicilerde kayda değer bir mekanizmaya ihtiyaç duyulur (ve tüm derleyiciler bunları tam olarak uygulamaz). diziler ve dizgiler ve bu sınırlayıcı oldu [Kernighan 81].

C dizileri doğal olarak daha güçlüdür. Onlara sınır eklemek, programcının ne için kullanabileceğini sınırlar. Bu kısıtlamalar, programcılar için faydalı olabilir, ancak zorunlu olarak da sınırlayıcı olabilir.


4
Bu hemen hemen orijinal soruya dikkat çekti. Bu ve C işletim sisteminin yazılması için cazip hale getirilmesinin bir parçası olarak programcının ne yaptığını kontrol etmeye geldiğinde kasıtlı olarak "hafifçe dokunma" tutulmasıydı.
ClickRick

5
Harika bağlantı, ayrıca sınırlayıcıyı kullanmak için dizelerin uzunluğunu depolayarak açıkça değiştirdiler to avoid the limitation on the length of a string caused by holding the count in an 8- or 9-bit slot, and partly because maintaining the count seemed, in our experience, less convenient than using a terminator- bunun için çok fazla :-)
Voo

5
Sonlandırılmamış diziler ayrıca C'nin çıplak metal yaklaşımına da uyar. K&R C kitabının , bir dil öğretici, referans ve standart çağrıların bir listesi ile 300 sayfadan daha az olduğunu unutmayın. O'Reilly Regex kitabım K&R C'nin neredeyse iki katı.
Michael Shopsin

22

C'nin yaratıldığı güne geri dönersek, ne kadar kısa bir israf olursa olsun, her dize için fazladan 4 baytlık alan var!

Başka bir sorun var - C'nin nesne yönelimli olmadığını unutmayın, bu nedenle tüm dizelerin uzunluğunu öneklendirirseniz, bunun bir derleyici gerçek türü olarak tanımlanması gerekir char*. Özel bir tür olsaydı, bir dizeyi sabit bir dizeyle karşılaştıramazdınız, yani:

String x = "hello";
if (strcmp(x, "hello") == 0) 
  exit;

bu statik dizgiyi bir String'e dönüştürmek için özel derleyici ayrıntılarına sahip olmalı veya uzunluk önekini dikkate almak için farklı string işlevlerine sahip olmalıydı.

Sonuçta sanırım, sadece Pascal demenin aksine uzunluk-öneki yöntemini seçmediler.


10
Sınır kontrolü de zaman alıyor. Bugünün açısından önemsiz, ancak insanların bir şeyleri yaklaşık 4 byte değer verdikleri zaman dikkate aldılar.
Steven Burnap

18
@StevenBurnap: 200 MB'lık bir görüntünün her pikselinden geçen bir iç döngü içindeyseniz, bugün bile önemsiz değildir. Genel olarak, C yazıyorsanız , hızlı gitmek istersiniz ve döngünüzün forsınırlara saygı duyması için ayarlanmış her yinelemede işe yaramaz bir sınır kontrolünde zaman harcamak istemezsiniz .
Matteo Italia

4
@ VF1 "geri gün içinde" iki byte olabilirdi (DEC PDP / 11 kimse?)
ClickRick

7
Bu sadece "gün geri" değil. C'nin işletim sistemi çekirdeği, aygıt sürücüsü, gömülü gerçek zamanlı yazılım vb. Gibi "taşınabilir bir montaj dili" olarak hedeflendiği yazılım için. Sınır kontrolünde yarım düzine talimatı boşa harcamak önemlidir ve birçok durumda “sınırların dışında” olmanız gerekir (başka bir program deposuna rasgele erişemezseniz nasıl bir hata ayıklayıcı yazabilirsiniz?).
James Anderson,

3
Bu aslında BCPL'nin uzun sayılan argümanlara sahip olduğunu düşünen oldukça zayıf bir argümandır. Her ne kadar Pascal sadece 1 kelime ile sınırlı olsa da, genellikle sadece 8 veya 9 bit, bu da biraz kısıtlayıcıydı (aynı zamanda dizgelerin parçalarını paylaşma olanağını da engelliyordu, ancak bu optimizasyon muhtemelen zaman için çok daha ileriydi). Ve bir dizgiyi uzunluğa sahip bir yapı olarak ilan etmek, diziyi takip etmek için gerçekten özel bir derleyici desteğine ihtiyaç duymaz ..
Voo

11

C'de, bir dizinin bitişik herhangi bir alt kümesi de bir dizidir ve bu şekilde çalıştırılabilir. Bu hem okuma hem de yazma işlemleri için geçerlidir. Boyut açıkça depolanırsa bu özellik geçerli olmaz.


6
"Tasarım farklı olurdu" tasarımın farklı olmasının bir nedeni değil.
VF1

7
@ VF1: Standard Pascal'da hiç programlandınız mı? Dizilerle makul esnek olması C yeteneği montaj çok büyük bir gelişme (hiçbir güvenlik) ve (tam dizi sınırları dahil overkill typesafety) türgüvenli dillerinin ilk nesil oldu
MSalters

5
Bir diziyi dilimleme yeteneği gerçekten de C89 tasarımı için büyük bir argümandır.

Eski okul Fortran hacker'ları da bu özelliği iyi kullanmaktadırlar (dilimin Fortran'daki bir diziye geçmesi gerekir). Programlamak veya hata ayıklamak için kafa karıştırıcı ve acı verici, ancak çalışırken hızlı ve zarif.
dmckee

3
Dilimlemeye izin veren ilginç bir tasarım alternatifi var: Uzunluğu dizilerin yanında saklamayın. Dizideki herhangi bir işaretçi için, uzunluğu işaretçiyle birlikte saklayın. (Yalnızca gerçek bir C dizisine sahip olduğunuzda, boyut derleme zaman sabitidir ve derleyicinin kullanımına açıktır.) Daha fazla yer kaplar, ancak uzunluğu korurken dilimlemeye izin verir. &[T]Örneğin Rust bunu yapar .

8

Dizileri uzunlukları ile etiketlemenin en büyük sorunu, bu uzunluğu depolamak için gereken alan ve ne şekilde depolanması gerektiği sorusudur (kısa diziler için bir ekstra bayt kullanmak genellikle sakıncalı olmaz, dördü kullanmak olmaz) uzun diziler için ekstra bayt, ancak kısa diziler için bile dört bayt kullanmak olabilir). Daha büyük bir problem ise aşağıdaki gibi bir kod verilmesidir:

void ClearTwoElements(int *ptr)
{
  ptr[-2] = 0;
  ptr[2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo+2);
  ClearTwoElements(foo+7);
  ClearTwoElements(foo+1);
  ClearTwoElements(foo+8);
}

bu kodun ilk çağrıyı ClearTwoElementskabul edip ikincisini reddetmesinin tek yolu, hangi ClearTwoElementsdurumda olduğunu foobilmekle birlikte, her durumda dizinin bir bölümüne referans aldığını bilmek için yeterli bilgiyi elde etmek için yöntem olacaktır . Bu genellikle işaretçi parametrelerinin geçme maliyetini iki katına çıkarır. Ayrıca, her bir dizi bir işaretçi ile en sondan önce bir adrese öncülük etmişse (doğrulama için en verimli format), bunun için optimize edilmiş kod ClearTwoElementsşöyle bir şey olabilir:

void ClearTwoElements(int *ptr)
{
  int* array_end = ARRAY_END(ptr);
  if ((array_end - ARRAY_BASE(ptr)) < 10 ||
      (ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||          
      (array_end - 4) < ADDRESS(ptr)))
    trap();
  *(ADDRESS(ptr) - 4) = 0;
  *(ADDRESS(ptr) + 4) = 0;
}

Genel olarak, bir yöntem arayan kişinin, dizinin başlangıcına veya son öğenin bir yönteme meşru bir şekilde imleci geçebileceğini unutmayın; ancak yöntem, içeri aktarılan dizinin dışına çıkan öğelere erişmeye çalışırsa, bu işaretçiler herhangi bir soruna neden olur. Sonuç olarak, çağrılan bir yöntemin önce dizinin, bağımsız değişkenlerini doğrulamak için işaretçi aritmetiğinin kendisinin sınırları dışına çıkmayacağına ve ardından değişkenleri doğrulamak için bazı işaretçi hesaplamaları yapmasına yetecek kadar büyük olmasını sağlamak zorunda kalacaktı. Böyle bir onaylamada harcanan zaman muhtemelen gerçek bir iş yapmak için harcanan maliyeti aşacaktır. Dahası, yöntem yazılmış ve çağrılmışsa büyük olasılıkla daha verimli olabilir:

void ClearTwoElements(int arr[], int index)
{
  arr[index-2] = 0;
  arr[index+2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo,2);
  ClearTwoElements(foo,7);
  ClearTwoElements(foo,1);
  ClearTwoElements(foo,8);
}

Bir nesneyi tanımlamak için bir şeyle bir parçasını tanımlamak için bir şey birleştiren bir tür kavramı iyi bir tanesidir. Bununla birlikte, bir C tarzı işaretçi daha hızlıdır, ancak doğrulama yapmak gerekli değilse.


Diziler çalışma zamanının büyüklüğüne sahipse, işaretçiyi diziye gösterici, dizeden bir öğeye temel olarak farklı olacaktır. İkincisi, hiçbir zaman doğrudan eskiye dönüştürülemeyebilir (yeni dizi oluşturmadan). []işaretçiler için sözdizimi hala mevcut olabilir, ancak bu varsayımsal "gerçek" dizilerden farklı olabilir ve açıkladığınız sorun muhtemelen mevcut olmaz.
hyde

@hyde: Soru, nesne tabanı adresi bilinmeyen işaretçilerde aritmetife izin verilip verilmeyeceğidir. Ayrıca başka bir zorluk daha unuttum: yapılardaki diziler. Bunu düşünerek, her işaretçinin yalnızca işaretçinin adresini değil, aynı zamanda üst ve alt kanunu içermesini gerektirmeden, bir yapı içinde depolanan bir diziyi işaret edebilecek herhangi bir işaretçi türünün olacağından emin değilim. erişebileceği aralıklar.
supercat

Interseting noktası Bence bu hala Amon'un cevabını azaltıyor.
VF1

Soru diziler hakkında soru sorar. İşaretçi, hafıza adresidir ve niyeti anladığı sürece, sorunun öncülüyle değişmez. Diziler uzayacak, işaretçiler değişmeyecekti (dizgiden işaretçiye dizicinin yeni, farklı, benzersiz bir tür olması gerekir, yani işaretçinin yapılacağı gibi).
hyde

@hyde: Eğer dilin semantiğini yeterince değiştirmişse, dizilerin birleşik bir uzunluk içermesi mümkün olabilir, ancak yapılar içinde saklanan diziler bazı zorluklar doğurabilir. Oldukları gibi semantiklerle, dizi sınırları denetimi yalnızca işaretçilere dizi öğelerine uygulanan aynı denetimin kullanılması durumunda yararlı olacaktır.
supercat

7

C ve diğer birçok 3. nesil dil ile farkında olduğum tüm yeni diller arasındaki temel farklardan biri, C'nin programcı için hayatı kolaylaştıracak veya daha güvenli hale getirecek şekilde tasarlanmamış olmasıdır. Programcının ne yaptığını bildiği ve tam olarak ve sadece bunu yapmak istediği beklentisi ile tasarlandı. “Perdenin arkasında” hiçbir şey yapmaz, bu yüzden herhangi bir sürprizle karşılaşmazsınız. Derleyici düzeyinde en iyi duruma getirme bile isteğe bağlıdır (bir Microsoft derleyici kullanmadığınız sürece).

Bir programcının kodlarını kontrol ederek sınırları yazmak istiyorsa, C bunu yapmayı basitleştirir, ancak programcının alan, karmaşıklık ve performans açısından ilgili fiyatı ödemesi gerekir. Uzun yıllardır öfkeyle kullanmamış olmama rağmen, programlama öğretirken kısıt temelli karar verme kavramını aşmak için kullanıyorum. Temel olarak, bu istediğiniz bir şeyi yapmayı seçebileceğiniz anlamına gelir, ancak yaptığınız her kararın farkında olmanız gereken bir bedeli vardır. Başkalarına programlarının ne yapmasını istediğinizi söylemeye başladığınızda bu daha da önemli hale gelir.


3
C, geliştiği kadar “tasarlanmamış” değildi. Başlangıçta, böyle bir beyanı int f[5];oluşturmak olmaz fbeş maddelik dizi olarak; bunun yerine, eşdeğerdi int CANT_ACCESS_BY_NAME[5]; int *f = CANT_ACCESS_BY_NAME;. Eski beyan derleyici dizi zamanlarını gerçekten "anlamak" zorunda kalmadan işlenebilirdi; sadece alan tahsis etmek için bir assembler yönergesi çıkarması gerekiyordu ve sonra fbir dizi ile ilgisi olduğunu unutabilirdi . Dizi türlerinin tutarsız davranışları bundan kaynaklanmaktadır.
supercat

1
Hiçbir programcının, C'nin gerektirdiği ölçüde ne yaptığını bilmediği ortaya çıkıyor.
CodesInChaos

7

Kısa cevap:

C düşük seviyeli bir programlama dili olduğundan, bu sorunlara kendiniz bakmanızı bekler, ancak bu tam olarak nasıl uyguladığınıza daha fazla esneklik katar .

C bir uzunluk ile başlatılan bir dizi derleme zamanı kavramına sahiptir, ancak çalışma zamanında her şey basit bir şekilde verinin başlangıcına tek bir işaretçi olarak depolanır. Dizi uzunluğunu, dizi ile birlikte bir işleve geçirmek istiyorsanız, kendiniz yapın:

retval = my_func(my_array, my_array_length);

Veya işaretçi ve uzunlukta bir yapı veya başka bir çözüm kullanabilirsiniz.

Daha yüksek seviyeli bir dil, bunu dizi türünün bir parçası olarak sizin için yapar. C'de bunu kendin yapma sorumluluğu, aynı zamanda nasıl yapılacağını seçme esnekliği de verildi. Ve yazdığınız tüm kodlar zaten dizinin uzunluğunu biliyorsa, uzunluğu bir değişken olarak dolaştırmanız gerekmez.

Açık dezavantajı, işaretçiler olarak geçirilen dizileri denetleyen herhangi bir içsel sınır olmadan bazı tehlikeli kodlar yaratabileceğinizdir ancak bu, düşük seviye / sistem dillerinin ve verdikleri takasın niteliğidir.


1
+1 "Ve yazdığınız tüm kodlar zaten dizinin uzunluğunu biliyorsa, etrafındaki uzunluğu bir değişken olarak geçirmeniz gerekmez."
皞 皞 10:15

Sadece işaretçi + uzunluk yapısı dilde ve standart kütüphanede yapılmış olsaydı. Çok fazla güvenlik açıklarından kaçınılabilirdi.
CodesInChaos

O zaman gerçekten C olmazdı. Bunu yapan başka diller de var. C seni düşük seviyeye indirir.
thomasrutter

C düşük seviyeli bir programlama dili olarak icat edildi ve birçok lehçe hala düşük seviyeli programlamayı destekliyor, ancak birçok derleyici yazarı gerçekten düşük seviyeli dil olarak adlandırılamayan lehçeleri destekliyor. Onlar düşük seviyeli sözdizime izin veriyor ve hatta gerektiriyor, ancak daha sonra davranışı sözdizimi tarafından ima edilen anlambilimiyle eşleşmeyen üst düzey yapıları ortaya çıkarmaya çalışın.
supercat

5

Ekstra depolama sorunu bir sorun, ama bence küçük bir sorun. Sonuçta, çoğu zaman yine de uzunluğu izlemeniz gerekecek, ancak amon çoğu zaman statik olarak izlenebileceği konusunda iyi bir noktaya değindi.

Daha büyük bir sorun, uzunluğu nerede depolayacağınız ve ne kadar süre alacağınızdır. Her durumda işe yarayan tek bir yer yoktur. Verileri hemen önce sadece uzunluğu bellekte saklayabilirsiniz. Dizi hafızaya değil, UART tamponuna benzer bir şeyse?

Uzatmanın bırakılması, programcının uygun durum için kendi soyutlamalarını yaratmasına izin verir ve genel amaç için hazır birçok hazır kitaplık vardır. Bu soyutlama olmama neden gerçek soru kullanılan güvenlik duyarlı uygulamalarda?


1
You might say just store the length in the memory just before the data. What if the array isn't pointing to memory, but something like a UART buffer?Lütfen bunu biraz daha açıklar mısınız? Ayrıca çok sık olabilecek bir şey mi yoksa sadece nadir bir durum mu?
Mehdi

Eğer tasarlamış olsaydım, yazılan bir işlev argümanı T[]eşdeğer olmazdı, T*ancak işleve bir gösterici ve büyüklükten ibaretti. Sabit boyutlu diziler, C'de olduğu gibi işaretçilere çürütmek yerine, böyle bir dizi dilimine çürümeye neden olabilir. Bu yaklaşımın en büyük avantajı, kendi başına güvenli olması değildir, ancak standart kütüphane de dahil olmak üzere her şeyin yapabileceği bir kuraldır. inşa etmek.
CodesInChaos

1

Gönderen C Dil Gelişimi :

Görünüşe göre, yapılar makinedeki belleğe sezgisel bir şekilde eşleşmelidir, ancak bir dizi içeren bir yapıda, dizinin tabanını içeren işaretçiyi saklamak için iyi bir yer yoktu, ya da onu düzenlemek için uygun bir yol yoktu. başlatıldı. Örneğin, erken Unix sistemlerinin dizin girişleri C olarak tanımlanabilir.
struct {
    int inumber;
    char    name[14];
};
Yapının yalnızca soyut bir nesneyi karakterize etmekle kalmayıp, aynı zamanda bir dizinden okunabilecek bit koleksiyonunu tanımlamasını da istedim. Derleyici nameanlambilimin talep ettiği göstergeyi nereye gizleyebilir ? Yapılar daha soyut düşünülmüş olsa ve işaretçiler için yer bir şekilde gizlenebilse bile, karmaşık bir nesne tahsis ederken bu işaretleyicileri düzgün bir şekilde başlatmadaki teknik problemi nasıl çözebilirim?

Çözüm, tipik BCPL ve C tipi arasında evrimsel zincirde çok önemli bir sıçrama oluşturdu. İşaretçinin depoda gerçekleşmesini elimine etti ve bunun yerine, dizinin adı bir ifadede belirtildiğinde işaretleyicinin oluşturulmasına neden oldu. Bugünün C'sinde geçerli olan kural, dizi türündeki değerlerin, ifadelerde göründüğünde, diziyi oluşturan nesnelerin ilkine işaretçilere dönüştürülmesidir.

Bu pasaj, dizi ifadelerinin çoğu durumda işaretçilere neden çürümesine neden olur, ancak aynı sebep, dizi uzunluğunun neden dizinin kendisinde saklanmadığına da uygulanır; tür tanımı ile bellekteki gösterimi arasında bire bir eşleme yapmak istiyorsanız (Ritchie'nin yaptığı gibi), o zaman bu meta verileri depolamak için iyi bir yer yoktur.

Ayrıca, çok boyutlu dizileri düşünün; Her boyut için uzunluk meta verilerini nerede saklarsınız ki öyle bir şekilde diziyi geçmeye devam edersiniz

T *p = &a[0][0];

for ( size_t i = 0; i < rows; i++ )
  for ( size_t j = 0; j < cols; j++ )
    do_something_with( *p++ );

-2

Soru, C'de diziler olduğunu varsayar. Yok. Dizi olarak adlandırılan şeyler, sürekli veri dizileri ve işaretçi aritmetik işlemleri için sadece sözdizimsel bir şekerdir.

Aşağıdaki kod, bazı karakterlerin src'den dst'ye, aslında karakter dizgisi olduğunu bilmeden int-boyutlu parçalarda kopyalar.

char src[] = "Hello, world";
char dst[1024];
int *my_array = src; /* What? Compiler warning, but the code is valid. */
int *other_array = dst;
int i;
for (i = 0; i <= sizeof(src)/sizeof(int); i++)
    other_array[i] = my_array[i]; /* Oh well, we've copied some extra bytes */
printf("%s\n", dst);

C neden bu kadar basitleştirilmiş, uygun dizileri yok? Bu yeni soruya doğru cevabı bilmiyorum. Ancak bazı insanlar genellikle C'nin (biraz) daha okunaklı ve taşınabilir bir montaj aracı olduğunu söyler.


2
Soruyu cevapladığını sanmıyorum.
Robert Harvey,

2
Söylediğin doğru, ama soran kişi bunun neden böyle olduğunu bilmek istiyor .

9
Unutmayın, C takma adlarından biri "taşınabilir montaj" dır. Standardın daha yeni sürümleri daha yüksek seviyeli kavramlar eklerken, temelde, önemsiz makinelerin çoğunda ortak olan basit düşük seviyeli yapılar ve talimatlardan oluşur. Bu, dilde verilen tasarım kararlarının çoğunu yönlendirir. Çalışma zamanında var olan tek değişkenler tam sayılar, değişkenler ve işaretçilerdir. Talimatlar aritmetik, karşılaştırmalar ve atlama içerir. Neredeyse her şey bunun üzerine inşa edilmiş ince bir katmandır.

8
C'nin başka yapılarla nasıl aynı ikiliyi oluşturamadığınızı düşünerek dizileri olmadığını söylemek yanlıştır (peki, en azından dizi boyutlarını belirlemek için #defines kullanmayı düşünüyorsanız). C Diziler Var "Verileri sürekli dizileri", bu konuda şekerli bir şey. İşaretçiler gibi dizileri kullanmak, burada dizilimsel şekerdir (açıkça işaretçi aritmetiği yerine), diziler değil.
hyde

2
Evet, bu kodu göz önünde bulundurun: struct Foo { int arr[10]; }. arrbir dizidir, işaretçi değildir.
Steven Burnap
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.