¿Qué significa" desreferenciar " un puntero?


Por favor incluya un ejemplo con la explicación.

Author: Rakete1111, 2011-02-10

6 answers

Repasando la terminología básica

Es por lo general lo suficientemente bueno-a menos que esté programando asamblea - para prever un puntero contiene una dirección de memoria numérica, con 1 refiriéndose al segundo byte en la memoria del proceso, 2 el tercero, 3 el cuarto y así sucesivamente....

  • ¿Qué pasó con 0 y el primer byte? Bueno, vamos a llegar a eso más adelante-ver punteros nulos a continuación.
  • Para una definición más precisa de lo que los punteros almacenan, y cómo se relacionan la memoria y las direcciones, vea "Más sobre las direcciones de memoria, y por qué probablemente no necesita saber".

Cuando desea acceder a los datos / valores en la memoria a la que apunta el puntero, el contenido de la dirección con ese índice numérico, entonces dereference el puntero.

Diferentes lenguajes informáticos tienen diferentes notaciones para decirle al compilador o intérprete que ahora está interesado en el el valor apuntado-I se centra a continuación en C y C++.

Un escenario puntero

Considere en C, dado un puntero como p a continuación...

const char* p = "abc";

...cuatro bytes con los valores numéricos utilizados para codificar las letras 'a', 'b', 'c', y un byte 0 para denotar el final de los datos textuales, se almacenan en algún lugar de la memoria y la dirección numérica de esos datos se almacena en p.

Por ejemplo, si el literal de cadena estaba en la dirección 0x1000 y p un 32-bit puntero a 0x2000, el contenido de la memoria sería:

Memory Address (hex)    Variable name    Contents
1000                                     'a' == 97 (ASCII)
1001                                     'b' == 98
1002                                     'c' == 99
1003                                     0
...
2000-2003               p                1000 hex

Tenga en cuenta que no hay un nombre/identificador de variable para la dirección 0x1000, pero podemos referirnos indirectamente al literal de cadena usando un puntero que almacena su dirección: p.

Desreferenciar el puntero

Para referirnos a los caracteres a los que p apunta, desreferenciamos p usando una de estas notaciones (de nuevo, para C):

assert(*p == 'a');  // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
                     // p and 1 times the size of the things to which p points:
                     // In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b');  // Another notation for p[1]

También puede mover punteros a través de los datos apuntados, desreferenciándolos a medida que go:

++p;  // Increment p so it's now 0x1001
assert(*p == 'b');  // p == 0x1001 which is where the 'b' is...

Si usted tiene algunos datos que se pueden escribir, entonces usted puede hacer cosas como esta:

int x = 2;
int* p_x = &x;  // Put the address of the x variable into the pointer p_x
*p_x = 4;       // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4

Anteriormente, debe haber sabido en tiempo de compilación que necesitaría una variable llamada x, y el código le pide al compilador que organice dónde debe almacenarse, asegurando que la dirección estará disponible a través de &x.

Desreferenciar y acceder a un miembro de datos de estructura

En C, si tiene una variable que es un puntero a una estructura con miembros de datos, puede acceder a esos miembros usando el operador de desreferenciación ->:

typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159;  // Dereference and access data member x.d_
(*p).d_ *= -1;    // Another equivalent notation for accessing x.d_

Tipos de datos multibyte

Para usar un puntero, un programa de computadora también necesita cierta información sobre el tipo de datos que se está apuntando - si ese tipo de datos necesita más de un byte para representar, entonces el puntero normalmente apunta al byte con el número más bajo en los datos.

Así que, mirando un ejemplo un poco más complejo:

double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3);  // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4);  // Actually looks at bytes from address p + 1 * sizeof(double)
                       // (sizeof(double) is almost always eight bytes)
assert(++p);           // Advance p by sizeof(double)
assert(*p == 13.4);    // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8;       // Change sizes[3] from 19.4 to 29.8
                       // Note: earlier ++p and + 2 here => sizes[3]

Punteros para asignar dinámicamente memoria

A veces no sabe cuánta memoria necesitará hasta que su programa se esté ejecutando y vea qué datos se arrojan a él... luego puede asignar memoria dinámicamente usando malloc. Es una práctica común almacenar la dirección en un puntero...

int* p = malloc(sizeof(int)); // Get some memory somewhere...
*p = 10;            // Dereference the pointer to the memory, then write a value in
fn(*p);             // Call a function, passing it the value at address p
(*p) += 3;          // Change the value, adding 3 to it
free(p);            // Release the memory back to the heap allocation library

En C++, la asignación de memoria se realiza normalmente con el operador new, y la desasignación con delete:

int* p = new int(10); // Memory for one int with initial value 10
delete p;

