Dado:

int i = 42;
int j = 43;
int k = 44;

Mirando las variables direcciones sabemos que cada uno ocupa 4 bytes (en la mayoría de las plataformas).

Sin embargo, considerando:

int i = 42;
int& j = i;
int k = 44;

Vamos a ver que la variable i, de hecho, ocupa 4 bytes, pero j toma ninguno y k lleva de nuevo 4 bytes en la pila.

Lo que está sucediendo aquí? Parece j es simplemente inexistente en tiempo de ejecución. ¿Y qué acerca de una referencia que reciba como argumento de la función? Que debe tomar algo de espacio en la pila…

Y mientras estamos en ello – ¿por qué no puedo definir una matriz o referencias?

int&[] arr = new int&[SIZE]; //compiler error! array of references is illegal
  • ¿Cómo saber j toma «ninguno»? sizeof()? O depurador de la inspección? (Si el último, que podría ser optimizaciones.)
InformationsquelleAutor Yuval Adam | 2009-07-24

8 Comentarios

  1. 48

    todas partes la referencia j se encuentra, es reemplazado con la dirección de i. Así que, básicamente, la referencia de contenido de la dirección se resuelve en tiempo de compilación, y no hay necesidad de eliminar la referencia como un puntero en tiempo de ejecución.

    Sólo para aclarar lo que quiero decir por la dirección de i :

    void function(int& x)
    {
        x = 10;
    }
    
    int main()
    {
        int i = 5;
        int& j = i;
    
        function(j);
    }

    En el código anterior, j no debe tomar espacio en el principal de la pila, pero la referencia x de función tendrá un lugar en su pila. Eso significa que cuando se llama a función con j como un argumento, la dirección de i que se inserta en la pila de función. El compilador puede y no debe reservar espacio en la principal de la pila para j.

    Para la matriz de parte de las normas dicen ::

    Estándar De C++ 8.3.2/4:

    No habrá referencias a las referencias, no de las matrices de referencias,
    y no punteros a las referencias.

    ¿Por qué las matrices de las referencias son ilegales?

    • Este tipo de esquiva la pregunta de por qué j no ocupan ningún espacio en la pila. Si era solo «la dirección de i» tomaría hasta sizeof(int*) bytes.
    • No tomar ninguna pila, ya que el compilador sabe la dirección de yo. No es necesario almacenarlo.
    • Usted puede pensar de una variable de referencia como un sinónimo de la otra variable. No requiere más espacio de almacenamiento, ya que no es un verdadero «cosa», un nuevo nombre para una cosa. Un argumento de referencia, por otro lado, es esencialmente un valor de puntero y requiere de la memoria de un puntero.
    • el punto es que no es simplemente «el discurso del yo». Es otro nombre porque yo. En algunos casos, este «otro nombre» tiene que ser implementado como un puntero, por el almacenamiento de direcciones de i, que lleva un par de bytes, pero eso es un detalle de implementación, no se parte del concepto de referencia.
    • «la referencia de contenido de la dirección se resuelve en tiempo de compilación» espera un momento, pensé que los compiladores de no saber las direcciones de memoria de las variables en tiempo de compilación : «Local y la asignación dinámica de las variables tienen direcciones que no son conocidos por el compilador cuando el archivo de código fuente se compila». Entonces, ¿cómo funciona esto con referencias ?
    • ¿ const int& j = i; ? Si son el mismo, ¿cómo const obras?
    • La cuestión es aún parcialmente sin respuesta. Cuánto de referencia(x en este caso) se llevará a la memoria dentro de la función ?
    • Este es el problema con la lectura de un programa de C++ como la plasmación real de código de computadora. No es real código de computadora. Es una descripción de un programa de. @TonyParker, esto no es simplemente un significativo pregunta.

  2. 38

    Cómo se hace una referencia de C++ mirada,
    la memoria del sabio?

    No. El estándar de C++ sólo dice cómo debe comportarse, no como debe ser implementado.

    En el caso general, los compiladores suelen realizar referencias como punteros. Pero generalmente tienen más información sobre lo que es una referencia del punto a, y el uso que de la optimización.

    Recuerda que el único requisito para que una referencia es que se comporta como un alias para el objeto de referencia. Así que si el compilador encuentra este código:

    int i = 42;
    int& j = i;
    int k = 44;

    lo que se ve no es «crear un puntero a la variable i» (a pesar de que es cómo el compilador puede elegir implementar en algunos casos), sino de «hacer una nota en la tabla de símbolos que j ahora es un alias para i

    El compilador no tiene que crear una nueva variable para j, simplemente tiene que recordar que cuando j se hace referencia a partir de ahora, es que en realidad debería de intercambio y uso i lugar.

    Como para la creación de una matriz de referencias, no se puede hacer porque sería inútil y sin sentido.

    Cuando se crea un array con todos los elementos están predeterminados-construido. ¿Qué significa default-construir una referencia? ¿Qué es el punto a? Todo el punto de las referencias es que se re inicializado para hacer referencia a otro objeto, después de lo cual ellos no pueden estar sentados en una nueva mesa.

    Así que si se podía hacer, que terminaría con una matriz de referencias a nada. Y sería incapaz de cambiar para hacer referencia a algo porque había sido inicializado ya.

    • +1. En ISO C++, «la referencia no es un objeto». Como tal, es necesario no tener ningún recuerdo de la representación. Es sólo un alias.
    • Vale la pena destacar sin embargo, que si la referencia es un miembro de la clase, realmente no hay ninguna otra manera de hacerlo que a darle puntero-como almacenamiento, de otro modo (incluso si usted podría completo programa de optimizar lejos) la clase del tamaño sería impredecible y en general no es permitido cosa (considerar relleno/alineación para contra-ejemplos, a pesar de que estos son deterministas, dentro de ABI especificaciones)
    • Depende. E. g. si un miembro de referencia siempre se inicializa a un campo de otro miembro del mismo objeto, el compilador sólo podía tratar como un alias sin el uso de almacenamiento.
  3. 10

    Lo siento por el uso de asamblea para explicar esto, pero creo que esta es la mejor manera de entender las referencias.

        #include <iostream>
    
        using namespace std;
    
        int main()
        {
            int i = 10;
            int *ptrToI = &i;
            int &refToI = i;
    
            cout << "i = " << i << "\n";
            cout << "&i = " << &i << "\n";
    
            cout << "ptrToI = " << ptrToI << "\n";
            cout << "*ptrToI = " << *ptrToI << "\n";
            cout << "&ptrToI = " << &ptrToI << "\n";
    
            cout << "refToI = " << refToI << "\n";
            //cout << "*refToI = " << *refToI << "\n";
            cout << "&refToI = " << &refToI << "\n";
    
            return 0;
        }

    Salida de este código es como este

        i = 10
        &i = 0xbf9e52f8
        ptrToI = 0xbf9e52f8
        *ptrToI = 10
        &ptrToI = 0xbf9e52f4
        refToI = 10
        &refToI = 0xbf9e52f8

    Veamos el desmontaje(yo usé GDB para esto. 8,9 y 10 aquí están los números de línea de código)

    8           int i = 10;
    0x08048698 <main()+18>: movl   $0xa,-0x10(%ebp)

    Aquí $0xa es el 10(decimal) que se están asignando a i. -0x10(%ebp) aquí significa que el contenido de ebp register -16(decimal).
    -0x10(%ebp) puntos a la dirección de i en la pila.

    9           int *ptrToI = &i;
    0x0804869f <main()+25>: lea    -0x10(%ebp),%eax
    0x080486a2 <main()+28>: mov    %eax,-0x14(%ebp)

    Asignar la dirección de i a ptrToI. ptrToI está de nuevo en la pila ubicada en la dirección -0x14(%ebp), que es ebp – 20(decimal).

    10          int &refToI = i;
    0x080486a5 <main()+31>: lea    -0x10(%ebp),%eax
    0x080486a8 <main()+34>: mov    %eax,-0xc(%ebp)

    Ahora aquí está la trampa! Comparar el desmontaje de la línea 9 y 10 y podrá observador que ,-0x14(%ebp) es reemplazado por -0xc(%ebp) en la línea número 10. -0xc(%ebp) es la dirección de refToI. Se le asignan en la pila. Pero usted nunca será capaz de obtener esta dirección de código debido a que usted no necesita saber la dirección.

    Así; una referencia no ocupan memoria. En este caso es el de la pila de memoria, ya que hemos asignado como una variable local.
    La cantidad de memoria que hace es ocupar?
    Como mucho un puntero ocupa.

    Ahora vamos a ver cómo acceder a la referencia y punteros. Por simplicidad, me han demostrado que sólo una parte de la asamblea fragmento de

    16          cout << "*ptrToI = " << *ptrToI << "\n";
    0x08048746 <main()+192>:        mov    -0x14(%ebp),%eax
    0x08048749 <main()+195>:        mov    (%eax),%ebx
    19          cout << "refToI = " << refToI << "\n";
    0x080487b0 <main()+298>:        mov    -0xc(%ebp),%eax
    0x080487b3 <main()+301>:        mov    (%eax),%ebx

    Ahora comparar las dos líneas anteriores, verá sorprendente similitud. -0xc(%ebp) es la dirección real de refToI que nunca es accesible para usted.
    En términos simples, si usted piensa de referencia como un puntero normal, a continuación, acceder a una referencia es como ir a buscar el valor en la dirección indicada por la referencia. Lo que significa que el siguiente dos líneas de código que te dará el mismo resultado

    cout << "Value if i = " << *ptrToI << "\n";
    cout << " Value if i = " << refToI << "\n";

    Compare ahora esta

    15          cout << "ptrToI = " << ptrToI << "\n";
    0x08048713 <main()+141>:        mov    -0x14(%ebp),%ebx
    21          cout << "&refToI = " << &refToI << "\n";
    0x080487fb <main()+373>:        mov    -0xc(%ebp),%eax

    Supongo que son capaces de detectar lo que está sucediendo aquí.
    Si usted pide &refToI, el contenido de -0xc(%ebp) ubicación de la dirección se volvió y -0xc(%ebp) es donde refToi reside y su contenido no son nada, pero la dirección de i.

    Una última cosa, ¿por Qué esta línea, comentó?

    //cout << "*refToI = " << *refToI << "\n";

    Porque *refToI no está permitido y se le dará un error de tiempo de compilación.

    • ¿Hay alguna razón para ref ocupar memoria en lugar de aliasing en este código particular? Sería agradable ver la versión del compilador y de opciones de compilación.
  4. 9

    En la práctica, una referencia es equivalente a un puntero, excepto que el extra restricciones sobre la forma de referencias están autorizados a utilizar puede permitir que un compilador para «optimizar la distancia» en más de los casos (dependiendo de qué tan inteligente es el compilador es, la configuración de optimización, etc, etc, por supuesto).

  5. 8

    No se puede definir una matriz de referencias porque no hay ninguna sintaxis para inicializarlos. C++ no permite sin inicializar referencias. En cuanto a tu primera pregunta, el compilador no está bajo ninguna obligación de asignar el espacio necesario para las variables. No hay manera de tener j apuntar a otra variable, por lo que es efectivamente un alias para i en la función del alcance, y que es como el compilador trata.

  6. 6

    Algo que se menciona en otra parte – cómo conseguir que el compilador de dedicar algo de espacio de almacenamiento para una referencia:

    class HasRef
    {
        int &r;
    
    public:
        HasRef(int &n)
            : r(n) { }
    };

    Este niega el compilador la oportunidad de tratar simplemente como un tiempo de compilación alias (un nombre alternativo para el mismo almacenamiento).

  7. 3

    Referencias realmente no existen físicamente hasta que necesite para tener una manifestación física (es decir, como un miembro de un agregado).

    Tener una matriz de referencias es ilegal, probablemente debido a la anterior. Pero nada impide la creación de una matriz de estructuras o clases a las que han de hacer referencia a los miembros.

    Estoy seguro de que alguien le señale la cláusula estándar que habla de todo esto.

  8. 3

    No fija el compilador tiene una gran libertad en cómo implementar una referencia en una base de caso por caso. Así, en el segundo ejemplo se trata de j como un alias para que yo, nada más es necesario. Cuando se pasa un parámetro de referencia también puede utilizar una pila de compensación, de nuevo sin ninguna sobrecarga. Pero en otras situaciones se podría utilizar un puntero.

Dejar respuesta

Please enter your comment!
Please enter your name here