Pasar una matriz como argumento a una función en C

15 minutos de lectura

avatar de usuario
mohan majan

Escribí una función que contenía una matriz como argumento y la llamé pasando el valor de la matriz de la siguiente manera.

void arraytest(int a[])
{
    // changed the array a
    a[0] = a[0] + a[1];
    a[1] = a[0] - a[1];
    a[0] = a[0] - a[1];
}

void main()
{
    int arr[] = {1, 2};
    printf("%d \t %d", arr[0], arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d", arr[0], arr[1]);
}

Lo que encontré es que estoy llamando arraytest() función pasando valores, la copia original de int arr[] está cambiado.

¿Puedes explicar por qué?

  • Está pasando la matriz por referencia pero está modificando su contenido, por lo que está viendo un cambio en los datos.

    –Shaun Wilde

    4 de julio de 2011 a las 5:58

  • main() debe volver int.

    – subrayado_d

    11 de junio de 2020 a las 9:02

  • Aquí está la extensión natural de esta pregunta: Cómo pasar una matriz multidimensional a una función en C y C++. Y aquí están varios de mis enfoques a ese problema.

    – Gabriel grapas

    04/06/2021 a las 19:01


Al pasar una matriz como parámetro, esto

void arraytest(int a[])

significa exactamente lo mismo que

void arraytest(int *a)

vos tambien son modificando los valores en main.

Por razones históricas, las matrices no son ciudadanos de primera clase y no se pueden pasar por valor.

  • ¿Qué notación es mejor bajo qué circunstancias?

    – Heberto Mayorquín

    4 de septiembre de 2015 a las 12:07

  • @Ramon: usaría la segunda opción, ya que parece menos confuso e indica mejor que no obtiene una copia de la matriz.

    – Bo Person

    04/09/2015 a las 12:20

  • ¿Puedes explicar las “razones históricas”? Supongo que pasar valores necesitaría una copia y, por lo tanto, una pérdida de memoria … gracias

    – Jacquelyn Marquardt

    1 de noviembre de 2016 a las 20:15

  • @lucapozzobon: originalmente, C no tenía ningún valor de paso, excepto los valores individuales. no fue hasta struct se agregó al idioma que esto fue cambiado. Y luego se consideró demasiado tarde para cambiar las reglas de las matrices. Ya había 10 de usuarios. 🙂

    – Bo Person

    2 de noviembre de 2016 a las 9:26

  • …significa exactamente lo mismo que void arraytest(int a[1000]) etc, etc. Respuesta ampliada aquí: stackoverflow.com/a/51527502/4561887.

    – Gabriel grapas

    25 de julio de 2018 a las 20:56

avatar de usuario
grapas gabriel

Para pasar matrices 2D (o multidimensionales superiores), vea mi otra respuesta aquí: Cómo pasar una matriz multidimensional a una función en C y C++

Pasar matrices 1D como parámetros de función en C (y C++)

1. Uso de matriz estándar en C con decaimiento de tipo natural (ajuste) de matriz a ptr

@Bo Persson afirma correctamente en su gran respuesta aquí:

Al pasar una matriz como parámetro, esto

void arraytest(int a[])

significa exactamente lo mismo que

void arraytest(int *a)

Permítanme agregar algunos comentarios para agregar claridad a esos dos fragmentos de código:

// param is array of ints; the arg passed automatically "adjusts" (frequently said
// informally as "decays") from `int []` (array of ints) to `int *` 
// (ptr to int)
void arraytest(int a[])

// ptr to int
void arraytest(int *a)

Sin embargo, permítanme agregar también que las dos formas anteriores también:

  1. significa exactamente lo mismo que

     // array of 0 ints; automatically adjusts (decays) from `int [0]`
     // (array of zero ints) to `int *` (ptr to int)
     void arraytest(int a[0])
    
  2. lo que significa exactamente lo mismo que

     // array of 1 int; automatically adjusts (decays) from `int [1]`
     // (array of 1 int) to `int *` (ptr to int)
     void arraytest(int a[1])
    
  3. lo que significa exactamente lo mismo que

     // array of 2 ints; automatically adjusts (decays) from `int [2]`
     // (array of 2 ints) to `int *` (ptr to int)
     void arraytest(int a[2])
    
  4. lo que significa exactamente lo mismo que

     // array of 1000 ints; automatically adjusts (decays) from `int [1000]`
     // (array of 1000 ints) to `int *` (ptr to int)
     void arraytest(int a[1000])
    
  5. etc.

En cada uno de los ejemplos de matriz anteriores, y como se muestra en las llamadas de ejemplo en el código justo debajo, el tipo de parámetro de entrada se ajusta (decae) ​​a un int *y se puede llamar sin advertencias ni errores, incluso con opciones de compilación -Wall -Wextra -Werror encendido (ver mi repositorio aquí para obtener detalles sobre estas 3 opciones de compilación), así:

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to a pointer type: `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

De hecho, el valor de “tamaño” ([0], [1], [2], [1000]etc.) dentro del parámetro de matriz aquí es aparentemente solo para fines estéticos/de autodocumentación, y puede ser cualquier número entero positivo (size_t tipo que creo) que quieras!

En la práctica, sin embargo, debe usarlo para especificar el tamaño mínimo de la matriz que espera que reciba la función, de modo que al escribir código sea fácil de rastrear y verificar. Él MISRA-C-2012 estándar (compre/descargue el PDF de la versión 2012 de 236 páginas del estándar por £ 15.00 aquí) llega a afirmar (énfasis añadido):

Regla 17.5 El argumento de función correspondiente a un parámetro declarado de tipo matriz deberá tener un número apropiado de elementos.

Si un parámetro se declara como una matriz con un tamaño específico, el argumento correspondiente en cada llamada de función debe apuntar a un objeto que tenga al menos tantos elementos como la matriz.

El uso de un declarador de matriz para un parámetro de función especifica la interfaz de la función más claramente que el uso de un puntero. El número mínimo de elementos que espera la función se indica explícitamente, mientras que esto no es posible con un puntero.

En otras palabras, recomiendan usar el formato de tamaño explícito, aunque el estándar C técnicamente no lo impone.al menos ayuda a aclararle a usted como desarrollador, y a otros que usan el código, qué tamaño de matriz espera la función que pase.


2. Forzar la seguridad de tipos en arreglos en C

(No recomendado (corrección: algunas veces recomendado, especialmente para arreglos multidimensionales de tamaño fijo), pero posible. Vea mi breve argumento en contra de hacer esto al final. Además, para mi matriz multidimensional [ex: 2D array] versión de esto, vea mi respuesta aquí.)

Como @Winger Sendon señala en un comentario debajo de mi respuesta, podemos obligar a C a tratar una matriz tipo ser diferente en función de la matriz Talla!

Primero, debe reconocer que en mi ejemplo anterior, usando el int array1[2]; Me gusta esto: arraytest(array1); causas array1 para decaer automáticamente en un int *. SIN EMBARGO, si tomas el Dirección de array1 en su lugar y llamar arraytest(&array1)obtienes un comportamiento completamente diferente! Ahora, NO se descompone en un int *! Esto se debe a que si tomas el Dirección de una matriz entonces tú ya tienen un tipo de puntero, y los tipos de puntero NO se ajustan a otros tipos de puntero. Solo los tipos de matriz se ajustan a los tipos de puntero. Entonces, en cambio, el tipo de &array1 es int (*)[2]lo que significa “puntero a una matriz de tamaño 2 de int”o “puntero a una matriz de tamaño 2 de tipo int”o dicho también como “puntero a una matriz de 2 enteros”. Por lo tanto, puede FORZAR a C para verificar la seguridad de tipos en una matriz al pasar punteros explícitos a las matrices, como este:

// `a` is of type `int (*)[2]`, which means "pointer to array of 2 ints"; 
// since it is already a ptr, it can NOT automatically decay further
// to any other type of ptr 
void arraytest(int (*a)[2])
{
    // my function here
}

Esta sintaxis es difícil de leer, pero similar a la de un puntero de función. La herramienta en línea, cdeclnos dice que int (*a)[2] significa: “declarar a como puntero a la matriz 2 de int” (puntero a matriz de 2 ints). NO confunda esto con la versión SIN paréntesis: int * a[2]lo que significa: “declarar a como matriz 2 de puntero a int” (AKA: matriz de 2 punteros para intAKA: matriz de 2 int*s).

Ahora, esta función REQUIERE que la llames con el operador de dirección (&) así, usando como parámetro de entrada un PUNTERO A UN ARRAY DEL TAMAÑO CORRECTO!:

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

Esto, sin embargo, producirá una advertencia:

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

Puedes prueba este código aquí.

Para obligar al compilador de C a convertir esta advertencia en un error, de modo que siempre DEBE llamar arraytest(&array1); usando solo una matriz de entrada del tamaño correcto y tipo (int array1[2]; en este caso), agregar -Werror a sus opciones de construcción. Si ejecuta el código de prueba anterior en onlinegdb.com, hágalo haciendo clic en el ícono de ajustes en la esquina superior derecha y haga clic en “Banderas adicionales del compilador” para escribir esta opción. Ahora, esta advertencia:

main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]                                                            
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’    

se convertirá en este error de compilación:

main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
     arraytest(array1); // warning!
               ^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
 void arraytest(int (*a)[2])
      ^~~~~~~~~
cc1: all warnings being treated as errors

Tenga en cuenta que también puede crear punteros de “tipo seguro” para matrices de un tamaño determinado, como este:

int array[2]; // variable `array` is of type `int [2]`, or "array of 2 ints"

// `array_p` is a "type safe" ptr to array of size 2 of int; ie: its type
// is `int (*)[2]`, which can also be stated: "ptr to array of 2 ints"
int (*array_p)[2] = &array;

…pero yo no necesariamente Recomiendo esto (usar estas matrices de “tipo seguro” en C), ya que me recuerda mucho a las travesuras de C ++ utilizadas para forzar la seguridad de tipo en todas partes, al costo excepcionalmente alto de la complejidad de la sintaxis del lenguaje, la verbosidad y la dificultad de la arquitectura del código, y que No me gusta y he despotricado muchas veces antes (por ejemplo, ver “Mis pensamientos sobre C++” aquí).


Para pruebas y experimentación adicionales, vea también el enlace justo debajo.

Referencias

Ver enlaces arriba. También:

  1. Mi código de experimentación en línea: https://onlinegdb.com/B1RsrBDFD

Ver también:

  1. Mi respuesta sobre matrices multidimensionales (por ejemplo, matrices 2D) que expone lo anterior y utiliza el enfoque de “seguridad de tipos” para matrices multidimensionales donde tiene sentido: Cómo pasar una matriz multidimensional a una función en C y C++

  • void arraytest(int (*a)[1000]) es mejor porque entonces el compilador generará un error si el tamaño es incorrecto.

    – alero

    31 de julio de 2018 a las 15:10

  • @WingerSendon, sabía que había algunas sutilezas que necesitaba verificar aquí, y que la sintaxis es confusa (como la sintaxis de una función ptr es confusa), así que me tomé mi tiempo y finalmente actualicé mi respuesta con una gran sección nueva titulada Forcing type safety on arrays in Ccubriendo su punto.

    – Gabriel grapas

    9 de noviembre de 2020 a las 22:35

  • @GabrielStaples, Gracias. Tu respuesta es muy útil. ¿Me puede recomendar una referencia para aprender c avanzado de esta manera?

    – daryooosh

    15 de diciembre de 2020 a las 4:39


  • @daryooosh, lamentablemente no puedo. No tengo grandes referencias. He recogido esto un poco aquí, un poco allá, investigando profundamente durante muchos años. Lo mejor que puedo hacer es decirle que de vez en cuando dejo caer algo de lo que aprendo de esta manera en mi eRCaGuy_hola_mundo repositorio aquí. Sin embargo, tenga en cuenta que el material de seguridad tipo C que usé anteriormente debe usarse MUY con moderación. Complicará su código y disminuirá mucho la legibilidad, y no vale la pena. Concéntrese en la sintaxis simple cuando sea posible y haga que las cosas sean legibles.

    – Gabriel grapas

    15 de diciembre de 2020 a las 5:29


  • Tenga en cuenta también que el libro de texto clásico canónico de C es este K&R El lenguaje de programación C libro: en.wikipedia.org/wiki/The_C_Programming_Language.

    – Gabriel grapas

    15 de diciembre de 2020 a las 5:30

avatar de usuario
Alex

Está pasando el valor de la ubicación de memoria del primer miembro de la matriz.

Por lo tanto, cuando comienza a modificar la matriz dentro de la función, está modificando la matriz original.

Recuérdalo a[1] es *(a+1).

  • Supongo que faltan () para *a+1 debería ser *(a+1)

    – ShinTakezou

    4 de julio de 2011 a las 6:17

  • @Shin Gracias, ha pasado un tiempo desde que jugué con C.

    – Alex

    4 de julio de 2011 a las 6:18

avatar de usuario
andrushenko alexander

Pasar una matriz multidimensional como argumento a una función.
Pasar una matriz one dim como argumento es más o menos trivial. Echemos un vistazo a un caso más interesante de pasar una matriz de 2 dim. En C no puede usar una construcción de puntero a puntero (int **) en lugar de una matriz de 2 dim. Hagamos un ejemplo:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

Aquí he especificado una función que toma como primer argumento un puntero a una matriz de 5 enteros. Puedo pasar como argumento cualquier matriz de 2 dim que tenga 5 columnas:

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

Puede tener una idea para definir una función más general que pueda aceptar cualquier matriz de 2 dim y cambiar la firma de la función de la siguiente manera:

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

Este código se compilaría pero obtendrá un error de tiempo de ejecución al intentar asignar los valores de la misma manera que en la primera función. Entonces, en C, las matrices multidimensionales no son lo mismo que los punteros a los punteros … a los punteros. Un int(*arr)[5] es un puntero a un arreglo de 5 elementos, un int(*arr)[6] es un puntero a una matriz de 6 elementos, ¡y son punteros a diferentes tipos!

Bueno, ¿cómo definir argumentos de funciones para dimensiones más altas? ¡Simple, solo seguimos el patrón! Aquí está la misma función ajustada para tomar una matriz de 3 dimensiones:

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

Como era de esperar, puede tomar como argumento cualquier matriz de 3 dim que tenga en la segunda dimensión 4 elementos y en la tercera dimensión 5 elementos. Algo como esto estaría bien:

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

Pero tenemos que especificar todos los tamaños de las dimensiones hasta el primero.

Si quieres pasar una matriz unidimensional como argumento en una funcióntendría que declarar un parámetro formal de una de las siguientes tres formas y los tres métodos de declaración producen resultados similares porque cada le dice al compilador que se va a recibir un puntero entero.

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

Por lo tanto, está modificando los valores originales.

Gracias !!!

  • Estaba buscando su segundo ejemplo, ¿puede explicar cuáles son las ventajas de cada método?

    – disco

    16 de junio de 2020 a las 7:16

avatar de usuario
0_0perplejo

Las matrices siempre se pasan por referencia si usa a[] o *a:

int* printSquares(int a[], int size, int e[]) {   
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

int* printSquares(int *a, int size, int e[]) {
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

  • Estaba buscando su segundo ejemplo, ¿puede explicar cuáles son las ventajas de cada método?

    – disco

    16 de junio de 2020 a las 7:16

avatar de usuario
Comunidad

Las matrices en C se convierten, en la mayoría de los casos, en un puntero al primer elemento de la propia matriz. Y más en detalle, las matrices pasadas a funciones siempre se convierten en punteros.

Aquí una cita de K&R2nd:

Cuando se pasa un nombre de matriz a una función, lo que se pasa es la ubicación del elemento inicial. Dentro de la función llamada, este argumento es una variable local, por lo que un parámetro de nombre de matriz es un puntero, es decir, una variable que contiene una dirección.

Escritura:

void arraytest(int a[])

tiene el mismo significado que escribir:

void arraytest(int *a)

Entonces, a pesar de que no lo está escribiendo explícitamente, está pasando un puntero y está modificando los valores en el main.

Para más, realmente sugiero leer este.

Además, puede encontrar otras respuestas en SO aquí

¿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