p = new int[10];      // Memory for ten ints with unspecified initial value
delete[] p;

p = new int[10]();    // Memory for ten ints that are value initialised (to 0)
delete[] p;

Véase también Los punteros inteligentes C++ a continuación.

Pérdidas y fugas direcciones

A menudo un puntero puede ser la única indicación de dónde existen algunos datos o búfer en la memoria. Si se necesita el uso continuo de esos datos/búfer, o la capacidad de llamar a free() o delete para evitar fugas de memoria, entonces el programador debe operar en una copia del puntero...

const char* p = asprintf("name: %s", name);  // Common but non-Standard printf-on-heap

// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
    if (!isprint(*q))
        *q = '_';

printf("%s\n", p); // Only q was modified
free(p);

...o orquestar cuidadosamente la reversión de cualquier cambio...

const size_t n = ...;
p += n;
...
p -= n;  // Restore earlier value...

Punteros inteligentes C++

En C++, es una buena práctica usar puntero inteligente objetos para almacenar y administrar los punteros, desasignarlos automáticamente cuando se ejecutan los destructores de los punteros inteligentes. Desde C++11 la Biblioteca Estándar proporciona dos, unique_ptr para cuando hay un único propietario para un objeto asignado...

{
    std::unique_ptr<T> p{new T(42, "meaning")};
    call_a_function(p);
    // The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete

...y shared_ptr para la propiedad de acciones (utilizando recuento de referencia)...

{
    std::shared_ptr<T> p(new T(3.14, "pi"));
    number_storage.may_add(p); // Might copy p into its container
} // p's destructor will only delete the T if number_storage didn't copy

Punteros nulos

En C, NULL y 0 - y adicionalmente en C++ nullptr - se pueden usar para indicar que un puntero no actualmente contiene la dirección de memoria de una variable, y no debe ser desreferenciada o utilizada en la aritmética de punteros. Por ejemplo:

const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
char c;
while ((c = getopt(argc, argv, "f:")) != EOF)
    switch (c) {
      case f: p_filename = optarg; break;
    }
if (p_filename)  // Only NULL converts to false
    ...   // Only get here if -f flag specified

En C y C++, al igual que los tipos numéricos incorporados no necesariamente tienen por defecto 0, ni bools a false, los punteros no siempre se establecen en NULL. Todos estos se establecen en 0 / false / NULL cuando son variables static o (solo en C++) variables miembro directas o indirectas de objetos estáticos o sus bases, o se someten a cero inicialización (por ejemplo, new T(); y new T(x, y, z); realizan cero inicialización en los miembros de T incluyendo punteros, mientras que new T; no lo hace).

Además, cuando se asigna 0, NULL y nullptr a un puntero los bits en el puntero no son necesariamente todos restablecidos: el puntero puede no contener "0" en el nivel de hardware, o referirse a la dirección 0 en su espacio de direcciones virtuales. Al compilador se le permite almacenar algo más allí si tiene razones para hacerlo, pero lo que haga-si viene y compara el puntero con 0, NULL, nullptr u otro puntero que fue asignado a cualquiera de esos, la comparación debe funcionar como se esperaba. Por lo tanto, debajo del código fuente a nivel del compilador, "NULL" es potencialmente un poco "mágico" en los lenguajes C y C++...

Más sobre las direcciones de memoria, y por qué probablemente no necesita saber

Más estrictamente, los punteros inicializados almacenan un patrón de bits que identifica NULL o una (a menudo virtual) dirección de memoria.

El caso simple es donde se trata de un desplazamiento numérico en el en casos más complejos, el puntero puede ser relativo a algún área de memoria específica, que la CPU puede seleccionar en función de los registros de "segmento" de la CPU o de algún tipo de id de segmento codificado en el patrón de bits, y/o mirando en diferentes lugares dependiendo de las instrucciones del código de máquina usando la dirección.

Por ejemplo, una int* correctamente inicializada para apuntar a una variable int podría - después de convertir a una float* - acceder a un valor en la memoria "GPU" bastante distinto de la variable int, entonces una vez fundido a un puntero de función podría referirse a una memoria distinta que contiene los opcodes de la máquina para la función.

Los lenguajes de programación 3GL como C y C++ tienden a ocultar esta complejidad, tal que:

  • Si el compilador le da un puntero a una variable o función, puede desreferenciarla libremente (siempre y cuando la variable no esté destruida/desasignada mientras tanto) y es problema del compilador si, por ejemplo, un registro de CPU en particular necesita ser restaurado de antemano, o una instrucción de código máquina distinta utilizada

  • Si obtiene un puntero a un elemento en una matriz, puede usar la aritmética de puntero para moverse a cualquier otro lugar de la matriz, o incluso para formar una dirección un-pasado-el-final de la matriz que es legal comparar con otros punteros a elementos en la matriz (o que han sido movidos de manera similar por la aritmética de puntero al mismo valor un-pasado-el-final); de nuevo en C y C++, depende del compilador garantizar esto "solo funciona"

  • Funciones específicas del sistema operativo, por ejemplo, asignación de memoria compartida, pueden darle punteros, y que "solo funcionan" dentro del rango de direcciones que tiene sentido para ellos

  • Los intentos de mover punteros legales más allá de estos límites, o para emitir números arbitrarios a punteros, o usar punteros emitidos a tipos no relacionados, generalmente tienen comportamiento indefinido, así que debe evitarse en bibliotecas y aplicaciones de nivel superior, pero el código para Sistemas operativos, controladores de dispositivos, etc. puede necesitar confiar en el comportamiento dejado indefinido por C o C++, que sin embargo está bien definido por su hardware específico.

 581
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-03 00:08:18

Desreferenciar un puntero significa obtener el valor que se almacena en la ubicación de memoria apuntada por el puntero. El operador * se utiliza para hacer esto, y se llama el operador de desreferenciación.

int a = 10;
int* ptr = &a;

printf("%d", *ptr); // With *ptr I'm dereferencing the pointer. 
                    // Which means, I am asking the value pointed at by the pointer.
                    // ptr is pointing to the location in memory of the variable a.
                    // In a's location, we have 10. So, dereferencing gives this value.

// Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.

 *ptr = 20;         // Now a's content is no longer 10, and has been modified to 20.
 82
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-29 16:15:54

Un puntero es una "referencia" a un valor.. al igual que un número de llamada de la biblioteca es una referencia a un libro. "Desreferenciar" el número de llamada está pasando físicamente y recuperando ese libro.

int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;

// The * causes pA to DEREFERENCE...  `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4.. 

Si el libro no está allí, el bibliotecario comienza a gritar, cierra la biblioteca, y un par de personas están listas para investigar la causa de que una persona vaya a encontrar un libro que no está allí.

 14
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-10 18:08:39

En palabras simples, desreferenciar significa acceder al valor desde una determinada ubicación de memoria contra la que apunta el puntero.

 9
Author: Fahad Naeem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-11 19:50:55

Código y explicación de Fundamentos del puntero :

La operación de desreferencia comienza en el puntero y sigue su flecha sobre para acceder a su punta. El objetivo puede ser para mirar el estado puntiagudo o para cambiar el estado puntiagudo. El operación de desreferencia en un puntero solo funciona si el puntero tiene un puntiagudo must el puntiagudo debe ser asignado y el puntero debe ser establecido para señalarlo. El error más común en el código del puntero se olvida de establecer arriba pointee. Los más comunes fallo en tiempo de ejecución debido a ese error en el código es una desreferencia fallida operación. En Java el incorrecto la desreferencia se marcará cortésmente por el sistema de tiempo de ejecución. En compilado lenguajes como C, C++ y Pascal, la desreferencia incorrecta a veces accidente, y otras veces memoria corrupta en algunos sutiles, al azar manera. Errores de puntero en compilado los idiomas pueden ser difíciles de rastrear por esta razón.

void main() {   
    int*    x;  // Allocate the pointer x
    x = malloc(sizeof(int));    // Allocate an int pointee,
                            // and set x to point to it
    *x = 42;    // Dereference x to store 42 in its pointee   
}
 7
Author: ash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-10 09:21:46

Creo que todas las respuestas anteriores están equivocadas, ya que indique que la desreferenciación significa acceder al valor real. Wikipedia da la definición correcta en su lugar: https://en.wikipedia.org/wiki/Dereference_operator

Opera sobre una variable puntero, y devuelve un valor l equivalente al valor en la dirección del puntero. Esto se llama "desreferenciar" el puntero.

Dicho esto, podemos desreferenciar el puntero sin nunca acceder al valor que señala a. Por ejemplo:

char *p = NULL;
*p;

Desreferenciamos el puntero NULO sin acceder a su valor. O podríamos hacer:

p1 = &(*p);
sz = sizeof(*p);

De nuevo, desreferenciar, pero nunca acceder al valor. Dicho código NO se bloqueará: El bloqueo ocurre cuando realmente acceso los datos por un puntero inválido. Sin embargo, desafortunadamente, según el el estándar, desreferenciar un puntero no válido es un indefinido comportamiento (con algunas excepciones), incluso si no intentas toque los datos reales.

Así que en corto: desreferenciar el puntero significa aplicar el dereference operator to it. Ese operador sólo devuelve un l-valor para su uso futuro.

 3
Author: stsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-11 19:52:13