Dizilerde neden [5] == 5 [a] söz konusudur?


1622

Joel, Stack Overflow podcast # 34'te belirtildiği gibi, C Programlama Dili'nde (aka: K&R), C'deki dizilerin bu özelliğinden bahsedilmektedir:a[5] == 5[a]

Joel işaretçi aritmetiği yüzünden olduğunu söylüyor ama hala anlamıyorum. Neden oluyora[5] == 5[a] ?


48
[+] gibi bir şey de * (a ++) VEYA * (++ a) gibi çalışır mı?
Egon

45
@Egon: Bu çok yaratıcı ama maalesef derleyiciler böyle çalışmıyor. Derleyici a[1]dizeler değil, bir dizi belirteç olarak yorumlar : * ({a {operatörü} + {integer} 1 tamsayı konumu) * ile aynıdır ({integer} 1 {operator} + {a tamsayı konumu} a) ancak * ile aynı değildir ({a {operator} + {operator} + 'nın {tamsayı konumu)
Dinah

11
Bununla ilgili ilginç bir bileşik varyasyonu, bir ifade olarak sahip olduğunuz ve kullanıldığı Illogical dizi erişiminde gösterilmektedir . char bar[]; int foo[];foo[i][bar]
Jonathan Leffler

5
@EldritchConundrum, neden 'derleyici sol parçanın bir işaretçi olup olmadığını kontrol edemiyor' diye düşünüyorsunuz? Evet yapabilir. Bu doğrudur a[b]= *(a + b)herhangi bir için ave bancak dil tasarımcıların serbest seçim oldu için +her türlü değişmeli tanımlanmalıdır. Hiçbir şey i + pizin verirken onların yasaklanmasını engelleyemez p + i.
ach

13
@Andrey One genellikle +değişmeli olmayı bekler , bu yüzden asıl sorun, işaretçi işlemlerinin ayrı bir ofset operatörü tasarlamak yerine aritmetiğe benzemesini seçmektir.
Eldritch Conundrum

Yanıtlar:


1924

C standardı []operatörü şu şekilde tanımlar :

a[b] == *(a + b)

Bu nedenle a[5]:

*(a + 5)

ve 5[a]şunları değerlendirir:

*(5 + a)

adizinin ilk öğesine bir işaretçi. a[5]5 elementten daha fazla aolan ve aynı olan değerdir *(a + 5)ve ilkokul matematiğinden bunların eşit olduğunu biliyoruz (toplama değişmeli ).


325
Daha fazla * ((5 * sizeof (a)) + a) gibi olup olmadığını merak ediyorum. Büyük açıklama olsa.
John MacIntyre

92
@Dinah: C derleyicisi açısından haklısın. Sizeof'e gerek yok ve bahsettiğim ifadeler aynı. Ancak derleyici, makine kodu üretirken sizeof'i dikkate alacaktır. A int dizisi ise, bunun yerine bir a[5]şey derleyecektirmov eax, [ebx+20][ebx+5]
Mehrdad Afshari

12
@Dinah: A bir adrestir, örneğin 0x1230. A 32 bit int dizisindeyse, o zaman bir [0] 0x1230'da, bir [1] 0x1234'te, bir [2] 0x1238'de ... a [5] x1244'de vb. 0x1230, yanlış olan 0x1235 alıyoruz.
James Curran

36
@ sr105: Bu, + işleci için özel bir durumdur; burada işlenenlerden biri bir işaretçi diğeri bir tamsayıdır. Standart, sonucun işaretçi türünde olacağını söylüyor. Derleyici / yeterince akıllı olmalıdır.
aib

48
"ilkokul matematik biz bu eşit olduğunu biliyorum" - Sana basitleştiriyoruz anlıyorum, ama bu olduğu gibi düşünenlerin beraberim üzerinde basitleştirerek. Bu temel değil *(10 + (int *)13) != *((int *)10 + 13). Başka bir deyişle, burada ilkokul aritmetiğinden daha fazlası var. Değişebilirlik, derleyiciye hangi işlenenin bir işaretçi olduğunu (ve nesnenin boyutuna) tanıdığına bağlıdır. Başka bir deyişle (1 apple + 2 oranges) = (2 oranges + 1 apple), ama (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH

288

Çünkü dizi erişimi işaretçiler olarak tanımlanır. a[i]*(a + i)değişmeli anlamına gelen ortalama olarak tanımlanır .


42
Diziler işaretçiler olarak tanımlanmaz, ancak bunlara erişim tanımlanır .
Yörüngedeki Hafiflik Yarışları

5
Ben "eşittir *(i + a), hangi olarak yazılabilir i[a]" eklerdim .
Jim Balter

4
Aşağıdaki gibi standart teklif alıntı öneririz: 6.5.2.1: 2 Köşeli parantez [] bir ifade izleyen bir postfix ifade bir dizi nesnesinin bir öğesinin abone bir atama olduğunu. Alt simge operatörünün [] tanımı, E1 [E2] 'nin (* ((E1) + (E2))) ile aynı olmasıdır. İkili + işleci için geçerli dönüştürme kuralları nedeniyle, E1 bir dizi nesnesi ise (eşdeğer olarak bir dizi nesnesinin ilk öğesine bir işaretçi) ve E2 bir tam sayı ise, E1 [E2], E2 [E2] E1 (sıfırdan sayma).
Vality

Daha doğru olmak için: Diziler eriştiğinizde işaretçiler olarak bozulur.
12431234123412341234123

Nitpick: " *(a + i)Değişmeli" demek mantıklı değil . Ancak, *(a + i) = *(i + a) = i[a]çünkü ilave değişmeli.
Andreas Rejbrand

231

Bence bir şey diğer cevaplar tarafından kaçırılıyor.

Evet, p[i]tanım gereği eşdeğerdir *(p+i)(eklenme değişmeli olduğu için) eşdeğerdir *(i+p), (yine []operatörün tanımı ile ) eşdeğerdiri[p] .

(Ve array[i] , dizi adı örtük olarak dizinin ilk öğesine bir işaretçi olarak dönüştürülür.)

Ancak, eklemenin değişebilirliği bu durumda çok açık değildir.

Her iki işlenen de aynı türdeyse veya ortak bir türe yükseltilen farklı sayısal türlerde olduğunda, değişebilirlik tam anlamıyla mantıklıdır: x + y == y + x .

Ancak bu durumda, özellikle bir işlenenin bir işaretçi ve diğerinin bir tam sayı olduğu işaretçi aritmetiğinden bahsediyoruz. (Tamsayı + tamsayı farklı bir işlemdir ve işaretçi + işaretçi saçmalıktır.)

C standardının +operatörün tanımı ( N1570 6.5.6):

Ek olarak, her iki işlenen de aritmetik tipe sahip olmalı ya da bir işlenen tam bir nesne tipine işaretçi ve diğeri de tamsayı tipine sahip olmalıdır.

Kolayca söyleyebileceği gibi:

Ek olarak, her iki işlenen de aritmetik tipe veya sol işlenen tam nesne tipine bir işaretçi ve sağ işlenen tamsayı tipine sahip olmalıdır.

bu durumda hem i + pve i[p]yasadışı olurdu.

C ++ terimleriyle, +gevşek bir şekilde şu şekilde tanımlanabilecek iki aşırı yüklenmiş işleç setimiz var:

pointer operator+(pointer p, integer i);

ve

pointer operator+(integer i, pointer p);

sadece birincisi gerçekten gerekli.

Öyleyse neden bu şekilde?

C ++, bu tanımı B'den alan (dizi indekslemesinin değişebilirliği açıkça 1972 Kullanıcı Referansı B'de belirtilmiştir ) C'den devralmıştır , bu da onu BCPL'den (1967 tarihli manuel) almıştır; önceki diller (CPL? Algol?).

Bu nedenle dizi indekslemesinin toplama açısından tanımlandığı ve eklemenin, bir işaretçi ve bir tamsayı bile, değişmeli olduğu fikri, C'nin ata dillerine onlarca yıl öncesine dayanır.

Bu diller, modern C'den çok daha az güçlü yazılmıştır. Özellikle, işaretçiler ve tamsayılar arasındaki ayrım genellikle göz ardı edildi. (Erken C programcıları, bazen unsignedanahtar sözcük dile eklenmeden önce işaretçileri işaretsiz tamsayılar olarak kullandılar .) Bu nedenle, ekleri değişmez yapma fikri, çünkü işlenenlerin farklı türlerde olması, muhtemelen bu dillerin tasarımcılarına gerçekleşmezdi. Bir kullanıcı iki "şey" eklemek isterse, bu "şeyler" tamsayı, işaretçi veya başka bir şey olsun, bunu önlemek dile bağlı değildir.

Yıllar geçtikçe, bu kuraldaki herhangi bir değişiklik mevcut kodu ihlal ederdi (1989 ANSI C standardı iyi bir fırsat olabilir).

İşaretçiyi sola ve sağa tamsayıyı koymayı gerektirecek şekilde C ve / veya C ++ 'ı değiştirmek varolan bazı kodları kırabilir, ancak gerçek ifade gücü kaybı olmaz.

Şimdi de aynı şeye sahibiz arr[3]ve 3[arr]anlamıyoruz, ancak ikinci form asla IOCCC dışında görünmemelidir .


12
Bu tesis harika açıklaması. Üst düzey bir bakış açısından, 3[arr]ilginç bir eser olduğunu düşünüyorum , ancak nadiren kullanılmalıdır. Bir süre önce sorduğum bu soruya kabul edilen cevap (< stackoverflow.com/q/1390365/356> ) sözdizimi hakkında düşündüğüm yolu değiştirdi. Bu tür şeyleri yapmanın teknik olarak doğru ve yanlış bir yolu olmasa da, bu tür özellikler uygulama ayrıntılarından ayrı bir şekilde düşünmeye başlar. Uygulama ayrıntılarına sabitlendiğinizde kısmen kaybolan bu farklı düşünme biçiminin faydası vardır.
Dinah

3
Toplama değişmeli. C standardının tanımlaması için aksi takdirde garip olurdu. Bu nedenle, "Ek olarak, her iki işlenen de aritmetik tipe veya sol işlenen, tam bir nesne tipine işaretçi olacak ve sağ işlenen tamsayı tipine sahip olacaktır." - Bu, bir şeyler ekleyen çoğu insan için anlamlı olmaz.
iheanyi

9
@iheanyi: Toplama genellikle değişmeli ve genellikle aynı türden iki işlenen alır. İşaretçi ekleme, bir işaretçi ve bir tamsayı eklemenize izin verir, ancak iki işaretçi eklemenize izin vermez. İşaretçinin sol işlenen olmasını gerektiren yeterince garip bir özel durum olan IMHO önemli bir yük olmayacaktır. (Bazı diller dize birleştirme için "+" kullanır; bu kesinlikle değişmez değildir.)
Keith Thompson

3
@supercat, bu daha da kötü. Bu bazen x + 1! = 1 + x anlamına gelir. Bu, eklemenin birleştirici özelliğini tamamen ihlal eder.
iheanyi

3
@iheanyi: Bence değişmeli mülkiyet demek istediniz; Çoğu uygulamada (1LL + 1U) -2! = 1LL + (1U-2) olduğu için ekleme zaten ilişkilendirici değildir. Aslında, değişiklik şu anda olmayan bazı durumları ilişkisel hale getirecektir, örneğin 3U + (UINT_MAX-2L) (3U + UINT_MAX) -2'ye eşit olacaktır. Bununla birlikte, en iyi şey, dilin, promotable tamsayılar ve cebir halkaları "sarmak" için yeni farklı türler eklemesi, böylece 65535'i içeren bir 2'ye eklenmesi , boyutundan bağımsız olarak 1 değerine sahip ring16_tbir a verecektir . ring16_tint
supercat

196

Ve tabi ki

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

Bunun ana nedeni, C'nin tasarlandığı 70'lerde, bilgisayarların çok fazla belleğe sahip olmamasıydı (64KB çoktu), bu yüzden C derleyicisi çok sözdizimi kontrolü yapmadı. Dolayısıyla " X[Y]" oldukça körü körüne " *(X+Y)"

Bu aynı zamanda " +=" ve " ++" sözdizimlerini de açıklar . " A = B + C" Biçimindeki her şey aynı derlenmiş biçime sahipti. Ancak, B, A ile aynı nesne ise, bir montaj düzeyi optimizasyonu mevcuttu. Ancak derleyici bunu tanıyacak kadar parlak değildi, bu yüzden geliştiricinin ( A += C) yapması gerekiyordu . Benzer şekilde, öyleyse C, 1farklı bir montaj düzeyi optimizasyonu mevcuttu ve derleyici bunu tanımadığı için geliştirici bunu açıkça yapmak zorunda kaldı. (Son zamanlarda derleyiciler yapar, bu yüzden bu sözdizimleri bugünlerde büyük ölçüde gereksizdir)


127
Aslında bu yanlış olarak değerlendirilir; ilk terim "ABCD" [2] == 2 ["ABCD"] doğru veya 1 ve 1 olarak değerlendirilir! = 'C': D
Jonathan Leffler

8
@Jonathan: Aynı belirsizlik bu yazının orijinal başlığının düzenlenmesine yol açtı. Matematiksel denklik, kod sözdizimi veya sözde kod olarak eşit değerler miyiz? Matematiksel denkliği savunuyorum, ancak kod hakkında konuştuğumuzdan, her şeyi kod sözdizimi açısından görüntülediğimizden kaçamıyoruz.
Dinah

19
Bu bir efsane değil mi? Yani derleyici için basitleştirmek için + = ve ++ operatörleri oluşturuldu? Bazı kodlar onlarla daha açık hale gelir ve derleyicinin ne yaparsa yapsın, sözdizimine sahip olmak yararlıdır.
Thomas Padron-McCarthy

6
+ = ve ++ 'ın bir başka önemli yararı daha vardır. değerlendirilirken sol taraf bazı değişkenleri değiştirirse, değişiklik yalnızca bir kez yapılır. a = a + ...; iki kez yapacak.
Johannes Schaub - litb

8
Hayır - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Bir dizgenin
silinmesi

55

Dinah'ın sorunu hakkında kimsenin bahsetmediği bir şey sizeof :

Bir işaretçiye yalnızca bir tamsayı ekleyebilirsiniz, birlikte iki işaretçi ekleyemezsiniz. Bu şekilde, bir tamsayıya bir işaretçi veya bir işaretçiye bir tamsayı eklerken, derleyici her zaman hangi bitin dikkate alınması gereken bir boyuta sahip olduğunu bilir.


1
Kabul edilen cevabın yorumlarında bu konuda oldukça kapsamlı bir konuşma var. Düzenlemede adı geçen sohbeti orijinal soruya yönlendirdim, ancak sizeof ile ilgili geçerli endişenizi doğrudan ele almadım. Bunu en iyi nasıl SO emin değilim. Orig'de başka bir düzenleme yapmalı mıyım? soru?
Dinah

50

Soruyu tam anlamıyla cevaplamak için. Her zaman doğru değildirx == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

baskılar

false

27
Aslında bir "nan" kendisine eşit değildir: cout << (a[5] == a[5] ? "true" : "false") << endl;öyle false.
TrueY

8
@TrueY: Özellikle NaN davası için (ve özellikle bu x == xher zaman doğru değil) belirtti. Bence niyeti buydu. Yani teknik olarak doğrudur (ve muhtemelen dedikleri gibi, en iyi doğrudur!).
Tim Čas

3
Soru C ile ilgili, kodunuz C kodu değil. Ayrıca daha iyi olan bir NANin de vardır , çünkü UB tanımlanmadığında (Çoğu uygulama tanımlanmaz , ancak çoğu uygulamada hala çalışır)<math.h>0.0/0.00.0/0.0__STDC_IEC_559____STDC_IEC_559__0.0/0.0
12431234123412341234123 14:08

26

Ben sadece bu çirkin sözdizimi "yararlı", ya da en azından aynı diziye pozisyonları atıfta indeksler bir dizi ile uğraşmak istediğinizde oynamak için çok eğlenceli olabileceğini öğrenmek. İç içe geçmiş köşeli parantezleri değiştirebilir ve kodu daha okunabilir hale getirebilir!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Tabii ki, gerçek kodda bunun için bir kullanım örneği olmadığından eminim, ama yine de ilginç buldum :)


Gördüğünüzde i[a][a][a]i bir dizi için bir işaretçi veya bir dizi veya bir dizi için bir işaretçi dizisi ... ve abir dizin olduğunu düşünüyorum. Gördüğünüzde a[a[a[i]]], a'nın bir diziye veya diziye işaretçi olduğunu ve ibir dizin olduğunu düşünüyorsunuz.
12431234123412341234123

1
Vaov! Bu "aptal" özelliğin çok güzel kullanımı. Bazı problemlerde algoritmik yarışmada yararlı olabilir))
Serge Breusov

