2010-05-14 18 views
34

Comprendo bastante bien el operador de desreferenciación, la dirección del operador y los punteros en general.C++ Indicadores de devolución/Referencias

sin embargo me confundo cuando veo cosas como esta:

int* returnA() { 
    int *j = &a; 
    return j; 
} 

int* returnB() { 
    return &b; 
} 

int& returnC() { 
    return c; 
} 

int& returnC2() { 
    int *d = &c; 
    return *d; 
} 
  1. En returnA() que estoy pidiendo para devolver un puntero; solo para aclarar esto funciona porque j es un puntero?
  2. En returnB() Estoy pidiendo que devuelva un puntero; dado que un puntero apunta a una dirección, la razón por la cual returnB() funciona es porque estoy devolviendo &b?
  3. En returnC() estoy pidiendo una dirección de int que se devolverá. Cuando devuelvo c, ¿el operador & se "anexa" automáticamente al c?
  4. En returnC2() estoy pidiendo nuevamente una dirección de int para devolver. ¿Funciona *d porque los punteros apuntan a una dirección?

Supongamos que a, b, c se inicializan en números enteros como Global.

¿Alguien puede validar si estoy en lo correcto con mis cuatro preguntas?

+2

Las respuestas a estas preguntas dependen enteramente en las declaraciones de a, b y c, que parecen estar ausentes del código. –

+0

Supongamos que a, b, c están inicializados. –

+0

Las variables son ints. –

Respuesta

22

En returnA() Estoy pidiendo que devuelva un puntero; solo para aclarar esto funciona porque j es un puntero?

Sí, int *j = &a inicializa j para apuntar a a. Luego devuelve el valor de j, que es la dirección de a.

En returnB() Estoy pidiendo que devuelva un puntero; dado que un puntero apunta a una dirección, la razón por la cual returnB() funciona es porque estoy devolviendo & b?

Sí. Aquí sucede lo mismo que arriba, solo en un solo paso. &b da la dirección de b.

En returnC() Estoy pidiendo que se devuelva una dirección de int. Cuando regreso c, ¿el operador & se anexa automáticamente?

No, es una referencia a un int que se devuelve. Una referencia no es una dirección de la misma manera que un puntero: es solo un nombre alternativo para una variable. Por lo tanto, no necesita aplicar el operador & para obtener una referencia de una variable.

En returnC2() Estoy pidiendo nuevamente una dirección de int para ser devuelto. ¿Funciona * porque los punteros apuntan a una dirección?

Nuevamente, es una referencia a un int que se devuelve. *d se refiere a la variable original c (cualquiera que sea), apuntada por c.Y esto puede convertirse implícitamente en una referencia, al igual que en returnC.

Los punteros generalmente no apuntan a una dirección (aunque pueden, por ejemplo, int** es un puntero al puntero a int). Los punteros son una dirección de algo. Cuando declara el puntero como something*, ese punto apunta a something. Por lo tanto, en mi ejemplo anterior, int** declara un puntero a int*, que pasa a ser un puntero.

+0

¿Puede proporcionar una respuesta coherente al tercer problema (la función returnC())? Aunque c no es una variable de referencia, ¿por qué la función acepta c como su valor de retorno aunque exige referencias y se devuelve una variable entera? – chosentorture

+0

@ChosenTorture, creo que Tyler lo explicó muy bien en su respuesta a continuación, así que lo voté y sigo el principio DRY :-) –

+0

He leído lo que Tyler escribió cuidadosamente. O no responde mi pregunta o tal vez no entiendo algo. Le pido que lea lo que estoy escribiendo. Usted escribió que _No, es una referencia a un int que se devuelve_. Entonces, cuando hago que un tipo de función sea 'int &', ¿eso le dice al programador que se debe devolver ** una referencia o que se debe devolver ** una referencia y que se devolverá? Si el primer caso es verdadero, entonces el código no debería funcionar. Pero porque lo hace, lo último es verdad. Todo lo que estoy preguntando es la razón por qué? ¿Es este un proceso de conversión implícito o algo así? – chosentorture

2

En returnC() y returnC2() no está pidiendo que devuelva la dirección.

Ambas funciones devuelven referencias a los objetos.
Una referencia no es la dirección de nada, es un nombre alternativo de algo (esto puede significar que el compilador puede (o puede no depender de la situación) usar una dirección para representar el objeto (o puede saber también para mantenerlo registrado))).

Todo lo que sabe que una referencia apunta a un objeto específico.
Mientras que una referencia en sí misma no es un objeto solo un nombre alternativo.

+0

Aquí es donde me estaba confundiendo; Pensé que estaba devolviendo direcciones; no "alias" –

1

Todos sus ejemplos producen un comportamiento de tiempo de ejecución indefinido. Está devolviendo punteros o referencias a elementos que desaparecen después de que la ejecución abandona la función.

Aclaro:

int * returnA() 
{ 
    static int a; // The static keyword keeps the variable from disappearing. 
    int * j = 0; // Declare a pointer to an int and initialize to location 0. 
    j = &a;  // j now points to a. 
    return j;  // return the location of the static variable (evil). 
} 

En su función, la variable j se asigna a apuntar a a 's ubicación temporal. Al salir de su función, la variable a desaparece, pero su ubicación anterior se devuelve a través del j. Como a ya no existe en la ubicación señalada por j, se producirá un comportamiento indefinido al acceder al *j.

Las variables dentro de las funciones no se deben modificar mediante referencia o puntero por otro código. Puede suceder aunque produce un comportamiento indefinido.

