Wat doet ** in C-taal?

Ik ben nieuw bij C met een goede achtergrond in Java en ik probeer pointers en arrays te begrijpen.

Ik weet dat subscript operator[]deel uitmaakt van een arraydefinitie, dus:

int numbers[] = {1,3,4,5};

zou een integer-array maken, die in het geheugen zou worden weergegeven als 16 bytes, 4 partijen van 4 bytes:

numbers[0] = 1, address 0061FF1C
numbers[1] = 3, address 0061FF20
numbers[2] = 4, address 0061FF24
numbers[3] = 5, address 0061FF28

Als het echter om pointers gaat, begint mijn kennis af te brokkelen, dus als ik een pointer naar de arraynummers zou maken, zou ik het volgende doen:

int *pNumbers = &numbers[0];

die er ongeveer zo uit zou zien:

En ik vermoed dat het een grootte van 4 bytes zou zijn?

Hoewel de **die ik lees als “wijzer naar een aanwijzer”, wat voor mij geen zin heeft, waarom zou iemand een aanwijzer naar een aanwijzer willen, zeker als a->b-> c dan zou a->c voldoende zijn? Ik weet dat ik iets mis, en het moet iets met arrays te maken hebben, aangezien argvvan het type char[ ]of char **zoals hieronder te zien:

int main(int argc, char **argv){}

Dus:

  • wat is dit (**)?
  • wat voor nut heeft het?
  • hoe wordt het in het geheugen weergegeven?

Antwoord 1, autoriteit 100%

In C worden argumenten doorgegeven door waarden. Als je bijvoorbeeld een integer-variabele hebt in hoofd