26

Güzel soru / cevaplar.

Sadece C işaretçi ve diziler olmadıklarını işaret etmek istiyorum aynı bu durumda fark önemli olmamasına rağmen,.

Aşağıdaki beyanları göz önünde bulundurun:

int a[10];
int* p = a;

İçinde a.out, sembol adizinin başlangıcı olan pbir adrestir ve sembol bir işaretçinin saklandığı adrestir ve bu bellek konumundaki işaretçinin değeri dizinin başlangıcıdır.


2
Hayır, teknik olarak aynı değiller. Bazı b'yi int * const olarak tanımlar ve bunu bir diziye işaret ettirirseniz, bu hala bir işaretçidir, yani sembol tablosunda b, dizinin bulunduğu yeri gösteren bir adresi depolayan bir bellek konumuna karşılık gelir. .
PolyThinker

4
Çok iyi bir nokta. Global bir sembolü bir modülde char s [100] olarak tanımladığımda çok kötü bir hataya sahip olduğumu hatırlıyorum, extern char * s olarak ilan ediyorum; başka bir modülde. Hepsini birbirine bağladıktan sonra program çok garip davrandı. Çünkü extern bildirimini kullanan modül dizinin ilk baytını char için bir gösterici olarak kullanıyordu.
Giorgio

1
Başlangıçta, C'nin büyükbaba veya büyükanne BCPL'sinde bir dizi bir göstericiydi. Yani, yazdığınızda aldığınız şey (C harfine çevirdim) int a[10], başka bir yerde 10 tamsayı için yeterli mağazaya işaret eden 'a' adlı bir işaretçiydi. Böylece a + i ve j + i aynı forma sahipti: birkaç bellek yerinin içeriğini ekleyin. Aslında, BCPL'nin türsüz olduğunu düşünüyorum, bu yüzden aynılardı. Ve sizeof tipi ölçekleme uygulanmadı, çünkü BCPL tamamen kelime yönelimli (kelime adresli makinelerde de).
dave

