2015-07-04 2 views
14

Stavo facendo alcuni esercizi con puntatori e matrici in C e ho notato che tutti i miei quattro metodi restituivano la stessa risposta. La mia domanda è che ci sono degli svantaggi nell'utilizzare uno dei miei metodi qui sotto? Sono sbalordito da come tutti questi quattro mi danno lo stesso risultato. Ho appena notato che puoi usare un puntatore come se fosse un array e puoi anche usare un array come se fosse un puntatore?Puntatori e matrici in C, Necessità di ulteriori informazioni

char *name = "Madonah"; 
int i= 0; 
for (i=0;i<7; i++){ 
    printf("%c", *(name+i)); 
} 

char name1 [7] = "Madonah"; 
printf("\n"); 
int j= 0; 
for (j=0;j<7; j++){ 
    printf("%c", name1[j]); 
} 

char *name2 = "Madonah"; 
printf("\n"); 
int k= 0; 
for (k=0;k<7; k++){ 
    printf("%c", name2[k]); 
} 

char name3 [7] = "Madonah"; 
printf("\n"); 
int m= 0; 
for (m=0;m<7; m++){ 
    printf("%c", *(name+m)); 
} 

Risultati:

Madonah 
Madonah 
Madonah 
Madonah 
+0

utilizzare 'char * nome = malloc (sizeof (char) * 8); strcpy (name, "Madonah"); 'così puoi liberare lo spazio in seguito facilmente. – LittleByBlue

+3

Ricordarsi di allocare memoria per ''\ 0''. I valori letterali stringa in C vengono automaticamente aggiunti con ''\ 0'' –

+2

Nota: il primo e l'ultimo sono identici, tranne il nome della variabile indice (' i' vs. 'm'); 'name3' non è usato in questo codice. – WhozCraig

risposta

22

È vero che i puntatori e gli array sono equivalent in alcuni contesti, "equivalente" significa né che siano identici e nemmeno intercambiabili. Gli array non sono puntatori.
È l'aritmetica dei puntatori e l'indicizzazione degli array che sono equivalenti, i puntatori e gli array sono diversi.

quale è preferibile e vantaggi/svantaggi?

Dipende dal modo in cui si desidera utilizzarli. Se non si vuole modificare stringa quindi è possibile utilizzare

char *name = "Madonah"; 

E 'sostanzialmente equivalente a

char const *name = "Madonah"; 

*(name + i) e name[i] entrambi sono uguali. Preferisco name[i] su *(name + i) in quanto è ordinato e utilizzato più frequentemente dai programmatori C e C++.

Se vi piace modificare la stringa allora si può andare con

char name1[] = "Madonah"; 
+5

Mentre 'char * name =" Madonah ";' e 'char const * name =" Madonah ";' sono per lo più equivalenti, raccomanderei di usare quest'ultimo quando possibile; un programma C non deve tentare di modificare la memoria sottostante a un valore letterale stringa e 'const' ti aiuta ad evitare di infrangere involontariamente quella regola. –

+3

Qui ci sono numerosi problemi: 1) 'const' = costante, ovvero che non si intende modificare il valore della variabile. 2) 'const' dice al compilatore di non aspettarsi modifiche al valore, quindi può ottimizzare le cose dietro le quinte. 3) 'const' può anche/genera avvisi se si tenta di modificarlo. 4) 'char * name =" a "' e 'char name [] =" a "' sono identici, ma l'ultimo è preferito per stringhe/buffer inizializzati. 5) "voglio" non è una parola. – Geoffrey

+1

@Geoffrey; Direi di fare qualche ricerca qui su SO per tutti i tuoi punti da 1 a 4. Grazie per il punto 5. – haccks

2

In tutti i casi, in C pointer + index è lo stesso pointer[index]. Inoltre, in C, un nome di matrice utilizzato in un'espressione viene considerato come puntatore al primo elemento dell'array. Le cose diventano un po 'più mistificanti se si considera che l'addizione è commutativa, il che rende anche legale index + pointer e index[pointer] legale. Generalmente i compilatori generano un codice simile, non importa come lo scrivi.

