Sök…


Introduktion

En pekare är en typ av variabel som kan lagra adressen till ett annat objekt eller en funktion.

Syntax

  • <Datatyp> * <Variabelnamn>;
  • int * ptrToInt;
  • void * ptrToVoid; / * C89 + * /
  • struktur someStruct * ptrToStruct;
  • int ** ptrToPtrToInt;
  • int arr [längd]; int * ptrToFirstElem = arr; / * För <C99 måste 'längd' vara en kompileringstidskonstant, för> = C11 kan det behöva vara en. * /
  • int * arrayOfPtrsToInt [längd]; / * För <C99 måste 'längd' vara en kompileringstidskonstant, för> = C11 kan det behöva vara en. * /

Anmärkningar

Asteriskens placering påverkar inte definitionen:

/* The * operator binds to right and therefore these are all equivalent. */
int *i;
int * i;
int* i;

När man definierar flera pekare på en gång kräver emellertid var sin egen asterisk:

int *i, *j; /* i and j are both pointers */
int* i, j;  /* i is a pointer, but j is an int not a pointer variable */

Ett antal pekare är också möjligt, där en asterisk ges före arrayvariabelns namn:

int *foo[2]; /* foo is a array of pointers, can be accessed as *foo[0] and *foo[1] */

Vanliga fel

Felaktig användning av pekare är ofta en källa till buggar som kan inkludera säkerhetsfel eller programkrasch, oftast på grund av segmenteringsfel.

Kontrollerar inte för tilldelningsfel

Tilldelning av minne är inte garanterad att lyckas och kan istället returnera en NULL pekare. Att använda det returnerade värdet, utan att kontrollera om allokeringen är framgångsrik, åberopar odefinierat beteende . Detta leder vanligtvis till en krasch, men det finns ingen garanti för att en krasch kommer att hända så att förlita sig på det kan också leda till problem.

Till exempel osäkra sätt:

struct SomeStruct *s = malloc(sizeof *s);
s->someValue = 0; /* UNSAFE, because s might be a null pointer */

Säkert sätt:

struct SomeStruct *s = malloc(sizeof *s);
if (s)
{
    s->someValue = 0; /* This is safe, we have checked that s is valid */
}

Använda bokstavliga siffror istället för storlek på när du begär minne

För en given kompilator / maskinkonfiguration har typer en känd storlek; Det finns emellertid inte någon standard som definierar att storleken på en viss typ (utom char ) kommer att vara densamma för alla kompilator / maskinkonfigurationer. Om koden använder 4 istället för sizeof(int) för minnesallokering kan den fungera på den ursprungliga maskinen, men koden är inte nödvändigtvis portabel till andra maskiner eller kompilatorer. Fasta storlekar för typer bör ersättas med sizeof(that_type) eller sizeof(*var_ptr_to_that_type) .

Icke-bärbar tilldelning:

 int *intPtr = malloc(4*1000);    /* allocating storage for 1000 int */
 long *longPtr = malloc(8*1000);  /* allocating storage for 1000 long */

Bärbar tilldelning:

 int *intPtr = malloc(sizeof(int)*1000);     /* allocating storage for 1000 int */
 long *longPtr = malloc(sizeof(long)*1000);  /* allocating storage for 1000 long */

Eller ännu bättre:

 int *intPtr = malloc(sizeof(*intPtr)*1000);     /* allocating storage for 1000 int */
 long *longPtr = malloc(sizeof(*longPtr)*1000);  /* allocating storage for 1000 long */

Minnet läcker

Underlåtenhet att avdela minnet med hjälp av free leder leder till en uppbyggnad av icke återanvändbart minne, som inte längre används av programmet; detta kallas ett minnesläcka . Minne läcker avfall minnesresurser och kan leda till allokeringsfel.

Logiska fel

Alla tilldelningar måste följa samma mönster:

  1. Tilldelning med malloc (eller calloc )
  2. Användning för att lagra data
  3. Avdelning med free

Underlåtenhet att hålla sig till detta mönster, som att använda minne efter ett anrop till free ( dangling pointer ) eller före ett anrop till malloc ( vild pekare ), ringa free två gånger ( "double free"), etc, normalt orsakar en segmente fel och resulterar i en krasch av programmet.

Dessa fel kan vara kortvariga och svåra att felsöka - till exempel frigörs vanligtvis inte fritt minne omedelbart av operativsystemet, och därför kan dinglande pekare kvarstå ett tag och verkar fungera.

I system där det fungerar är Valgrind ett ovärderligt verktyg för att identifiera vilket minne som läckt ut och var det ursprungligen tilldelades.

Skapa pekare för att stapla variabler

Att skapa en pekare förlänger inte livslängden för den variabel som pekas på. Till exempel:

int* myFunction() 
{
    int x = 10;
    return &x;
}

Här, x har automatisk lagringstiden (vanligen kallad stapel tilldelning). Eftersom den tilldelas på bunten är dess livslängd bara så länge som myFunction körs; efter att myFunction har gått ut förstörs variabeln x . Denna funktion får adressen till x (med &x ) och returnerar den till den som ringer, och lämnar uppringaren med en pekare till en icke-existerande variabel. Att försöka komma åt denna variabel åberopar då odefinierat beteende .

De flesta kompilatorer raderar inte en stapelram efter att funktionen har avslutats, vilket gör att du returnerar den returnerade pekaren ofta ger dig de förväntade data. När en annan funktion kallas kan emellertid minnet som pekas överskrivas, och det verkar som om de data som pekas på har skadats.

För att lösa detta, antingen malloc till lagringsenheten för variabeln som ska returneras och returnerar en pekare till den nyligen skapade lagringen, eller kräver att en giltig pekare skickas till funktionen istället för att returnera en, till exempel:

#include <stdlib.h>
#include <stdio.h>

int *solution1(void) 
{
    int *x = malloc(sizeof *x);
    if (x == NULL) 
    {
        /* Something went wrong */
        return NULL;
    }

    *x = 10;

    return x;
}

void solution2(int *x) 
{
    /* NB: calling this function with an invalid or null pointer 
       causes undefined behaviour. */

    *x = 10;
}

int main(void) 
{
    { 
        /* Use solution1() */

        int *foo = solution1();  
        if (foo == NULL)
        {
            /* Something went wrong */
            return 1;
        }

        printf("The value set by solution1() is %i\n", *foo);
        /* Will output: "The value set by solution1() is 10" */

        free(foo);    /* Tidy up */
    }

    {
        /* Use solution2() */

        int bar;
        solution2(&bar); 

        printf("The value set by solution2() is %i\n", bar);
        /* Will output: "The value set by solution2() is 10" */
    }

    return 0;
}

Inkrementering / decrementing och dereferencing

Om du skriver *p++ att öka vad som pekas av p , har du fel.

Inkrementering / dekrementering av inlägg utförs innan avbrytning. Därför kommer detta uttryck att öka pekaren p själv och returnera det som pekades av p innan steget ökas utan att ändra det.

Du bör skriva (*p)++ att öka vad som pekas av p .

Denna regel gäller också för postdekrementering: *p-- kommer att minska pekaren p själv, inte vad som pekas av p .

Överföra en pekare

int a = 1;
int *a_pointer = &a;

För att avlägsna a_pointer och ändra värdet på a använder vi följande operation

*a_pointer = 2;

Detta kan verifieras med följande tryckta uttalanden.

printf("%d\n", a); /* Prints 2 */
printf("%d\n", *a_pointer); /* Also prints 2 */

Man skulle emellertid ta sig en felaktig åsidosättande av en NULL eller på annat sätt ogiltig pekare. Detta

int *p1, *p2;

p1 = (int *) 0xbad;
p2 = NULL;

*p1 = 42;
*p2 = *p1 + 1;

är vanligtvis odefinierat beteende . p1 kanske inte tas bort eftersom den pekar på en adress 0xbad som kanske inte är en giltig adress. Vem vet vad som finns där? Det kan vara operativsystemminne eller ett annat programminne. Den enda tidskoden som denna används är i inbäddad utveckling som lagrar särskild information på hårdkodade adresser. p2 kan inte tas bort eftersom det är NULL , vilket är ogiltigt.

Överföra en pekare till en struktur

Låt oss säga att vi har följande struktur:

struct MY_STRUCT 
{
    int my_int;
    float my_float;
};

Vi kan definiera MY_STRUCT att utelämna struct nyckelordet så att vi inte behöver skriva struct MY_STRUCT varje gång vi använder det. Detta är dock valfritt.