Ben farkı anlamak için en iyi yolu karşılaştırmak olduğunu düşünüyorum int*p = a;için int b = 5; ikincisi, "b" ve "5" iki tamsayılar olduğu ise ancak "5" sabit bir değer iken "b" bir değişkendir. Benzer şekilde, "p" ve "a" karakterlerinin adresleridir, ancak "a" sabit bir değerdir.
James Curran

20

C'deki işaretçiler için,

a[5] == *(a + 5)

ve ayrıca

5[a] == *(5 + a)

Dolayısıyla a[5] == 5[a].


15

Bir cevap değil, sadece düşünce için yiyecek. Sınıfta aşırı yüklenmiş dizin / alt simge operatörü varsa, ifade 0[x]çalışmaz:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

İnt sınıfına erişimimiz olmadığından bu yapılamaz:

class int
{
   int operator[](const Sub&);
};

2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
Ben Voigt

1
Gerçekten derlemeyi denediniz mi? Sınıf dışında uygulanamayan operatörler vardır (statik olmayan fonksiyonlar gibi)!
Ajay

3
Üzgünüz, haklısın. " operator[]tam olarak bir parametreye sahip statik olmayan bir üye işlevi olacaktır." Bu kısıtlamaya aşinaydım operator=, uygulandığını düşünmedim [].
Ben Voigt