int main( void )
{
    int x = 10;
    //...

en de volgende functie

void f( int x )
{
    x = 20;
    printf( "x = %d\n", x );
} 

dan als je de functie in main op deze manier aanroept

f( x );

dan krijgt de parameter de waarde van variabele xin main. De parameter zelf neemt echter een andere omvang in het geheugen in beslag dan het argument. Dus eventuele wijzigingen van de parameter in de functie hebben geen invloed op de oorspronkelijke variabele in het algemeen, omdat deze wijzigingen plaatsvinden in verschillende geheugenbereiken.

Dus hoe verander je de variabele in main in de functie?

Je moet een verwijzing naar de variabele doorgeven met behulp van pointers.

In dit geval ziet de functiedeclaratie eruit als

void f( int *px );

en de functiedefinitie zal zijn

void f( int *px )
{
    *px = 20;
    printf( "*px = %d\n", *px );
} 

In dit geval is het geheugenbereik dat wordt ingenomen door de oorspronkelijke variabele xgewijzigd omdat we binnen de functie toegang krijgen tot dit bereik met behulp van de aanwijzer

   *px = 20;

Natuurlijk moet de functie in main worden aangeroepen, zoals

f( &x );

Houd er rekening mee dat de parameter zelf die de pointer pxis, zoals gewoonlijk een lokale variabele van de functie is. Dat is de functie die deze variabele maakt en initialiseert met het adres van variabele x.

Laten we nu aannemen dat u in het algemeen een pointer hebt gedeclareerd, bijvoorbeeld op de volgende manier

int main( void )
{
   int *px = malloc( sizeof( int ) );
   //..

En de functie gedefinieerd als

void f( int *px )
{
    px = malloc( sizeof( int ) );
    printf( "px = %p\n", px );
}

Aangezien parameter pxeen lokale variabele is die eraan wordt toegekend, heeft een waarde geen invloed op de oorspronkelijke aanwijzer. De functie verandert een andere hoeveelheid geheugen dan de omvang die wordt ingenomen door de oorspronkelijke aanwijzer pxin het hoofdmenu.

Hoe verander ik de originele aanwijzer in de functie?
Geef het gewoon door als referentie!

Bijvoorbeeld

f( &px );
//...
void f( int **px )
{
    *px = malloc( sizeof( int ) );
    printf( "*px = %p\n", *px );
}

In dit geval wordt de waarde die is opgeslagen in de oorspronkelijke aanwijzer binnen de functie gewijzigd omdat de functie die gebruikmaakt van dereferentie toegang heeft tot hetzelfde geheugenbereik als waar de oorspronkelijke aanwijzer was gedefinieerd.


Antwoord 2, autoriteit 32%

V: wat is dit (**)?

A: Ja, dat is het precies. Een verwijzing naar een
aanwijzer.

V: wat voor nut heeft het?

A: Het heeft een aantal toepassingen. Met name bij het weergeven van 2-dimensionale gegevens (afbeeldingen, enz.). In het geval van jouw voorbeeld kan char** argvworden gezien als een array van een array van chars. In dit geval wijst elke char*naar het begin van een string. Je zou deze gegevens eigenlijk zelf expliciet zo kunnen declareren.

char* myStrings[] = {
    "Hello",
    "World"
};
char** argv = myStrings;
// argv[0] -> "Hello"
// argv[1] -> "World"

Als je een pointer zoals een array opent, worden het nummer waarmee je het indexeert en de grootte van het element zelf gebruikt om te verschuiven naar het adres van het volgende element in de array. Je zou ook zo toegang kunnen krijgen tot al je nummers, en in feite is dit eigenlijk wat C doet. Houd er rekening mee dat de compiler weet hoeveel bytes een type als intgebruikt tijdens het compileren. Het weet dus hoe groot elke stap moet zijn naar het volgende element.

*(numbers + 0) = 1, address 0x0061FF1C
*(numbers + 1) = 3, address 0x0061FF20
*(numbers + 2) = 4, address 0x0061FF24
*(numbers + 3) = 5, address 0x0061FF28

De operator *wordt de dereferentie-operator genoemd. Het wordt gebruikt om de waarde uit het geheugen op te halen waarnaar wordt verwezen door een aanwijzer. numbersis letterlijk slechts een verwijzing naar het eerste element in je array.

In het geval van mijn voorbeeld zou myStringser ongeveer zo uit kunnen zien, ervan uitgaande dat een pointer/adres 4 bytes is, wat betekent dat we op een 32-bits machine zitten.

myStrings = 0x0061FF14
// these are just 4 byte addresses
(myStrings + 0) -> 0x0061FF14 // 0 bytes from beginning of myStrings
(myStrings + 1) -> 0x0061FF18 // 4 bytes from beginning of myStrings
myStrings[0] -> 0x0061FF1C // de-references myStrings @ 0 returning the address that points to the beginning of 'Hello'
myStrings[1] -> 0x0061FF21 // de-references myStrings @ 1 returning the address that points to the beginning of 'World'
// The address of each letter is 1 char, or 1 byte apart
myStrings[0] + 0 -> 0x0061FF1C  which means... *(myStrings[0] + 0) = 'H'
myStrings[0] + 1 -> 0x0061FF1D  which means... *(myStrings[0] + 1) = 'e'
myStrings[0] + 2 -> 0x0061FF1E  which means... *(myStrings[0] + 2) = 'l'
myStrings[0] + 3 -> 0x0061FF1F  which means... *(myStrings[0] + 3) = 'l'
myStrings[0] + 4 -> 0x0061FF20  which means... *(myStrings[0] + 4) = 'o'

Antwoord 3, autoriteit 20%

De traditionele manier om het argument argvte schrijven is char *argv[], wat meer informatie geeft over wat het is, een array van verwijzingen naar tekens (dwz een array van snaren).

Bij het doorgeven van een array aan een functie vervalt deze echter tot een pointer, waardoor u een pointer naar de pointer naar charof char **overhoudt.


Natuurlijk kunnen dubbele sterretjes ook worden gebruikt bij het derefereren van een aanwijzer naar een aanwijzer, dus zonder de toegevoegde context aan het einde van de vraag zijn er twee antwoorden op de vraag wat **betekent in C, afhankelijk van de context.

Om door te gaan met het argv-voorbeeld, een manier om het eerste teken van het eerste element in argvte krijgen, is door argv[0][0], ofu kunt de dereferentie-operator twee keer gebruiken, zoals in **argv.

Array-indexering en dereferentie is op de meeste plaatsen uitwisselbaar, omdat voor elke pointer ofarray pen index ide uitdrukking p[i]is gelijk aan *(p + i). En als i0is, dan hebben we *(p + 0)die kan worden ingekort tot *(p)wat hetzelfde is als *p.

Als curiositeit, want p[i]is gelijk aan *(p + i)en de commutatieve eigenschapvan optellen, de uitdrukking *(p + i)is gelijk aan *(i + p)wat ertoe leidt dat p[i]gelijk is aan i[p].


Tot slot een waarschuwing over overmatig gebruik van pointers, je hoort misschien ooit de zin three-star programmeur, dat is wanneer men drie sterretjes gebruikt zoals in ***(zoals in een aanwijzer naar een aanwijzer naar een aanwijzer). Maar om te citeren uit de link

Voor alle duidelijkheid: een ThreeStarProgrammer genoemd worden is meestal geen compliment

En nog een waarschuwing: Een array van arrays is niethetzelfde als een aanwijzer naar een aanwijzer(Link naar een oud antwoord van mij, dat ook de geheugenlay-out van een aanwijzer naar een aanwijzer laat zien als vervanging van een array van arrays. )


Antwoord 4, autoriteit 14%

**in declaratie staat voor pointer naar pointer. Pointer is zelf een datatype en net als andere datatypes kan het een pointer hebben.

int i = 5, j = 6; k = 7;
int *ip1 = &i, *ip2 = &j; 
int **ipp = &ip1;  

Aanwijzer naar aanwijzer is handig bij het toewijzen van dynamische 2D-array. Een 10×10 2D-array toewijzen (mag niet aaneengesloten zijn)

int **m = malloc(sizeof(int *)*10;  
for(int i = 0; i < 10; i++)
    m[i] = malloc(sizeof(int)*10  

Het wordt ook gebruikt wanneer u de waarde van een aanwijzer wilt wijzigen via een functie.

void func (int **p, int n)  
{
    *p = malloc(sizeof(int)*n); // Allocate an array of 10 elements 
}
int main(void)
{
    int *ptr = NULL;
    int n = 10;
    func(&ptr, n);
    if(ptr)
    {
        for(int i = 0; i < n; i++)
        {  
             ptr[i] = ++i;
        }  
    }
    free(ptr);
}

Meer lezen: Aanwijzer naar aanwijzer.


Antwoord 5, autoriteit 11%

**staat voor pointer to pointer zoals u de naam kent. Ik zal elk van uw vragen uitleggen:

wat is dit (**)?

Aanwijzer naar aanwijzer. Soms noemen mensen dubbele wijzer. Bijvoorbeeld:

int a = 3;
int* b = &a; // b is pointer. stored address of a
int**b = &b;  // c is pointer to pointer. stored address of b
int***d = &c; // d is pointer to pointer to pointer. stored address of d. You get it. 

hoe wordt het in het geheugen weergegeven?

cin bovenstaand voorbeeld is gewoon een normale variabele en heeft dezelfde representatie als andere variabelen (pointer, int …). Geheugengrootte van variabele c is hetzelfde als ben is afhankelijk van het platform. Bijvoorbeeld, 32-bits computer, elk variabel adres bevat 32-bits, dus de grootte is 4 bytes (8×4=32 bit). Op een 64-bits computer is elk variabel adres 64-bits, dus de grootte is 8 bytes (8×8=64 bit).

wat voor nut heeft het?

Er zijn veel toepassingen voor aanwijzer naar aanwijzer, afhankelijk van uw situatie. Hier is bijvoorbeeld een voorbeeld dat ik heb geleerd in mijn algoritmeklasse. Je hebt een gekoppelde lijst. Nu wil je een methode schrijven om die gekoppelde lijst te wijzigen, en je methode kan de kop van de gekoppelde lijst veranderen. (Voorbeeld: verwijder één element met waarde gelijk aan 5, verwijder head-element, verwissel, …). Je hebt dus twee gevallen:

1. Als je net een aanwijzer van het head-element passeert. Misschien wordt dat head-element verwijderd en is deze aanwijzer niet meer geldig.

2. Als je pointer of pointer van head-element passeert.In het geval dat je head-element wordt verwijderd, heb je geen probleem omdat de pointer of pointer er nog steeds is. Het verandert gewoon de waarden van een ander hoofdknooppunt.

Je kunt hier verwijzen naar het bovenstaande voorbeeld: wijzer naar aanwijzer in gekoppelde lijst

Een ander gebruik is het gebruik in een tweedimensionale array. C is anders dan Java. Tweedimensionale array in C, in feite gewoon een continu geheugenblok. Tweedimensionale array in Java is een blok met meerdere geheugens (afhankelijk van uw matrixrij)

Ik hoop dat dit helpt 🙂


Antwoord 6, autoriteit 9%

**staat voor een aanwijzer op een aanwijzer. Als u een parameter per referentie wilt doorgeven, gebruikt u *, maar als u de aanwijzer zelf wilt doorgeven per referentie, dan hebt u een aanwijzer naar de aanwijzer nodig, Vandaar **.


7, Autoriteit 9%

Overweeg als u een tabel met aanwijzingen hebt – zoals een tabel met snaren (aangezien snaren in “C” worden afgehandeld als aanwijzingen op het eerste teken van de tekenreeks).

Dan heb je een aanwijzer naar de eerste aanwijzer in de tabel nodig. Vandaar de “Char **”.

Als u een inline-tabel met alle waarden hebt, zoals een tweedimensionale tabel met gehele getallen, is het volledig mogelijk om weg te komen met slechts één niveau van indirection (d.w.z. slechts een eenvoudige aanwijzer, zoals “int *”). Maar wanneer er een aanwijzer in het midden is die moet worden gedeplucteerd om tot het eindresultaat te komen, creëert dat een tweede niveau van indirection en is dan de pointer-to-pointer essentieel.

Nog een verduidelijking hier. In “C”, Derferencing via Pointer Notation (b.v. “* PTR”) vs array index notatie (bijvoorbeeld PTR [0]) heeft weinig verschil, behalve de voor de hand liggende indexwaarde in array notatie. De enige Asterisk VS-haakjes is echt belangrijk bij het toewijzen van een variabele (b.v. int * x; is heel anders dan int x [1]).


8, Autoriteit 9%

van uw int *voorbeeld u zegt

En ik vermoed dat het van grootte 4 bytes zou zijn?

In tegenstelling tot JAVA, geeft C niet de exacte afmetingen van zijn gegevenstypen op. Verschillende implementaties kunnen en gebruiken verschillende maten (maar elke implementatie moet consistent zijn). 4-byte intS zijn tegenwoordig gebruikelijk, maar ints kan zijn als kleine twee bytes, en niets beperkt ze hen tot vier. De afmeting van de wijzers is nog minder opgegeven, maar het hangt meestal af van de hardware-architectuur waarop de C-implementatie is gericht. De meest voorkomende wijzermaten zijn vier bytes (typisch voor 32-bits architecturen) en acht bytes (gemeenschappelijk voor 64-bits architecturen).

Wat is dit (**)?

In de context die u presenteert, maakt het deel uit van de Type Designator char **, die een aanwijzer naar een aanwijzer naar charbeschrijft, net zoals u dacht.

Welk gebruik heeft het?

min of meer hetzelfde gebruik als aanwijzer naar elk ander gegevenstype. Soms wilt u of moet u indirect toegang krijgen tot een aanwijzerwaarde, net zoals u misschien wilt of toegang hebt tot een waarde van een ander type indirect. Het is ook handig om naar (het eerste element van) een reeks aanwijzers te wijzen, waaruit het wordt gebruikt in de tweede parameter op een C main()Functie.

In dit specifieke geval, elke char *in de punt van punt zelf naar een van de opdrachtregel-argumenten van het programma.

Hoe wordt het weergegeven in het geheugen?

C Geeft niet op, maar typisch aanwijzingen tot aanwijzingen hebben dezelfde weergave als aanwijzingen op een ander type waarde. De waarde die het wijst is gewoon een aanwijzerwaarde.


Antwoord 9, autoriteit 9%

Ik denk dat ik hier mijn eigen antwoord ga toevoegen en iedereen heeft geweldig werk geleverd, maar ik was echt in de war over het nut van een verwijzing naar een aanwijzer. De reden waarom ik dit bedacht, is omdat ik de indruk had dat alle waarden behalve pointers werden doorgegeven door waarde en pointers werden doorgegeven door middel van referentie. Zie het volgende:

void f(int *x){
    printf("x: %d\n", *x);
    (*x)++;
}
void main(){
   int x = 5;
   int *px = &x;
   f(px);
   printf("x: %d",x);
}

zou opleveren:

x: 5
x: 6

Dit deed me denken (om de een of andere reden) dat pointers werden doorgegeven door middel van referentie terwijl we de pointer doorgeven, deze manipuleren en vervolgens uitbreken en de nieuwe waarde afdrukken. Als je een aanwijzer in een functie kunt manipuleren… waarom zou je een aanwijzer naar een aanwijzer gebruiken om de aanwijzer te manipuleren om mee te beginnen!

Dit leek me verkeerd, en terecht, want het zou dwaas zijn om een aanwijzer te hebben om een aanwijzer te manipuleren terwijl je al een aanwijzer in een functie kunt manipuleren. Het ding met C echter; is alles wordt op waarde doorgegeven, zelfs pointers. Laat me het verder uitleggen met behulp van enkele pseudo-waarden in plaats van de adressen.

//this generates a new pointer to point to the address so lets give the
//new pointer the address 0061FF28, which has the value 0061FF1C.
void f(int 0061FF1C){
    // this prints out the value stored at 0061FF1C which is 5
    printf("x: %d\n", 5);
    // this FIRST gets the value stored at 0061FF1C which is 5
    // then increments it so thus 6 is now stored at 0061FF1C
    (5)++;
}
void main(){
   int x = 5;
   // this is an assumed address for x
   int *px = 0061FF1C;
   /*so far px is a pointer with the address lets say 0061FF24 which holds
    *the value 0061FF1C, when passing px to f we are passing by value...
    *thus 0061FF1C is passed in (NOT THE POINTER BUT THE VALUE IT HOLDS!)
    */
   f(px);
   /*this prints out the value stored at the address of x (0061FF1C) 
    *which is now 6
    */
   printf("x: %d",6);
}

Mijn grootste misverstand over pointers naar pointers is de pass-by-waarde versus pass-by-referentie. De oorspronkelijke aanwijzer is helemaal niet aan de functie doorgegeven, dus we kunnen niet veranderen naar welk adres het verwijst, alleen het adres van de nieuwe aanwijzer (die de illusie heeft de oude aanwijzer te zijn omdat het naar het adres wijst waar de oude aanwijzer was wijzen naar!).


Antwoord 10, autoriteit 7%

Het is een aanwijzer naar een aanwijzer. Als je je afvraagt waarom je een aanwijzer naar een aanwijzer zou willen gebruiken, is hier een soortgelijke thread die dat op verschillende goede manieren beantwoordt.

Waarom dubbele aanwijzer gebruiken? of Waarom pointers naar pointers gebruiken?


Antwoord 11, autoriteit 2%

** is bijvoorbeeld een aanwijzer naar een aanwijzer. char **argvis hetzelfde als char *argv[]en dit is hetzelfde met char argv[][]. Het is een matrix.

Je kunt een matrix declareren met bijvoorbeeld 4 regels, maar een ander aantal kolommen, zoals JaggedArrays.

Het wordt weergegeven als een matrix.

Hierheb je een vertegenwoordiging in het geheugen.


Antwoord 12, autoriteit 2%

Ik zou char **argvbegrijpen als char** argv. Nu is char*in feite een array van char, dus (char*)*is een array van arrays van char.

Met andere (losse) woorden, argvis een array van strings. In dit specifieke voorbeeld: de oproep

myExe dummyArg1 dummyArg2

in console zou argvals

argv[0] = "myExe"
argv[1] = "dummyArg1"
argv[2] = "dummyArg2"

Antwoord 13

In feite zijn in C-arrays pointers:

char* string = "HelloWorld!";

is gelijk aan dit: char string[] = "HelloWorld";
En dit: char** argvis zoals je zei een “wijzer naar een aanwijzer”.

Het kan worden gezien als een array van strings, d.w.z. meerdere strings. Maar onthoud dat strings char pointers zijn!

Zie : in Java is de main methode gelijk aan de C main functie. Het is zoiets als dit:

public static void main(String[] args){}

Dat wil zeggen een array van strings. Het werkt op dezelfde manier in C, String[] argswordt char** argsof char* args[].

Samenvattend: type* name = blablabla;
is mogelijk een array van “type”.
En type** name = blabla;is mogelijk een array van arrays.

Other episodes