Pasando un Array por referencia en C

8 minutos de lectura

avatar de usuario
costagero

Soy nuevo en C y tengo una duda.

Dado que las funciones de C crean copias locales de sus argumentos, me pregunto por qué el siguiente código funciona como se esperaba:

void function(int array[]){

    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

int main(){

    int array[] = {1,2,3};

    function(array);

    printf("%d %d %d",array[0],array[1],array[2]);

    return 0;
}

Con la salida de línea siendo 4 5 6.

¿Por qué funciona esto mientras que lo siguiente no?

void function(int integer){

    integer = 2;
}

int main(){

    int integer = 1;

    function(integer);

    printf("%d",integer);

    return 0;
}

La salida es solo 1 en este caso.

Versión corta: ¿Por qué las funciones pueden modificar los valores de sus variables principales si se pasan como matriz?

¡Gracias a todos!

  • Cualquiera que piense que los arreglos son “realmente” apuntadores necesita leer la sección 6 del Preguntas frecuentes sobre comp.lang.c. Resumen rápido: No, no lo son.

    –Keith Thompson

    28/10/2013 a las 23:15

avatar de usuario
kfson

Esto se debe al hecho de que las matrices tienden a decaer en punteros.

int a[] = { 1, 2, 3 };
int* p = a; // valid: p is now the address of a[0]
a = p;  // NOT valid.

printf("a = %p\n", a);
printf("p = %p\n", p); // prints same address as a

a y p imprimirá el mismo valor.

Al contrario de lo que otros han dicho, a es no un puntero, simplemente puede decaer a uno. http://c-faq.com/aryptr/aryptrechev.html

en tu primera function() lo que se pasa es la dirección del primer elemento de la matriz, y el cuerpo de la función lo desreferencia. De hecho, el compilador está tratando el prototipo de la función así:

void function(int* array /*you wrote int array[]*/){
    array[0] = 4;
    array[1] = 5;
    array[2] = 6;   
}

function(&array[0]);

Esto tiene que suceder porque dijiste “matriz de tamaño desconocido” (matriz int[]). El compilador no pudo garantizar la deducción de la cantidad de pila requerida para pasar por valor, por lo que decae a un puntero.

—- Editar —-

Combinemos ambos ejemplos y usemos nombres más distintivos para aclarar las cosas.

#include <stdio.h>

void func1(int dynArray[]) {
    printf("func1: dynArray = %p, &dynArray[0] = %p, dynArray[0] = %d\n",
             dynArray, &dynArray[0], dynArray[0]);
}

void func2(int* intPtr) {
    printf("func2: intPtr = %p, &intPtr[0] = %p, intPtr[0] = %d\n",
             intPtr, &intPtr[0], intPtr[0]);
}

void func3(int intVal) {
    printf("func3: intVal = %d, &intValue = %p\n",
             intVal, &intVal);
}

int main() {
    int mainArray[3] = { 1, 2, 3 };
    int mainInt = 10;

    printf("mainArray = %p, &mainArray[0] = %p, mainArray[0] = %d\n",
             mainArray, &mainArray, mainArray[0]);
    func1(mainArray);
    func2(mainArray);

    printf("mainInt = %d, &mainInt = %p\n",
             mainInt, &mainInt);
    func3(mainInt);

    return 0;
}

Demostración en vivo en ideone: http://ideone.com/P8C1f4

mainArray = 0xbf806ad4, &mainArray[0] = 0xbf806ad4, mainArray[0] = 1
func1: dynArray = 0xbf806ad4, &dynArray[0] = 0xbf806ad4, dynArray[0] = 1
func2: intPtr = 0xbf806ad4, &intPtr[0] = 0xbf806ad4, intPtr[0] = 1

mainInt = 10, &mainInt = 0xbf806acc
func3: intVal = 10, &intValue = 0xbf806ad0

En func1 y func2 “dynArray” e “intPtr” son variables locales, pero son variables de puntero en las que reciben la dirección de “mainArray” de main.

Este comportamiento es específico de las matrices. Si colocara la matriz dentro de una estructura, podría pasarla por valor.

  • Lo que se pasa no es la dirección de la matriz, es la dirección del primer elemento de la matriz. (Misma dirección de memoria, diferente tipo).

    –Keith Thompson

    28/10/2013 a las 23:12

  • Entonces, por ejemplo, cuando usamos array[1]es eso [1] implícitamente desreferenciando el puntero ya? Además, he intentado inicializar la matriz como matriz int[3] y también se utiliza en matriz[3] dentro del encabezado de la función, pero todavía imprime 4 5 6, aunque el compilador podría garantizar la cantidad de pila requerida para pasar todo por valor. ¿Me estoy perdiendo de algo? ¡Gracias!

    – Costagero

    28/10/2013 a las 23:16

  • @Rogério, dado “int a[]={1,2,3}”, la expresión a[1] es precisamente equivalente a *(a+1). Es decir, “a” es la dirección del primer int, “a+1” es la dirección del int inmediatamente siguiente y “*(a+1)” es el int al que apunta esa expresión. El operador de subíndice se puede considerar como azúcar sintáctico. En cuanto a su segundo punto, vea mi comentario anterior. No puede pasar una matriz por valor en C. No importa que el compilador tenga suficiente información para hacerlo. Así no es como se define el lenguaje.

    – John Auld

    28/10/2013 a las 23:25

  • Volví a redactar la respuesta ligeramente: La decadencia posee que suceda en el caso de una matriz de tamaño dinámico por ese motivo, pero también sucede en otros casos. También se agregó alguna explicación adicional en —- editar —-.

    – kfson

    28/10/2013 a las 23:27

  • ¿Esto implica que una matriz de tamaño estático se pasaría por valor? O solamente cuando está envuelto en una estructura?

    – Máx.

    14 dic 2016 a las 20:32

avatar de usuario
StarkDesbordamiento

Una matriz pasada a una función se convierte en un puntero. Cuando pasa un puntero como argumento a una función, simplemente da la dirección de la variable en la memoria. Entonces, cuando modifica el valor de la celda de la matriz, edita el valor debajo de la dirección dada a la función.

Cuando pasa un entero simple a una función, el entero se copia en la pila, cuando modifica el entero dentro de la función, modifica la copia del entero, no el original.

Recordatorio de los diferentes tipos de memoria en C

En C, podemos usar tres tipos de memoria:

  • la pila, utilizada para variables locales y llamadas a funciones: cuando creamos una variable en main(), usamos la pila para almacenar la variable, y cuando se llama a una función, los parámetros dados al método se registran en la pila. Cuando salimos de una función, “abrimos” estos parámetros para volver al estado original, con la variable utilizada antes de la llamada de la función. (anécdota: un stackoverflow es cuando hackeamos la pila para usar variables anteriores en una función sin pasarlas como parámetros)
  • el montón que corresponde a la memoria asignada dinámicamente: cuando necesitamos una gran cantidad de datos, usamos este montón porque la pila está limitada a unos pocos megabytes.
  • el código donde se almacenan las instrucciones del programa

En el caso de esta matriz pasada por una función, que es un puntero (dirección a otra variable), se almacena en la pila, cuando llamamos a la función, copiamos el puntero en la pila.

En el caso del entero, también se almacena en la pila, cuando llamamos a la función, copiamos el entero.

Si queremos modificar el entero, podemos pasar la dirección del entero para modificar el valor debajo del puntero, así:

void function(int *integer)
{
    *integer = 2;
}

int main()
{
    int integer = 1;
    function(&integer);

    printf("%d", integer);

    return 0;
}

  • No, una matriz no es un puntero. Una expresión de matriz, en la mayoría de los contextos, se convierte implícitamente en un puntero al primer elemento del objeto de matriz.

    –Keith Thompson

    28/10/2013 a las 23:12

  • “convertido en un puntero”: falso y probablemente confuso para los programadores que provienen de lenguajes como C#. Una matriz en C/C++ es dos cosas. Es un bloque de memoria que contiene N instancias de un tipo determinado y un puntero a esa memoria. El ABI no proporciona ningún mecanismo para reenviar dichos datos, por lo que solo puede lograrlo haciendo coincidir perfectamente el argumento que desea pasar, ya sea una huella digital explícita codificada o en C ++ una plantilla para hacer una para usted. int f(char (&arg)[64]) o template<class T, size_t N> int f(T (&arg)[N]).

    – kfson

    06/03/2015 a las 21:16

Hay una diferencia entre ‘pasar por referencia’ y ‘pasar por valor’

Pasar por referencia conduce a una ubicación en la memoria donde pasar por valor pasa el valor directamente, una variable de matriz siempre es una referencia, por lo que apunta a una ubicación en la memoria. Los enteros pasarán por valor por defecto

En el primer código, está pasando la dirección de la matriz que apunta al elemento superior de la matriz. Entonces, cuando modifica el valor en la función y regresa a la función principal, todavía está accediendo a la misma matriz que está en la misma dirección. Esto se llama paso por referencia.

Sin embargo, en el segundo caso, el valor del entero se copia de la función principal a la función llamada. En otras palabras, los dos enteros están en direcciones diferentes en la memoria. Por lo tanto, modificar uno no modifica el otro.

avatar de usuario
ramon

El nombre de la matriz es un puntero al primer elemento de la matriz. En el primer ejemplo de código, ha pasado un puntero a la ubicación de memoria que contiene el primer elemento de la matriz. En el segundo ejemplo de código, ha pasado un número entero por valor, por lo que no tiene nada que ver con la variable local denominada “entero”.

revisa ese enlace

Pasar por referencia y pasar por valor

Pasar por Referencia/Valor en C++

  • El nombre de la matriz se convierte en un puntero al primer elemento de la matriz en la mayoría de los contextos. El nombre de la matriz no es un puntero.

    – Este no es mi verdadero nombre

    30/10/2013 a las 21:30

  • El nombre de la matriz se convierte en un puntero al primer elemento de la matriz en la mayoría de los contextos. El nombre de la matriz no es un puntero.

    – Este no es mi verdadero nombre

    30/10/2013 a las 21:30

¿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