1
Tabii ki, []operatörün tanımını değiştirirseniz, bir daha asla eşdeğer olmaz ... eğer a[b]eşitse *(a + b)ve bunu değiştirirseniz, aşırı yüklemeniz gerekir int::operator[](const Sub&);ve intbir sınıf değildir ...
Luis Colorado

7
Bu değil ... ... C'dir.
MD XF

11

Ted Jensen tarafından C POINTERS VE TORORLAR ÜZERİNDEKİ BİR EĞİTİM'de çok iyi bir açıklaması var .

Ted Jensen bunu şöyle açıkladı:

Aslında, bu doğrudur, yani nerede yazarsa yazsın herhangi bir sorun olmadan a[i]değiştirilebilir *(a + i). Aslında, derleyici her iki durumda da aynı kodu yaratacaktır. Böylece işaretçi aritmetiğinin dizi indeksleme ile aynı şey olduğunu görüyoruz. Her iki sözdizimi de aynı sonucu verir.

Bu işaretçiler ve dizilerin aynı şey olduğunu söylemiyor, değil. Sadece bir dizinin belirli bir elemanını tanımlamak için, biri dizi indeksleme ve diğeri de aynı sonuçları veren işaretçi aritmetiği kullanan iki sözdizimi seçeneğine sahip olduğumuzu söylüyoruz.

