sirven referencia que punteros puntero por paso pasar para los funciones con aritmetica c++ pointers casting reference

c++ - referencia - punteros en c



¿Por qué está permitido lanzar un puntero a una referencia? (5)

Bueno, ese es el propósito de reinterpret_cast ! Como su nombre lo sugiere, el propósito de ese elenco es reinterpretar una región de memoria como un valor de otro tipo. Por este motivo, al usar reinterpret_cast , siempre puede convertir un lvalue de un tipo en una referencia de otro tipo.

Esto se describe en 5.2.10 / 10 de la especificación del lenguaje. También dice allí que reinterpret_cast<T&>(x) es lo mismo que *reinterpret_cast<T*>(&x) .

El hecho de que estés arrojando un puntero en este caso es total y completamente irrelevante. No, el puntero no se desreferencia automáticamente (teniendo en cuenta la *reinterpret_cast<T*>(&x) , incluso se podría decir que ocurre lo contrario: la dirección de ese puntero se toma automáticamente). El puntero en este caso sirve simplemente como "alguna variable que ocupa una región en la memoria". El tipo de esa variable no hace diferencia alguna. Puede ser un double , un puntero, un int o cualquier otro lvalue. La variable simplemente se trata como región de memoria que usted reinterpreta como otro tipo.

En cuanto al elenco estilo C, simplemente se interpreta como reinterpret_cast en este contexto, por lo que lo anterior se aplica inmediatamente.

En su segundo ejemplo, adjuntó la referencia c a la memoria ocupada por la variable de puntero pc . Cuando hiciste c = ''B'' , escribiste con fuerza el valor ''B'' en esa memoria, destruyendo por completo el valor del puntero original (sobrescribiendo un byte de ese valor). Ahora el puntero destruido apunta a una ubicación impredecible. Más tarde intentaste desreferenciar ese puntero destruido. Lo que sucede en tal caso es una cuestión de pura suerte. El programa puede bloquearse, ya que el puntero generalmente no es irreversible. O puede tener suerte y hacer que su puntero apunte a una ubicación impredecible pero válida. En ese caso, tu programa generará algo. Nadie sabe lo que producirá y no tiene ningún significado en absoluto.

Uno puede reescribir su segundo programa en un programa equivalente sin referencias

int main(){ char* pc = new char(''A''); char* c = (char *) &pc; std::cout << *pc << "/n"; *c = ''B''; std::cout << *pc << "/n"; }

Desde el punto de vista práctico, en una plataforma little-endian su código sobrescribiría el byte menos significativo del puntero. Dicha modificación no hará que el puntero apunte demasiado lejos de su ubicación original. Por lo tanto, es más probable que el código imprima algo en lugar de colgarse. En una plataforma de big-endian, su código destruiría el byte más significativo del puntero, lanzándolo de manera salvaje para apuntar a una ubicación totalmente diferente, lo que haría que su programa sea más propenso a fallar.

Originalmente siendo el tema de esta pregunta , surgió que el OP simplemente pasó por alto la desreferencia. Mientras tanto, esta respuesta me hizo pensar a mí y a otros: ¿por qué se permite lanzar un puntero a una referencia con un molde de estilo C o reinterpret_cast ?

int main() { char c = ''A''; char* pc = &c; char& c1 = (char&)pc; char& c2 = reinterpret_cast<char&>(pc); }

El código anterior se compila sin ninguna advertencia o error (con respecto al modelo) en Visual Studio, mientras que GCC solo le dará una advertencia, como se muestra here .

Lo primero que pensé fue que el puntero de alguna manera automágicamente se desreferencia (normalmente trabajo con MSVC, por lo que no obtuve la advertencia que muestra GCC), y probé lo siguiente:

#include <iostream> int main() { char c = ''A''; char* pc = &c; char& c1 = (char&)pc; std::cout << *pc << "/n"; c1 = ''B''; std::cout << *pc << "/n"; }

Con el resultado muy interesante que se muestra here . Parece que estás accediendo a la variable apuntada, pero al mismo tiempo, no lo estás.

Ideas? Explicaciones? Cotizaciones estándar?


Está permitido porque C ++ permite casi cualquier cosa cuando lanzas.

Pero en cuanto al comportamiento:

  • pc es un puntero de 4 bytes
  • (char) pc intenta interpretar el puntero como un byte, en particular el último de los cuatro bytes
  • (char &) pc es el mismo, pero devuelve una referencia a ese byte
  • Cuando imprime la PC por primera vez, no pasó nada y ve la letra que almacenó
  • c = ''B'' modifica el último byte del puntero de 4 bytes, por lo que ahora apunta a otra cosa
  • Cuando imprime de nuevo, ahora está señalando una ubicación diferente que explica su resultado.

Dado que se modifica el último byte del puntero, la nueva dirección de memoria está cerca, por lo que es poco probable que esté en una pieza de memoria a la que su programa no tiene acceso. Es por eso que no obtienes seg-fault. El valor real obtenido no está definido, pero es muy probable que sea cero, lo que explica el resultado en blanco cuando se interpreta como un carácter.


Me tomó un tiempo asimilarlo, pero creo que finalmente lo conseguí.

El estándar C ++ especifica que un cast reinterpret_cast<U&>(t) es equivalente a *reinterpret_cast<U*>(&t) .

En nuestro caso, U es char , t es char* .

Ampliando esos, vemos que sucede lo siguiente:

  • llevamos la dirección del argumento al elenco, obteniendo un valor de tipo char** .
  • reinterpret_cast este valor a char*
  • Desreferenciamos el resultado, produciendo un valor de char .

reinterpret_cast te permite transmitir desde cualquier tipo de puntero a cualquier otro tipo de puntero. Y entonces, un reparto de char** a char* está bien formado.


Trataré de explicar esto usando mi intuición arraigada sobre referencias y punteros en lugar de confiar en el lenguaje del estándar.

  • C no tenía tipos de referencia, solo tenía valores y punteros,
    ya que, físicamente en la memoria, solo tenemos valores y punteros.
  • En C ++ hemos agregado referencias a la sintaxis, pero puedes pensar en ellas como una especie de azúcar sintáctico: no hay una estructura de datos para contener referencias.

Bueno, ¿qué "es" una referencia desde esa perspectiva? O más bien, ¿cómo "implementarías" una referencia? Con un puntero, por supuesto. Entonces, cada vez que vea una referencia en algún código, puede pretender que en realidad solo es un puntero que se ha usado de una manera especial: si int x; y int& y{x}; entonces realmente tenemos un int* y_ptr = &x ; y si decimos y = 123; simplemente nos referimos a *(y_ptr) = 123; . Esto no difiere de cómo, cuando usamos subíndices C de matriz ( a[1] = 2; ) lo que realmente ocurre es que a es "decaído" para significar puntero a su primer elemento, y luego lo que se ejecuta es *(a + 1) = 2 .

(Nota al margen: los compiladores en realidad no siempre tienen punteros detrás de cada referencia, por ejemplo, el compilador puede usar un registro para la variable referida, y luego un puntero no puede señalarlo. Pero la metáfora sigue siendo bastante segura .)

Habiendo aceptado la metáfora de "la referencia es realmente solo un puntero en el disfraz", ahora no debería sorprendernos que podamos ignorar este disfraz con un reinterpret_cast<>() .

PS - std::ref también es solo un puntero cuando se profundiza en él.


cuando estás lanzando, con un elenco estilo C o con un reinterpret_cast, básicamente le estás diciendo al compilador que mire para otro lado ("no te molesta, sé lo que estoy haciendo").

C ++ le permite decirle al compilador que haga eso. Eso no significa que sea una buena idea ...