Diferencia entre argumentos de función declarados con & y * en C++

7 minutos de lectura

avatar de usuario
tim

Escribí el siguiente ejemplo:

#include <iostream>
double f(double* x, double* y)
{
    std::cout << "val x: " << *x << "\n";
    std::cout << "val y: " << *y << "\n";
    return *x * *y;
}
double f2(double &x, double &y)
{
    std::cout << "val x: " << x << "\n";
    std::cout << "val y: " << y << "\n";
    return x * y;
}
int main()
{
    double a, b;
    a = 2;
    b = 3; 
    std::cout << f(&a, &b) << "\n";
    std::cout << f2(a, b) << "\n";
    return 0;
}   

en la funcion f Declaro x e y como punteros de los cuales puedo obtener el valor usando *x. al llamar f Necesito pasar la dirección de mis argumentos pasados, es por eso que paso &a, &b. f2 es lo mismo excepto que la definición es diferente.

Ahora mi pregunta es: ¿Son realmente iguales en lo que respecta a la gestión de la memoria? ¿Ambos no hacen ninguna copia del valor pasado sino que pasan una referencia? me pregunto sobre f2 porque no pude leer la dirección de x en f2 asique saber más sobre x e y en f (ahí sé dirección Y valor).

¡Gracias por adelantado!

Editar: De acuerdo, gracias, después de investigar un poco más, encontré un tema bastante útil:

Puntero vs. Referencia También hay un enlace a las pautas de codificación de Google http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments que es bastante useful Siento (como entendí ahora, es una forma de gusto del sujeto) hacer más claro

  • Esto no es C, esto es C++.

    usuario395760

    28 de abril de 2011 a las 9:50

  • Ah, veo que en CI siempre hay que usar punteros, ¡de acuerdo, gracias! ¡No sabía eso hasta ahora!

    – Tim

    28 de abril de 2011 a las 10:01


  • >Guía de estilo de Google: evítela. Estaba desactualizado en 2008 (fecha de la respuesta vinculada), y hoy es aún peor.

    – MSalters

    28 de abril de 2011 a las 11:09

  • @MSalters evitar cuál? ¿El estilo ‘f’ o el estilo ‘f2’?

    – Abhinav

    08/09/2016 a las 18:55

  • @Abhinav: Evite la Guía de estilo de Google. Está completamente desactualizado.

    – MSalters

    9 de septiembre de 2016 a las 7:12

f2 está tomando sus argumentos por referencia, lo cual es esencialmente una alias por los argumentos que pasas. La diferencia entre puntero y referencia es que una referencia no puede ser NULL. Con el f tienes que pasar el Dirección (usando & operador) de los parámetros que está pasando al puntero, donde cuando pasa por referencia simplemente pasa los parámetros y se crea el alias.

Pasando por referencia const (const double& ref) es preferible cuando no va a cambiar los argumentos dentro de la función, y cuando los va a cambiar, use una referencia no constante.

Los punteros se usan principalmente cuando necesita poder pasar NULL a sus parámetros, obviamente necesitaría verificar dentro de su función si el puntero no estaba NULL antes de usarlo.

  • ¿Por qué alguien necesita pasar Nulo a los parámetros?

    – ¡Bienvenido!

    18 mayo 2014 a las 11:25

  • Bueno, probablemente necesitan ser poder para pasar nulo si es necesario. Obviamente no lo van a pasar todo el tiempo.

    – Ludwik

    15 de julio de 2014 a las 6:25

  • P: “La diferencia entre puntero y referencia es que una referencia no puede ser NULL”. La respuesta más simple y clara, cuándo debe y cuándo no usar * vs & como argumentos de función. Un alias nunca puede ser NULL.

    – LXSoft

    11 de diciembre de 2017 a las 12:49

  • Cuál es la diferencia entre f2 y haciendo lo mismo pero sin & entonces es como double f3(double x, double y)?

    – Aarón Franke

    5 de diciembre de 2020 a las 8:18

  • @AaronFranke para que no duplique datos en la memoria

    – Mauricio Cortázar

    15 de mayo a las 21:28

Esto es solo azúcar sintáctico para evitar tener que usar * cada vez que hace referencia al argumento. Todavía puedes usar & tener la dirección de x en f2.

  • Personalmente creo que es más que eso. Establece que el argumento NO PUEDE ser nulo y NO es opcional. Con punteros nunca se puede saber y siempre se debe comprobar. Con las referencias, la firma de la función hace cumplir la intención del programador y conduce a un código más limpio que refleja con mayor precisión esa intención.

    – Len Holgate

    28/04/2011 a las 10:00

  • Además, un argumento de puntero podría esperar una matriz. Un argumento de referencia toma específicamente un solo objeto, sin necesidad de documentación adicional.

    –Mike Seymour

    28 de abril de 2011 a las 12:13

Otra diferencia que no se ha mencionado es que no se puede cambiar a qué se refiere una referencia. Esto no hace mucha diferencia en el ejemplo de llamada de función que se muestra en la pregunta original.

int X(10), Y(20);
int *pX = X;
int& rY = Y;

*pX = 15; // change value of X
rY = 25;  // change value of Y

pX = Y;   // pX now points to Y

rY siempre apunta a Y y no se puede mover.

Las referencias no se pueden usar para indexar en matrices simples como punteros.

avatar de usuario
quamrana

En mi cabeza, los parámetros de las funciones son siempre pasado por valor. pasando un int es fácil de imaginar, pasando un double es más grande y pasa un struct o class podría ser muy grande de hecho.
Pero pasar un puntero a algo, bueno, solo estás pasando una dirección por valor. (Un puntero tiene a menudo un tamaño conveniente para la CPU al igual que un int.)
Una referencia es muy similar, y ciertamente pienso en una referencia como un puntero, pero con azúcar sintáctico para que parezca que el objeto al que hace referencia ha sido pasado por valor.

También puede pensar en una referencia como un const puntero, es decir:

int i;
int j;
int* p = &i;           // pointer to i
int* const cp = p;     // cp points to i, but cp cannot be modified
p = &j;                // OK - p is modified to point to j
*cp = 0;               // OK - i is overwritten
cp = &j;               // ERROR - cp cannot be modified

int& ri = i;           // ri refers to i
ri = 1;                // i is overwritten
ri = j;                // i is overwritten again
                       // Did you think ri might refer to j?

Entonces, un puntero hace el doble de tiempo: es un valor por derecho propio, pero también puede apuntar a otro valor cuando lo desreferencia, por ejemplo: *p.
Además, tener parámetros de referencia significa que no puede hacer que se refieran a nada más durante el tiempo de vida de la función porque no hay forma de expresarlo.

Se supone que una referencia no puede inicializarse con nullpero considera esto:

void foo(int& i);

int* p = 0;
foo(*p);

Esto significa que los punteros deben verificarse antes de usarlos, pero las referencias no pueden verificarse. La implementación de foo() podría intentar leer o escribir en i lo que provocará una infracción de acceso.

En el ejemplo anterior, el puntero p debería han sido verificados antes de ser utilizados en la llamada a foo:

if (p) foo(*p);

Deberías haber sabido leer x dirección en ambas funciones.

Para hacerlo en f2por supuesto debe prefijar x por un & ya que hay, x es un referencia a un doble, y quieres un Dirección.

Una diferencia que vale la pena notar entre las referencias y los punteros es que las primeras no pueden ser NULL. Tú deber pase algo (válido) mientras que al proporcionar un puntero, debe especificar en la documentación si está permitido/bien definido pasar NULL.

Otra diferencia es una cuestión de legibilidad: el uso de referencias en lugar de punteros (cuando sea posible) hace que el código esté menos abarrotado de * y ->.

¿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