Vraag Met arrays, waarom is het zo dat een [5] == 5 [a]?


Zoals Joel aangeeft Stack Overflow-podcast # 34, in C Programmeertaal (ook bekend als: K & R), er is melding gemaakt van deze eigenschap van arrays in C: a[5] == 5[a]

Joel zegt dat het vanwege wijzerrekenkunde is, maar ik begrijp het nog steeds niet. Waarom doet a[5] == 5[a]?


1427
2017-12-19 17:01


oorsprong


antwoorden:


De C-standaard definieert de [] operator als volgt:

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

daarom a[5] zal evalueren om:

*(a + 5)

en 5[a] zal evalueren om:

*(5 + a)

a is een verwijzing naar het eerste element van de array. a[5] is de waarde die 5 is elementen verder van a, wat hetzelfde is als *(a + 5)en van basisschool wiskunde weten we dat die gelijk zijn (toevoeging is commutatieve).


1704
2017-12-19 17:04



Omdat array-toegang is gedefinieerd in termen van verwijzingen. a[i] is gedefinieerd om te betekenen *(a + i), wat commutatief is.


273
2017-12-19 17:05



Ik denk dat er iets wordt gemist door de andere antwoorden.

Ja, p[i] is per definitie equivalent aan *(p+i), wat (omdat de toevoeging commutatief is) equivalent is *(i+p), wat (wederom, door de definitie van de [] operator) is gelijk aan i[p].

(En in array[i], wordt de arraynaam impliciet geconverteerd naar een aanwijzer naar het eerste element van de array.)

Maar de commutativiteit van toevoeging is in dit geval niet helemaal vanzelfsprekend.

Wanneer beide operanden van hetzelfde type zijn, of zelfs van verschillende numerieke typen die worden gepromoveerd tot een gewoon type, is commutativiteit volkomen logisch: x + y == y + x.

Maar in dit geval hebben we het specifiek over pointer rekenkunde, waarbij de ene operand een pointer is en de andere een geheel getal. (Geheel getal + geheel getal is een andere bewerking en aanwijzer + aanwijzer is onzin.)

De C-standaard beschrijving van de + operator (N1570 6.5.6) zegt:

Om te worden toegevoegd, moeten beide operanden een rekenkundig type hebben, of een   operand is een verwijzing naar een compleet objecttype en het andere   zal een integer type hebben.

Het had net zo gemakkelijk kunnen zeggen:

Voor optelling moeten beide operanden een rekenkundig type hebben, of links   operand is een verwijzing naar een compleet objecttype en de rechter operand   zal een integer type hebben.

in welk geval beide i + p en i[p] zou illegaal zijn.

In C ++ -termen hebben we echt twee sets overbelasting + operatoren, die losjes kunnen worden beschreven als:

pointer operator+(pointer p, integer i);

en

pointer operator+(integer i, pointer p);

waarvan alleen de eerste echt nodig is.

Dus waarom is het zo?

C ++ heeft deze definitie overgenomen van C, die deze van B heeft gekregen (de commutativiteit van array-indexering wordt expliciet vermeld in 1972 Gebruikersreferentie naar B), waar het vandaan kwam BCPL (handleiding uit 1967), die misschien zelfs uit eerdere talen is gekomen (CPL? Algol?).

Dus het idee dat array-indexering wordt gedefinieerd in termen van optellen, en die toevoeging, zelfs van een aanwijzer en een integer, is commutatief, gaat vele decennia terug, tot de vooroudertalen van C.

Die talen waren veel minder sterk getypt dan moderne C is. In het bijzonder werd het onderscheid tussen wijzers en gehele getallen vaak genegeerd. (Vroege C-programmeurs gebruikten soms pointers als niet-getekende gehele getallen, vóór de unsigned sleutelwoord is toegevoegd aan de taal.) Dus het idee om toevoeging niet-commutatief te maken omdat de operanden van verschillende typen zijn, zou waarschijnlijk niet bij de ontwerpers van die talen zijn opgekomen. Als een gebruiker twee 'dingen' wilde toevoegen, of die 'dingen' gehele getallen, aanwijzers of iets anders zijn, was het niet aan de taal om dit te voorkomen.

En in de loop der jaren zou elke wijziging van die regel de bestaande code hebben verbroken (hoewel de ANSI C-norm van 1989 een goede gelegenheid zou kunnen zijn).

Het veranderen van C en / of C ++ om te vereisen dat de wijzer aan de linkerkant en het gehele getal aan de rechterkant worden geplaatst, zou een aantal bestaande codes kunnen breken, maar er zou geen verlies van echte expressieve kracht zijn.

Dus nu hebben we arr[3] en 3[arr] wat exact hetzelfde betekent, hoewel de laatste vorm nooit buiten de IOCCC.


186
2017-08-23 01:37



En natuurlijk

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

De belangrijkste reden hiervoor was dat computers in de jaren 70, toen C werd ontworpen, niet veel geheugen hadden (64 KB was veel), dus de C-compiler deed niet veel syntaxiscontrole. Vandaar "X[Y]"was nogal blindelings vertaald in"*(X+Y)"

Dit verklaart ook de "+="en"++"syntaxes. Alles in de vorm"A = B + C"had dezelfde gecompileerde vorm, maar als B hetzelfde object als A was, dan was er een optimalisatie op assembly-niveau beschikbaar, maar de compiler was niet slim genoeg om het te herkennen, dus de ontwikkelaar moest (A += C). Evenzo, als C was 1, er was een andere optimalisatie voor het montageniveau beschikbaar en de ontwikkelaar moest dit opnieuw expliciet maken, omdat de compiler dit niet herkende. (Meer recent compilers doen, dus die syntaxes zijn tegenwoordig grotendeels onnodig)


184
2017-12-19 17:07



Iets waar niemand over Dinah's probleem mee heeft gezegd sizeof:

U kunt alleen een geheel getal aan een aanwijzer toevoegen. U kunt niet twee aanwijzers tegelijk toevoegen. Op die manier weet de compiler altijd wanneer een wijzer aan een geheel getal of een integer aan een aanwijzer wordt toegevoegd, welk bit een grootte heeft waarmee rekening moet worden gehouden.


51
2018-02-11 15:56



Om de vraag letterlijk te beantwoorden. Het is niet altijd waar dat x == x

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

prints

false

46
2017-08-11 13:50



Leuke vraag / antwoorden.

Ik wil er alleen op wijzen dat C-verwijzingen en arrays niet de dezelfde, hoewel in dit geval het verschil niet essentieel is.

Beschouw de volgende verklaringen:

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

In a.out, het symbool een is op een adres dat aan het begin van de array staat, en symbool p bevindt zich op een adres waar een aanwijzer is opgeslagen en de waarde van de aanwijzer op die geheugenlocatie is het begin van de array.


23
2017-12-20 08:16



Ik kom erachter dat deze lelijke syntaxis "nuttig" kan zijn, of in ieder geval erg leuk om mee te spelen als je wilt omgaan met een reeks indexen die verwijzen naar posities in dezelfde array. Het kan geneste vierkante haken vervangen en de code leesbaarder maken!

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)

}

Natuurlijk ben ik er vrij zeker van dat er geen use-case voor is in echte code, maar ik vond het sowieso interessant :)


21
2018-06-10 19:50