2011-01-05 17 views
34

Según entendí, las matrices eran simplemente punteros constantes de una secuencia de valores, y cuando declaraba una matriz en C, estaba declarando un puntero y asignando espacio para la secuencia a la que apunta.En C, ¿están los punteros de matrices o se utilizan como punteros?

Pero esto me confunde: el siguiente 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); 

cuando se compila con Apple GCC da el siguiente resultado:

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

(mi máquina es de 64 bits, punteros son 8 bytes de longitud) .

Si 'y' es un puntero constante, ¿por qué tiene un tamaño de 20, como la secuencia de valores a los que apunta? ¿El nombre de la variable 'y' es reemplazado por una dirección de memoria durante el tiempo de compilación siempre que sea apropiado? ¿Son las matrices, entonces, algún tipo de azúcar sintáctico en C que simplemente se traduce a cosas del puntero cuando se compila?

+2

posible duplicado de una pregunta de C++ - faq del sitio: [¿El nombre de la matriz es un puntero en C?] (Http://stackoverflow.com/questions/1641957/is-array-name-a-pointer-in- c) –

Respuesta

49

He aquí el texto exacto de la norma C (n1256):

6.3.2.1 Lvalues, arrays y designadores de función
...
3 Excepto cuando es el operando de la sizeof operador o el unario & operador, o es una cadena literal utilizado para inicializar una matriz, una expresión que tiene el tipo '' gama de tipo '' se convierte en una expresión con el tipo '' puntero a tipo ’Que apunta al elemento inicial del objeto de matriz y no es un lvalue. Si el objeto de matriz tiene una clase de almacenamiento de registro, el comportamiento no está definido.

Lo importante a recordar aquí es que hay una diferencia entre un objeto (en términos de C, es decir, algo que ocupa la memoria) y el expresión utilizada para referirse a ese objeto.

Cuando se declara una matriz tal como

int a[10]; 

la objeto designado por la expresión a es una matriz (es decir,, un bloque contiguo de memoria lo suficientemente grande como para contener 10 int valores), y el tipo de la expresión a es "matriz de 10 elementos de int" o int [10]. Si la expresión a aparece en un contexto que no sea el operando de los operadores sizeof o &, entonces su tipo se convierte implícitamente en int *, y su valor es la dirección del primer elemento.

En el caso de que el operador sizeof, si el operando es una expresión de tipo T [N], entonces el resultado es el número de bytes en el objeto de matriz, no en un puntero a ese objeto: N * sizeof T.

En el caso de que el operador &, el valor es la dirección de la matriz, que es la misma que la dirección del primer elemento de la matriz, pero el tipo de la expresión es diferente: dada la declaración T a[N];, el tipo de la expresión &a es T (*)[N], o el puntero a la matriz N-elemento de T. el valor es el mismo que a o &a[0] (la dirección de la matriz es la misma que la dirección del primer elemento de la array), pero la diferencia en tipos importa. Por ejemplo, teniendo en cuenta el 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á la salida del orden de

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

OIA, avanzando p añade sizeof int (4) al valor original, mientras que el avance ap añade 10 * sizeof int (40)

Más lenguaje estándar:

6.5.2.1 subíndices de matriz

Restricciones

1 Una de las expresiones tendrán el tipo '' puntero al objeto tipo '', el otro expresión tendrá un tipo entero, y el resultado tiene el tipo '' tipo ''.

Semántica

2 una expresión postfix seguido de una expresión en corchetes [] es una designación subindicada de un elemento de un objeto array. La definición del operador de subíndice [] es que E1[E2] es idéntico a (*((E1)+(E2))). Debido a las reglas de conversión que se aplican a la + operador binario, si E1 es un objeto array (equivalentemente, un puntero al elemento inicial de un objeto de matriz) y E2 es un número entero, E1[E2] designa el elemento -ésimo E2 de E1 (conteo de cero).

Por lo tanto, cuando se subraya una expresión de matriz, lo que sucede debajo del capó es que se calcula el desplazamiento desde la dirección del primer elemento en la matriz y se desreferencia el resultado.La expresión

a[i] = 10; 

es equivalente a

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

que es equivalente a

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

que es equivalente a

i[a] = 10; 

Sí, subíndices de matriz en C es conmutativo; por el amor de Dios, nunca hagas esto en el código de producción.

Desde subíndices de matriz se define en términos de las operaciones de puntero, se puede aplicar el operador subíndice a las expresiones de tipo de puntero, así como el tipo de matriz:

int *p = malloc(sizeof *p * 10); 
int i; 
for (i = 0; i < 10; i++) 
    p[i] = some_initial_value(); 

Aquí hay una tabla práctica de recordar algunos de estos conceptos:

 
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 de aquí, el patrón para las matrices de mayor dimensión debe ser claro.

Por lo tanto, en resumen: las matrices no son punteros. En la mayoría de los contextos, las expresiones de matriz se convierten a tipos de puntero.

23

Las matrices no son punteros, aunque en la mayoría de las expresiones un nombre de matriz se evalúa como un puntero al primer elemento de la matriz. Entonces, es muy, muy fácil usar un nombre de matriz como un puntero. A menudo verá el término 'decaimiento' utilizado para describir esto, como en "la matriz descompuesta en un puntero".

Una excepción es como el operando del operador sizeof, donde el resultado es el tamaño de la matriz (en bytes, no en elementos).

Un par adicional de temas relacionados con esto:

un parámetro de matriz a una función es una ficción - el compilador realmente pasa un puntero normal (esto no se aplica parámetros para hacer referencia a matriz en C++) , por lo que no puede determinar el tamaño real de una matriz transferida a una función; debe pasar esa información de otra forma (tal vez utilizando un parámetro adicional explícito o utilizando un elemento centinela, como las cadenas C)

Además, idioma común para obtener la cantidad de elementos en una matriz es utilizar una macro como:

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

Esto tiene el problema de aceptar ya sea un nombre de matriz, donde funcionará, o un puntero, donde dará un resultado sin sentido sin advertencia del compilador. Existen versiones más seguras de la macro (particularmente para C++) que generarán una advertencia o error cuando se use con un puntero en lugar de una matriz. Consulte los siguientes elementos SO:


Nota: C99 VLA (matrices de longitud variable) podría no seguir todas estas reglas (en particular, pueden ser pasados como parámetros con el tamaño de matriz conocido por la función llamada). Tengo poca experiencia con VLA, y hasta donde sé, no son ampliamente utilizados. Sin embargo, sí quiero señalar que la discusión anterior podría aplicarse de manera diferente a los VLA.

+0

por lo que se hizo el tamaño de la excepción porque era útil ... ¡no sabía que hubiera una forma de saber el tamaño de una matriz! (Aunque todavía no es tan útil porque solo encuentra el tamaño de las matrices con tamaño fijo, pero creo que es mejor que definir muchas constantes para el mismo propósito) –

6

se evalúa en tiempo de compilación, y el compilador sabe si el operando es una matriz o un puntero. Para las matrices, da la cantidad de bytes ocupados por la matriz. Su matriz es char[] (y sizeof(char) es 1), por lo que sizeof da la cantidad de elementos.Para obtener el número de elementos en el caso general, un lenguaje común es (aquí por int):

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

Para los perros de sizeof da el número de bytes ocupados por el tipo de puntero en bruto.

-1

Si 'Y' es un puntero constante, ¿por qué tiene un tamaño de 20, al igual que la secuencia de valores que apunta ?

Debido z es la dirección de la variable, y siempre volver 8 para su máquina. Debe utilizar el puntero de desreferencia (&) para obtener el contenido de una variable.

EDIT: Una buena distinción entre los dos: http://www.cs.cf.ac.uk/Dave/C/node10.html

+0

Pregunta por y, y usted está respondiendo acerca de z, lo cual es confuso . Está claro por qué z tiene un tamaño de 8. No está claro para el OP por qué y no; a eso no das ninguna respuesta. Además, '&' es el operador de dirección en C; el operador de desreferencia es '*'. –

1

En

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

y

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

En el primer caso (alineación descontando) 12 bytes se almacenará para hola con el espacio asignado inicializado a hola allí mientras que en el segundo hola allí se almacena en otro lugar (posiblemente espacio estático) y hello se inicializa para apuntar a la cadena dada.

hello[2] así como *(hello + 2) devolverá 'e' en ambos casos sin embargo.