¿Por qué C no permite un tipo de retorno de matriz?

¿Por qué es esto válido en C

int * foo(int a,int b){ ... } 

pero esto no es válido

 int [] foo(int a,int b){ ... } 

La syntax es algo divertida.

 int foo(int a, int b) [] { ... } 

Pero no está permitido de todos modos. Ver n1256 6.7.5.3 párrafo 1, “Declaradores de función”.

Un declarador de funciones no especificará un tipo de retorno que sea un tipo de función o un tipo de matriz.

Puede devolver punteros a matrices:

 int (*foo(int a, int b)) []; // Weird syntax, n'est-ce pas? 

Pero también podrías devolver un puntero, ya que los siguientes son equivalentes:

 int array[] = { ... }; int *x = array, (*y)[] = &array; x[0]; (*y)[0]; // same as above, just with more cumbersome syntax 

Normalmente, si una función necesita devolver una matriz de int , puede devolver el puntero o pasar el puntero hacia adentro. Uno de los siguientes:

 int *func(int a, int b); // Allocated by func void func(int a, int b, int *array); // Allocated by caller void func(int a, int b, int **array); // Allocated by func 

El “struct-hack” también funciona para matrices que tienen un tamaño fijo:

 struct { int arr[50]; } array_struct; struct array_struct func(int a, int b); 

Pero esto no se recomienda a menos que la matriz sea pequeña.

Razón fundamental:

Las matrices a menudo son grandes y con frecuencia tienen un tamaño desconocido hasta el tiempo de ejecución. Dado que los parámetros y los valores de retorno se pasan utilizando la stack y los registros (en todos los ABI que conozco), y la stack tiene un tamaño fijo, es algo peligroso pasar un objeto tan grande alrededor de la stack. Algunas ABI tampoco manejan los valores de retorno grandes con gracia, lo que podría generar copias adicionales de los valores de retorno.

El siguiente código también puede ser peligroso:

 void func(...) { int arr[BIG_NUMBER]; // potential for stack overflow int *ptr = alloca(sizeof(int) * BIG_NUMBER); // potential for stack overflow } 

En C, las matrices de paso por valor no se admiten directamente (incluso cuando se escribe int [] como un parámetro en realidad se interpreta como int * ), si recuerdo correctamente es una especie de artefacto del pasaje de BCPL a C.

Dicho esto, puedes devolver matrices que los encapsulan en struct :

 struct { int value[20] } foo(int a, int b) { /* ... */ } 

(Este truco obviamente funciona también para parámetros)

Primero, recuerde que los arreglos en C son en realidad solo azúcar sintáctica alrededor de punteros a bloques de memoria. Por lo tanto, C no restringe la funcionalidad del lenguaje al forzar el uso de la notación anterior (vea el ejemplo a continuación).

Además, es posible que hayan hecho esta elección para evitar que los progtwigdores tempranos escriban código como este:

 char *itoa(int n){ char retbuf[25]; sprintf(retbuf, "%d", n); return retbuf; } 

árbitro

Que parece lo suficientemente simple, pero ¿qué sucede con los puntos de memoria retbuf al final de la función? ¿Pueden las funciones de llamada confiar en los datos del puntero que recibe?