typedef struct MY_STRUCT MY_STRUCT;

Om vi då har en pekare till en instans av denna struktur

MY_STRUCT *instance;

Om detta uttalande visas i filens omfattning, kommer instance att initialiseras med en nollpekare när programmet startar. Om detta uttalande visas i en funktion, definieras dess värde. Variabeln måste initieras för att peka på en giltig MY_STRUCT variabel, eller till ett dynamiskt tilldelat utrymme, innan den kan återfördelas. Till exempel:

MY_STRUCT info = { 1, 3.141593F };
MY_STRUCT *instance = &info;

När pekaren är giltig kan vi använda den till att få åtkomst till medlemmarna med en av två olika notationer:

int a = (*instance).my_int;
float b = instance->my_float;

Medan båda dessa metoder fungerar är det bättre att använda pilen -> operatören snarare än kombinationen av parenteser, dereference * -operatören och punkten . operatör eftersom det är lättare att läsa och förstå, särskilt med kapslade användningar.

En annan viktig skillnad visas nedan:

MY_STRUCT copy = *instance;
copy.my_int    = 2;

I detta fall innehåller copy en kopia av instance innehåll. Ändring av my_int av copy kommer inte att ändra den i instance .

MY_STRUCT *ref = instance;
ref->my_int    = 2;

I detta fall är ref en referens till instance . my_int ändrar my_int med referensen kommer det att ändras i instance .

Det är vanligt att använda pekare till strukturer som parametrar i funktioner snarare än själva strukturerna. Att använda strukturerna som funktionsparametrar kan leda till att stacken flyter över om strukturen är stor. Att använda en pekare till en struktur använder bara tillräckligt med stapelutrymme för pekaren, men kan orsaka biverkningar om funktionen ändrar strukturen som skickas in i funktionen.

Funktionspekare

Pekare kan också användas för att peka på funktioner.

Låt oss ta en grundläggande funktion:

int my_function(int a, int b)
{
    return 2 * a + 3 * b;
}

Låt oss nu definiera en pekare av den funktionens typ:

int (*my_pointer)(int, int);

För att skapa en, använd bara den här mallen:

return_type_of_func (*my_func_pointer)(type_arg1, type_arg2, ...)

Vi måste då tilldela denna pekare till funktionen:

my_pointer = &my_function;

Denna pekare kan nu användas för att ringa funktionen:

/* Calling the pointed function */
int result = (*my_pointer)(4, 2);

...

/* Using the function pointer as an argument to another function */
void another_function(int (*another_pointer)(int, int))
{
    int a = 4;
    int b = 2;
    int result = (*another_pointer)(a, b);

    printf("%d\n", result);
}

Även om den här syntaxen verkar mer naturlig och sammanhängande med grundläggande typer, kräver inte attribut- och renoveringsfunktionspekare användning av & och * -operatörer. Så följande utdrag är lika giltigt:

/* Attribution without the & operator */
my_pointer = my_function;

/* Dereferencing without the * operator */
int result = my_pointer(4, 2);

För att öka läsbarheten för funktionspekare kan skrivkockar användas.

typedef void (*Callback)(int a);

void some_function(Callback callback)
{
    int a = 4;
    callback(a);
}

Ett annat läsbarhetstrick är att C-standarden gör att man kan förenkla en funktionspekare i argument som ovan (men inte i variabel deklaration) till något som ser ut som en funktionsprototyp; Följaktligen kan följande användas likvärdigt för funktionsdefinitioner och deklarationer:

void some_function(void callback(int))
{
    int a = 4;
    callback(a);
}

Se även

Funktionspekare

Initiera pekare

Initialisering av pekarna är ett bra sätt att undvika vilda pekare. Initieringen är enkel och skiljer sig inte från initieringen av en variabel.

#include <stddef.h>

int main()
{
    int *p1 = NULL; 
    char *p2 = NULL;
    float *p3 = NULL;

         /* NULL is a macro defined in stddef.h, stdio.h, stdlib.h, and string.h */

    ...
}    

I de flesta operativsystem kommer ofta att använda en pekare som har initialiserats till NULL ofta resultera i att programmet kraschar omedelbart, vilket gör det enkelt att identifiera orsaken till problemet. Att använda en oinitialiserad pekare kan ofta orsaka svåra att diagnostisera buggar.

