Em C, são apontadores arrays ou usados como ponteiros?

O meu entendimento era que os arrays eram simplesmente ponteiros constantes para uma sequência de valores, e quando declarou um array em C, Estava a declarar um ponteiro e a atribuir espaço para a sequência para a qual aponta.

mas isto confunde-me: o seguinte código:

char y[20];
char *z = y;

printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);

quando compilado com Apple GCC dá o seguinte resultado:

y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930

(a minha máquina tem 64 bits, os ponteiros têm 8 bytes de comprimento).

Se ' y ' é um ponteiro constante, porque é que tem um tamanho de 20, como o sequência de valores que aponta? O nome da variável " y " é substituído por um endereço de memória durante o tempo de compilação sempre que é apropriado? São arrays, então, algum tipo de açúcar sintático em C que é apenas traduzido para coisas pontiagudas quando compilado?

Author: salvador p, 2011-01-05

6 answers

Aqui está a linguagem exacta da norma C ( N1256):

6.3.2.1 valores, matrizes e designadores de funções
...
3, Excepto quando for o operando do sizeof ou operador unário & operador, ou é um literal de cadeia de caracteres usada para inicializar uma matriz, uma expressão que tem o tipo "matriz de tipo" é convertido para uma expressão do tipo "ponteiro para tipo", que aponta para o elemento inicial do objeto de matriz e não é um lvalue. Se o objeto array tem classe de armazenamento de registro, o comportamento é indefinido.

A coisa importante a lembrar aqui é que há uma diferença entre um objeto (em termos C, significando algo que ocupa a memória) e a expressãousada para se referir a esse objeto.

Quando se declara uma matriz como

int a[10];

O objecto designado pela expressão a é uma matriz (ou seja, um bloco contíguo de memória suficientemente grande para conter 10 int), e o tipo de expressão A é " Matriz de 10 elementos de int", ou int [10]. Se a expressão de a aparece em um contexto diferente do operando dos operadores sizeof ou &, então seu tipo é implicitamente convertido em int *, e seu valor é o endereço do primeiro elemento.

No caso do operador sizeof, Se o operando é uma expressão do tipo T [N], Então o resultado é o número de bytes em o objeto array, não em um ponteiro para esse objeto: N * sizeof T.

No caso de a & operador, o valor é o endereço da matriz, que é o mesmo que o endereço do primeiro elemento da matriz, mas a tipo a expressão é diferente, dada a declaração de T a[N];, o tipo da expressão &a é T (*)[N], ou ponteiro para N-elemento de matriz de t valor é o mesmo que a ou &a[0] (o endereço da matriz é o mesmo que o endereço de o primeiro elemento da matriz), mas a diferença nos tipos importa. Por exemplo, dado o código

int a[10];
int *p = a;
int (*ap)[10] = &a;

printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);

Verá a saída por ordem de

p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80

Agora, avançando p acrescenta sizeof int (4) ao valor original, enquanto que avançando ap acrescenta 10 * sizeof int (40).

Língua mais padrão:

6.5.2.1 Matriz subscripting

Restrições

1 uma das expressões deve ter o tipo " ponteiro para objecto Tipo ", a outra expressão deve ter o tipo inteiro, e o resultado tem o tipo "Tipo".

Semântica

2 uma expressão postfix seguida por uma expressão entre parêntesis rectos [] é uma designação subscrita de um elemento de um objeto array. A definição do operador de índice [] é que E1[E2] é idêntico a (*((E1)+(E2))). Por causa das regras de conversão que se aplicam ao operador binário +, Se E1 é um objeto array (equivalentemente, um ponteiro para o o elemento inicial de um objeto array) e E2 é um inteiro, E1[E2] designa o elemento E2 - ésimo de E1 (contando a partir de zero).

Assim, quando você subscreve uma expressão de array, o que acontece sob o capô é que o offset do endereço do primeiro elemento no array é computado e o resultado é dereferenciado. A expressão

a[i] = 10;

É equivalente a

*((a)+(i)) = 10;

Que é equivalente a

*((i)+(a)) = 10;

Que é equivalente a

 i[a] = 10;

Sim, a subscrição de matriz em C é comutativa; por amor de Deus, nunca faça isso em código de produção.

Uma vez que a subscrição de uma lista é definida em termos de operações de ponteiro, poderá aplicar o operador do Índice às expressões do tipo de ponteiro, assim como ao tipo de lista:

int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
  p[i] = some_initial_value(); 
Aqui está uma mesa útil para lembrar alguns destes conceitos:
Declaration: T a[N];

Expression    Type    Converts to     Value
----------    ----    ------------    -----
         a    T [N]   T *             Address of the first element in a;
                                        identical to writing &a[0]
        &a    T (*)[N]                Address of the array; value is the same
                                        as above, but the type is different
  sizeof a    size_t                  Number of bytes contained in the array
                                        object (N * sizeof T)
        *a    T                       Value at a[0]
      a[i]    T                       Value at a[i]
     &a[i]    T *                     Address of a[i] 

Declaration: T a[N][M];

Expression     Type        Converts to     Value
----------     ----        ------------    -----
          a    T [N][M]    T (*)[M]        Address of the first subarray (&a[0])
         &a    T (*)[N][M]                 Address of the array (same value as
                                             above, but different type)
   sizeof a    size_t                      Number of bytes contained in the
                                             array object (N * M * sizeof T)
         *a    T [M]      T *              Value of a[0], which is the address
                                             of the first element of the first subarray
                                             (same as &a[0][0])
       a[i]    T [M]      T *              Value of a[i], which is the address
                                             of the first element of the i'th subarray
      &a[i]    T (*)[M]                    Address of the i-th subarray; same value as
                                             above, but different type
sizeof a[i]    size_t                      Number of bytes contained in the i'th subarray
                                             object (M * sizeof T)
      *a[i]    T                           Value of the first element of the i'th 
                                             subarray (a[i][0])
    a[i][j]    T                           Value at a[i][j]
   &a[i][j]    T *                         Address of a[i][j]

Declaration: T a[N][M][O];

Expression        Type             Converts to
----------        ----             -----------
         a        T [N][M][O]      T (*)[M][O]
        &a        T (*)[N][M][O]
        *a        T [M][O]         T (*)[O]
      a[i]        T [M][O]         T (*)[O]
     &a[i]        T (*)[M][O]
     *a[i]        T [O]            T *
   a[i][j]        T [O]            T *
  &a[i][j]        T (*)[O]
  *a[i][j]        T 
a[i][j][k]        T
A partir daqui, o padrão das matrizes de dimensões superiores deve ser claro.

Então, em resumo: arrays não são apontadores. Na maioria dos contextos, array expressões são convertidas para tipos de ponteiros.

 51
Author: John Bode, 2011-01-05 19:36:14

Arrays não são ponteiros, embora na maioria das expressões um nome de array avalie para um ponteiro para o primeiro elemento do array. Então é muito, muito fácil de usar um nome de array como um ponteiro. Você vai ver muitas vezes o termo 'decaimento' usado para descrever isso, como em "a matriz decaída a um ponteiro".

Uma exceção é como o operando para o operador sizeof, onde o resultado é o tamanho da matriz (em bytes, Não elementos).

Mais um par de questões relacionadas com isto:

Um parâmetro de matriz para uma função é uma ficção - o compilador realmente passa de um simples ponteiro (isso não se aplica a referência a matriz de parâmetros em C++), portanto, você não pode determinar o tamanho real de uma matriz passada para uma função que você deve passar essa informação de alguma outra forma (talvez usando uma explícita parâmetro adicional, ou usando uma sentinela elemento - C / cordas)

Também, um idioma comum para obter o número de elementos em um array é usar uma macro tipo:

#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0]))

Isto tem o problema de aceitar um nome de array, onde ele vai funcionar, ou um ponteiro, onde ele vai dar um resultado sem sentido sem aviso do compilador. Existem versões mais seguras da macro (particularmente para C++) que irão gerar um aviso ou erro quando é usado com um ponteiro em vez de um array. Veja os seguintes itens:


Nota: C99 VLAs (matrizes de comprimento variável) pode não seguir todas estas regras (em particular, eles podem ser passados como parâmetros com o tamanho da matriz conhecido pela função chamada). Tenho pouca experiência com VLAs, e, tanto quanto sei, não são muito usadas. No entanto, gostaria de salientar que a discussão acima pode aplicar-se de forma diferente à VLAs.

 23
Author: Michael Burr, 2017-05-23 12:09:24

sizeof é avaliado em tempo de compilação, e o compilador sabe se o operando é um array ou um ponteiro. Para arrays ele dá o número de bytes ocupados pelo array. Seu array é um char[] (e sizeof(char) é 1), assim sizeof acontece dar-lhe o número de elementos. Para obter o número de elementos no caso geral, um idioma comum é (aqui para int):

int y[20];
printf("number of elements in y is %lu\n", sizeof(y) / sizeof(int));

Para os ponteiros sizeof indica o número de bytes ocupados pelo tipo de ponteiro bruto.

 6
Author: Péter Török, 2011-01-05 17:43:48
Além do que os outros disseram, talvez este artigo ajude: http://en.wikipedia.org/wiki/C_%28programming_language%29#Array-pointer_interchangeability
 1
Author: Mark Loeser, 2011-01-05 17:31:36

Em

char hello[] = "hello there"
int i;

E

char* hello = "hello there";
int i;

, Em primeira instância, (desconto de alinhamento) 12 bytes serão armazenados para " olá " com o espaço alocado inicializada para olá enquanto que, no segundo olá é armazenado em outro lugar (possivelmente espaço estático) e hello é inicializado para apontar para a cadeia especificada.

hello[2] assim como *(hello + 2) irá retornar ' e ' em ambos os casos, no entanto.

 1
Author: doron, 2011-01-05 17:44:20

Se ' y ' é um ponteiro constante, porque é que tem um tamanho de 20, como a sequência de valores a que aponta?

Porque {[[0]} é o endereço da variável, e irá sempre devolver 8 pela sua máquina. Você precisa usar o ponteiro dereference ( & ) para obter o conteúdo de uma variável.

EDITAR: UMA boa distinção entre os dois: http://www.cs.cf.ac.uk/Dave/C/node10.html

 -2
Author: Andrew Sledge, 2011-01-05 17:40:28