Aanwijzers afdrukken in C

Ik probeerde iets te begrijpen met verwijzingen, dus schreef ik deze code:

#include <stdio.h>
int main(void)
{
    char s[] = "asd";
    char **p = &s;
    printf("The value of s is: %p\n", s);
    printf("The direction of s is: %p\n", &s);
    printf("The value of p is: %p\n", p);
    printf("The direction of p is: %p\n", &p);
    printf("The direction of s[0] is: %p\n", &s[0]);
    printf("The direction of s[1] is: %p\n", &s[1]);
    printf("The direction of s[2] is: %p\n", &s[2]);
    return 0;
}

Bij het compileren met gcc krijg ik deze waarschuwingen:

$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm
main.c: In function ‘main’:
main.c:6: warning: initialization from incompatible pointer type
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’

(De vlaggen voor gcc zijn omdat ik C89 moet zijn)

Waarom incompatibele typen aanwijzer? Is de naam van een array niet een verwijzing naar het eerste element? Dus als s een pointer naar ‘a’ is, moet &seen char **zijn, nee?
En waarom krijg ik de andere waarschuwingen? Moet ik de pointers casten met (void *) om ze af te drukken?

En tijdens het hardlopen krijg ik zoiets als dit:

$ ./main-bin
The value of s is: 0xbfb7c860
The direction of s is: 0xbfb7c860
The value of p is: 0xbfb7c860
The direction of p is: 0xbfb7c85c
The direction of s[0] is: 0xbfb7c860
The direction of s[1] is: 0xbfb7c861
The direction of s[2] is: 0xbfb7c862

Hoe kan de waarde van s en zijn richting (en natuurlijk de waarde van p) hetzelfde zijn?


Antwoord 1, autoriteit 100%

“s” is geen “char*”, het is een “char[4]”. En dus is “&s” geen “teken**”, maar eigenlijk “een verwijzing naar een array van 4 tekens”. Uw compiler kan “&s” behandelen alsof u “&s[0]” heeft geschreven, wat ongeveer hetzelfde is, maar een “char*” is.

Als je “char** p = &s;” schrijft je probeert te zeggen: “Ik wil dat p wordt ingesteld op het adres van het ding dat momenteel naar “asd” verwijst. Maar momenteel is er niets dat wijstnaar “asd”. Er is slechts een array die heeft“asd”;

char s[] = "asd";
char *p = &s[0];  // alternately you could use the shorthand char*p = s;
char **pp = &p;

Antwoord 2, autoriteit 59%

Ja, uw compiler verwacht void *. Cast ze gewoon naar ongeldig *.

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);

Antwoord 3, autoriteit 12%

Als u de naam van een array als argument aan een functie doorgeeft, wordt deze behandeld alsof u het adres van de array hebt doorgegeven. Dus & s en s zijn identieke argumenten. Zie K&R 5.3. &s[0] is hetzelfde als &s, omdat het het adres van het eerste element van de array nodig heeft, wat hetzelfde is als het adres van de array zelf.

Voor alle anderen, hoewel alle aanwijzers in wezen geheugenlocaties zijn, worden ze nog steeds getypt, en de compiler zal waarschuwen voor het toewijzen van het ene type aanwijzer aan het andere.

  • void* p;zegt dat p een geheugenadres is, maar ik weet niet wat er in het geheugen staat
  • char* s;zegt dat s een geheugenadres is, en de eerste byte bevat een teken
  • char** ps;zegt dat ps een geheugenadres is, en de vier bytes daar (voor een 32-bits systeem) bevatten een aanwijzer van het type char*.

cf http://www.oberon2005.ru/paper/kr_c.pdf(e-bookversie van K&R)


Antwoord 4, autoriteit 9%

Het is geen pointer naar karakter char*maar een pointer naar array van 4 karakters: char* [4]. Met g++ compileert het niet:

main.cpp: In functie ‘int main(int, char**)’: main.cpp:126: fout:
kan ‘char (*)[4]’ niet converteren naar ‘char**’ in initialisatie

Bovendien zeggen de linux-manpagina’s :

p

Het argument void * pointer wordt hexadecimaal afgedrukt (alsof door %#x of
%#lx).
Het zou een pointer naar ongeldig moeten zijn.

U kunt uw code wijzigen in:

char* s = "asd";
char** p = &s;
printf("The value of s is: %p\n", s);
printf("The address of s is: %p\n", &s);
printf("The value of p is: %p\n", p);
printf("The address of p is: %p\n", &p);
printf("The address of s[0] is: %p\n", &s[0]);
printf("The address of s[1] is: %p\n", &s[1]);
printf("The address of s[2] is: %p\n", &s[2]);

resultaat:

De waarde van s is: 0x403f00

Het adres van s is: 0x7fff2df9d588

De waarde van p is: 0x7fff2df9d588

Het adres van p is: 0x7fff2df9d580

Het adres van s[0] is: 0x403f00

Het adres van s[1] is: 0x403f01

Het adres van s[2] is: 0x403f02


Antwoord 5, autoriteit 3%

regel wijzigen:

char s[] = “asd”;

naar:

char *s = “asd”;

en dingen zullen duidelijker worden


Antwoord 6, autoriteit 3%

Je kunt de waarde (d.w.z. het adres van) een statische array niet wijzigen. In technische termen is de l-waarde van een array het adres van het eerste element. Vandaar s == &s. Het is gewoon een eigenaardigheid van de taal.


Antwoord 7, autoriteit 3%

Normaal gesproken wordt het als een slechte stijl beschouwd om onnodig verwijzingen naar (void*) te casten. Hier heb je echter de casts nodig om (void*) op de printf-argumenten te gebruiken omdat printf variadisch is. Het prototype vertelt de compiler niet naar welk type de aanwijzers moeten worden geconverteerd op de oproepsite.


Antwoord 8

Je hebt gebruikt:

char s[] = "asd";

Hier verwijst eigenlijk naar de bytes “asd”. Het adres van s, zou ook naar deze locatie verwijzen.

Als je hebt gebruikt:

char *s = "asd";

de waarde van s en & s zou anders zijn, aangezien s in feite een verwijzing naar de bytes “asd” zou zijn.

Je gebruikte:

char s[] = "asd";
char **p = &s;

Hier verwijst naar de bytes “asd”. p is een pointer naar een pointer naar karakters, en is ingesteld op a het adres van karakters. Met andere woorden, je hebt te veel indirectheden in p. Als je char *s = “asd” hebt gebruikt, zou je deze extra indirectheid kunnen gebruiken.

Other episodes