¿Qué es el array decaying?


¿Qué es la descomposición de una matriz? ¿Hay alguna relación con los punteros de matriz?

Author: Peter Mortensen, 2009-09-22

8 answers

Se dice que los arrays "decaen" en punteros. Una matriz de C++ declarada como int numbers [5] no se puede volver a apuntar, es decir, no se puede decir numbers = 0x5a5aff23. Más importante aún, el término decaimiento significa pérdida de tipo y dimensión; numbers decae en int* al perder la información de dimensión (cuenta 5) y el tipo ya no es int [5]. Busque aquí casos donde el decaimiento no ocurre.

Si está pasando una matriz por valor, lo que realmente está haciendo es copiar un puntero, un puntero al primero de la matriz el elemento se copia al parámetro (cuyo tipo también debe ser un puntero del tipo del elemento array). Esto funciona debido a la naturaleza decayente del array; una vez decayado, sizeof ya no da el tamaño del array completo, porque esencialmente se convierte en un puntero. Esta es la razón por la que se prefiere (entre otras razones) pasar por referencia o puntero.

Tres formas de pasar en un array1:

void by_value(const T* array)   // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])

Los dos últimos darán la información adecuada sizeof, mientras que el primero no lo hará desde el argumento de la matriz ha decaído para ser asignado al parámetro.

1 La constante U debe conocerse en tiempo de compilación.

 214
Author: phoebus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:18

Los arrays son básicamente los mismos que los punteros en C/C++, pero no del todo. Una vez que se convierte un array:

const int a[] = { 2, 3, 5, 7, 11 };

En un puntero (que funciona sin lanzar, y por lo tanto puede suceder inesperadamente en algunos casos):

const int* p = a;

Se pierde la capacidad del operador sizeof para contar elementos en el array:

assert( sizeof(p) != sizeof(a) );  // sizes are not equal

Esta habilidad perdida se conoce como "decaimiento".

Para más detalles, echa un vistazo a este artículo de sobre el decaimiento de matrices.

 79
Author: system PAUSE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-23 23:04:32

Esto es lo que dice el estándar (C99 6.3.2.1 / 3-Otros operandos-Lvalues, arrays y designadores de funciones):

Excepto cuando es el operando del operador sizeof o el operador & único, o es un string literal usado para inicializar un array, una expresión que tiene el tipo "array of type" es convertido a una expresión con el tipo "puntero a tipo" que apunta al elemento inicial de el objeto array y no es un lvalue.

Esto significa que prácticamente cada vez que se utiliza el nombre de la matriz en una expresión, se convierte automáticamente en un puntero al 1er elemento de la matriz.

Tenga en cuenta que los nombres de función actúan de manera similar, pero los punteros de función se usan mucho menos y de una manera mucho más especializada que no causa tanta confusión como la conversión automática de nombres de matriz a punteros.

El estándar C++ (conversión de matriz a puntero 4.2) afloja el requisito de conversión a (énfasis mío):

An lvalue o rvalue de tipo "array of N T" o "array of unknown bound of T" puede convertirse en un rvalue de tipo "puntero a T."

Así que la conversión no tiene que suceda como casi siempre sucede en C (esto permite que las funciones se sobrecarguen o las plantillas coincidan en el tipo de matriz).

Esta es también la razón por la que en C debe evitar el uso de parámetros de matriz en prototipos/definiciones de funciones (en mi opinión, no estoy seguro de si hay algún acuerdo general). Le causar confusión y son una ficción de todos modos-utilice parámetros de puntero y la confusión podría no desaparecer por completo, pero al menos la declaración de parámetros no está mintiendo.

 41
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-22 19:46:58

"Decay" se refiere a la conversión implícita de una expresión de un tipo de matriz a un tipo de puntero. En la mayoría de los contextos, cuando el compilador ve una expresión de matriz, convierte el tipo de la expresión de "matriz N-element de T "a" puntero a T " y establece el valor de la expresión en la dirección del primer elemento de la matriz. Las excepciones a esta regla son cuando una matriz es un operando de los operadores sizeof o &, o la matriz es un literal de cadena que se utiliza como un inicializador en una declaración.

Supongamos el siguiente código:

char a[80];
strcpy(a, "This is a test");

La expresión a es de tipo "matriz de 80 elementos de char" y la expresión "Esto es una prueba" es de tipo "matriz de 16 elementos de char" (en C; en C++ los literales de cadena son matrices de const char). Sin embargo, en la llamada a strcpy(), ninguna expresión es un operando de sizeof o &, por lo que sus tipos se convierten implícitamente a "puntero a char", y sus valores se establecen en la dirección del primer elemento de cada uno. Lo que strcpy() recibe no son matrices, sino punteros, como se ve en su prototipo:

