Il nome di un array è un puntatore?


203

Il nome di un array è un puntatore in C? In caso contrario, qual è la differenza tra il nome di un array e una variabile puntatore?


4
No. Ma array è lo stesso e array [0]

36
@pst: &array[0]produce un puntatore, non un array;)
jalf

28
@Nava (e pst): array e & array [0] non sono proprio gli stessi. Caso in questione: sizeof (array) e sizeof (& array [0]) danno risultati diversi.
Thomas Padron-McCarthy,

1
@Thomas è d'accordo, ma in termini di puntatori, quando si dereference array e & array [0], producono lo stesso valore di array [0] .ie * array == array [0]. Nessuno intendeva dire che questi due puntatori sono uguali, ma in questo caso specifico (che punta al primo elemento) è possibile utilizzare il nome dell'array.
Nava Carmon,

1
Questi potrebbero anche aiutarti a capire: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah,

Risposte:


255

Un array è un array e un puntatore è un puntatore, ma nella maggior parte dei casi i nomi degli array vengono convertiti in puntatori. Un termine spesso usato è che decadono in puntatori.

Ecco un array:

int a[7];

a contiene spazio per sette numeri interi e puoi inserire un valore in uno di essi con un compito, in questo modo:

a[3] = 9;

Ecco un puntatore:

int *p;

pnon contiene spazi per numeri interi, ma può puntare a uno spazio per un numero intero. Ad esempio, possiamo impostarlo in modo che punti a uno dei punti dell'array a, come il primo:

p = &a[0];

Ciò che può confondere è che puoi anche scrivere questo:

p = a;

Questo non copia il contenuto dell'array anel puntatore p(qualunque cosa significhi). Al contrario, il nome dell'array aviene convertito in un puntatore al suo primo elemento. Quindi quel compito fa lo stesso del precedente.

Ora puoi usare pin modo simile a un array:

p[3] = 17;

Il motivo per cui funziona è che l'operatore di dereferenziazione dell'array in C [ ], è definito in termini di puntatori. x[y]significa: inizia con il puntatore x, yfai avanzare gli elementi dopo ciò a cui punta il puntatore, quindi prendi tutto quello che c'è. Utilizzando la sintassi aritmetica del puntatore, si x[y]può anche scrivere come *(x+y).

Affinché ciò funzioni con un array normale, come il nostro a, il nome ain a[3]deve prima essere convertito in un puntatore (nel primo elemento in a). Quindi avanziamo di 3 elementi e prendiamo qualunque cosa ci sia. In altre parole: prendere l'elemento nella posizione 3 dell'array. (Qual è il quarto elemento dell'array, poiché il primo è numerato 0.)

Quindi, in sintesi, i nomi di array in un programma C vengono (nella maggior parte dei casi) convertiti in puntatori. Un'eccezione è quando utilizziamo l' sizeofoperatore su un array. Se afosse convertito in un puntatore in questo contesto, sizeof adarebbe le dimensioni di un puntatore e non dell'array reale, il che sarebbe piuttosto inutile, quindi in quel caso asignifica l'array stesso.


5
Una conversione automatica simile viene applicata ai puntatori a funzione - entrambi functionpointer()e (*functionpointer)()significano la stessa cosa, stranamente.
Carl Norum,

3
Non ha chiesto se array e puntatori sono uguali, ma se il nome di un array è un puntatore
Ricardo Amores,

32
Il nome di un array non è un puntatore. È un identificatore per una variabile di tipo array, che ha una conversione implicita in puntatore del tipo di elemento.
Pavel Minaev,

29
Inoltre, a parte sizeof()l'altro contesto in cui non esiste un decadimento del puntatore array-> è l'operatore &- nel tuo esempio sopra, &asarà un puntatore a un array di 7 int, non un puntatore a un singolo int; cioè, sarà il suo tipo int(*)[7], a cui non è implicitamente convertibile int*. In questo modo, le funzioni possono effettivamente portare i puntatori ad array di dimensioni specifiche e applicare la restrizione tramite il sistema di tipi.
Pavel Minaev,

3
@ onmyway133, controlla qui per una breve spiegazione e ulteriori citazioni.
Carl Norum,

36

Quando un array viene utilizzato come valore, il suo nome rappresenta l'indirizzo del primo elemento.
Quando un array non viene utilizzato come valore, il suo nome rappresenta l'intero array.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

20

Se un'espressione di tipo array (come il nome dell'array) appare in un'espressione più grande e non è l'operando dell'operatore &o sizeof, allora il tipo dell'espressione array viene convertito da "array N-element di T" in "pointer to T", e il valore dell'espressione è l'indirizzo del primo elemento dell'array.

In breve, il nome dell'array non è un puntatore, ma nella maggior parte dei contesti viene trattato come se fosse un puntatore.

modificare

Rispondere alla domanda nel commento:

Se uso sizeof, conto la dimensione dei soli elementi dell'array? Quindi l'array "head" occupa anche spazio con le informazioni sulla lunghezza e un puntatore (e questo significa che occupa più spazio di un normale puntatore)?

