2010-07-15 6 views
22

La seguente dichiarazione in C:Sintassi del puntatore in C: perché * si applica solo alla prima variabile?

int* a, b; 

dichiareremo a come tipo int* e b come tipo int. Sono ben consapevole di questa trappola, ma quello che voglio sapere è perché funziona in questo modo. Perché non dichiara anche int* come , come la maggior parte delle persone si aspetterebbe intuitivamente? In altre parole, perché * si applica al nome della variabile, piuttosto che al tipo?

Certo si potrebbe scrivere in questo modo di essere più coerente di come è in realtà opere:

int *a, b; 

Tuttavia, io e tutti quelli che ho parlato a pensare in termini di una è di tipo " puntatore a int ", anziché a è un puntatore ad alcuni dati e il tipo di quei dati è" int ".

Questa è stata solo una pessima decisione dei progettisti di C o c'è qualche buona ragione per cui viene analizzata in questo modo? Sono certo che la domanda abbia avuto una risposta prima, ma non riesco a trovarla usando la ricerca.

+7

A dire il vero, se il linguaggio C ha modificato il tipo con '*, allora avremmo chiesto a" perché non 'int * a, b;' mi dai un puntatore e un intero? ". – bta

+2

@bta, sì, vorremmo - ma penso che avrebbe confuso meno persone. Ad ogni modo, il punto non è discutere la decisione che è stata presa 40 anni fa, è solo per capire se ci sono ragioni per le quali non sono a conoscenza. – EMP

+0

Si potrebbe leggere questo http://c-faq.com/decl/charstarws.html – Amarghosh

risposta

17

C'è una pagina web su The Development of the C Language che dice "La sintassi di queste dichiarazioni riflette l'osservazione che i, * pi e ** ppi generano tutti un tipo int quando vengono utilizzati in un'espressione". Cerca quella frase nella pagina per trovare la sezione pertinente che parla di questa domanda.

9

Ci può essere un ulteriore ragione storica, ma ho sempre inteso in questo modo:

Una dichiarazione, un tipo.

Se a, b, c, d e deve essere lo stesso tipo qui:

int a, b, c, d; 

Poi tutto sulla linea deve un intero come pure.

int a, *b, **c, ***d; 

Le 4 interi:

  1. un
  2. * b
  3. ** c
  4. *** d

Può essere correlato alla precedenza degli operatori, pure, o potrebbe essere stato a un certo punto nel passato.

+0

La domanda non era come l'hai capita. –

+0

@georg: è solo una formulazione diversa dei due precedenti. come l'hai capito? – eruciform

+3

@Georg, l'eruciforme è corretta. Da K & R C, "La dichiarazione del puntatore ip,' int * ip', è intesa come un mnemonico, dice che l'espressione '* ip' è un' int'. La sintassi della dichiarazione per una variabile imita la sintassi di espressioni in cui potrebbe apparire la variabile Questo * è * il motivo della sintassi. –

2

Il * modifica il nome della variabile, non lo specificatore del tipo. Questo è principalmente dovuto al modo in cui viene analizzato il *. Prendete queste dichiarazioni:

char* x; 
char *x; 

Queste dichiarazioni sono equivalenti. L'operatore * deve essere compreso tra l'identificatore del tipo e il nome della variabile (viene trattato come un operatore infisso), ma può andare su entrambi i lati dello spazio. Detto questo, la dichiarazione

int* a, b; 

non avrebbe b un puntatore, perché non c'è * adiacente ad essa. Il * funziona solo sugli oggetti su entrambi i lati di esso.

Inoltre, pensateci in questo modo: quando scrivete la dichiarazione int x;, state indicando che x è un numero intero. Se è un puntatore a un numero intero, allora *y è un numero intero. Quando scrivi int *y;, stai indicando che *y è un numero intero (che è quello che vuoi). Nella dichiarazione char a, *b, ***c;, si sta indicando che la variabile a, il valore senza autorizzazione di b e il valore triple-deferite di c sono tutti di tipo char. Dichiarare le variabili in questo modo rende l'uso dell'operatore stella (quasi) coerente con il dereferenziamento.

Sono d'accordo che sarebbe quasi più sensato che fosse il contrario. Per evitare questa trappola, mi sono fatto sempre una regola per dichiarare i puntatori su una linea da soli.

+2

Questo è lo status quo, ma * perché * è in questo modo? –

31

Le dichiarazioni C sono state scritte in questo modo in modo da utilizzare "dichiarazioni specchi". Questo è il motivo per dichiarare gli array come questo:

int a[10]; 

si dovesse avere invece la regola si propone, dove è sempre

type identifier, identifier, identifier, ... ; 

... quindi array dovrebbe logicamente devono essere dichiarate come questo :

int[10] a; 

che va bene, ma non rispecchia come si usa a. Si noti che questo vale anche per le funzioni, anche - dichiariamo funzioni in questo modo:

void foo(int a, char *b); 

piuttosto che

void(int a, char* b) foo; 

In generale, gli "specchi dichiarazione usano" regola significa che hai solo per ricordare un set di regole di associatività, che si applicano a entrambi gli operatori come *, [] e () quando si utilizza il valore e i token corrispondenti in dichiaratori come *, [] e ().