Ciò consente di aggiungere cose interessanti come "hello"[2] == 'l' e 2["hello"] == 'l'.

+0

Questo "*' pointer + index' è lo stesso di 'pointer [index]' * "non è corretto. Dovrebbe o leggere "* dereferencing' pointer + index' è lo stesso di 'pointer [index]' * "o" * 'pointer + index' è lo stesso di prendere l'indirizzo di' pointer [index] '*". – alk

11

In C, a[b], b[a], *(a+b) sono equivalenti e non c'è alcuna differenza tra questi 3. modo da avere solo 2 casi:

char *name = "Madonah"; /* case 1 */ 

e

char name3 [7] = "Madonah"; /* case 2 */ 

Il primo è un puntatore che punta a una stringa letterale. Quest'ultimo è un array di 7 caratteri.

quale è preferito dipende dall'utilizzo di name3.

Se non intenzione di modificare la stringa è possibile utilizzare (1) e vorrei anche rendere const char* per chiarire e garantire la stringa letterale non viene modificato accidentalmente. Modifica stringa letterale è undefined behaviour in C

Se fai bisogno di modificarlo poi (2) deve essere utilizzato in quanto è un array di caratteri che è possibile modificare. Una cosa da notare è che nel caso (2), hai specificato esplicitamente la dimensione dell'array come 7. Ciò significa che l'array di caratteri name3 non ha un terminatore null (\0) alla fine. Pertanto non può essere utilizzato come stringa . Preferirei non specificare la dimensione della matrice e lasciare che il compilatore calcolarlo:

char name3 [] = "Madonah"; /* case 2 */ 
/* an array of 8 characters */ 
1

Per praticare puntatore e array di idiomi in C, questi sono tutti i blocchi di codice valido e illustrative dei diversi modi di esprimere la stessa cosa.

Per codice di produzione, non si utilizzerà nessuno di questi; useresti qualcosa che sia più facile da leggere per gli umani e che sia più probabile che venga ottimizzato dal compilatore. Si potrebbe fare a meno del ciclo del tutto e solo dire:

printf("%s", name); 

(Si noti che questo richiede name includere il carattere \0 alla fine della stringa, su cui si basa printf vostri name1 e name3 definizioni, come scritto,. non allocare gli 8 byte necessari.)

Se stavi cercando di fare qualcosa di complicato nel tuo codice che richiedesse uno dei metodi più prolissi che hai postato, quale metodo sceglieresti dipenderebbe esattamente da quale difficile cosa stavi cercando di fare (cosa che ovviamente spiegherebbe nei commenti del codice - altrimenti, guarderesti il ​​tuo codice sei mesi s più tardi e chiediti: "Che diavolo stavo facendo qui ??"). In generale, per estrarre i caratteri da una stringa, name[i] è un idioma più comune di *(name+i).

+2

perché stai stampando una stringa? invece di char? –

1
  1. char name[] = "Madonah";
  2. char* name = "Madonah";

Quando dichiarata all'interno di una funzione, la prima opzione produce operazioni aggiuntive ogni volta che la funzione viene chiamata. Questo perché il contenuto della stringa viene copiato dalla sezione dei dati RO nello stack ogni volta che viene chiamata la funzione.

Nella seconda opzione, il compilatore imposta semplicemente la variabile in modo che puntino all'indirizzo di quella stringa in memoria, che è costante durante l'esecuzione del programma.

Quindi, a meno che non abbiate intenzione di modificare il contenuto dell'array locale (non la stringa originale - quella di sola lettura), potete optare per la seconda opzione.

Si noti che tutti i dettagli di cui sopra dipendono dall'implementazione del compilatore e non sono dettati dallo standard in linguaggio C.

2

è conveniente utilizzare la sintassi di matrice per l'accesso casuale

int getvalue(int *a, size_t x){return a[x];} 

e la sintassi dei puntatori per l'accesso sequenziale

void copy_string(char *dest, const char *src){while((*dest++=*src++));} 

