Wat is het verschil tussen char array en char pointer in C?

Ik probeer pointers in C te begrijpen, maar ik ben momenteel in de war met het volgende:

  • char *p = "hello"
    

    Dit is een char-aanwijzer die naar de tekenreeks wijst, beginnend bij h.

  • char p[] = "hello"
    

    Dit is een array waarin hallowordt opgeslagen.

Wat is het verschil als ik beide variabelen in deze functie doorgeef?

void printSomething(char *p)
{
    printf("p: %s",p);
}

Antwoord 1, autoriteit 100%

char*en char[]zijn verschillende typen, maar het is niet in alle gevallen meteen duidelijk. Dit komt omdat arrays vervallen in pointers, wat betekent dat als een expressie van het type char[]wordt gegeven waar een van het type char*wordt verwacht, de compiler zet de array automatisch om in een pointer naar zijn eerste element.

Uw voorbeeldfunctie printSomethingverwacht een aanwijzer, dus als u er een array op de volgende manier aan probeert door te geven:

char s[10] = "hello";
printSomething(s);

De compiler doet alsof je dit hebt geschreven:

char s[10] = "hello";
printSomething(&s[0]);

Antwoord 2, autoriteit 38%

Eens kijken:

#include <stdio.h>
#include <string.h>
int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this
    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both
    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5
    return 0;
}

foo* en foo[] zijn verschillende typen en worden verschillend behandeld door de compiler (pointer = adres + representatie van het type van de aanwijzer, array = pointer + optionele lengte van de array, indien bekend, bijvoorbeeld als de array statisch is toegewezen), vindt u de details in de norm. En op het niveau van runtime geen verschil tussen hen (in assembler, nou ja, bijna, zie hieronder).

Ook is er een gerelateerde vraagin de C Veelgestelde vragen:

V: Wat is het verschil tussen deze initialisaties?

char a[] = "string literal";   
char *p  = "string literal";   

Mijn programma loopt vast als ik een nieuwe waarde probeer toe te kennen aan p[i].

A: een letterlijke tekenreeks (de formele term voor een tekenreeks met dubbele aanhalingstekens in C-bron) kan op twee enigszins verschillende manieren worden gebruikt:

  1. Als initialisatie voor een array van char, zoals in de declaratie van char a[] , specificeert het de beginwaarden van de tekens in die array (en, indien nodig, de grootte ervan).
  2. Ergens anders verandert het in een naamloze, statische reeks tekens, en deze naamloze reeks kan worden opgeslagen in het alleen-lezen geheugen en kan daarom niet noodzakelijkerwijs worden gewijzigd. In een expressiecontext wordt de array in één keer omgezet in een pointer, zoals gebruikelijk (zie paragraaf 6), dus de tweede declaratie initialiseert p om te verwijzen naar het eerste element van de naamloze array.

Sommige compilers hebben een schakelaar die bepaalt of letterlijke tekenreeksen schrijfbaar zijn of niet (voor het compileren van oude code), en sommige hebben opties om ervoor te zorgen dat letterlijke tekenreeksen formeel worden behandeld als arrays van const char (voor een betere foutopsporing).

p>

Zie ook vragen 1.31, 6.1, 6.2, 6.8 en 11.8b.

Referenties: K&R2 Sec. 5,5 blz. 104

ISO-sec. 6.1.4, sec. 6.5.7

Rationale sec. 3.1.4

H&S sec. 2.7.4 blz. 31-2


Antwoord 3, autoriteit 19%

Wat is het verschil tussen char-array versus char-aanwijzer in C?

C99 N1256 concept

Er zijn twee verschillende toepassingen van letterlijke tekenreeksen:

  1. Initialiseer char[]:

    char c[] = "abc";      
    

    Dit is “meer magie”, en beschreven op 6.7.8/14 “Initialisatie”:

    Een array van tekentypes kan optioneel worden geïnitialiseerd door een letterlijke tekenreeks
    ingesloten in beugels. Opeenvolgende tekens van de letterlijke tekenreeks (inclusief de
    null-teken beëindigen als er ruimte is of als de array een onbekende grootte heeft) initialiseer de
    elementen van de array.

    Dit is dus slechts een snelkoppeling voor:

    char c[] = {'a', 'b', 'c', '\0'};
    

    Net als elke andere gewone array, kan cworden gewijzigd.

  2. Overal anders: het genereert een:

    Dus als je schrijft:

    char *c = "abc";
    

    Dit is vergelijkbaar met:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Let op de impliciete cast van char[]naar char *, wat altijd legaal is.

    Als u vervolgens c[0]wijzigt, wijzigt u ook __unnamed, wat UB is.

    Dit is gedocumenteerd in 6.4.5 “Letterlijke tekenreeksen”:

    5 In vertaalfase 7 wordt een byte of code met de waarde nul toegevoegd aan elke multibyte
    tekenreeks die het resultaat is van een letterlijke tekenreeks of letterlijke tekens. Het multibyte-teken
    sequentie wordt vervolgens gebruikt om een array van statische opslagduur en -lengte te initialiseren
    voldoende om de reeks te bevatten. Voor letterlijke tekenreeksen hebben de array-elementen:
    type char, en worden geïnitialiseerd met de individuele bytes van het multibyte-teken
    volgorde […]

    6 Het is niet gespecificeerd of deze arrays verschillend zijn, op voorwaarde dat hun elementen de . hebben
    passende waarden. Als het programma een dergelijke array probeert te wijzigen, is het gedrag:
    niet gedefinieerd.

