O nome de uma matriz é um ponteiro em C? Caso contrário, qual é a diferença entre o nome de uma matriz e uma variável de ponteiro?
&array[0]
produz um ponteiro, não uma matriz;)
O nome de uma matriz é um ponteiro em C? Caso contrário, qual é a diferença entre o nome de uma matriz e uma variável de ponteiro?
&array[0]
produz um ponteiro, não uma matriz;)
Respostas:
Uma matriz é uma matriz e um ponteiro é um ponteiro, mas na maioria dos casos os nomes de matriz são convertidos em ponteiros. Um termo usado com frequência é que eles se deterioram em ponteiros.
Aqui está uma matriz:
int a[7];
a
contém espaço para sete números inteiros e você pode colocar um valor em um deles com uma atribuição, assim:
a[3] = 9;
Aqui está um ponteiro:
int *p;
p
não contém espaços para números inteiros, mas pode apontar para um espaço para um número inteiro. Podemos, por exemplo, configurá-lo para apontar para um dos locais da matriz a
, como o primeiro:
p = &a[0];
O que pode ser confuso é que você também pode escrever o seguinte:
p = a;
Isso não copia o conteúdo da matriz a
no ponteiro p
(o que isso significaria). Em vez disso, o nome da matriz a
é convertido em um ponteiro para seu primeiro elemento. Portanto, essa tarefa faz o mesmo que a anterior.
Agora você pode usar de p
maneira semelhante a uma matriz:
p[3] = 17;
A razão pela qual isso funciona é que o operador de desreferenciamento de matriz em C [ ]
, é definido em termos de ponteiros. x[y]
significa: comece com o ponteiro x
, y
avance os elementos depois do que o ponteiro aponta e, em seguida, pegue o que estiver lá. Usando a sintaxe aritmética do ponteiro, x[y]
também pode ser escrito como *(x+y)
.
Para que isso funcione com uma matriz normal, como nossa a
, o nome a
in a[3]
deve primeiro ser convertido em um ponteiro (para o primeiro elemento em a
). Em seguida, avançamos 3 elementos e levamos o que estiver lá. Em outras palavras: pegue o elemento na posição 3 na matriz. (Qual é o quarto elemento da matriz, já que o primeiro é numerado como 0.)
Portanto, em resumo, os nomes de matrizes em um programa C são (na maioria dos casos) convertidos em ponteiros. Uma exceção é quando usamos o sizeof
operador em uma matriz. Se a
fosse convertido em um ponteiro nesse contexto, sizeof a
daria o tamanho de um ponteiro e não da matriz real, o que seria bastante inútil, portanto, nesse caso, a
significa a própria matriz.
functionpointer()
e (*functionpointer)()
significam a mesma coisa, estranhamente.
sizeof()
outro contexto em que não há decaimento da matriz -> ponteiro é operador &
- no exemplo acima, &a
será um ponteiro para uma matriz de 7 int
, não um ponteiro para uma única int
; ou seja, seu tipo será int(*)[7]
, o que não é implicitamente convertível em int*
. Dessa forma, as funções podem, na verdade, levar ponteiros para matrizes de tamanho específico e impor a restrição por meio do sistema de tipos.
Quando uma matriz é usada como um valor, seu nome representa o endereço do primeiro elemento.
Quando uma matriz não é usada como um valor, seu nome representa a matriz inteira.
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 */
Se uma expressão do tipo de matriz (como o nome da matriz) aparecer em uma expressão maior e não for o operando de &
ousizeof
operadores , o tipo da expressão da matriz será convertido de "matriz do elemento N de T" para "ponteiro para T", e o valor da expressão é o endereço do primeiro elemento na matriz.
Em resumo, o nome da matriz não é um ponteiro, mas na maioria dos contextos é tratado como se fosse um ponteiro.
Editar
Respondendo à pergunta no comentário:
Se eu usar sizeof, conto o tamanho apenas dos elementos da matriz? Então a matriz "head" também ocupa espaço com as informações sobre comprimento e um ponteiro (e isso significa que ocupa mais espaço do que um ponteiro normal)?
Quando você cria uma matriz, o único espaço alocado é o espaço para os próprios elementos; nenhum armazenamento é materializado para um ponteiro separado ou para qualquer metadado. Dado
char a[10];
o que você recebe na memória é
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
A expressão a
se refere à matriz inteira, mas não há objeto a
separado dos próprios elementos da matriz. Assim, sizeof a
fornece o tamanho (em bytes) de toda a matriz. A expressão &a
fornece o endereço da matriz, que é o mesmo que o endereço do primeiro elemento . A diferença entre &a
e &a[0]
é o tipo do resultado 1 - char (*)[10]
no primeiro caso echar *
no segundo.
Onde as coisas ficam estranhas é quando você deseja acessar elementos individuais - a expressão a[i]
é definida como resultado de *(a + i)
- dado um valor de endereço a
, i
elementos de deslocamento ( não bytes ) desse endereço e desreferenciando o resultado.
O problema é que a
não é um ponteiro ou um endereço - é o objeto inteiro da matriz. Portanto, a regra em C de que sempre que o compilador vê uma expressão do tipo de matriz (como a
, que possui tipo char [10]
) e essa expressão não é o operando dos sizeof
operadores ou unários &
, o tipo dessa expressão é convertido ("decaimentos") para um tipo de ponteiro ( char *
), e o valor da expressão é o endereço do primeiro elemento da matriz. Portanto, a expressão a
tem o mesmo tipo e valor que a expressão &a[0]
(e por extensão, a expressão *a
tem o mesmo tipo e valor que a expressãoa[0]
).
C foi derivado de uma linguagem anterior chamada B e em B a
era um objeto ponteiro separado dos elementos da matriz a[0]
,a[1]
etc. Ritchie queria manter semântica matriz de B, mas ele não quer mexer com armazenar o objeto ponteiro separado. Então ele se livrou disso. Em vez disso, o compilador converterá expressões de matriz em expressões de ponteiro durante a tradução, conforme necessário.
Lembre-se de que eu disse que matrizes não armazenam nenhum metadado sobre seu tamanho. Assim que a expressão da matriz "decair" para um ponteiro, tudo o que você tem é um ponteiro para um único elemento. Esse elemento pode ser o primeiro de uma sequência de elementos ou um único objeto. Não há como saber com base no ponteiro em si.
Quando você passa uma expressão de matriz para uma função, tudo o que a função recebe é um ponteiro para o primeiro elemento - não tem idéia do tamanho da matriz (é por isso que a gets
função era uma ameaça e foi removida da biblioteca). Para que a função saiba quantos elementos a matriz possui, você deve usar um valor sentinela (como o terminador 0 nas seqüências de caracteres C) ou deve passar o número de elementos como um parâmetro separado.
sizeof
é um operador e avalia o número de bytes no operando (uma expressão que denota um objeto ou um nome de tipo entre parênteses). Portanto, para uma matriz, sizeof
avalia o número de elementos multiplicado pelo número de bytes em um único elemento. Se um int
tiver 4 bytes de largura, uma matriz de 5 elementos ocupará int
20 bytes.
[ ]
especial também? Por exemplo, int a[2][3];
então para x = a[1][2];
, embora possa ser reescrito como x = *( *(a+1) + 2 );
, aqui a
não é convertido em um tipo de ponteiro int*
(embora, se a
for um argumento de uma função, deva ser convertido em int*
).
a
tem tipo int [2][3]
, que "decai" para digitar int (*)[3]
. A expressão *(a + 1)
tem tipo int [3]
, que "decai" para int *
. Assim, *(*(a + 1) + 2)
terá tipo int
. a
aponta para a primeira matriz de 3 elementos de int
, a + 1
aponta para a segunda matriz de 3 elementos de int
, *(a + 1)
é a segunda matriz de 3 elementos de int
, *(a + 1) + 2
aponta para o terceiro elemento da segunda matriz de int
, assim *(*(a + 1) + 2)
como o terceiro elemento da segunda matriz de int
. Como isso é mapeado para o código da máquina depende inteiramente do compilador.
Uma matriz declarada assim
int a[10];
aloca memória por 10 int
s. Você não pode modificar, a
mas pode fazer aritmética com ponteiros a
.
Um ponteiro como esse aloca memória apenas para o ponteiro p
:
int *p;
Não aloca nenhum int
s. Você pode modificá-lo:
p = a;
e use subscritos de matriz como você pode com um:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
s com duração de armazenamento automático".
O nome da matriz, por si só, gera um local de memória, para que você possa tratar o nome da matriz como um ponteiro:
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 outras coisas bacanas que você pode fazer para apontar (por exemplo, adicionar / subtrair um deslocamento), também pode fazer em uma matriz:
printf("value at memory location %p is %d", a + 1, *(a + 1));
Em termos de idioma, se C não expôs a matriz como apenas um tipo de "ponteiro" (pedanticamente, é apenas um local de memória. Não pode apontar para um local arbitrário na memória, nem pode ser controlado pelo programador). Sempre precisamos codificar isso:
printf("value at memory location %p is %d", &a[1], a[1]);
Penso que este exemplo lança alguma luz sobre o assunto:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
Ele compila bem (com 2 avisos) no gcc 4.9.2 e imprime o seguinte:
a == &a: 1
oops :-)
Portanto, a conclusão é não, o array não é um ponteiro, não é armazenado na memória (nem mesmo um somente leitura) como um ponteiro, mesmo que pareça, pois você pode obter seu endereço com o operador & . Mas - opa - esse operador não funciona :-)), de qualquer forma, você foi avisado:
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);
O C ++ recusa essas tentativas com erros em tempo de compilação.
Editar:
Isto é o que eu pretendia demonstrar:
#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;
}
Mesmo que c
e a
"aponte" para a mesma memória, você pode obter o endereço do c
ponteiro, mas não pode obter o endereço do a
ponteiro.
-std=c11 -pedantic-errors
, adicionando , você receberá um erro do compilador ao escrever código C inválido. O motivo é porque você tenta atribuir a int (*)[3]
a uma variável de int**
, que são dois tipos que não têm absolutamente nada a ver um com o outro. Então, o que esse exemplo deve provar, não faço ideia.
int **
tipo não é o ponto, é melhor usar o void *
para isso.
O nome da matriz se comporta como um ponteiro e aponta para o primeiro elemento da matriz. Exemplo:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
Ambas as instruções de impressão fornecerão exatamente a mesma saída para uma máquina. No meu sistema, deu:
0x7fff6fe40bc0
Uma matriz é uma coleção de elementos secuenciais e contíguos na memória. Em C, o nome de uma matriz é o índice do primeiro elemento e, aplicando um deslocamento, você pode acessar o restante dos elementos. Um "índice para o primeiro elemento" é realmente um ponteiro para uma direção da memória.
A diferença com as variáveis de ponteiro é que você não pode alterar o local para o qual o nome da matriz está apontando, portanto, é semelhante a um ponteiro const (é semelhante, não é o mesmo. Veja o comentário de Mark). Mas também não é necessário desreferenciar o nome da matriz para obter o valor se você usar aritmética de ponteiro:
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'
Então a resposta é meio 'sim'.
Nome da matriz é o endereço do 1º elemento de uma matriz. Então, sim, o nome da matriz é um ponteiro const.