Varning:

Resultatet av att du hänvisar till en NULL pekare är odefinierat, så det kommer inte nödvändigtvis att orsaka en krasch även om det är det naturliga beteendet hos operativsystemet som programmet kör på. Kompilatoroptimeringar kan maskera kraschen, orsaka att kraschen inträffar före eller efter punkten i källkoden där nollpekaren erference inträffade, eller orsaka att delar av koden som innehåller nollpekarens bortfall oväntat tas bort från programmet. Debug builds brukar inte visa dessa beteenden, men detta garanteras inte av språkstandarden. Annat oväntat och / eller oönskat beteende är också tillåtet.

Eftersom NULL aldrig pekar på en variabel, till tilldelat minne eller till en funktion är det säkert att använda som ett skyddsvärde.

Varning:

Vanligtvis definieras NULL som (void *)0 . Men detta innebär inte att den tilldelade minnesadressen är 0x0 . För mer förtydligning, se C-faq för NULL-pekare

Observera att du också kan initialisera pekare för att innehålla andra värden än NULL.

int i1;

int main()
{
   int *p1 = &i1;
   const char *p2 = "A constant string to point to";
   float *p3 = malloc(10 * sizeof(float));
}

Adress för operatör (&)

För alla objekt (dvs. variabel, matris, förening, struktur, pekare eller funktion) kan den unära adressoperatören användas för att få åtkomst till det objektets adress.

Anta att

int i = 1;              
int *p = NULL;

Så då ett uttalande p = &i; , kopierar adressen till variabeln i till pekaren p .

Det uttrycks som p poäng till i .

printf("%d\n", *p); skriver ut 1, vilket är värdet på i .

Pekare Aritmetik

Se här: Pointer Aritmetic

void * pekare som argument och returnerar värden till standardfunktioner

K & R

void* är en fångst av alla typer för pekare på objekttyper. Ett exempel på detta är med malloc funktionen, som förklaras som

void* malloc(size_t);

Pekaren till tomgång-returtyp innebär att det är möjligt att tilldela returvärdet från malloc till en pekare till någon annan typ av objekt:

int* vector = malloc(10 * sizeof *vector);

Det anses allmänt som god praxis att inte uttryckligen kasta värdena in och ut ur ogiltiga pekare. I specifikt fall av malloc() beror detta på att kompilatorn med en uttrycklig roll annars antar, men inte varnar om, en felaktig malloc() för malloc() , om du glömmer att inkludera stdlib.h . Det handlar också om att använda rätt beteende hos ogiltiga pekare för att bättre anpassa sig till DRY (inte upprepa dig själv) -principen; jämföra ovanstående med följande, där följande kod innehåller flera onödiga ytterligare platser där en skrivfel kan orsaka problem:

int* vector = (int*)malloc(10 * sizeof int*);

På liknande sätt funktioner som

void* memcpy(void *restrict target, void const *restrict source, size_t size);

har sina argument specificerade som void * eftersom adressen till något objekt, oavsett typ, kan skickas in. Här bör ett samtal inte heller använda en roll

unsigned char buffer[sizeof(int)];
int b = 67;
memcpy(buffer, &b, sizeof buffer);

Const Pointers

Enstaka pekare

  • Pekare till ett int

    Pekaren kan peka på olika heltal och int kan ändras genom pekaren. Detta provkod tilldelar b för att peka på int b ändrar sedan b : s värde till 100 .

    int b;
    int* p;
    p = &b;    /* OK */
    *p = 100;  /* OK */
    
  • Pekare till en const int

    Pekaren kan peka på olika heltal men int värdet kan inte ändras genom pekaren.

    int b;
    const int* p;
    p = &b;    /* OK */
    *p = 100;  /* Compiler Error */
    
  • const pekaren till int

    Pekaren kan bara peka på ett int men int värde kan ändras genom pekaren.

    int a, b;
    int* const p = &b; /* OK as initialisation, no assignment */
    *p = 100;  /* OK */
    p = &a;    /* Compiler Error */
    
  • const pekaren till const int

    Pekaren kan bara peka på ett int och int kan inte ändras genom pekaren.

    int a, b;
    const int* const p = &b; /* OK as initialisation, no assignment */
    p = &a;   /* Compiler Error */
    *p = 100; /* Compiler Error */
    