6.7.8/32 “Initialisatie” geeft een direct voorbeeld:

VOORBEELD 8: De aangifte

char s[] = "abc", t[3] = "abc";

definieert “plain” char array-objecten sen twaarvan de elementen worden geïnitialiseerd met letterlijke tekenreeksen.

Deze verklaring is identiek aan

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

De inhoud van de arrays kan worden gewijzigd. Aan de andere kant, de verklaring

char *p = "abc";

definieert pmet het type “pointer to char” en initialiseert het om te verwijzen naar een object met het type “array of char” met lengte 4 waarvan de elementen worden geïnitialiseerd met een letterlijke tekenreeks. Als een poging wordt gedaan om pte gebruiken om de inhoud van de array te wijzigen, is het gedrag niet gedefinieerd.

GCC 4.8 x86-64 ELF-implementatie

Programma:

#include <stdio.h>
int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compileren en decompileren:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Uitvoer bevat:

char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusie: GCC slaat char*op in de sectie .rodata, niet in .text.

Als we hetzelfde doen voor char[]:

char s[] = "abc";

we verkrijgen:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

zodat het in de stapel wordt opgeslagen (ten opzichte van %rbp).

Houd er echter rekening mee dat het standaard linker-script .rodataen .textin hetzelfde segment plaatst, dat uitvoering heeft maar geen schrijfrechten heeft. Dit kan worden waargenomen met:

readelf -l a.out

die bevat:

Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

Antwoord 4, autoriteit 4%

Je mag de inhoud van een stringconstante niet wijzigen, dat is waar de eerste pnaar verwijst. De tweede pis een array die is geïnitialiseerd met een tekenreeksconstante en u kuntde inhoud ervan wijzigen.


Antwoord 5, autoriteit 3%

Voor dit soort gevallen is het effect hetzelfde: je geeft uiteindelijk het adres door van het eerste teken in een reeks tekens.

De verklaringen zijn echter duidelijk niet hetzelfde.

Het volgende reserveert geheugen voor een tekenreeks en ook voor een tekenaanwijzer, en initialiseert vervolgens de aanwijzer om naar het eerste teken in de tekenreeks te wijzen.

char *p = "hello";

Terwijl het volgende geheugen alleen voor de tekenreeks opzij zet. Dus het kan eigenlijk minder geheugen gebruiken.

char p[10] = "hello";

Antwoord 6, autoriteit 2%

Van APUE, Sectie 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

… Voor het eerste sjabloon wordt de naam op de stapel toegewezen, omdat we een . gebruiken
array variabele. Voor de tweede naam gebruiken we echter een pointer. In dit geval alleen de
geheugen voor de aanwijzer zelf bevindt zich op de stapel; de compiler regelt de string om
worden opgeslagen in het alleen-lezen segment van het uitvoerbare bestand. Wanneer de functie mkstempprobeert
om de string te wijzigen, treedt er een segmentatiefout op.

De geciteerde tekst komt overeen met de uitleg van @Ciro Santilli.


Antwoord 7

Voor zover ik me kan herinneren, is een array eigenlijk een groep aanwijzers.
Bijvoorbeeld

p[1]== *(&p+1)

is een waar statement


Antwoord 8

char p[3] = "hello"? moet zijn char p[6] = "hello"onthoud dat er een ‘\0’ teken aan het einde van een “string” in C staat.

hoe dan ook, array in C is slechts een verwijzing naar het eerste object van een aanpassingsobject in het geheugen. de enige verschillende s zijn in de semantiek. terwijl je de waarde van een aanwijzer kunt wijzigen om naar een andere locatie in het geheugen te wijzen, zal een array, nadat deze is gemaakt, altijd naar dezelfde locatie wijzen.
ook bij het gebruik van array worden “new” en “delete” automatisch voor je gedaan.

Other episodes