Dopo qualche ulteriore riflessione, penso che sia anche la pena sottolineare che l'ortografia "puntatore a int " come "int*" è solo una conseguenza della "specchi dichiarazione usano" in ogni caso. Se si dovesse utilizzare un altro stile di dichiarazione, sarebbe probabilmente più opportuno digitare "Puntatore su int" come "&int" o qualcosa di completamente diverso come "@int".

+1

Hmm, questo non mi sembra un buon motivo per me. Io * do * voglio dichiarare gli array come 'int [10]', che è come Java e C# lo fanno. Esistono altri esempi nella sintassi C dell'uso del mirroring delle dichiarazioni? – EMP

+0

@Evgeny, C è un linguaggio molto diverso da Java. Un array Java è completamente diverso. La variabile array contiene solo un riferimento modificabile a un array. Una variabile di array C * è * la matrice. Un altro esempio di "dichiarazione uso specchi" è una dichiarazione di una funzione che restituisce un puntatore a un numero intero. 'int * foo (int bar);'. Ciò riflette il fatto che '* foo (3)' è un int. Ti suggerisco di leggere K & R C 2nd ed., Dove questo è spiegato. –

+1

@Evgeny: l'altro esempio più ovvio sono le dichiarazioni di funzione, scritte per assomigliare alla chiamata di funzione. – caf

1

Perché se la dichiarazione

int* a, b; 

dovesse dichiarare b come un puntatore troppo, allora non avreste alcun modo per dichiarare

int* a; 
int b; 

su una sola riga.

D'altra parte, si può fare

int*a, *b; 

per ottenere quello che vuoi.

Pensateci in questo modo: il modo in cui è ora è ancora il modo più conciso e tuttavia unico per farlo. Questo è ciò che C è principalmente circa :)

+1

Sì, beh, allo stesso modo non è possibile dichiarare un int e un float sulla stessa linea - quindi cosa? In realtà, non puoi tenerli nella stessa * istruzione *, ma potresti avere una * linea * che dice 'int * a; int b; '- nessun problema. – EMP

2

suppongo che è legato alla sintassi completa dichiarazione per tipo modificatori:

int x[20], y; 
int (*fp)(), z; 

In questi esempi, ci si sente molto più evidente che i modificatori sono solo ad uno degli le dichiarazioni. Una ipotesi è che una volta che K & R ha deciso di progettare i modificatori in questo modo, è sembrato "corretto" che i modificatori riguardassero solo una dichiarazione.

Una nota a parte, mi sento di raccomandare solo te stesso limitando ad una variabile per dichiarazione:

int *x; 
int y; 
+0

Sì, è * più ovvio qui, ma in realtà, chi diavolo dichiara un puntatore a funzione e un int nella stessa frase? :) – EMP

+1

@Evgeny: ti manca il punto. Per la sintassi del puntatore di funzione è facile capire perché i modificatori influenzano solo una variabile. Se la lingua era incoerente e alcuni modificatori influiscono su una variabile, un altro modificatore influisce su tutte le variabili, sarebbe una cattiva progettazione. –

0

posso solo immaginare il motivo per cui si dovrebbe ripetere il * per ogni nome di variabile menzionato su una linea per fare tutti loro puntatori. Forse è dovuto a una decisione sulla coerenza linguistica? Lasciatemi illustrare con un esempio:


Si ipotizzi di avere una funzione foo dichiarato come segue:

int foo() { ... } 

Supponiamo anche che si desidera dichiarare due puntatori a funzione a questa funzione:

int (*fooptr1, fooptr2)(); 
// this doesn't work; and even if it did, what would the syntax possibly 
// look like to initializing them to function foo? 
// int (*fooptr1 = foo, fooptr2 = foo)() ? 

int (*fooptr1)() = foo, (*fooptr2)() = foo; 
// this works. 

In questo caso, è sufficiente deve ripetere l'intera dichiarazione del tipo B altre variabili, e non puoi sbagliare perché non c'è altro modo per farlo (data la sintassi della dichiarazione C così com'è).


Ora, forse si pensava, se ci sono casi in cui la dichiarazione del tipo deve essere ripetuta per tutte le variabili, forse questo dovrebbe essere solo il caso generale.

(non dimenticate che io sono solo indovinando qui.)

1

consideri la dichiarazione:

int *a[10]; 
int (*b)[10]; 

Il primo è un array di dieci puntatori a numeri interi, il secondo è un puntatore a una matrice di dieci interi.

Ora, se * fosse collegato alla dichiarazione del tipo, non sarebbe sintatticamente valido mettere tra parentesi una parentesi. Quindi dovresti trovare un altro modo per differenziare tra le due forme.

+3

"Va notato che la dichiarazione di un puntatore a un array non è così utile" Cosa? Con 'int (* b) [10]', puoi assegnare 'b' per puntare ad es. un elemento arbitrario di 'int ar [ROW_COUNT] [10]', e l'aritmetica del puntatore opererà per riga (ad esempio dopo 'ar + = 1',' ar' viene incrementato di una riga). Per favore spiega come farlo con un "puntatore semplice" (non sai cosa significhi realmente "semplice"). –

+0

Hai ragione, stavo pensando più all'uso di quel puntatore come parametro e non ho considerato il caso che hai menzionato. Correggere ora. –