Quando si crea un array, l'unico spazio allocato è lo spazio per gli elementi stessi; non viene materializzata alcuna memoria per un puntatore separato o metadati. Dato

char a[10];

quello che ottieni in memoria è

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

L' espressione si a riferisce all'intero array, ma non esiste alcun oggetto a separato dagli elementi dell'array stessi. Pertanto, sizeof afornisce la dimensione (in byte) dell'intero array. L'espressione &afornisce l'indirizzo dell'array, che è lo stesso dell'indirizzo del primo elemento . La differenza tra &ae &a[0]è il tipo del risultato 1 - char (*)[10]nel primo caso e char *nel secondo.

Il punto in cui le cose si fanno strane è quando si desidera accedere a singoli elementi - l'espressione a[i]è definita come il risultato di *(a + i)- dato un valore di indirizzo a, ielementi di offset ( non byte ) da quell'indirizzo e dereference il risultato.

Il problema è che anon è un puntatore o un indirizzo: è l'intero oggetto array. Pertanto, la regola in C che ogni volta che il compilatore vede un'espressione di tipo array (come a, che ha tipo char [10]) e quell'espressione non è l'operando degli operatori sizeofunari o &, il tipo di quell'espressione viene convertito ("decadimenti") a un tipo di puntatore ( char *) e il valore dell'espressione è l'indirizzo del primo elemento dell'array. Pertanto, l' espressione a ha lo stesso tipo e valore dell'espressione &a[0](e, per estensione, l'espressione *aha lo stesso tipo e valore dell'espressione a[0]).

C è stato derivato da un linguaggio in precedenza chiamato B, e in B a è stato un oggetto puntatore separato dagli elementi dell'array a[0], a[1]ecc Ritchie ha voluto mantenere la semantica di array di B, ma non voleva fare confusione con l'archiviazione l'oggetto puntatore separato. Quindi se ne è liberato. Al contrario, il compilatore convertirà le espressioni di matrice in espressioni di puntatore durante la traduzione, se necessario.

Ricorda che ho detto che le matrici non memorizzano metadati delle loro dimensioni. Non appena quell'espressione di matrice "decade" in un puntatore, tutto ciò che hai è un puntatore a un singolo elemento. Quell'elemento può essere il primo di una sequenza di elementi oppure può essere un singolo oggetto. Non c'è modo di saperlo in base al puntatore stesso.

Quando si passa un'espressione di array a una funzione, tutta la funzione che riceve è un puntatore al primo elemento - non ha idea di quanto sia grande l'array (ecco perché la getsfunzione era una tale minaccia e alla fine è stata rimossa dalla libreria). Affinché la funzione sappia quanti elementi ha l'array, è necessario utilizzare un valore sentinella (come il terminatore 0 nelle stringhe C) oppure passare il numero di elementi come parametro separato.


  1. Quale * può * influire sull'interpretazione del valore dell'indirizzo - dipende dalla macchina.

Ho cercato per molto tempo questa risposta. Grazie! E se sai, potresti dire un po 'di più che cosa è un'espressione array. Se uso sizeof, conto la dimensione dei soli elementi dell'array? Quindi l'array "head" occupa anche spazio con le informazioni sulla lunghezza e un puntatore (e questo significa che occupa più spazio di un normale puntatore)?
Andriy Dmytruk,

E un'altra cosa. Un array di lunghezza 5 è di tipo int [5]. Quindi è da dove conosciamo la lunghezza quando chiamiamo sizeof (array) - dal suo tipo? E questo significa che matrici di diversa lunghezza sono come diversi tipi di costanti?
Andriy Dmytruk,

@AndriyDmytruk: sizeofè un operatore e valuta il numero di byte nell'operando (un'espressione che indica un oggetto o un nome di tipo tra parentesi). Quindi, per un array, sizeofvaluta il numero di elementi moltiplicato per il numero di byte in un singolo elemento. Se an ha intuna larghezza di 4 byte, una matrice di 5 elementi intoccupa 20 byte.
John Bode,

Anche l'operatore non è [ ]speciale? Ad esempio, int a[2][3];quindi per x = a[1][2];, sebbene possa essere riscritto come x = *( *(a+1) + 2 );, qui anon viene convertito in un tipo di puntatore int*(sebbene se aè un argomento di una funzione in cui dovrebbe essere convertito int*).
Stan,

2
@Stan: l'espressione aha tipo int [2][3], che "decade" da digitare int (*)[3]. L'espressione *(a + 1)ha tipo int [3], che "decade" int *. Quindi, *(*(a + 1) + 2)avrà tipo int. apunta al primo array di 3 elementi di int, a + 1punta al secondo array di 3 elementi di int, *(a + 1) è il secondo array di 3 elementi di int, *(a + 1) + 2punta al terzo elemento del secondo array di int, quindi *(*(a + 1) + 2) è il terzo elemento del secondo array di int. Il modo in cui viene mappato al codice macchina dipende interamente dal compilatore.
John Bode,

