Qual é a diferença entre as seguintes declarações:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Qual é a regra geral para entender declarações mais complexas?
const
e volatile
, que são importantes e complicados, estão faltando nesse artigo.
Qual é a diferença entre as seguintes declarações:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Qual é a regra geral para entender declarações mais complexas?
const
e volatile
, que são importantes e complicados, estão faltando nesse artigo.
Respostas:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
O terceiro é o mesmo que o primeiro.
A regra geral é a precedência do operador . Pode ficar ainda mais complexo à medida que ponteiros de função entram em cena.
( ) [ ]
associam da esquerda para a direita e tem precedência maior do que *
assim que ler int* arr[8]
como uma matriz de tamanho 8 onde cada elemento aponta para um int e int (*arr)[8]
como um ponteiro para uma matriz de tamanho 8 que contém números inteiros
Use o programa cdecl , conforme sugerido pela 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>
Isso funciona do outro jeito também.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
Não sei se ele tem um nome oficial, mas o chamo de Thingy Direita Esquerda (TM).
Comece na variável, depois vá para a direita, esquerda e direita ... e assim por diante.
int* arr1[8];
arr1
é uma matriz de 8 ponteiros para números inteiros.
int (*arr2)[8];
arr2
é um ponteiro (o parêntese bloqueia a direita esquerda) para uma matriz de 8 números inteiros.
int *(arr3[8]);
arr3
é uma matriz de 8 ponteiros para números inteiros.
Isso deve ajudá-lo com declarações complexas.
int *a[][10]
enquanto o último consegue.
( ) [ ]
e da direita para esquerda* &
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
[5]
) representa a dimensão interna. Isso significa que (*a[8])
é a primeira dimensão e, portanto, é a representação externa da matriz. O que cada elemento dentro a
aponta para um array de inteiros diferente de tamanho 5.
A resposta para os dois últimos também pode ser deduzida da regra de ouro em C:
A declaração segue o uso.
int (*arr2)[8];
O que acontece se você desreferenciar arr2
? Você obtém uma matriz de 8 números inteiros.
int *(arr3[8]);
O que acontece se você retirar um elemento arr3
? Você recebe um ponteiro para um número inteiro.
Isso também ajuda ao lidar com ponteiros para funções. Para pegar o exemplo de sigjuice:
float *(*x)(void )
O que acontece quando você desreferencia x
? Você obtém uma função que você pode chamar sem argumentos. O que acontece quando você chama? Ele retornará um ponteiro para a float
.
A precedência do operador é sempre complicada, no entanto. No entanto, o uso de parênteses também pode ser confuso, pois a declaração segue o uso. Pelo menos, para mim, intuitivamente arr2
parece uma matriz de 8 ponteiros para ints, mas na verdade é o contrário. Apenas leva algum tempo para se acostumar. Motivo suficiente para sempre adicionar um comentário a essas declarações, se você me perguntar :)
edit: exemplo
A propósito, acabei de me deparar com a seguinte situação: uma função que tem uma matriz estática e usa aritmética do ponteiro para verificar se o ponteiro da linha está fora dos limites. Exemplo:
#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 < 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;
}
Resultado:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Observe que o valor da borda nunca muda, então o compilador pode otimizar isso. Isso é diferente do que você pode querer inicialmente usar:: const int (*border)[3]
que declara a borda como um ponteiro para uma matriz de 3 números inteiros que não alterará o valor enquanto a variável existir. No entanto, esse ponteiro pode ser apontado para qualquer outra matriz a qualquer momento. Em vez disso, queremos esse tipo de comportamento (porque essa função não altera nenhum desses números inteiros). A declaração segue o uso.
(ps: fique à vontade para melhorar esta amostra!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Como regra geral, os operadores unários direito (como []
, ()
, etc) têm preferência sobre os de esquerda. Portanto, int *(*ptr)()[];
seria um ponteiro que aponta para uma função que retorna uma matriz de ponteiros para int (obtenha os operadores corretos o mais rápido possível, assim que sair do parêntese)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
sob o GCC 8 com$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
permite que uma expressão como p()[3]
(ou (*p)()[3]
) seja usada posteriormente.
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
e chame assim: o foo(arr)[4];
que deve conter arr[2][4]
, certo?
Eu acho que podemos usar a regra simples ..
example int * (*ptr)()[];
start from ptr
" ptr
é um ponteiro para" ir para a direita .. seu ") agora vá para a esquerda é um" ("saia para a direita" () "so" para uma função que não aceita argumentos "vá para a esquerda" e retorna um ponteiro "go direita "para uma matriz" vá para a esquerda "de números inteiros"
)
, agora vá para a esquerda ... é *
"um ponteiro para" vá para a direita ... é )
, agora vá para a esquerda ... é a (
sair, vá ()
para a direita "para uma função que não aceita argumentos" vá para a direita ... []
"e retorne uma matriz de" vá para a direita ;
, então vá para a esquerda ... *
"ponteiros para" vá para a esquerda ... int
"números inteiros"
Aqui está um site interessante que explica como ler tipos complexos em C: http://www.unixwiz.net/techtips/reading-cdecl.html
Aqui está como eu o interpreto:
int *something[n];
Nota sobre precedência: o operador de subscrito da matriz (
[]
) tem prioridade mais alta que o operador de desreferência (*
).
Então, aqui aplicaremos o []
antes *
, tornando a declaração equivalente a:
int *(something[i]);
Observe como uma declaração faz sentido:
int num
significanum
é umint
,int *ptr
ouint (*ptr)
significa, (valor emptr
) é umint
, que fazptr
um ponteiro paraint
.
Isso pode ser lido como, (o valor de (valor no i-ésimo índice de alguma coisa)) é um número inteiro. Portanto, (o valor no i-ésimo índice de alguma coisa) é um (ponteiro inteiro), o que torna o algo uma matriz de ponteiros inteiros.
No segundo,
int (*something)[n];
Para entender essa afirmação, você deve estar familiarizado com este fato:
Nota sobre a representação do ponteiro da matriz:
somethingElse[i]
é equivalente a*(somethingElse + i)
Então, substituindo somethingElse
por (*something)
, obtemos *(*something + i)
, que é um número inteiro conforme a declaração. Então, (*something)
nos deu uma matriz, que faz algo equivalente a (ponteiro para uma matriz) .
Eu acho que a segunda declaração é confusa para muitos. Aqui está uma maneira fácil de entender isso.
Vamos ter uma matriz de números inteiros, ou seja int B[8]
.
Vamos também ter uma variável A que aponta para B. Agora, o valor em A é B, ie (*A) == B
. Portanto, A aponta para uma matriz de números inteiros. Na sua pergunta, arr é semelhante a A.
Da mesma forma, em int* (*C) [8]
, C é um ponteiro para uma matriz de ponteiros para um número inteiro.
int *arr1[5]
Nesta declaração, arr1
há uma matriz de 5 ponteiros para números inteiros. Motivo: colchetes têm maior precedência sobre * (operador de desreferenciamento). E nesse tipo, o número de linhas é fixo (5 aqui), mas o número de colunas é variável.
int (*arr2)[5]
Nesta declaração, arr2
é um ponteiro para uma matriz inteira de 5 elementos. Razão: Aqui, colchetes () têm maior precedência que []. E nesse tipo, o número de linhas é variável, mas o número de colunas é fixo (5 aqui).
No ponteiro para um número inteiro, se o ponteiro for incrementado, ele será o próximo número inteiro.
na matriz do ponteiro se o ponteiro for incrementado, ele pula para a próxima matriz