Pekare till pekare

  • Pekare till en pekare till ett int

    Denna kod tilldelar adressen till p1 till till dubbelpekaren p (som sedan pekar på int* p1 (som pekar på int )).

    Ändrar sedan p1 att peka på int a . Ändrar sedan värdet på a till 100.

    void f1(void)
    {
      int a, b;
      int *p1;
      int **p;
      p1 = &b; /* OK */
      p = &p1; /* OK */
      *p = &a; /* OK */
      **p = 100; /* OK */
    }
    
  • Pekaren till pekaren till en const int

     void f2(void)
    {
      int b;
      const int *p1;
      const int **p;
      p = &p1; /* OK */
      *p = &b; /* OK */
      **p = 100; /* error: assignment of read-only location ‘**p’ */
    }
    
  • Pekare till const till ett int

    void f3(void)
    {
      int b;
      int *p1;
      int * const *p;
      p = &p1; /* OK */
      *p = &b; /* error: assignment of read-only location ‘*p’ */
      **p = 100; /* OK */
    }
    
  • const pointer to pointer to int

    void f4(void)
    {
      int b;
      int *p1;
      int ** const p = &p1; /* OK as initialisation, not assignment */
      p = &p1; /* error: assignment of read-only variable ‘p’ */
      *p = &b; /* OK */
      **p = 100; /* OK */
    }
    
  • Pekare till const Pekare till const int

    void f5(void)
    {
      int b;
      const int *p1;
      const int * const *p;
      p = &p1; /* OK */
      *p = &b; /* error: assignment of read-only location ‘*p’ */
      **p = 100; /* error: assignment of read-only location ‘**p’ */
    }
    
  • const pointer to pointer to const int

    void f6(void)
    {
      int b;
      const int *p1;
      const int ** const p = &p1; /* OK as initialisation, not assignment */
      p = &p1; /* error: assignment of read-only variable ‘p’ */
      *p = &b; /* OK */
      **p = 100; /* error: assignment of read-only location ‘**p’ */
    }
    
  • const pointer till const pointer to int

    void f7(void)
    {
      int b;
      int *p1;
      int * const * const p = &p1; /* OK as initialisation, not assignment */
      p = &p1; /* error: assignment of read-only variable ‘p’  */
      *p = &b; /* error: assignment of read-only location ‘*p’ */
      **p = 100; /* OK */
    }
    

Samma asterisk, olika betydelser

Premiss

Det mest förvirrande med pekarsyntaxen i C och C ++ är att det faktiskt finns två olika betydelser som gäller när pekarsymbolen, asterisken ( * ), används med en variabel.

Exempel

Först använder du * att deklarera en pekvariabel.

int i = 5;
/* 'p' is a pointer to an integer, initialized as NULL */
int *p = NULL;
/* '&i' evaluates into address of 'i', which then assigned to 'p' */
p = &i;
/* 'p' is now holding the address of 'i' */

När du inte deklarerar (eller multiplicerar) används * för att eliminera en pekvariabel :

*p = 123;
/* 'p' was pointing to 'i', so this changes value of 'i' to 123 */

När du vill att en befintlig pekvariabel ska hålla adressen till en annan variabel använder du inte * , men gör det så här:

p = &another_variable;

En vanlig förvirring bland nybörjare i C-programmering uppstår när de deklarerar och initierar en pekvariabel samtidigt.

int *p = &i;

Eftersom int i = 5; och int i; i = 5; ge samma resultat, kanske några av dem trodde int *p = &i; och int *p; *p = &i; ge samma resultat också. Fakta är, nej, int *p; *p = &i; kommer att försöka uppmana en oinitialiserad pekare som kommer att resultera i UB. Använd aldrig * när du inte förklarar eller gör en pekare bort.

Slutsats

Asterisken ( * ) har två distinkta betydelser inom C i förhållande till pekare, beroende på var den används. När det används i en variabel deklaration bör värdet på den högra sidan av lika sidan vara ett pekarvärde till en adress i minnet. När den används med en redan deklarerad variabel kommer asterisken att eliminera pekarvärdet , följa det till den pekade platsen i minnet och tillåta att det lagrade värdet tilldelas eller hämtas.

Hämtmat