Siendo pedantes, los punteros devueltos deben declararse como apuntando a datos constantes. Las referencias devueltas deben ser const:

const char * Hello() 
{ 
    static const char text[] = "Hello"; 
    return text; 
} 

La función anterior devuelve un puntero a datos constantes. Otro código puede acceder (leer) los datos estáticos pero no puede modificarse.

const unsigned int& Counter() 
{ 
    static unsigned int value = 0; 
    value = value + 1; 
    return value; 
} 

En la función anterior, el value se inicializa a cero en la primera entrada. Todas las siguientes ejecuciones de esta función provocan que value se incremente en uno. La función devuelve una referencia a un valor constante. Esto significa que otras funciones pueden usar el valor (desde lejos) como si fuera una variable (sin tener que desviar un puntero).

En mi opinión, un puntero se utiliza para un parámetro u objeto opcional. Se pasa una referencia cuando el objeto debe existir. Dentro de la función, un parámetro referenciado significa que el valor existe, sin embargo, se debe verificar que un puntero sea nulo antes de desreferenciarlo. Además, con una referencia, hay más garantía de que el objeto de destino es válido. Un puntero podría apuntar a una dirección no válida (no nula) y causar un comportamiento indefinido.

+0

Si supone que 'a',' b' y 'c' son globales, entonces el código de muestra está bien. Si hubieran sido declarados como estáticos dentro de las funciones, también estaría bien. Las estadísticas no se mueven dentro y fuera de ubicaciones temporales. Sus ubicaciones son estáticas. Siempre puede devolver un puntero o una referencia a una variable estática. No siempre es una buena idea (reentrada y multihilo), pero funciona. –

1

Semánticamente, las referencias actúan como direcciones.Sin embargo, sintácticamente, son el trabajo del compilador, no el tuyo, y puedes tratar una referencia como si fuera el objeto original al que apunta, incluyendo enlazar otras referencias a él y hacer que se refieran también al objeto original. Diga adiós a la aritmética del puntero en este caso.

La desventaja de esto es que no se puede modificar a qué se refieren, están limitados en el tiempo de construcción.

60

Aunque Peter respondió su pregunta, una cosa que le confunde claramente es los símbolos * y &. La parte difícil de entender esto es que ambos tienen dos significados diferentes que tienen que ver con la indirección (incluso excluyendo el tercer significado de * para la multiplicación y & para el bit-y).

  • *, cuando se utiliza como parte de un tipo indica que el tipo es un puntero: int es un tipo, por lo int* es una de tipo puntero a int, y int** es una puntero-a-puntero-a-int tipo.

  • & cuando se utiliza como parte de un tipo indica que el tipo es una referencia. int es un tipo, por lo que int& es una referencia-a-int (no existe la referencia a la referencia). Las referencias y punteros se usan para cosas similares, pero son bastante diferentes y no intercambiables. Una referencia se considera mejor como un alias o nombre alternativo para una variable existente. Si x es int, puede simplemente asignar int& y = x para crear un nuevo nombre y para x. Los términos posteriores, x y y se pueden utilizar indistintamente para hacer referencia al mismo número entero. Las dos principales implicaciones de esto son que las referencias no pueden ser NULL (ya que debe haber una variable original para referenciar), y que no necesita usar ningún operador especial para obtener el valor original (porque es solo un nombre alternativo, no es un puntero). Las referencias tampoco pueden ser reasignadas.

  • * cuando se utiliza como un operador unariorealiza una operación llamada eliminar la referencia (que no tiene nada que ver con referencia tipos!). Esta operación solo tiene sentido en los punteros. Cuando desreferencia un puntero, recupera lo que apunta. Por lo tanto, si p es un puntero-a-int, *p es el int al que se apunta.

  • & cuando se utiliza como un operador unariorealiza una operación llamada dirección de. Eso es bastante auto explicativo; si x es una variable, entonces &x es la dirección de x. La dirección de una variable se puede asignar a un puntero al tipo de esa variable. Entonces, si x es int, entonces &x se puede asignar a un puntero de tipo int*, y ese puntero apuntará a x. P.ej. si asigna int* p = &x, entonces *p se puede utilizar para recuperar el valor de x.

Así que recuerde, el sufijo de tipo & es para las referencias, y no tiene nada que ver con la operatoria unario &, que tiene que ver con conseguir direcciones para su uso con los punteros. Los dos usos no tienen ninguna relación. Y * como un sufijo de tipo declara un puntero, mientras que * como operador unario realiza una acción en los punteros.

+6

Gracias por una de las explicaciones más claras que he visto sobre estos operadores. –

+0

Muy claro muchas gracias! – ChaoSXDemon

+0

Finalmente veo una solución que me puede ayudar a entender esto. – ryf9059

5

Tyler, que era una explicación muy útil, he hecho un poco experimento utilizando el depurador de Visual Studio para aclarar esta diferencia aún más: -

int sample = 90; 
int& alias = sample; 
int* pointerToSample = &sample; 

Name     Address      Type 
&alias    0x0112fc1c {90}    int * 
&sample    0x0112fc1c {90}    int * 
pointerToSample  0x0112fc1c {90}    int * 
*pointerToSample 90      int 
alias 90          int & 
&pointerToSample  0x0112fc04 {0x0112fc1c {90}} int * * 

Diseño de memoria

PointerToSample  Sample/alias 
_______________......____________________ 
0x0112fc1c |   | 90 | 
___________|___.....__|________|_______... 

[0x0112fc04] ...  [0x0112fc1c 
Cuestiones relacionadas