Somente o *
, []
e ()
os operadores têm qualquer significado em declarações (C ++ acrescenta &
, mas não vamos entrar em detalhes aqui).
Na declaração
int *p;
o int
-ness de p
é especificado pelo especificador de tipo int
, enquanto o ponteiro de p
é especificado pelo declarador *p
.
O tipo de p
é "ponteiro para int
"; esse tipo é totalmente especificado pela combinação do especificador de tipo int
mais o declarador *p
.
Em uma declaração, o declarador introduz o nome da coisa que está sendo declarada ( p
) junto com informações adicionais sobre o tipo não fornecidas pelo especificador de tipo ("ponteiro para"):
T v; // v is a single object of type T, for any type T
T *p; // p is a pointer to T, for any type T
T a[N]; // a is an N-element array of T, for any type T
T f(); // f is a function returning T, for any type T
Isso é importante - ponteiro, matriz e função são especificados como parte do declarador, não como o especificador de tipo 1 . Se você escrever
int* a, b, c;
será analisado como
int (*a), b, c;
portanto, somente a
será declarado como um ponteiro para int
; b
e c
são declarados como int
s regulares .
A *
, []
e ()
os operadores podem ser combinados para criar tipos arbitrariamente complexas:
T *a[N]; // a is an N-element array of pointers to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *(*f[N])(); // f is an N-element array of pointers to functions
// returning pointer to T
T *(*(*(*f)[N])())[M] // f is a pointer to an N-element array of pointers
// to functions returning pointers to M-element
// arrays of pointers to T
Observe que *
, []
e ()
obedecem às mesmas regras de precedência em declarações que fazer em expressões. *a[N]
é analisado como *(a[N])
nas declarações e expressões.
O que é realmente importante perceber é que a forma de uma declaração corresponde à forma da expressão no código. Voltando ao nosso exemplo original, temos um ponteiro para um número inteiro chamado p
. Se queremos recuperar esse valor inteiro, usamos o *
operador para desreferenciar p
, da seguinte forma:
x = *p;
O tipo da expressão *p
é int
que segue da declaração
int *p;
Da mesma forma, se tivermos uma matriz de ponteiros para double
e desejamos recuperar um valor específico, indexamos na matriz e desreferimos o resultado:
y = *ap[i];
Novamente, o tipo da expressão *ap[i]
é double
, que segue a declaração
double *ap[N];
Então, por que não ++
jogar um papel em uma declaração como *
, []
ou ()
? Ou qualquer outro operador como +
ou ->
ou &&
?
Bem, basicamente, porque a definição da linguagem diz isso. Ele só deixa de lado *
, []
e ()
de desempenhar qualquer papel em uma declaração, uma vez que você tem que ser capaz de especificar ponteiro, matriz e tipos de função. Não há um tipo "increment-this" separado, portanto não há necessidade de ++
fazer parte de uma declaração. Não há nenhuma "-este bit a bit" tipo, por isso não há necessidade de unário &
, |
, ^
, ou ~
para ser parte de uma declaração de qualquer um. Para tipos que usam o .
operador de seleção de membro, usamos as tags struct
e union
na declaração. Para tipos que usam o ->
operador, usamos as tags struct
e union
em conjunto com o *
operador no declarador.
- Obviamente, você pode criar nomes de typedef para tipos de ponteiro, matriz e função, como
typedef int *iptr;
iptr a,b,c; // all three of a, b, and c are pointers to int
mas, novamente, é o declarador *iptr
que especifica o ponteiro do nome do typedef.
void move(int *units) { ... }
, é um operador indireto. Considerado parte do tipo, também pode ser escritovoid move(int* units) { ... }
, embora eu prefira o estilo anterior. Você lê os dois como "int ponteiro". Veja também stackoverflow.com/a/8911253