Primero debes entender que en lenguaje C, el concepto de pase por referencia no existe, porque no es posible declarar una referencia tal como se lo hace en otros lenguajes como por ejemplo, C++.
Así que vamos recordar tres conceptos:
pasar por referencia: Significa que pasarás la variable original a una referencia, por lo tanto, durante la ejecución de una función, podremos alterar el contenido de la variable original.
pasar por valor: Significa que pasarás una copia de la variable original a un parámetro.
pasar por puntero: Significa que pasarás la variable original a un puntero, por lo tanto, durante la ejecución de una función, podremos alterar el contenido de la variable original.
Quiero que quede claro que en C no usamos el concepto de pase por referencia, sino, pase por puntero, que básicamente hacen lo mismo, pero la forma de usarlo es diferente.
Ejemplo (1):
void funcion_init(int *varRef)
{
*varRef = 10;
}
int numero = 0;
funcion_init(&numero);
printf("%d", numero);
En este ejemplo se está pasando por puntero y esto se debe porque la dirección de memoria de la variable numero
la recibe un puntero (es decir, int* varRef
). Por lo tanto, podemos alterar el contenido de la variable numero
.
Ejemplo (2):
void funcion_init(int *varRef)
{
*varRef = 10;
}
int *numero = malloc(sizeof(int));
*numero = 0;
funcion_init(numero);
printf("%d", *numero);
El resultado por pantalla es 10
y esto se debe porque en este ejemplo si se está pasando por valor, es decir, se está enviando una copia del contenido de numero
, que justamente coincide con la dirección de memoria del espacio que se haya reservado con malloc
.
Ojo: Aquí no se está pasando por puntero porque no estamos pasando la variable original (en este caso es int* numero
), por lo tanto, nunca podremos alterar el contenido de numero
.
Entonces duramente la ejecución de dicha función, podemos modificar el contenido de dicho espacio (el que reservaste con malloc
).
Respondiendo a tu pregunta:
¿Pero no se supone que los punteros ya son direcciones de memoria en si?
Sí, los punteros internamente consumen un espacio de memoria, donde guardan una dirección de memoria de X variable.
Una representación en memoria sería así:
int* numero
|0x04| |0x06|
(0x06) (0)
En esta representación, la dirección 0x04
es la del puntero, donde guarda la dirección 0x06
(que guarda el valor 0
). De este modo, cuando ejecutas esta función:
funcion_init(numero);
Lo que realmente pasas es la dirección 0x06
y para lograrlo, se debe primero acceder al contenido del puntero (que en nuestro ejemplo está alojado en la dirección 0x04
).
Ejemplo (3):
void funcion_init(int **varRef)
{
**varRef = 10;
}
int *numero = malloc(sizeof(int));
*numero = 0;
funcion_init(&numero);
printf("%d", *numero);
En este ejemplo estamos pasando por puntero y esto se debe porque en realidad lo que estamos pasando es la dirección de memoria (que para nosotros es la variable original) del puntero numero
. Entonces como el parámetro varRef
es un puntero doble, puede guardar la dirección de memoria de un puntero, por ende, podemos alterar dos cosas durante la ejecución de funcion_init
:
Respondiendo a tu pregunta:
¿Qué se está pasando a la función, la dirección de memoria de una dirección de memoria?
No exactamente, lo que estás pasando es simplemente una dirección de memoria.
Lo comprobamos con este diagrama de memoria:
int* numero int** varRef
|0x04| |0x06| |0x08|
(0x06) (0) (0x04)
Cuando esta función se ejecute:
funcion_init(&numero);
Lo que estás pasando es la dirección de memoria donde está alojado el puntero numero
, en este caso, en la dirección 0x04
. Por lo tanto, el parámetro varRef
lo que realmente recibe, es la dirección de memoria del puntero, es decir, la dirección 0x04
.
Pregunta frecuente:
¿Los arrays en C se pasan por referencia?
Respuesta: No. Porque el concepto de pase por referencia en C no existe.
Lo que si podríamos decir, es que los arrays si se pasan por puntero, porque lo que realmente estás pasando es la variable original, que obviamente coincide con la dirección de memoria del primer elemento del array y por ende, podemos modificar el array original.
Conclusión:
No uses el término pasar por referencia en C, trae confusiones. Lo ideal es reemplazarlo por el término pasar por puntero.