Pasando por referencia en C

7 minutos de lectura

avatar de usuario
Aks

Si C no admite pasar una variable por referencia, ¿por qué funciona?

#include <stdio.h>

void f(int *j) {
  (*j)++;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);

  return 0;
}

Producción:

$ gcc -std=c99 test.c
$ a.exe
i = 21 

  • ¿En qué parte de este código estás pasando? referencia?

    – Atul

    27 de febrero de 2015 a las 2:42

  • Cabe señalar que C no tiene pase por referencia, solo puede ser emulado utilizando punteros.

    – Un tipo programador

    9 de marzo de 2016 a las 13:05

  • La afirmación correcta es “C no admite implícitamente pasando una variable por referencia”: debe crear explícitamente una referencia (con &) antes de llamar a la función y anular su referencia explícitamente (con *) en la función.

    – Chris Dodd

    26 mayo 2017 a las 18:49


  • Su salida de código es exactamente igual cuando llama f(&i); esta es una implementación de paso por referencia, que no existe únicamente en CC paso por referencia

    – EsmaeelE

    9 de enero de 2019 a las 21:33


  • @Someprogrammerdude Pasar un puntero es pasar por referencia. Este parece ser uno de esos hechos de los que los programadores C “expertos” se enorgullecen. Como si les diera una patada. “Oh, podrías PENSAR que C tiene paso por referencia, pero no, en realidad es solo el valor de una dirección de memoria que se pasa harharhar”. Pasar por referencia literalmente solo significa pasar la dirección de memoria donde se almacena una variable en lugar del valor de la variable en sí. Eso es lo que permite C, y es pass-by-reference cada vez que pasas un puntero, porque un puntero es un referencia a una ubicación de memoria de variables.

    – ICW

    21 de agosto de 2019 a las 18:19


avatar de usuario
Ely

Eso no es pasar por referencia, es pasar por valor como dijeron otros.

El lenguaje C se pasa por valor sin excepción. Pasar un puntero como parámetro no significa pasar por referencia.

La regla es la siguiente:

Una función no puede cambiar el valor real de los parámetros.

(La cita anterior es en realidad del libro K&R)


Intentemos ver las diferencias entre los parámetros escalares y punteros de una función.

Variables escalares

Este breve programa muestra el paso por valor usando una variable escalar. param se llama el parámetro formal y variable en la invocación de la función se llama parámetro real. nota incrementando param en la función no cambia variable.

#include <stdio.h>

void function(int param) {
    printf("I've received value %d\n", param);
    param++;
}

int main(void) {
    int variable = 111;

    function(variable);
    printf("variable %d\m", variable);
    return 0;
}

El resultado es

I've received value 111
variable=111

Ilusión de referencia de paso

Cambiamos ligeramente la pieza de código. param es un puntero ahora.

#include <stdio.h>

void function2(int *param) {
    printf("I've received value %d\n", *param);
    (*param)++;
}

int main(void) {
    int variable = 111;

    function2(&variable);
    printf("variable %d\n", variable);
    return 0;
}

El resultado es

I've received value 111
variable=112

Eso te hace creer que el parámetro se pasó por referencia. No era. Se pasó por valor, siendo el valor del parámetro una dirección. El valor de tipo int se incrementó, y ese es el efecto secundario que nos hace pensar que fue una llamada de función de paso por referencia.

Punteros: pasados ​​por valor

¿Cómo podemos mostrar/probar ese hecho? Bueno, tal vez podamos probar el primer ejemplo de variables escalares, pero en lugar de escalares usamos direcciones (punteros). Veamos si eso puede ayudar.

#include <stdio.h>