Det är viktigt att tänka på dina P och Q, så att säga, när du hanterar pekare. Tänk på när du använder asterisken och vad det betyder när du använder den där. Att förbise denna lilla detalj kan leda till buggy och / eller odefinierat beteende som du verkligen inte vill ha att ta itu med.

Pekare till pekare

I C kan en pekare referera till en annan pekare.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int A = 42;
  int* pA = &A;
  int** ppA = &pA;
  int*** pppA = &ppA;

  printf("%d", ***pppA); /* prints 42 */

  return EXIT_SUCCESS;
}

Men referens och referens direkt är inte tillåtet.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int A = 42;
  int* pA = &A;
  int** ppA = &&A; /* Compilation error here! */
  int*** pppA = &&&A;  /* Compilation error here! */

  ...

Introduktion

En pekare förklaras ungefär som alla andra variabler, förutom att en asterisk ( * ) är placerad mellan typen och namnet på variabeln för att beteckna den är en pekare.

int *pointer; /* inside a function, pointer is uninitialized and doesn't point to any valid object yet */

För att deklarera två pekvariabler av samma typ, i samma deklaration, använd asterisk-symbolen före varje identifierare. Till exempel,

int *iptr1, *iptr2;
int *iptr3,  iptr4;  /* iptr3 is a pointer variable, whereas iptr4 is misnamed and is an int */

Adress- eller referensoperatören betecknad med en ampersand ( & ) ger adressen till en given variabel som kan placeras i en pekare av lämplig typ.

int value = 1;
pointer = &value;

Operatören för indirection eller dereference betecknad med en asterisk ( * ) får innehållet i ett objekt som pekaren pekar på.

printf("Value of pointed to integer: %d\n", *pointer);
/* Value of pointed to integer: 1 */

Om pekaren pekar på en struktur eller unionstyp så kan du avlägsna den och komma åt dess medlemmar direkt med hjälp av -> operatören:

SomeStruct *s = &someObject;
s->someMember = 5; /* Equivalent to (*s).someMember = 5 */

I C är en pekare en distinkt värdetyp som kan omfördelas och på annat sätt behandlas som en variabel i sig. Till exempel skriver följande exempel ut värdet på själva pekaren (variabeln).

printf("Value of the pointer itself: %p\n", (void *)pointer);
/* Value of the pointer itself: 0x7ffcd41b06e4 */
/* This address will be different each time the program is executed */

Eftersom en pekare är en variabel variabel, är det möjligt för den att inte peka på ett giltigt objekt, antingen genom att ställas in på null

pointer = 0;     /* or alternatively */
pointer = NULL;

eller helt enkelt genom att innehålla ett godtyckligt bitmönster som inte är en giltig adress. Det senare är en mycket dålig situation, eftersom den inte kan testas innan pekaren upphävs, det finns bara ett test för fallet en pekare är noll:

if (!pointer) exit(EXIT_FAILURE);

En pekare får endast avskärmas om den pekar på ett giltigt objekt, annars är beteendet odefinierat. Många moderna implementationer kan hjälpa dig genom att höja något slags fel, t.ex. ett segmenteringsfel och avsluta exekveringen, men andra kan bara lämna ditt program i ogiltigt tillstånd.

Värdet som returneras av duferensoperatören är ett muterbart alias till den ursprungliga variabeln, så det kan ändras, ändra den ursprungliga variabeln.

*pointer += 1;
printf("Value of pointed to variable after change: %d\n", *pointer);
/* Value of pointed to variable after change: 2 */

Pekare kan också tilldelas på nytt. Detta innebär att en pekare som pekar på ett objekt senare kan användas för att peka på ett annat objekt av samma typ.

int value2 = 10;
pointer = &value2;
printf("Value from pointer: %d\n", *pointer);
/* Value from pointer: 10 */

Som alla andra variabler har pekare en viss typ. Du kan inte tilldela adressen till en short int till en pekare till en long int , till exempel. Sådant beteende benämns typpunkning och är förbjudet i C, även om det finns några få undantag.

Även om pekaren måste vara av en specifik typ är minnet som tilldelas för varje typ av pekare lika med det minne som används av miljön för att lagra adresser, snarare än storleken på den typ som pekas på.

#include <stdio.h>

