Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C] pointer to array

Pagina: 1
Acties:

  • DizzyVacation
  • Registratie: November 2006
  • Niet online
Ik kreeg vanochtend wat code te zien waarbij ik iets zag waarvan ik niet kan snappen dat het werkt.
Zie het volgende stukje code:

C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
    int foo[][2] = {{1, 2}, {3, 4}};
    int *p;
    
    p = *foo;
    
    printf("%p\n%p\n", foo, p);
    printf("%i, %i", p[0], p[1]);
    
    return 0;
}


Dit stukje code werkt perfect.
Ik meende toch echt dat regel 8 er zo uit moest zien:
C:
1
p = (int *)foo;


Kan iemand mij uitleggen waarom de pointer niet de waarde 1 krijgt?
Ik heb dit met gcc getest.

  • brama
  • Registratie: Februari 2001
  • Niet online
Je kunt int foo[][2] ook lezen als int ** foo. *(int ** foo) wordt dan int *, wat ook het type van p is.

Wat jij schrijft zal overigens precies hetzelfde doen, alhoewel je daar (int **) naar (int *) cast, wat eigenlijk niet klopt. Wat volgens mij het netste is:

C:
1
int[] p = *foo


Of duidelijker:

C:
1
int[] p = foo[0]


Clue is dat foo een dubbele pointer is, en niet een enkele. Als je p 1 wilt laten worden, moet je dus 2 dereferences doen:

C:
1
2
3
4
5
int p = **foo;

// of

int p = foo[0][0]

I mentioned it once, but I think I got away with it.


  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Waarom zou het in jouw optiek:
C:
1
p = (int *)foo;


moeten zijn? foo is een array van een array van ints. Array en pointer arithmetic zijn exchangable, maar dan zou je moeten inzien dat die cast niet echt logisch is omdat je in feite een pointer naar een int pointer probeert te casten naar een int pointer, i.e. in feite int** naar int*. Dat lijkt me niet te kloppen.

Wat jij in je code example nu doet met p = *foo is foo (pointer naar een int pointer) dereferencen waardoor je een int pointer overhoudt. Lijkt me vrij triviaal :-)

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:32
brama schreef op woensdag 28 juli 2010 @ 19:56:
Je kunt int foo[]\[2] ook lezen als int ** foo.
Dit klopt niet. Een tweedimensionale array is niet hetzelfde als een array van pointers. Daarin verschilt C van talen als Java. De rest van je uitleg klopt daardoor ook niet, sorry. ;)

foo is simpelweg een tweedimensionele array en de declaratie is equivalent aan:
C:
1
int foo[2][2] = {{1, 2}, {3, 4}};

Behalve dat de eerste dimensie toevallig door de compiler berekend wordt. Aangezien foo van type int\[2]\[2] is, zal foo[i] van type int\[2] zijn. *foo is simpelweg equivalent aan foo\[0] en het resultaat is dus een ééndimensionele array, die naar int* geconverteerd kan worden.

Tot zover het verhaal op basis van typering. Wat betreft object layout, hebben alle arrays in C dezelfde layout: alle elementen staan direct achter elkaar zonder enige vorm van padding tussen elementen, ongeacht de dimensies. Een tweedimensionale array van 2x2 heeft dus precies dezelfde layout als een ééndimensionale array van 4. Vandaar dat de code voorgesteld door de TS;
C:
1
p = (int *)foo;

Ook een geldige oplossing is. De array wordt dan namelijk eerst geconverteerd naar een pointer-naar-array (int*[2]); de pointer wijst dan naar de eerste deelarray ({1, 2}) en door de cast hou je een pointer naar 1 over. Het effect is dus precies hetzelfde.

Overigens schrijf ik dit soort conversies zelf meestal zo:
C:
1
int *p = &foo[0][0];

Om te benadrukken dat ik een pointer naar het eerste element probeer te krijgen. De uitwerking is hetzelfde maar het voorkomt verwarring die kan ontstaan door impliciete array-naar-pointer conversies.

  • DizzyVacation
  • Registratie: November 2006
  • Niet online
Ik had het volgende in gedachte:
foo is in dit geval niks anders is dan een pointer naar een blok data, waar de 2 dimensionale array staat:
code:
1
foo -> |1|2|3|4|


Als ik foo nu dereference dacht ik de waarde 1 te verkrijgen.
Dit gebeurt dus alleen niet omdat de compiler weet dat ik een 2 dimensionale array gebruik?
Vandaar dat dan de door mij aangegeven cast ook nodig is.

