O nome de uma matriz é um ponteiro?

203

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?

Lundin
fonte
4
Não. Mas matriz é a mesma & matriz [0]
36
@pst: &array[0]produz um ponteiro, não uma matriz;)
jalf
28
@Nava (e pst): array e & array [0] não são realmente iguais. Caso em questão: sizeof (array) e sizeof (& array [0]) dão resultados diferentes.
43610 Thomas Padron-McCarthy
1
@ Thomas concorda, mas em termos de ponteiros, quando você desrefere matriz e & matriz [0], eles produzem o mesmo valor de matriz [0] .ie * matriz == matriz [0]. Ninguém quis dizer que esses dois ponteiros são iguais, mas nesse caso específico (apontando para o primeiro elemento) você também pode usar o nome da matriz.
Nava Carmon
1
Isso também pode ajudar no seu entendimento: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah

Respostas:

255

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;

pnã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 ano 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 pmaneira 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, yavance 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 ain 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 sizeofoperador em uma matriz. Se afosse convertido em um ponteiro nesse contexto, sizeof adaria o tamanho de um ponteiro e não da matriz real, o que seria bastante inútil, portanto, nesse caso, asignifica a própria matriz.

Thomas Padron-McCarthy
fonte
5
Uma conversão automática semelhante é aplicada aos ponteiros de função - ambos functionpointer()e (*functionpointer)()significam a mesma coisa, estranhamente.
241 Carl Norum
3
Ele não perguntou se matrizes e ponteiros são os mesmos, mas se o nome de um array é um ponteiro
Ricardo Amores
32
Um nome de matriz não é um ponteiro. É um identificador para uma variável do tipo array, que possui uma conversão implícita em ponteiro do tipo de elemento.
Pavel Minaev 29/10/09
29
Além disso, além do sizeof()outro contexto em que não há decaimento da matriz -> ponteiro é operador &- no exemplo acima, &aserá 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.
Pavel Minaev 29/10/2009
3
@ onmyway133, confira aqui uma breve explicação e outras citações.
Carl Norum
36

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 */
pmg
fonte
20

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 afornece o tamanho (em bytes) de toda a matriz. A expressão &afornece o endereço da matriz, que é o mesmo que o endereço do primeiro elemento . A diferença entre &ae &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, ielementos de deslocamento ( não bytes ) desse endereço e desreferenciando o resultado.

O problema é que anã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 sizeofoperadores 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 *atem 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 getsfunçã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.


  1. O que * pode * afetar a interpretação do valor do endereço - depende da máquina.
John Bode
fonte
Há muito tempo que procuramos esta resposta. Obrigado! E, se você souber, poderia dizer um pouco mais sobre o que é uma expressão de matriz. 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)?
Andriy Dmytruk
E mais uma coisa. Uma matriz de comprimento 5 é do tipo int [5]. Então é daí que sabemos o tamanho quando chamamos sizeof (array) - de seu tipo? E isso significa que matrizes de diferentes comprimentos são como diferentes tipos de constantes?
Andriy Dmytruk
@AndriyDmytruk: 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, sizeofavalia o número de elementos multiplicado pelo número de bytes em um único elemento. Se um inttiver 4 bytes de largura, uma matriz de 5 elementos ocupará int20 bytes.
John Bode
O operador não é [ ]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 anão é convertido em um tipo de ponteiro int*(embora, se afor um argumento de uma função, deva ser convertido em int*).
Stan
2
@ Stan: A expressão atem 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. aaponta para a primeira matriz de 3 elementos de int, a + 1aponta para a segunda matriz de 3 elementos de int, *(a + 1) é a segunda matriz de 3 elementos de int, *(a + 1) + 2aponta 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.
John Bode
5

Uma matriz declarada assim

int a[10];

aloca memória por 10 ints. Você não pode modificar, amas 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 ints. 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
Grumdrig
fonte
2
Matrizes nem sempre são alocadas na pilha. Esse é um detalhe de implementação que variará de compilador para compilador. Na maioria dos casos, matrizes estáticas ou globais serão alocadas de uma região de memória diferente da pilha. Matrizes de tipos const podem ser atribuídos a partir mais uma região da memória
Mark Bessey
1
Acho que Grumdrig quis dizer "aloca 10 ints com duração de armazenamento automático".
Lightness Races in Orbit
4

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]);
Michael Buen
fonte
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 ce a"aponte" para a mesma memória, você pode obter o endereço do cponteiro, mas não pode obter o endereço do aponteiro.

Palo
fonte
1
"Compila bem (com 2 avisos)". Isso não está bem. Se você pedir ao gcc para compilá-lo como padrão C apropriado -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.
Lundin
Obrigado Lundin pelo seu comentário. Você sabe que existem muitos padrões. Tentei esclarecer o que eu quis dizer com a edição. O int **tipo não é o ponto, é melhor usar o void *para isso.
Palo
-3

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
Amitesh Ranjan
fonte
-4

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'.

Ricardo Amores
fonte
1
Um nome de matriz não é o mesmo que um ponteiro const. Dado: int a [10]; int * p = a; sizeof (p) e sizeof (a) não são os mesmos.
Mark Bessey
1
Existem outras diferenças. Em geral, é melhor seguir a terminologia usada pelo Padrão C, que especificamente a chama de "conversão". Citação: "Exceto quando é o operando do operador sizeof ou o operador unary &, ou é uma string literal usada para inicializar uma matriz, uma expressão que tem o tipo '' matriz do tipo '' é convertida em uma expressão com o tipo ' 'ponteiro para o tipo' 'que aponta para o elemento inicial do objeto da matriz e não é um valor l. Se o objeto da matriz tiver uma classe de armazenamento de registro, o comportamento será indefinido. "
Pavel Minaev 29/10/2009
-5

Nome da matriz é o endereço do 1º elemento de uma matriz. Então, sim, o nome da matriz é um ponteiro const.

SAQIB SOHAIL BHATTI
fonte