Escribí una función que contiene una matriz como argumento y la llamo 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 aunque estoy llamando a la arraytest()
función pasando valores, int arr[]
se cambia la copia original de .
¿Puede explicar por qué?
c
arrays
function
parameters
parameter-passing
Mohan Mahajan
fuente
fuente
main()
debe regresarint
.Respuestas:
Al pasar una matriz como parámetro, esto
void arraytest(int a[])
significa exactamente lo mismo que
void arraytest(int *a)
por lo que está modificando los valores en main.
Por razones históricas, las matrices no son ciudadanos de primera clase y no pueden pasarse por valor.
fuente
struct
se agregó al lenguaje que esto se cambió. Y luego se consideró demasiado tarde para cambiar las reglas de las matrices. Ya había 10 de usuarios. :-)void arraytest(int a[1000])
etc., etc. Respuesta ampliada aquí: stackoverflow.com/a/51527502/4561887 .Si desea pasar una matriz de una sola dimensión como argumento en una función , tendrá 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 uno le dice al compilador que un puntero entero va para ser recibido .
int func(int arr[], ...){ . . . } int func(int arr[SIZE], ...){ . . . } int func(int* arr, ...){ . . . }
Entonces, está modificando los valores originales.
Gracias !!!
fuente
No está pasando la matriz como copia. Es solo un puntero que apunta a la dirección donde el primer elemento de la matriz está en la memoria.
fuente
Estás pasando la dirección del primer elemento de la matriz
fuente
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 que se pasan a funciones siempre se convierten en punteros.
Aquí una cita de K & R2nd :
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, es porque está pasando un puntero y, por lo tanto, está modificando los valores en el archivo principal.
Para más, sugiero leer esto .
Además, puede encontrar otras respuestas sobre SO aquí
fuente
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.
Recuerda que
a[1]
es*(a+1)
.fuente
En C, excepto en algunos casos especiales, una referencia de matriz siempre "decae" a un puntero al primer elemento de la matriz. Por lo tanto, no es posible pasar una matriz "por valor". Una matriz en una llamada de función se pasará a la función como un puntero, que es análogo a pasar la matriz por referencia.
EDITAR: Hay tres casos especiales en los que una matriz no decae a un puntero a su primer elemento:
sizeof a
no es lo mismo quesizeof (&a[0])
.&a
no es lo mismo que&(&a[0])
(y no es exactamente lo mismo que&a[0]
).char b[] = "foo"
no es lo mismo quechar b[] = &("foo")
.fuente
int a[10]
y asigné un valor aleatorio a cada elemento. Ahora, si paso esta matriz a una función usandoint y[]
oint y[10]
o, yint *y
luego, en esa función, usosizeof(y)
Respuesta será el puntero de bytes que se ha asignado. Entonces, en este caso, se descompondrá como un puntero. Sería útil si incluye esto también. Ver este postimg.org/image/prhleuezdsizeof
operar en la función en la matriz que definimos originalmente, entonces decaerá como una matriz, pero si paso otra función, entonces usará elsizeof
operador, decaerá como un puntero.&a
no es exactamente lo mismo que&a[0]
cuandoa
es una matriz. ¿Cómo es eso? En mi programa de prueba, ambos muestran ser iguales, tanto en la función donde se declara la matriz como cuando se pasan a una función diferente. 2. El escritor escribe que "char b[] = "foo"
no es lo mismo quechar b[] = &("foo")
". Para mí, este último ni siquiera se compila. ¿Se trata sólo de mí?Pasar una matriz multidimensional como argumento a una función. Pasar una matriz de un 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 un puntero para puntero construct (
int **
) en lugar de 2 dim array. 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, una matriz multidimensional no es lo mismo que punteros a punteros ... a punteros. An
int(*arr)[5]
es un puntero a una matriz de 5 elementos, anint(*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 superiores? Simple, ¡simplemente 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 es de esperar, puede tomar como argumento 3 matrices dim que tengan en la segunda dimensión 4 elementos y en la tercera dimensión 5 elementos. Cualquier cosa como esta estaría bien:
arr[1][4][5] arr[2][4][5] ... arr[10][4][5] ...
Pero tenemos que especificar todos los tamaños de dimensiones hasta el primero.
fuente
Uso de matriz estándar en C con decaimiento de tipo natural de matriz a ptr
@Bo Persson afirma correctamente en su gran respuesta aquí :
Sin embargo, permítame agregar también que las dos formas anteriores también:
significa exactamente lo mismo que
void arraytest(int a[0])
que significa exactamente lo mismo que
void arraytest(int a[1])
que significa exactamente lo mismo que
void arraytest(int a[2])
que significa exactamente lo mismo que
void arraytest(int a[1000])
etc.
En cada uno de los ejemplos de arreglos anteriores, el tipo de parámetro de entrada decae a an
int *
, y se puede llamar sin advertencias ni errores, incluso con las opciones de compilación-Wall -Wextra -Werror
activadas (consulte mi repositorio aquí para obtener detalles sobre estas 3 opciones de compilación), como esta:int array1[2]; int * array2 = array1; // works fine because `array1` automatically decays from an array type // to `int *` arraytest(array1); // works fine because `array2` is already an `int *` arraytest(array2);
Como cuestión de hecho, el valor "tamaño" (
[0]
,[1]
,[2]
,[1000]
, etc.) dentro del parámetro de matriz aquí es aparentemente sólo por propósitos / estético auto-documentación, y puede ser cualquier número entero positivo (size_t
tipo I creo) que usted quiera!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. El estándar MISRA-C-2012 ( compre / descargue el PDF de la versión 2012 de 236 páginas del estándar por £ 15.00 aquí ) va tan lejos como para afirmar (énfasis agregado):
En otras palabras, recomiendan usar el formato de tamaño explícito, aunque el estándar C técnicamente no lo hace cumplir, 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 pases.
Forzar la seguridad de tipos en matrices en C
Como señala @Winger Sendon en un comentario debajo de mi respuesta, podemos obligar a C a tratar un tipo de matriz para que sea diferente según el tamaño de la matriz !
Primero, debe reconocer que en mi ejemplo anterior, el uso de
int array1[2];
este tipo de:arraytest(array1);
hacearray1
que se descomponga automáticamente en unint *
. SIN EMBARGO, si toma la dirección de en suarray1
lugar y llamaarraytest(&array1)
, ¡obtendrá un comportamiento completamente diferente! ¡Ahora, NO se descompone en unint *
! En cambio, el tipo de&array1
esint (*)[2]
, que significa "puntero a una matriz de tamaño 2 de int" , o "puntero a una matriz de tamaño 2 de tipo int" . Por lo tanto, puede FORZAR a C para verificar la seguridad de tipos en una matriz, como esta: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, cdecl , nos dice que eso
int (*a)[2]
significa: "declarar como puntero a la matriz 2 de int" (puntero a la matriz de 2int
s). NO confunda esto con la versión sin paréntesis:,int * a[2]
que significa: "declare a como matriz 2 de puntero a int" (matriz de 2 punteros aint
).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 ARREGLO 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)
Puede probar este código aquí .
Para forzar 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 y tipo correctos (int array1[2];
en este caso), agregue-Werror
a sus opciones de compilación. Si está ejecutando el código de prueba anterior en onlinegdb.com, haga clic en el icono de engranaje en la parte superior derecha y haga clic en "Banderas extra del compilador" para escribir esta opción. Ahora, esta advertencia:se convertirá en este error de compilación:
Tenga en cuenta que también puede crear punteros de "tipo seguro" para matrices de un tamaño determinado, como este:
int array[2]; // "type safe" ptr to array of size 2 of int: int (*array_p)[2] = &array;
... pero no lo recomiendo necesariamente , ya que me recuerda muchas de las payasadas de C ++ que se usan para forzar la seguridad de los tipos en todas partes, con el costo excepcionalmente alto de la complejidad de la sintaxis del lenguaje, la verbosidad y la dificultad para diseñar el código, y que no me gusta y he despotricado muchas veces antes (por ejemplo, consulte "Mis pensamientos sobre C ++" aquí ).
Para pruebas y experimentación adicionales, consulte también el enlace que se encuentra a continuación.
Referencias
Vea los enlaces de arriba. También:
fuente
void arraytest(int (*a)[1000])
es mejor porque entonces el compilador generará un error si el tamaño es incorrecto.Forcing type safety on arrays in C
, que cubre su punto.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; }
fuente