char *strcpy(char *dest, const char *src);

Esto no es lo mismo que un puntero de matriz. Por ejemplo:

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;

Tanto ptr_to_first_element como ptr_to_array tienen el mismo valor ; la dirección base de a. Sin embargo, son tipos diferentes y se tratan de manera diferente, como se muestra a continuación:

a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]

Recuerde que la expresión a[i] se interpreta como *(a+i) (que solo funciona si el tipo de matriz se convierte en un tipo de puntero), por lo que ambos a[i] y ptr_to_first_element[i] trabajan lo mismo. La expresión (*ptr_to_array)[i] se interpreta como *(*a+i). Las expresiones *ptr_to_array[i] y ptr_to_array[i] pueden llevar a advertencias o errores del compilador dependiendo del contexto; definitivamente harán lo incorrecto si esperas que evalúen a a[i].

sizeof a == sizeof *ptr_to_array == 80

De nuevo, cuando una matriz es un operando de sizeof, no se convierte a un tipo puntero.

sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
                                                  is on your platform

ptr_to_first_element es un simple puntero a char.

 23
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-22 20:21:27

Los arrays, en C, no tienen valor.

Donde se espera el valor de un objeto pero el objeto es un array, se usa la dirección de su primer elemento, con el tipo pointer to (type of array elements).

En una función, todos los parámetros se pasan por valor (los arrays no son una excepción). Cuando pasas un array en una función "decae en un puntero" (sic); cuando comparas un array con otra cosa, de nuevo "decae en un puntero" (sic); ...

void foo(int arr[]);

La función foo espera el valor de matriz. Pero, en C, los arrays no tienen ningún valor! Así que foo obtiene en su lugar la dirección del primer elemento de la matriz.

int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }

En la comparación anterior, arr no tiene valor, por lo que se convierte en un puntero. Se convierte en un puntero a int. Ese puntero se puede comparar con la variable ip.

En la sintaxis de indexación de matrices que está acostumbrado a ver, de nuevo, el arr es 'decayed to a pointer'

arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */

Las únicas veces que una matriz no decae en un puntero son cuando es el operando de la sizeof operator, o el & operator (el operador 'address of'), o como un literal de cadena utilizado para inicializar una matriz de caracteres.

 12
Author: pmg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-22 17:55:23

Es cuando el array se pudre y está siendo apuntado; -)

En realidad, es solo que si desea pasar una matriz en algún lugar, pero el puntero se pasa en su lugar (porque quién demonios pasaría toda la matriz por usted), la gente dice que la matriz pobre decayó a puntero.

 6
Author: Michael Krelin - hacker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-22 17:28:58

La descomposición del array significa que, cuando un array se pasa como parámetro a una función, se trata de manera idéntica ("decae a") a un puntero.

void do_something(int *array) {
  // We don't know how big array is here, because it's decayed to a pointer.
  printf("%i\n", sizeof(array));  // always prints 4 on a 32-bit machine
}

int main (int argc, char **argv) {
    int a[10];
    int b[20];
    int *c;
    printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
    printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
    printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
    do_something(a);
    do_something(b);
    do_something(c);
}

Hay dos complicaciones o excepciones a lo anterior.

Primero, cuando se trata de matrices multidimensionales en C y C++, solo se pierde la primera dimensión. Esto se debe a que los arrays están dispuestos contiguamente en la memoria, por lo que el compilador debe conocer todo menos la primera dimensión para poder calcular las compensaciones en ese bloque de memoria.

void do_something(int array[][10])
{
    // We don't know how big the first dimension is.
}

int main(int argc, char *argv[]) {
    int a[5][10];
    int b[20][10];
    do_something(a);
    do_something(b);
    return 0;
}

En segundo lugar, en C++, puede usar plantillas para deducir el tamaño de los arrays. Microsoft usa esto para las versiones en C++ de funciones CRT seguras como strcpy_s , y puede usar un truco similar para obtener de manera confiable el número de elementos en una matriz.

 2
Author: Josh Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-21 08:49:27

Tl;dr: Cuando usas un array que has definido, en realidad estarás usando un puntero a su primer elemento.

Así:

  • Cuando escribes arr[idx] realmente solo estás diciendo *(arr + idx).
  • las funciones realmente nunca toman matrices como parámetros, solo punteros, incluso cuando se especifica un parámetro de matriz.

Tipo de excepciones a esta regla:

  • Puede pasar matrices de longitud fija a funciones dentro de un struct.
  • sizeof() da el tamaño tomado por el array, no el tamaño de un puntero.
 0
Author: einpoklum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-13 09:59:08