Şimdi, bu son ifadeye, bir kısmına .. bakmak (a + i), + operatörü ve C kurallarını kullanarak basit bir ektir, böyle bir ifadenin değişmeli olduğu. Yani (a + i) aynıdır (i + a). Böylece yazabildiğimiz *(i + a)kadar kolay yazabiliriz *(a + i). Ama *(i + a)gelebilirdi i[a]! Bunların hepsinden ilginç bir gerçek geliyorsa:

char a[20];

yazı

a[3] = 'x';

yazmakla aynı

3[a] = 'x';

4
a + i basit bir ekleme DEĞİLDİR çünkü işaretçi aritmetiğidir. a öğesinin boyutu 1 (karakter) ise, evet, tamsayı + gibidir. Ancak (örneğin) bir tamsayı ise, + 4 * i'ye eşdeğer olabilir.
Alex Brown

@AlexBrown Evet, işaretçi aritmetiğidir, bu yüzden ilk cümlenizin yanlış olmasından dolayı, (a * karakterine) ilk olarak a a (char *) atmadıkça (int'nin 4 karakter olduğu varsayılarak). İşaretçi aritmetiğinin gerçek değer sonucuna neden bu kadar çok insanın asıldığını gerçekten anlamıyorum. İşaretçi aritmetiğinin tüm amacı, temel işaretçi değerlerini soyutlamak ve programcının adres değerleri yerine manipüle edilen nesneleri düşünmesini sağlamaktır.
jschultz410

8

Sorunun cevaplandığını biliyorum, ancak bu açıklamayı paylaşmaya dayanamadım.

Derleyici tasarımının ilkelerini hatırlıyorum a, bir intdizi ve boyutun int2 bayt olduğunu ve Taban adresinin a1000 olduğunu varsayalım .

Nasıl Çalışır a[5]->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Yani,

Benzer şekilde c kodu 3 adresli kodlara bölünürse, 5[a]->

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Temel olarak her iki ifade de bellekte aynı yere işaret ediyor ve dolayısıyla a[5] = 5[a],.

Bu açıklama aynı zamanda dizilerdeki negatif indekslerin C cinsinden çalışmasının nedenidir.

yani erişirsem a[-5]bana

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Bana 990 numaralı konumda nesne döndürecek.


6

Olarak Cı-diziler , arr[3]ve 3[arr]aynıdır ve bunlara karşılık gelen işaretçi açıklamalardır *(arr + 3)için *(3 + arr). Ancak tam tersine [arr]3veya [3]arrdoğru değildir ve sözdizimi hatasına neden olur (arr + 3)*ve (3 + arr)*geçerli ifadeler değildir. Nedeni, kayıttan çıkarma operatörünün, adresten sonra değil, ifadenin verdiği adresin önüne yerleştirilmesi gerektiğidir.


6

c derleyicisinde

a[i]
i[a]
*(a+i)

dizideki bir öğeye başvurmanın farklı yollarıdır! (TÜM WEIRD'DE DEĞİL)


5

Şimdi biraz tarih. Diğer diller arasında BCPL, C'nin erken gelişimi üzerinde oldukça büyük bir etkiye sahipti. BCPL'de aşağıdaki gibi bir dizi bildirdiyseniz:

let V = vec 10

aslında 10 kelimeyi değil 11 kelimeyi tahsis etti. Tipik olarak V ilk karakterdi ve hemen ardından gelen kelimenin adresini içeriyordu. Böylece C'den farklı olarak, V isimlendirmesi o konuma gitti ve dizinin sıfırıncı elemanının adresini aldı. Bu nedenle BCPL'de dizi indirimi,

let J = V!5

J = !(V + 5)dizinin temel adresini almak için V getirilmesi gerektiğinden gerçekten (BCPL sözdizimini kullanarak) yapmak zorundaydı . Böylece V!5ve5!V eşanlamlı. Bir fıkra gözlemi olarak, WAFL (Warwick Fonksiyonel Dili) BCPL'de yazılmıştır ve belleğimin en iyisi, veri depolama olarak kullanılan düğümlere erişmek için öncekinden ziyade ikinci sözdizimini kullanma eğilimindedir. Bu 35 ila 40 yıl önce bir yerden verildi, bu yüzden hafızam biraz paslı. :)

Ek depolama alanı ile dağıtım yapma ve derleyicinin adlandırıldığında dizinin temel adresini eklemesini sağlama daha sonra geldi. C tarih belgesine göre bu, C'ye yapılar eklendiğinde gerçekleşti.

Not o !BCPL bir tekli önek operatörü ve indirection yapıyor her iki durumda da bir ikili infix operatörü hem de oldu. sadece ikili biçim, dolaylı işlem yapmadan önce iki işlenenin eklenmesini içermekteydi. BCPL'nin (ve B'nin) kelime odaklı doğası göz önüne alındığında, bu aslında çok mantıklıydı. "İşaretçi ve tamsayı" kısıtlaması, veri türlerini kazandığında ve sizeofbir şey haline geldiğinde C'de gerekli hale getirildi.


1

Bu, yalnızca dil desteği nedeniyle mümkün olan bir özelliktir.

Derleyici, yorumlanması a[i]olarak *(a+i)ve ifade 5[a]için değerlendirir *(5+a). Toplama değişmeli olduğu için her ikisinin de eşit olduğu ortaya çıkıyor. Dolayısıyla ifade olarak değerlendirilir true.


Gereksiz olmasına rağmen bu açık, özlü ve kısadır.
Bill K

0

C cinsinden

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

İşaretçi bir "değişken"

dizi adı "anımsatıcı" veya "eşanlamlı"

p++;geçerli ama a++geçersiz

a[2] 2 [a] 'ya eşittir, çünkü bunların her ikisindeki dahili işlem

"İşaretçi Aritmetiği" dahili olarak

*(a+3) eşittir *(3+a)


-4

işaretçi türleri

1) Verilere İşaretçi

int *ptr;

2) Verilere Sabit İşaretçi

int const *ptr;

3) Verileri Sabitlemek İçin Sabit İşaretçi

int const *const ptr;

ve diziler listemizden (2) türünde
Ne zaman bir dizi tanımlamak tek bir seferde adres ilklendir olan bu pointer
biz bir atar oluyor çünkü biz değiştirmek veya programımıza const değerini değiştirmek olamayacağını bildiği gibi HATA derleme sırasında zaman

Bulduğum en büyük fark ...

İşaretçiyi bir adresle yeniden başlatabiliriz, ancak bir diziyle aynı durumu yeniden başlatamayız.

======
ve sorunuza geri dönme ...
a[5]hiçbir şey değildir, ancak listemizdeki (2) tür bir işaretçi gibi - adresi (insanlar temel adres olarak adlandırır) içererek *(a + 5)
kolayca anlayabilirsiniz - bu operatör işaretçi ile değiştirilebilir
a
[]* .

en sonunda...

a[5] == *(a +5) == *(5 + a) == 5[a] 
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.