Molti compilatori possono ottimizzare gli accessi sequenziali puntatori migliore dell'uso di indici di array.

+0

Si noti che per un argomento, 'char * x' e' char x [] 'sono identici. Per l'array, 'x' è regolato su un puntatore. – Olaf

+0

@Olaf eccetto che troppo spesso l'indice dell'array non è in size_t (o ptrdiff_t) e incorre in un'istruzione di movimento extra. – technosaurus

+0

Non aggiungerei mai l'ottimizzazione qui. I compilatori moderni molto probabilmente convertiranno gli accessi indicizzati all'aritmetica del puntatore, quindi non c'è alcuna differenza reale. Per i tipi di variabili dell'indice: sono d'accordo, ma preferirei size_t in realtà, ma 'ptrdiff_t' è un punto interessante, a cui non ho pensato. – Olaf

1

posso sapere che uno è facile da usare [...]

Prova attaccare di utilizzare l'operatore di indicizzazione [].

Una volta il codice diventa più complesso e si nota che le cose sono "più facili" da codificare utilizzando le espressioni aritmetiche del puntatore.

Ciò si verifica in genere anche quando si inizia a utilizzare l'operatore di indirizzo: &.

Se per esempio si vede la vostra auto nella necessità di codificare:

char s[] = "Modonah"; 
char * p = &s[2]; /* This gives you the address of the 'd'. */ 

noterete subito che è più "facile" da scrivere:

char * p = s + 2; /* This is the same as char * p = &s[2];. */ 
6

Proprio in aggiunta a quello che altri hanno detto , Aggiungerò un'immagine per una migliore illustrazione. Se si dispone di

char a[] = "hello"; 
char *p = "world"; 

Cosa succede nel primo caso la memoria sia sufficiente per a (6 caratteri) nello stack di solito, e la stringa "ciao" viene copiato nella memoria che inizia alle a. Quindi, è possibile modificare questa regione di memoria.

Nel secondo caso il "mondo" viene assegnato da qualche altra parte (di solito nella regione di sola lettura) e viene restituito un puntatore a tale memoria che viene semplicemente memorizzato in p. Non è possibile modificare la stringa letterale in questo caso tramite p.

Ecco come appare:

enter image description here

Ma per la tua domanda bastone per la notazione che è più facile, preferisco []. Ulteriori informazioni sulla relazione tra matrici e puntatori è here.

+1

Oh grazie per questo chiarimento. Ho imparato molto dai commenti che il diagramma spiega molto. Ho una struttura così ora so come usare []. –

+1

Ottimo! Mi stavo chiedendo perché le altre risposte non contenessero questi diagrammi. Questo è il modo per spiegare i puntatori e gli array. – rpax

0

Una matrice è solo una variabile che contiene diversi valori, ogni valore ha un indice, probabilmente lo sapete già.
I puntatori sono una di quelle cose di cui non hai bisogno di sapere finché non ti rendi conto che devi usarle. I puntatori non sono variabili essi stessi sono letteralmente indicatori di variabili.
Un esempio di come e perché si potrebbe voler utilizzare un puntatore.
Si crea una variabile in una funzione che si desidera utilizzare in un'altra funzione.
È possibile passare la variabile alla nuova funzione nell'intestazione della funzione. Copia efficacemente i valori dalla variabile originale a una nuova variabile locale alla nuova funzione.Le modifiche apportate ad essa nella nuova funzione modificano solo la nuova variabile nella nuova funzione.
Ma cosa succede se si desidera che le modifiche apportate alla nuova funzione modificino la variabile originale in cui si trova nella prima funzione?
Si utilizza un puntatore.
Invece di passare la variabile attuale alla nuova funzione, si passa un puntatore alla variabile. Ora le modifiche apportate alla variabile nella nuova funzione si riflettono nella variabile originale nella prima funzione.
Questo è il motivo per cui nel proprio esempio utilizzare il puntatore all'array e utilizzare l'array effettivo mentre nella stessa funzione ha risultati identici. Entrambi stanno dicendo "cambia questo array".