Ik probeerde trouwens geheel foo als een groot blok data te zien en niet alleen de eerste twee waardes.

[ Voor 12% gewijzigd door DizzyVacation op 28-07-2010 22:22 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:32
Hou in gedachten dat *p gewoon hetzelfde is als p[0]: het is dus maar één niveau van indirectie. foo is een array van arrays van int, dus één niveau van indirectie levert een array van int op, geen element uit die array.

Je gedachte over de inhoud van de array als blok van data klopt, maar zoals je hebt gemerkt is het type van een array van groot belang. Een int\[4] array en een int\[2]\[2] array hebben dezelfde memory lay-out maar indexeren werkt anders.

  • brama
  • Registratie: Februari 2001
  • Niet online
Soultaker schreef op woensdag 28 juli 2010 @ 21:33:
[...]

Dit klopt niet. Een tweedimensionale array is niet hetzelfde als een array van pointers. Daarin verschilt C van talen als Java. De rest van je uitleg klopt daardoor ook niet, sorry. ;)
Volgens mij kloppen mijn voorbeelden allemaal hoor. Dat de typering anders zal ik niet ontkennen, maar waarom zou je een array van arrays onder water niet kunnen zien als een array van pointers? Een array is weinig anders een een pointer naar een geheugenlocatie van aaneengesloten elementen, net zoals *p hetzelfde oplevert als p[0].

I mentioned it once, but I think I got away with it.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 20:32
Nee, dat begrijp je dus verkeerd. :) Een array is een aantal objecten op een rijtje. Een array kan impliciet geconverteerd worden naar een pointer naar het eerste object, maar daarmee zijn arrays en pointers niet hetzelfde. Dat onderscheid kun je vaak negeren, maar is juist van belang bij het maken van arrays van arrays en dergelijke. (Een andere situatie waarin het verschil relevant is, is als je met sizeof() de grootte van een array wil bepalen).
C:
1
2
3
4
int a[2][2] = { {1,2}, {3, 4} };
int (*b)[2] = a;       /* OK: impliciete array naar pointer conversie. */
int **c = a;           /* Ongeldige initialisatie: types zijn niet compatible! */
int **d = (int**)a;    /* Illegale cast! Compileert wel, maar werkt niet. */


Een jagged array (array van pointers naar arrays eigenlijk) waar jij het over hebt, heeft een andere geheugenlayout dan een echte tweedimensionale array, en daarom is foo op geen mogelijke manier te converteren naar of te lezen als van het type int**.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 20:54

.oisyn

Moderator Devschuur®

Demotivational Speaker

Het is ook logisch als je naar de types kijkt. Een int** is een pointer naar een int*. Oftewel, als je een int** dereferencet dan verwacht je dat er op die plek een pointer staat. Een int[x][y] is daarentegen een 2D array, daar komen geen pointers aan te pas. Als je de int[x][y] dereferencet, dan verwacht je een int[y], oftewel, y aaneengesloten ints. En dus geen pointer!

Een mogelijke geheugenlayout voor int** a en int b[2][2] die we beiden opzetten als {{1, 2}, {3, 4}} is als volgt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
adres   data     omschrijving

0000:   0200     De variabele 'a' - zijn data is een geheugenadres, int**
0004:   1        b[0][0], int
0008:   2        b[0][1], int
000C:   3        b[1][0], int
0010:   4        b[1][1], int

...

0200:   0380     a[0], int*
0204:   02c8     a[1], int*

...

02c8:   3        a[1][0], int
02cc:   4        a[1][1], int

...

0380:   1        a[0][0], int
0384:   2        a[0][1], int

Zoals je ziet, b bestaat gewoon uit 4 ints, niets meer. Daarentegen is a niets meer dan een enkele pointer. Vandaar dat sizeof(b) op een 32 bits platform typisch 16 is (4 x 4 bytes), terwijl sizeof(a) typisch 4 is (slechts die ene pointer). Verder wijst a naar een array van 2 elementen. Elk element in die array is een pointer, die ook weer naar een array van 2 elementen wijst, waar dan uiteindelijk de ints in staan. De daadwerkelijke ints in de array hoeven elkaar dus helemaal niet allemaal op te volgen, zoals ik hier laat zien.

[ Voor 59% gewijzigd door .oisyn op 29-07-2010 01:51 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1