int main(void) {
    printf("Size of int pointer: %zu\n", sizeof (int*));      /* size 4 bytes */
    printf("Size of int variable: %zu\n", sizeof (int));      /* size 4 bytes */
    printf("Size of char pointer: %zu\n", sizeof (char*));    /* size 4 bytes */
    printf("Size of char variable: %zu\n", sizeof (char));    /* size 1 bytes */
    printf("Size of short pointer: %zu\n", sizeof (short*));  /* size 4 bytes */
    printf("Size of short variable: %zu\n", sizeof (short));  /* size 2 bytes */
    return 0;
}

(OBS: om du använder Microsoft Visual Studio, som inte stöder C99- eller C11-standarderna, måste du använda %Iu 1 istället för %zu i exemplet ovan.)

Observera att resultaten ovan kan variera från miljö till miljö i antal men alla miljöer skulle visa lika stora storlekar för olika typer av pekare.

Utdrag baserat på information från Cardiff University C Pointers Introduction

Pekare och matriser

Pekare och matriser är intimt kopplade i C. Matriser i C hålls alltid på sammanhängande platser i minnet. Pekarens aritmetik skalas alltid efter storleken på det objekt som pekas på. Så om vi har en grupp med tre dubblar och en pekare till basen, *ptr hänvisar till den första dubbelen, *(ptr + 1) till den andra, *(ptr + 2) till den tredje. En mer bekväm notering är att använda arraynotation [] .

double point[3] = {0.0, 1.0, 2.0};
double *ptr = point;

/* prints x 0.0, y 1.0 z 2.0 */ 
printf("x %f y %f z %f\n", ptr[0], ptr[1], ptr[2]);

Så i huvudsak är ptr och matrisnamnet utbytbara. Denna regel innebär också att en matris sönderfaller till en pekare när den överförs till en subrutin.

double point[3] = {0.0, 1.0, 2.0};

printf("length of point is %s\n", length(point));

/* get the distance of a 3D point from the origin */ 
double length(double *pt)
{
   return sqrt(pt[0] * pt[0] + pt[1] * pt[1] + pt[2] * pt[2])
}

En pekare kan peka på vilket element som helst i en matris eller på elementet bortom det sista elementet. Det är emellertid ett fel att ställa in en pekare till något annat värde, inklusive elementet före matrisen. (Anledningen är att på segmenterade arkitekturer adressen innan det första elementet kan korsa en segmentgräns, ser kompilatorn att det inte händer för det sista elementet plus ett).


Fotnot 1: Information om Microsoft-format kan hittas via printf() och syntax för formatspecifikation .

Polymorfiskt beteende med ogiltiga pekare

qsort() är ett bra exempel på hur man kan använda tomrumspekare för att få en enda funktion att fungera på ett stort antal olika typer.

void qsort (
    void *base,                                 /* Array to be sorted */
    size_t num,                                 /* Number of elements in array */
    size_t size,                                /* Size in bytes of each element */
    int (*compar)(const void *, const void *)); /* Comparison function for two elements */

Arrayen som ska sorteras överförs som en ogiltig pekare, så en grupp av alla typer av element kan manövreras. De nästa två argumenten berättar qsort() hur många element det bör förvänta sig i matrisen och hur stort, i byte, varje element är.

Det sista argumentet är en funktionspekare till en jämförelsefunktion som i sig tar två ogiltiga pekare. Genom att få den som ringer tillhandahåller denna funktion kan qsort() effektivt sortera element av vilken typ som helst.

Här är ett exempel på en sådan jämförelsefunktion, för att jämföra flottörer. Observera att alla jämförelsefunktioner som skickas till qsort() måste ha denna typsignatur. Hur det görs polymorf är genom att kasta de ogiltiga pekarargumenten till pekare av den typ av element vi vill jämföra.

int compare_floats(const void *a, const void *b)
{
    float fa = *((float *)a);
    float fb = *((float *)b);
    if (fa < fb)
        return -1;
    if (fa > fb)
        return 1;
    return 0;
}

Eftersom vi vet att qsort kommer att använda den här funktionen för att jämföra flottörer, kastar vi de ogiltiga pekarargumenten tillbaka till float-pekare innan du återfester dem.

Nu är användningen av den polymorfa funktionen qsort på en matris "matris" med längden "len" mycket enkel:

qsort(array, len, sizeof(array[0]), compare_floats);


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow