C puntatore all'array / array di puntatori disambiguazione


463

Qual è la differenza tra le seguenti dichiarazioni:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

Qual è la regola generale per comprendere dichiarazioni più complesse?


54
Ecco un ottimo articolo sulla lettura di dichiarazioni complesse in C: unixwiz.net/techtips/reading-cdecl.html
jesper,

@jesper Sfortunatamente, le qualificazioni conste volatile, che sono sia importanti che difficili, mancano in quell'articolo.
non utente

@ non un utente quelli non sono rilevanti per questa domanda. Il tuo commento non è pertinente. Per favore, astenersi.
user64742

Risposte:


439
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

Il terzo è uguale al primo.

La regola generale è la precedenza dell'operatore . Può diventare ancora più complesso quando i puntatori a funzione entrano in scena.


4
Quindi, per sistemi a 32 bit: int * arr [8]; / * 8x4 byte assegnati, per ogni puntatore / int (* arr) [8]; / 4 byte assegnati, solo un puntatore * /
George,

10
No. int * arr [8]: 8x4 byte allocati totali , 4 byte per ogni puntatore. int (* arr) [8] ha ragione, 4 byte.
Mehrdad Afshari,

2
Avrei dovuto rileggere quello che ho scritto. Intendevo 4 per ogni puntatore. Grazie per l'aiuto!
George,

4
Il motivo per cui il primo è uguale all'ultimo è che è sempre consentito avvolgere le parentesi attorno ai dichiaratori. P [N] è un dichiaratore di array. P (....) è un dichiaratore di funzioni e * P è un dichiaratore di puntatori. Quindi tutto quanto segue è uguale a senza parentesi (tranne per una delle funzioni '"()": int (((* p))); void ((g (void))); int * (a [1]); void (* (p ())).
Johannes Schaub - litb

2
Ben fatto nella tua spiegazione. Per un riferimento approfondito su precedenza e associatività degli operatori, fare riferimento a pagina 53 di The C Programming Language (ANSI C seconda edizione) di Brian Kernighan e Dennis Ritchie. Gli operatori si ( ) [ ] associano da sinistra a destra e hanno una precedenza più elevata rispetto a quella *letta int* arr[8]come una matrice di dimensioni 8 in cui ogni elemento punta a un int e int (*arr)[8]come puntatore a una matrice di dimensioni 8 che contiene numeri interi
Mushy,

267

Usa il programma cdecl , come suggerito da K&R.

$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>

Funziona anche nell'altro modo.

cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )

@ankii La maggior parte delle distribuzioni Linux dovrebbe avere un pacchetto. Potresti anche creare il tuo binario.
sigjuice,

ah scusa per non aver parlato, macOS qui. vedrà se disponibile, altrimenti anche il sito Web va bene. ^^ grazie per avermelo fatto notare. Sentiti libero di segnalare NLN.
ankii,

2
@ankii Puoi installarlo da Homebrew (e forse MacPorts?). Se quelli non sono di tuo gusto, è banale crearne uno tuo dal link Github in alto a destra su cdecl.org (l'ho appena creato su macOS Mojave). Quindi copia semplicemente il binario cdecl sul tuo PERCORSO. Consiglio $ PATH / bin, perché non è necessario coinvolgere root in qualcosa di così semplice.
sigjuice,

Oh, non avevo letto il piccolo paragrafo sull'installazione in readme. solo alcuni comandi e flag per la gestione delle dipendenze. Installato usando brew. :)
ankii,

1
Quando ho letto questo per la prima volta ho pensato: "Non scenderò mai a questo livello". Il giorno dopo l'ho scaricato.
Ciro Santilli 27 冠状 病 六四 事件 法轮功

126

Non so se abbia un nome ufficiale, ma lo chiamo il Thingy (TM) di destra e di sinistra.

Inizia dalla variabile, poi vai a destra, a sinistra, a destra ... e così via.

int* arr1[8];

arr1 è un array di 8 puntatori a numeri interi.

int (*arr2)[8];

arr2 è un puntatore (la parentesi blocca il lato destro-sinistro) verso un array di 8 numeri interi.

int *(arr3[8]);

arr3 è un array di 8 puntatori a numeri interi.

Questo dovrebbe aiutarti con dichiarazioni complesse.


19
L'ho sentito fare riferimento al nome di "La regola a spirale", che può essere trovato qui .
Fouric

6
@InkBlend: la regola della spirale è diversa dalla regola destra-sinistra . Il primo fallisce in casi come int *a[][10]mentre il secondo ha successo.
legends2k

1
@dogeen Pensavo che quel termine avesse a che fare con Bjarne Stroustrup :)
Anirudh Ramanathan,

1
Come dicevano InkBlend e legends2k, questa è Spiral Rule che è più complessa e non funziona in tutti i casi, quindi non c'è motivo di usarla.
kotlomoy,

Non dimenticare l'associazione di ( ) [ ]sinistra a destra di e di destra a sinistra di* &
Mushy,

28
int *a[4]; // Array of 4 pointers to int

int (*a)[4]; //a is a pointer to an integer array of size 4

int (*a[8])[5]; //a is an array of pointers to integer array of size 5 

Il terzo non dovrebbe essere: a è un array di puntatori ad un array intero di dimensioni 8? Voglio dire, ciascuno degli array di interi sarà della dimensione 8 giusto?
Rushil Paul,

2
@Rushil: no, l'ultimo pedice ( [5]) rappresenta la dimensione interna. Ciò significa che (*a[8])è la prima dimensione, quindi la rappresentazione esterna dell'array. Ciò a cui a punta ciascun elemento è una matrice intera diversa di dimensione 5.
zeboidlund

Grazie per il terzo. Sto cercando come scrivere array di puntatori su array.
Deqing,

15

La risposta per gli ultimi due può anche essere dedotta dalla regola d'oro in C:

La dichiarazione segue l'uso.

int (*arr2)[8];

Cosa succede se fai la dereferenza arr2? Ottieni una matrice di 8 numeri interi.

int *(arr3[8]);

Cosa succede se prendi un elemento da arr3? Ottieni un puntatore a un numero intero.

Questo aiuta anche quando si tratta di puntatori a funzioni. Per prendere l'esempio di sigjuice:

float *(*x)(void )

Cosa succede quando si dereference x? Ottieni una funzione che puoi chiamare senza argomenti. Cosa succede quando lo chiami? Restituirà un puntatore a float.

La precedenza dell'operatore è sempre difficile, però. Tuttavia, l'uso delle parentesi può anche essere fonte di confusione perché la dichiarazione segue l'uso. Almeno, per me, arr2sembra intuitivamente come un array di 8 puntatori a inte, ma in realtà è il contrario. Ci vuole solo un po 'per abituarsi. Motivo sufficiente per aggiungere sempre un commento a queste dichiarazioni, se me lo chiedi :)

modifica: esempio

A proposito, mi sono appena imbattuto nella seguente situazione: una funzione che ha una matrice statica e che utilizza l'aritmetica del puntatore per vedere se il puntatore di riga è fuori limite. Esempio:

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

#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))

int *
put_off(const int newrow[2])
{
    static int mymatrix[3][2];
    static int (*rowp)[2] = mymatrix;
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);

    memcpy(rowp, newrow, sizeof(*rowp));
    rowp += 1;
    if (rowp == border) {
        rowp = mymatrix;
    }

    return *rowp;
}

int
main(int argc, char *argv[])
{
    int i = 0;
    int row[2] = {0, 1};
    int *rout;

    for (i = 0; i &lt; 6; i++) {
        row[0] = i;
        row[1] += i;
        rout = put_off(row);
        printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
    }

    return 0;
}

Produzione:

0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]

Si noti che il valore di border non cambia mai, quindi il compilatore può ottimizzarlo via. Questo è diverso da quello che potresti voler usare inizialmente const int (*border)[3]:: che dichiara il bordo come un puntatore a un array di 3 numeri interi che non cambieranno valore finché esiste la variabile. Tuttavia, tale puntatore può essere puntato su qualsiasi altro array in qualsiasi momento. Vogliamo quel tipo di comportamento per l'argomento, invece (perché questa funzione non cambia nessuno di questi numeri interi). La dichiarazione segue l'uso.

(ps: sentiti libero di migliorare questo campione!)



3

Come regola generale, gli operatori unari destra (come [], (), ecc) prendere preferenza rispetto a quelle di sinistra. Quindi, int *(*ptr)()[];sarebbe un puntatore che punta a una funzione che restituisce una matrice di puntatori a int (ottenere gli operatori giusti appena possibile quando si esce dalla parentesi)


Questo è vero, ma è anche ilegal. Non è possibile avere una funzione che restituisce un array. Ho provato e ottenuto questo: error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];sotto GCC 8 con$ gcc -std=c11 -pedantic-errors test.c
Cacahuete Frito

1
Il motivo del compilatore per dare quell'errore è che sta interpretando la funzione come restituire un array, come afferma l'interpretazione corretta della regola di precedenza. È ilegal come una dichiarazione, ma la dichiarazione legale int *(*ptr)();consente un'espressione simile p()[3](o (*p)()[3]) da utilizzare in seguito.
Luis Colorado,

Ok, se lo capisco, stai parlando di creare una funzione che restituisce un puntatore al primo elemento di un array (non un array stesso), e in seguito usi quella funzione come se stesse restituendo un array? Idea interessante. Lo proverò. int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }e chiamalo così: foo(arr)[4];quale dovrebbe contenere arr[2][4], giusto?
Cacahuete Frito,

giusto ... ma avevi ragione anche tu, e la dichiarazione era ilegal. :)
Luis Colorado,

2

Penso che possiamo usare la semplice regola ..

example int * (*ptr)()[];
start from ptr 

" ptrè un puntatore a" vai verso destra ..it ")" ora vai a sinistra è un "(" esci vai a destra "()" così "a una funzione che non accetta argomenti" vai a sinistra "e restituisce un puntatore" vai a destra "in un array" vai a sinistra "di numeri interi"


Lo migliorerei un po ': "ptr è un nome che si riferisce a" vai a destra ... è ), ora vai a sinistra ... è *"un puntatore a" vai a destra ... è ), ora vai a sinistra ... è a (come out, vai a destra ()così "verso una funzione che non accetta argomenti" vai a destra ... []"e restituisce una matrice di" vai a destra ;, quindi vai a sinistra ... *"puntatori a" vai a sinistra ... int"numeri interi
Cacahuete Frito,


2

Ecco come lo interpreto:

int *something[n];

Nota sulla precedenza: l'operatore di sottoscrizione dell'array ( []) ha una priorità più alta dell'operatore di dereference ( *).

Quindi, qui applicheremo il []precedente *, rendendo la dichiarazione equivalente a:

int *(something[i]);

Nota su come ha senso una dichiarazione: int numsignifica numè unint , int *ptro int (*ptr)mezzo, (valore at ptr) è un int, che indica un ptrpuntatore a int.

Questo può essere letto come, (il valore di (valore all'indice del qualcosa)) è un numero intero. Quindi, (valore all'ultimo indice di qualcosa) è un (puntatore intero), che rende il qualcosa un array di puntatori interi.

Nel secondo,

int (*something)[n];

Per dare un senso a questa affermazione, devi conoscere questo fatto:

Nota sulla rappresentazione puntatore dell'array: somethingElse[i]è equivalente a*(somethingElse + i)

Quindi, sostituendo somethingElsecon (*something), otteniamo *(*something + i), che è un numero intero come da dichiarazione. Quindi, (*something)ci ha dato un array, che rende equivalente a (puntatore a un array) .


0

Immagino che la seconda dichiarazione sia confusa per molti. Ecco un modo semplice per capirlo.

Consente di disporre di una matrice di numeri interi, ad es int B[8].

Diamo anche una variabile A che punta a B. Ora, il valore in A è B, cioè (*A) == B. Quindi A punta a una matrice di numeri interi. Nella tua domanda, arr è simile ad A.

Allo stesso modo, in int* (*C) [8], C è un puntatore a un array di puntatori a numeri interi.


0
int *arr1[5]

In questa dichiarazione, arr1è un array di 5 puntatori a numeri interi. Motivo: le parentesi quadre hanno una precedenza maggiore su * (operatore di dereferncing). E in questo tipo, il numero di righe è fisso (5 qui), ma il numero di colonne è variabile.

int (*arr2)[5]

In questa dichiarazione, arr2è un puntatore a un array intero di 5 elementi. Motivo: qui, le parentesi () hanno una precedenza maggiore di []. E in questo tipo, il numero di righe è variabile, ma il numero di colonne è fisso (5 qui).


-7

Nel puntatore a un numero intero se il puntatore viene incrementato, passa al numero intero successivo.

nell'array di puntatore se il puntatore viene incrementato, passa all'array successivo


" nella matrice del puntatore se il puntatore viene incrementato, passa alla matrice successiva " questo è chiaramente sbagliato.
aprile
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.