void function2(int *param) {
    printf("address param is pointing to %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("address ptr is pointing to %d\n", ptr);
    return 0;
}

El resultado será que las dos direcciones son iguales (no te preocupes por el valor exacto).

Resultado de ejemplo:

address param is pointing to -1846583468
address ptr   is pointing to -1846583468

En mi opinión, esto demuestra claramente que los punteros se pasan por valor. De lo contrario ptr sería NULL después de la invocación de la función.

  • “dirección de ptr” no es correcta: imprimes el puntero, que es la dirección de la variable, no su dirección, que habría sido ´printf(“dirección de ptr %d\n”, &ptr);´ . La capa técnica de pasar una referencia es pasar un puntero, ¡no una ilusión! En function2, *param en realidad es una referencia.

    – Sam Ginrich

    25 de febrero a las 8:24

  • Correcto. Lo que se demuestra en la parte “Punteros: pasados ​​por valor” en realidad debería reformularse como: “printf(“donde el puntero param apunta a %d\n”, param);” y “printf(“donde el puntero ptr apunta a %d\n”, ptr);”. Bien pillado^^ Y creo que lo que se pretendía mostrar era: “printf(“dirección de parámetros de puntero %d\n”, &param);” y “printf(“puntero ptrs dirección %d\n”, &ptr);”

    – Daniel

    4 abr a las 16:59


  • Sí, tienes razón. Corrijo eso. De hecho, quería mostrar que las direcciones a las que apuntan son las mismas. Las direcciones de los punteros no son realmente interesantes aquí (y no son las mismas).

    – Ely

    4 abr a las 19:18


p es una variable de puntero. Su valor es la dirección de i. Cuando llamas f, tu pasar el valor de p, que es la dirección de i.

  • donde el “paso por referencia” considera i como valor, que es *p

    – Sam Ginrich

    3 de marzo a las 9:52

En C, para pasar por referencia se utiliza el operador dirección de & que debería usarse contra una variable, pero en su caso, dado que ha usado la variable de puntero p, no necesita prefijarlo con el operador de dirección. Habría sido cierto si hubieras usado &i como el parámetro: f(&i).

También puede agregar esto, para desreferenciar p y ver cómo ese valor coincide i:

printf("p=%d \n",*p);

  • donde el “paso por referencia” considera i como valor, que es *p

    – Sam Ginrich

    3 de marzo a las 9:52

avatar de usuario
Francisco Zapata

Respuesta corta: Sí, C implementa el paso de parámetros por referencia mediante punteros.

Al implementar el paso de parámetros, los diseñadores de lenguajes de programación usan tres estrategias diferentes (o modelos semánticos): transferir datos al subprograma, recibir datos del subprograma o hacer ambas cosas. Estos modelos se conocen comúnmente como modo de entrada, modo de salida y modo de entrada y salida, respectivamente.

Los diseñadores de lenguajes han ideado varios modelos para implementar estas tres estrategias elementales de paso de parámetros:

Pass-by-Value (semántica en modo inout) Pass-by-Result (semántica en modo out) Pass-by-Value-Result (semántica en modo inout) Pass-by-Reference (semántica en modo inout) Pass-by-Name (modo inout semántica)

Pass-by-reference es la segunda técnica para pasar parámetros en modo inout. En lugar de copiar datos de un lado a otro entre la rutina principal y el subprograma, el sistema de tiempo de ejecución envía una ruta de acceso directo a los datos del subprograma. En esta estrategia el subprograma tiene acceso directo a los datos compartiendo efectivamente los datos con la rutina principal. La principal ventaja de esta técnica es que es absolutamente eficiente en tiempo y espacio porque no hay necesidad de duplicar espacio y no hay operaciones de copia de datos.

Implementación de paso de parámetros en C: C implementa la semántica de paso por valor y también de paso por referencia (modo inout) utilizando punteros como parámetros. El puntero se envía al subprograma y no se copia ningún dato real. Sin embargo, debido a que un puntero es una ruta de acceso a los datos de la rutina principal, el subprograma puede cambiar los datos de la rutina principal. C adoptó este método de ALGOL68.

Implementación de paso de parámetros en C++: C++ también implementa la semántica de paso por referencia (modo inout) usando punteros y también usando un tipo especial de puntero, llamado tipo de referencia. Los punteros de tipo de referencia se desreferencian implícitamente dentro del subprograma, pero su semántica también se pasa por referencia.

Entonces, el concepto clave aquí es que el paso por referencia implementa una ruta de acceso a los datos en lugar de copiar los datos en el subprograma. Las rutas de acceso a datos pueden ser punteros desreferenciados explícitamente o punteros desreferenciados automáticamente (tipo de referencia).

Para obtener más información, consulte el libro Conceptos de lenguajes de programación de Robert Sebesta, 10.ª edición, capítulo 9.

¿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