5

Un array dichiarato come questo

int a[10];

alloca memoria per 10 ints. Non è possibile modificare ama è possibile eseguire l'aritmetica del puntatore con a.

Un puntatore come questo alloca memoria solo per il puntatore p:

int *p;

Non alloca alcun ints. Puoi modificarlo:

p = a;

e usa gli array di array come puoi con un:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

2
Le matrici non sono sempre allocate nello stack. Yhat è un dettaglio di implementazione che varierà da compilatore a compilatore. Nella maggior parte dei casi, gli array statici o globali verranno allocati da un'area di memoria diversa rispetto allo stack. Le matrici di tipi const possono essere allocate da un'altra regione di memoria
Mark Bessey,

1
Penso che Grumdrig intendesse dire "alloca 10 ints con durata di memorizzazione automatica".
Razze di leggerezza in orbita

4

Il nome dell'array da solo fornisce una posizione di memoria, quindi è possibile trattare il nome dell'array come un puntatore:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

E altre cose interessanti che puoi fare al puntatore (ad esempio aggiungendo / sottraendo un offset), puoi anche fare un array:

printf("value at memory location %p is %d", a + 1, *(a + 1));

Dal punto di vista del linguaggio, se C non esponesse l'array come una sorta di "puntatore" (pedanticamente è solo una posizione di memoria. Non può indicare una posizione arbitraria in memoria, né può essere controllata dal programmatore). Dobbiamo sempre codificare questo:

printf("value at memory location %p is %d", &a[1], a[1]);

1

Penso che questo esempio faccia luce sul problema:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

Compila bene (con 2 avvertimenti) in gcc 4.9.2 e stampa quanto segue:

a == &a: 1

oops :-)

Quindi, la conclusione è no, l'array non è un puntatore, non è archiviato in memoria (nemmeno uno di sola lettura) come puntatore, anche se sembra che sia, poiché puoi ottenere il suo indirizzo con l'operatore & . Ma - oops - quell'operatore non funziona :-)), in entrambi i casi, sei stato avvisato:

p.c: In function main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

C ++ rifiuta tali tentativi con errori in fase di compilazione.

Modificare:

Questo è ciò che intendevo dimostrare:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

Anche se ce a"punta" alla stessa memoria, è possibile ottenere l'indirizzo del cpuntatore, ma non è possibile ottenere l'indirizzo del apuntatore.


1
"Si compila bene (con 2 avvisi)". Non va bene. Se si dice a gcc di compilarlo come standard C corretto aggiungendo -std=c11 -pedantic-errors, si ottiene un errore del compilatore per la scrittura di codice C non valido. Il motivo è perché si tenta di assegnare int (*)[3]a una variabile di int**, che sono due tipi che non hanno assolutamente nulla a che fare l'uno con l'altro. Quindi, cosa dovrebbe dimostrare questo esempio, non ne ho idea.
Lundin

Grazie Lundin per il tuo commento. Sai che ci sono molti standard. Ho cercato di chiarire cosa volevo dire nella modifica. Il int **tipo non è il punto lì, si dovrebbe usare meglio void *per questo.
Palo,

-3

Il nome dell'array si comporta come un puntatore e punta al primo elemento dell'array. Esempio:

int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0

Entrambe le istruzioni di stampa forniranno esattamente lo stesso risultato per una macchina. Nel mio sistema ha dato:

0x7fff6fe40bc0

-4

Un array è una raccolta di elementi secuential e contigui in memoria. In C il nome di un array è l'indice del primo elemento e applicando un offset è possibile accedere al resto degli elementi. Un "indice del primo elemento" è in effetti un puntatore a una direzione della memoria.

La differenza con le variabili del puntatore è che non è possibile modificare la posizione a cui punta il nome dell'array, quindi è simile a un puntatore const (è simile, non è lo stesso. Vedere il commento di Mark). Ma anche che non è necessario dereferenziare il nome dell'array per ottenere il valore se si utilizza l'aritmetica del puntatore:

char array = "hello wordl";
char* ptr = array;

char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'

Quindi la risposta è un po 'sì.


1
Il nome di un array non corrisponde a un puntatore const. Dato: int a [10]; int * p = a; sizeof (p) e sizeof (a) non sono uguali.
Mark Bessey,

1
Ci sono altre differenze In generale, è meglio attenersi alla terminologia utilizzata dallo standard C, che in particolare lo definisce una "conversione". Citazione: "Tranne quando è l'operando dell'operatore sizeof o unario e operatore, oppure è una stringa letterale utilizzata per inizializzare un array, un'espressione che ha il tipo '' array di tipo '' viene convertita in un'espressione con tipo ' 'puntatore al tipo' 'che punta all'elemento iniziale dell'oggetto array e non è un valore. Se l'oggetto array ha una classe di archiviazione del registro, il comportamento non è definito. "
Pavel Minaev,

-5

Il nome dell'array è l'indirizzo del primo elemento di un array. Quindi sì il nome dell'array è un puntatore const.

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.