Diferencia entre pasar una matriz y un puntero de matriz a la función en C

4 minutos de lectura

¿Cuál es la diferencia entre las dos funciones en C?

void f1(double a[]) {
   //...
}

void f2(double *a) {
   //...
}

Si tuviera que llamar a las funciones en una matriz sustancialmente larga, ¿estas dos funciones se comportarían de manera diferente, ocuparían más espacio en la pila?

Primero, algunos estándar:

6.7.5.3 Declaradores de funciones (incluidos los prototipos)


7 Una declaración de un parámetro como ”matriz de tipo” se ajustará a ” puntero calificado para
tipo”, donde los calificadores de tipo (si los hay) son los especificados en el [ and ] de la derivación del tipo matriz. Si la palabra clave static también aparece dentro de la [ and ] de la derivación del tipo de matriz, entonces, para cada llamada a la función, el valor del argumento real correspondiente proporcionará acceso al primer elemento de una matriz con al menos tantos elementos como se especifica en la expresión de tamaño.

Entonces, en resumen, cualquier parámetro de función declarado como T a[] o T a[N] es tratado como si fue declarado T *a.

Entonces, ¿por qué los parámetros de matriz se tratan como si fueran declarados como punteros? Este es el por qué:

6.3.2.1 Valores L, matrices y designadores de función


3 Excepto cuando es el operando del sizeof operador o el unario & operador, o es un literal de cadena que se usa para inicializar una matriz, una expresión que tiene el tipo ”matriz de tipo” se convierte en una expresión de tipo ” puntero a tipo” que apunta al elemento inicial del objeto de matriz y no es un valor l. Si el objeto de la matriz tiene una clase de almacenamiento de registro, el comportamiento no está definido.

Dado el siguiente código:

int main(void)
{
  int arr[10];
  foo(arr);
  ...
}

en la llamada a foola expresión de matriz arr no es operando de ninguno sizeof o &por lo que su tipo se convierte implícitamente de “matriz de 10 elementos de int“a” puntero a int” de acuerdo con 6.2.3.1/3. Por lo tanto, foo recibirá un valor de puntero, en lugar de un valor de matriz.

Debido a 6.7.5.3/7, puede escribir foo como

void foo(int a[]) // or int a[10]
{
  ...
}

pero se interpretará como

void foo(int *a)
{
  ...
}

Por lo tanto, las dos formas son idénticas.

La última oración en 6.7.5.3/7 se introdujo con C99 y básicamente significa que si tiene una declaración de parámetro como

void foo(int a[static 10])
{
  ...
}

el parámetro real correspondiente a a debe ser una matriz con al menos 10 elementos.

  • Hay una diferencia cuando se usan compiladores MSVC C++ (al menos algunos más antiguos), debido a que el compilador modifica incorrectamente el nombre de la función de manera diferente en los dos casos (mientras reconoce que son iguales en otros casos), lo que genera problemas de enlace. Consulte el informe de error “No se solucionará” aquí connect.microsoft.com/VisualStudio/feedback/details/326874/…

    – grego

    12 de diciembre de 2013 a las 17:02

avatar de usuario
thomas pornin

La diferencia es puramente sintáctica. En C, cuando la notación de matriz se usa para un parámetro de función, se transforma automáticamente en una declaración de puntero.

  • @Kaushik: aunque son iguales en este caso, tenga en cuenta que no son iguales en el caso general

    – Blue Raja – Danny Pflughoeft

    6 abr 2011 a las 21:55


  • @BlueRaja: sí, es una de las trampas de C. La declaración de los parámetros de la función es muy similar a la declaración de variables locales, pero hay algunas diferencias sutiles (como esta transformación automática de matriz a puntero) que son propensas a morder al programador desprevenido.

    – Thomas Pornin

    7 de abril de 2011 a las 12:07

avatar de usuario
caltuntas

No, no hay diferencia entre ellos. Para probar, escribí este código C en el compilador Dev C++ (mingw):

#include <stdio.h>

void function(int* array) {
     int a =5;
}

void main() {  
     int array[]={2,4};
     function(array);
     getch();
}

Cuando desarmo principal función en .exe de ambas versiones de llamada del archivo binario en IDA, obtengo exactamente el mismo código de ensamblaje como se muestra a continuación:

push    ebp
mov     ebp, esp
sub     esp, 18h
and     esp, 0FFFFFFF0h
mov     eax, 0
add     eax, 0Fh
add     eax, 0Fh
shr     eax, 4
shl     eax, 4
mov     [ebp+var_C], eax
mov     eax, [ebp+var_C]
call    sub_401730
call    sub_4013D0
mov     [ebp+var_8], 2
mov     [ebp+var_4], 4
lea     eax, [ebp+var_8]
mov     [esp+18h+var_18], eax
call    sub_401290
call    _getch
leave
retn

Entonces no hay diferencia entre las dos versiones de esta llamada, al menos el compilador las amenaza por igual.

  • Lo sentimos, pero esto solo prueba que alguna versión de gcc genera el mismo ensamblado en x86 para ambos. Respuesta correcta, explicación incorrecta.

    – potencia lambda

    1 oct 2012 a las 22:16

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad