uso ¿Cuáles son las diferencias entre una variable de puntero y una variable de referencia en C++?




uso de punteros en java (20)

Sé que las referencias son azúcar sintáctica, por lo que el código es más fácil de leer y escribir.

Pero ¿cuáles son las diferencias?

Resumen de las respuestas y enlaces a continuación:

  1. Un puntero se puede reasignar cualquier cantidad de veces, mientras que una referencia no se puede reasignar después del enlace.
  2. Los punteros no pueden apuntar a ninguna parte ( NULL ), mientras que una referencia siempre se refiere a un objeto.
  3. No puede tomar la dirección de una referencia como puede con punteros.
  4. No hay "aritmética de referencia" (pero puede tomar la dirección de un objeto señalado por una referencia y hacer aritmética de punteros en él como en &obj + 5 ).

Para aclarar una idea falsa:

El estándar de C ++ es muy cuidadoso para evitar dictar cómo un compilador puede implementar referencias, pero cada compilador de C ++ implementa referencias como punteros. Es decir, una declaración como:

int &ri = i;

si no se optimiza por completo , asigna la misma cantidad de almacenamiento que un puntero y coloca la dirección de i en ese almacenamiento.

Entonces, tanto el puntero como la referencia usan la misma cantidad de memoria.

Como regla general,

  • Utilice referencias en los parámetros de función y tipos de retorno para proporcionar interfaces útiles y autodocumentadas.
  • Utilice punteros para implementar algoritmos y estructuras de datos.

LecturA INTERESANTE:

https://code.i-harness.com


¿Qué es una referencia de C ++ ( para programadores de C )

Una referencia se puede considerar como un puntero constante (¡no debe confundirse con un puntero a un valor constante!) Con direccionamiento automático automático, es decir, el compilador aplicará el operador * para usted.

Todas las referencias deben inicializarse con un valor que no sea nulo o la compilación fallará. No es posible obtener la dirección de una referencia (el operador de la dirección devolverá la dirección del valor de referencia en su lugar), ni tampoco es posible hacer aritmética en las referencias.

A los programadores de C les pueden disgustar las referencias de C ++ ya que ya no serán obvias cuando ocurra la indirección o si un argumento se pasa por valor o por puntero sin mirar las firmas de las funciones.

A los programadores de C ++ les puede disgustar el uso de punteros, ya que se consideran inseguros, aunque las referencias no son más seguras que los punteros constantes, excepto en los casos más triviales, carecen de la conveniencia de la indirección automática y tienen una connotación semántica diferente.

Considere la siguiente declaración de las preguntas frecuentes de C ++ :

Aunque a menudo se implementa una referencia utilizando una dirección en el lenguaje ensamblador subyacente, no piense en una referencia como un puntero de aspecto divertido a un objeto. Una referencia es el objeto. No es un puntero al objeto, ni una copia del objeto. Es el objeto.

Pero si una referencia realmente fuera el objeto, ¿cómo podría haber referencias colgantes? En lenguajes no administrados, es imposible que las referencias sean "más seguras" que los punteros. En general, simplemente no hay una forma de alias confiable de valores a través de los límites del alcance.

Por qué considero útiles las referencias a C ++

Al provenir de un fondo en C, las referencias a C ++ pueden parecer un concepto un tanto tonto, pero aún se deben usar en lugar de indicadores cuando sea posible: la indirección automática es conveniente, y las referencias son especialmente útiles cuando se trata de RAII , pero no debido a la seguridad percibida ventaja, sino más bien porque hacen que escribir código idiomático sea menos incómodo.

RAII es uno de los conceptos centrales de C ++, pero interactúa de manera no trivial con la copia de la semántica. Pasar objetos por referencia evita estos problemas ya que no se trata de copiar. Si las referencias no estuvieran presentes en el idioma, tendría que usar punteros, que son más incómodos de usar, violando así el principio de diseño del lenguaje de que la mejor solución debería ser más fácil que las alternativas.


Diferencia entre puntero y referencia.

Un puntero se puede inicializar a 0 y una referencia no. De hecho, una referencia también debe referirse a un objeto, pero un puntero puede ser el puntero nulo:

int* p = 0;

Pero no podemos tener int& p = 0;y tambiénint& p=5 ; .

De hecho, para hacerlo correctamente, debemos haber declarado y definido un objeto al principio, luego podemos hacer una referencia a ese objeto, por lo que la implementación correcta del código anterior será:

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

Otro punto importante es que podemos hacer la declaración del puntero sin inicialización, sin embargo, no se puede hacer tal cosa en el caso de una referencia que debe hacer referencia siempre a la variable o al objeto. Sin embargo, tal uso de un puntero es arriesgado, por lo que generalmente verificamos si el puntero en realidad apunta a algo o no. En el caso de una referencia, no es necesaria tal verificación, porque ya sabemos que la referencia a un objeto durante la declaración es obligatoria.

Otra diferencia es que el puntero puede apuntar a otro objeto; sin embargo, la referencia siempre hace referencia al mismo objeto, tomemos este ejemplo:

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

Otro punto: cuando tenemos una plantilla como una plantilla STL, un tipo de plantilla de clase siempre devolverá una referencia, no un puntero, para facilitar la lectura o asignar un nuevo valor utilizando el operador []:

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="

Aparte del azúcar sintáctico, una referencia es un puntero de const ( no un puntero a una const ). Debe establecer a qué se refiere cuando declara la variable de referencia y no puede cambiarla más adelante.

Actualización: ahora que lo pienso un poco más, hay una diferencia importante.

El objetivo de un puntero de const se puede reemplazar tomando su dirección y usando una conversión constante.

El objetivo de una referencia no puede ser reemplazado de ninguna manera por debajo de UB.

Esto debería permitir al compilador hacer más optimización en una referencia.


Contrariamente a la opinión popular, es posible tener una referencia que sea NULL.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

Por supuesto, es mucho más difícil hacerlo con una referencia, pero si lo consigues, te arrancarás el pelo tratando de encontrarlo. Las referencias no son inherentemente seguras en C ++!

Técnicamente, esta es una referencia no válida , no una referencia nula. C ++ no admite referencias nulas como concepto, como puede encontrar en otros idiomas. También hay otros tipos de referencias inválidas. Cualquier referencia no válida eleva el espectro del comportamiento indefinido , al igual que usar un puntero no válido.

El error real está en la eliminación de referencias del puntero NULL, antes de la asignación a una referencia. Pero no conozco ningún compilador que genere errores en esa condición: el error se propaga a un punto más adelante en el código. Eso es lo que hace que este problema sea tan insidioso. La mayoría de las veces, si no se hace referencia a un puntero NULO, se bloquea justo en ese punto y no hace falta mucha depuración para averiguarlo.

Mi ejemplo anterior es corto y artificial. Aquí hay un ejemplo más real del mundo.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(i1,i2,i3,i4,i5);  // crash occurs here due to memory access violation - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Quiero reiterar que la única forma de obtener una referencia nula es a través de un código con formato incorrecto, y una vez que lo tenga, tendrá un comportamiento indefinido. Nunca tiene sentido verificar una referencia nula; por ejemplo, puedes probar if(&bar==NULL)... pero el compilador podría optimizar la declaración de la existencia! Una referencia válida nunca puede ser NULA, por lo que, desde el punto de vista del compilador, la comparación siempre es falsa, y es libre de eliminar la cláusula if como código muerto: esta es la esencia del comportamiento indefinido.

La forma correcta de evitar problemas es evitar la desreferenciación de un puntero NULO para crear una referencia. Aquí hay una forma automatizada de lograr esto.

template<typename T>
T& deref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(deref(p));

Para ver un aspecto más antiguo de este problema de alguien con mejores habilidades de escritura, vea Referencias nulas de Jim Hyslop y Herb Sutter.

Para ver otro ejemplo de los peligros de anular la referencia a un puntero nulo, consulte Exponer el comportamiento indefinido al intentar portar el código a otra plataforma por Raymond Chen.


Las referencias son muy similares a los punteros, pero están diseñadas específicamente para ser útiles para optimizar los compiladores.

  • Las referencias están diseñadas de tal manera que es sustancialmente más fácil para el compilador rastrear qué alias de referencia y qué variables. Dos características principales son muy importantes: no hay "aritmética de referencia" ni reasignación de referencias. Esto permite al compilador averiguar qué alias de referencia hacen referencia las variables en el momento de la compilación.
  • Las referencias pueden hacer referencia a variables que no tienen direcciones de memoria, como las que el compilador elige colocar en registros. Si toma la dirección de una variable local, es muy difícil para el compilador colocarla en un registro.

Como ejemplo:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

Un compilador optimizado puede darse cuenta de que estamos accediendo a un [0] y un [1] bastante. Me encantaría optimizar el algoritmo para:

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

Para realizar dicha optimización, debe demostrar que nada puede cambiar la matriz [1] durante la llamada. Esto es bastante fácil de hacer. i nunca es menor que 2, por lo que array [i] nunca puede referirse a array [1]. maybeModify () recibe a0 como referencia (matriz de aliasing [0]). Debido a que no hay una aritmética de "referencia", el compilador solo tiene que probar que maybeModify nunca obtiene la dirección de x, y ha demostrado que nada cambia la matriz [1].

También tiene que demostrar que no hay formas de que una futura llamada pueda leer / escribir un [0] mientras tenemos una copia de registro temporal en a0. Esto es a menudo trivial de probar, porque en muchos casos es obvio que la referencia nunca se almacena en una estructura permanente como una instancia de clase.

Ahora haz lo mismo con los punteros.

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

El comportamiento es el mismo; solo que ahora es mucho más difícil probar que maybeModify nunca modifica la matriz [1], porque ya le dimos un puntero; El gato está fuera de la bolsa. Ahora tiene que hacer una prueba mucho más difícil: un análisis estático de maybeModify para probar que nunca escribe en & x + 1. También tiene que demostrar que nunca guarda un puntero que pueda referirse a la matriz [0], que es simplemente tan complicado

Los compiladores modernos están mejorando cada vez más en el análisis estático, pero siempre es bueno ayudarlos y usar referencias.

Por supuesto, salvo tales optimizaciones inteligentes, los compiladores de hecho convertirán las referencias en indicadores cuando sea necesario.

EDITAR: Cinco años después de publicar esta respuesta, encontré una diferencia técnica real donde las referencias son diferentes a una forma diferente de ver el mismo concepto de direccionamiento. Las referencias pueden modificar la vida útil de los objetos temporales de una manera que los punteros no pueden.

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

Normalmente, los objetos temporales como el creado por la llamada a createF(5) se destruyen al final de la expresión. Sin embargo, al vincular ese objeto a una referencia, ref , C ++ extenderá la vida útil de ese objeto temporal hasta que la ref quede fuera de alcance.


Olvidaste la parte más importante:

acceso de miembros con punteros usa ->
Acceso de miembros con usos de referencias .

foo.bar es claramente superior a foo->bar de la misma manera que vi es claramente superior a Emacs :-)


Una referencia nunca puede ser NULL .


A riesgo de agregar confusión, quiero agregar algo de información, estoy seguro de que depende principalmente de cómo el compilador implementa las referencias, pero en el caso de gcc, la idea de que una referencia solo puede apuntar a una variable en la pila. no es realmente correcto, toma esto por ejemplo:

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

Que produce esto:

THIS IS A STRING
0xbb2070 : 0xbb2070

¡Si observa que incluso las direcciones de memoria son exactamente iguales, significa que la referencia apunta con éxito a una variable en el montón! Ahora si realmente quieres ponerte raro, esto también funciona:

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

Que produce esto:

THIS IS A STRING

Por lo tanto, una referencia ES un puntero debajo del capó, ambos están simplemente almacenando una dirección de memoria, donde la dirección que apunta es irrelevante, ¿qué crees que pasaría si llamara a std :: cout << str_ref; DESPUÉS de llamar a delete & str_ref? Bueno, obviamente se compila bien, pero causa una falla de segmentación en tiempo de ejecución porque ya no apunta a una variable válida, esencialmente tenemos una referencia rota que aún existe (hasta que queda fuera de alcance), pero es inútil.

En otras palabras, una referencia no es más que un puntero que tiene abstraída la mecánica del puntero, lo que lo hace más seguro y más fácil de usar (sin matemáticas de punteros accidentales, sin mezclar '.' Y '->', etc.), suponiendo no intentes tonterías como mis ejemplos anteriores;)

Ahora, independientemente de cómo un compilador maneje las referencias, siempre tendrá algún tipo de puntero debajo del capó, porque una referencia debe referirse a una variable específica en una dirección de memoria específica para que funcione como se espera, no hay forma de evitar esto (por lo tanto, el término 'referencia').

La única regla importante que es importante recordar con referencias es que deben definirse en el momento de la declaración (con la excepción de una referencia en un encabezado, en ese caso, debe definirse en el constructor, una vez que el objeto en el que está contenido es construido es demasiado tarde para definirlo).

Recuerde, mis ejemplos anteriores son solo eso, ejemplos que demuestran qué es una referencia, ¡nunca querría usar una referencia de esa manera! Para el uso adecuado de una referencia, ya hay muchas respuestas aquí que golpean el clavo en la cabeza.


Existe una diferencia no técnica muy importante entre los punteros y las referencias: un argumento que se pasa a una función mediante un puntero es mucho más visible que un argumento que se pasa a una función por una referencia no constante. Por ejemplo:

void fn1(std::string s);
void fn2(const std::string& s);
void fn3(std::string& s);
void fn4(std::string* s);

void bar() {
    std::string x;
    fn1(x);  // Cannot modify x
    fn2(x);  // Cannot modify x (without const_cast)
    fn3(x);  // CAN modify x!
    fn4(&x); // Can modify x (but is obvious about it)
}

De vuelta en C, una llamada que parece fn(x)solo puede pasarse por valor, por lo que definitivamente no se puede modificar x; para modificar un argumento necesitarías pasar un puntero fn(&x). Entonces, si un argumento no estuviera precedido por un &sabías, no se modificaría. (Lo contrario, &significa que se modificó, no era cierto porque a veces tendría que pasar grandes estructuras de solo lectura con el constpuntero).

Algunos argumentan que esta es una característica tan útil al leer el código, que los parámetros de puntero siempre deben usarse para parámetros modificables en lugar de no constreferencias, incluso si la función nunca espera a nullptr. Es decir, esas personas argumentan que fn3()no deben permitirse las firmas de funciones como la anterior. Las pautas de estilo de C ++ de Google son un ejemplo de esto.


Existe una diferencia semántica que puede parecer esotérica si no está familiarizado con el estudio de los lenguajes informáticos de forma abstracta o incluso académica.

En el nivel más alto, la idea de las referencias es que son transparentes "alias". Su computadora puede usar una dirección para hacer que funcionen, pero se supone que no debe preocuparse por eso: se supone que debe pensar en ellos como "solo otro nombre" para un objeto existente y la sintaxis lo refleja. Son más estrictos que los punteros, por lo que su compilador puede advertirle de manera más confiable cuando está a punto de crear una referencia colgante, que cuando está a punto de crear un puntero colgante.

Más allá de eso, hay, por supuesto, algunas diferencias prácticas entre los punteros y las referencias. La sintaxis para usarlos es obviamente diferente, y no puede "volver a colocar" las referencias, tener referencias a la nada, o apuntar a referencias.


Otra diferencia es que puede tener punteros a un tipo de vacío (y significa puntero a cualquier cosa) pero las referencias a vacío están prohibidas.

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

No puedo decir que estoy realmente feliz con esta diferencia particular. Preferiría que se permitiera con el significado de referencia a cualquier cosa con una dirección y, por lo demás, el mismo comportamiento para las referencias. Permitiría definir algunos equivalentes de las funciones de la biblioteca C como memcpy usando referencias.


Otro uso interesante de las referencias es proporcionar un argumento predeterminado de un tipo definido por el usuario:

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

El sabor predeterminado utiliza el aspecto 'vincular const de un temporal' de las referencias.


Uso referencias a menos que necesite alguno de estos:

  • Los punteros nulos pueden usarse como un valor centinela, a menudo una forma barata de evitar la sobrecarga de funciones o el uso de un bool.

  • Puedes hacer aritmética en un puntero. Por ejemplo,p += offset;


Este programa puede ayudar a comprender la respuesta de la pregunta. Este es un programa simple de una referencia "j" y un puntero "ptr" que apunta a la variable "x".

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

Ejecute el programa y eche un vistazo a la salida y lo entenderá.

Además, dedique 10 minutos y vea este video: https://www.youtube.com/watch?v=rlJrrGV0iOg


Hay una diferencia fundamental entre los punteros y las referencias que no vi que nadie haya mencionado: las referencias permiten la semántica paso por referencia en los argumentos de función. Los punteros, aunque no son visibles al principio, no lo hacen: solo proporcionan semántica de paso por valor. Esto ha sido muy bien descrito en este artículo .

Saludos, y rzej


La diferencia es que la variable de puntero no constante (que no debe confundirse con un puntero a constante) puede cambiarse en algún momento durante la ejecución del programa, requiere que se use la semántica de puntero (&, *) operadores, mientras que las referencias se pueden establecer durante la inicialización solo (por eso puede establecerlos solo en la lista de inicializadores de constructores, pero no de otra manera) y usar el valor ordinario para acceder a la semántica. Básicamente, se introdujeron referencias para permitir el soporte para sobrecargas de operadores como había leído en un libro muy antiguo. Como alguien dijo en este hilo, el puntero se puede establecer en 0 o en el valor que desee. 0 (NULL, nullptr) significa que el puntero se inicializa con nada. Es un error anular la referencia al puntero nulo. Pero en realidad, el puntero puede contener un valor que no apunta a alguna ubicación de memoria correcta.A su vez, las referencias intentan no permitir que un usuario inicialice una referencia a algo a lo que no se puede hacer referencia debido al hecho de que siempre le proporciona un valor del tipo correcto. Aunque hay muchas formas de hacer que la variable de referencia se inicialice en una ubicación de memoria incorrecta, es mejor que no profundice en los detalles. En el nivel de la máquina, tanto el puntero como el trabajo de referencia son uniformes, a través de punteros. Digamos que en las referencias esenciales se encuentra el azúcar sintáctico. Las referencias de valores son diferentes a esto: naturalmente son objetos de pila / pila.Aunque hay muchas formas de hacer que la variable de referencia se inicialice en una ubicación de memoria incorrecta, es mejor que no profundice en los detalles. En el nivel de la máquina, tanto el puntero como el trabajo de referencia son uniformes, a través de punteros. Digamos que en las referencias esenciales se encuentra el azúcar sintáctico. Las referencias de valores son diferentes a esto: naturalmente son objetos de pila / pila.Aunque hay muchas formas de hacer que la variable de referencia se inicialice en una ubicación de memoria incorrecta, es mejor que no profundice en los detalles. En el nivel de la máquina, tanto el puntero como el trabajo de referencia son uniformes, a través de punteros. Digamos que en las referencias esenciales se encuentra el azúcar sintáctico. Las referencias de valores son diferentes a esto: naturalmente son objetos de pila / pila.


Siempre decido por this regla de C ++ Core Guidelines:

Prefiero T * sobre T y cuando "sin argumento" es una opción válida


Tal vez algunas metáforas ayuden; En el contexto de su pantalla de escritorio -

  • Una referencia requiere que especifique una ventana real.
  • Un puntero requiere la ubicación de un espacio en la pantalla para asegurar que contendrá cero o más instancias de ese tipo de ventana.

Una referencia a un puntero es posible en C ++, pero lo contrario no es posible significa que no es posible un puntero a una referencia. Una referencia a un puntero proporciona una sintaxis más limpia para modificar el puntero. Mira este ejemplo:

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

Y considere la versión C del programa anterior. En C, tiene que usar el puntero al puntero (direccionamiento múltiple), lo que lleva a la confusión y el programa puede parecer complicado.

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

Visite lo siguiente para obtener más información sobre la referencia al puntero:

Como dije, un puntero a una referencia no es posible. Prueba el siguiente programa:

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

Una referencia es un alias para otra variable, mientras que un puntero contiene la dirección de memoria de una variable. Las referencias se utilizan generalmente como parámetros de función para que el objeto pasado no sea la copia sino el objeto en sí.

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 




c++-faq