Hay un patrón cuando se trata de matrices y funciones; Es un poco difícil de ver al principio.
Cuando se trata de matrices, es útil recordar lo siguiente: cuando una expresión de matriz aparece en la mayoría de los contextos, el tipo de la expresión se convierte implícitamente de "matriz de elementos N de T" a "puntero a T", y su valor se establece para apuntar al primer elemento en la matriz. Las excepciones a esta regla son cuando la expresión de matriz aparece como un operando ya sea del &
o de sizeof
los operadores, o cuando se trata de una cadena literal de ser utilizado como un inicializador en una declaración.
Por lo tanto, cuando llama a una función con una expresión de matriz como argumento, la función recibirá un puntero, no una matriz:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
Es por eso que no utiliza el &
operador para argumentos correspondientes a "% s" en scanf()
:
char str[STRING_LENGTH];
...
scanf("%s", str);
Debido a la conversión implícita, scanf()
recibe un char *
valor que apunta al comienzo de la str
matriz. Esto es válido para cualquier función llamada con una expresión de matriz como argumento (casi cualquiera de las str*
funciones *scanf
y *printf
funciones, etc.).
En la práctica, probablemente nunca invocará una función con una expresión de matriz utilizando el &
operador, como en:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
Tal código no es muy común; debe conocer el tamaño de la matriz en la declaración de la función, y la función solo funciona con punteros a matrices de tamaños específicos (un puntero a una matriz de 10 elementos de T es un tipo diferente que un puntero a una matriz de 11 elementos de T).
Cuando una expresión de matriz aparece como un operando para el &
operador, el tipo de la expresión resultante es "puntero a la matriz de elementos N de T", o T (*)[N]
, que es diferente de una matriz de punteros ( T *[N]
) y un puntero al tipo base ( T *
)
Cuando se trata de funciones y punteros, la regla a recordar es: si desea cambiar el valor de un argumento y hacer que se refleje en el código de llamada, debe pasar un puntero a lo que desea modificar. Una vez más, las matrices arrojan un poco de una llave inglesa en las obras, pero primero nos ocuparemos de los casos normales.
Recuerde que C pasa todos los argumentos de función por valor; el parámetro formal recibe una copia del valor en el parámetro real, y cualquier cambio en el parámetro formal no se refleja en el parámetro real. El ejemplo común es una función de intercambio:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
Obtendrá el siguiente resultado:
antes del intercambio: a = 1, b = 2
después del intercambio: a = 1, b = 2
Los parámetros formales x
y y
son objetos distintos de a
y b
, por lo que cambia x
y y
no se refleja en a
y b
. Como queremos modificar los valores de a
y b
, debemos pasarles punteros a la función de intercambio:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
Ahora su salida será
antes del intercambio: a = 1, b = 2
después del intercambio: a = 2, b = 1
Tenga en cuenta que, en la función de intercambio, no cambiamos los valores de x
y y
, sino los valores de what x
y y
point to . Escribir en *x
es diferente de escribir en x
; no estamos actualizando el valor en x
sí mismo, obtenemos una ubicación x
y lo actualizamos en esa ubicación.
Esto es igualmente cierto si queremos modificar un valor de puntero; si escribimos
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
entonces estamos modificando el valor del parámetro de entrada stream
, no lo que stream
apunta , por lo que cambiar stream
no tiene efecto sobre el valor de in
; Para que esto funcione, debemos pasar un puntero al puntero:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Una vez más, las matrices arrojan un poco de una llave inglesa en las obras. Cuando pasa una expresión de matriz a una función, lo que recibe la función es un puntero. Debido a cómo se define la suscripción de matriz, puede usar un operador de subíndice en un puntero de la misma manera que puede usarlo en una matriz:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
Tenga en cuenta que los objetos de matriz pueden no asignarse; es decir, no puedes hacer algo como
int a[10], b[10];
...
a = b;
por lo tanto, debe tener cuidado cuando trabaje con punteros a matrices; algo como
void (int (*foo)[N])
{
...
*foo = ...;
}
no funciona