saltos referencias quitar puntero porque mueve lento forzcursor forzar esta desactivar aparece c pointers casting dereference

referencias - porque el cursor se mueve lento en autocad



Anular la referencia a este puntero me da-46, pero no estoy seguro de por qué (4)

Este es un programa que ejecuté:

#include <stdio.h> int main(void) { int y = 1234; char *p = &y; int *j = &y; printf("%d %d/n", *p, *j); }

Estoy un poco confundido acerca de la salida. Lo que estoy viendo es:

-46 1234

Escribí este programa como un experimento y no estaba seguro de lo que iba a generar. Esperaba posiblemente un byte de y .

¿Qué está pasando "detrás de escena" aquí? ¿Cómo me da -46 desreferenciación?

Como señalaron otros, tuve que hacer un casting explícito para no causar UB. No estoy cambiando esa línea de char *p = &y; a char *p = (char *)&y; para no invalidar las respuestas a continuación.

Este programa no está causando ningún comportamiento de UB como se señala here .


Hay un par de problemas con el código tal como está escrito.

En primer lugar, está invocando un comportamiento indefinido al intentar imprimir la representación numérica de un objeto char utilizando el especificador de conversión %d :

Borrador en línea de C 2011 , §7.21.6.1, subcláusula 9:

Si una especificación de conversión no es válida, el comportamiento es indefinido. 282) Si algún argumento no es el tipo correcto para la especificación de conversión correspondiente, el comportamiento es indefinido.

Sí, los objetos de tipo char se promueven a int cuando se pasan a funciones variadic; printf es especial, y si desea que la salida esté bien definida, entonces el tipo de argumento y el especificador de conversión deben coincidir. Para imprimir el valor numérico de un char con %d o argumento de unsigned char con %u , %o o %x , debe usar el modificador de longitud hh como parte de la especificación de conversión:

printf( "%hhd ", *p );

El segundo problema es que la línea

char *p = &y;

es una violación de restricción: char * e int * no son tipos compatibles y pueden tener diferentes tamaños y / o representaciones 2 . Por lo tanto, debe emitir explícitamente el origen al tipo de destino:

char *p = (char *) &y;

La única excepción a esta regla ocurre cuando uno de los operandos es void * ; entonces el elenco no es necesario.

Habiendo dicho todo eso, tomé su código y agregué una utilidad que volca la dirección y el contenido de los objetos en el programa. Así es como se ven y , p y j en mi sistema (SLES-10, gcc 4.1.2):

Item Address 00 01 02 03 ---- ------- -- -- -- -- y 0x7fff1a7e99cc d2 04 00 00 .... p 0x7fff1a7e99c0 cc 99 7e 1a ..~. 0x7fff1a7e99c4 ff 7f 00 00 .... j 0x7fff1a7e99b8 cc 99 7e 1a ..~. 0x7fff1a7e99bc ff 7f 00 00 ....

Estoy en un sistema x86, que es little-endian, por lo que almacena objetos de varios bytes que comienzan con el byte menos significativo en la dirección más baja:

BE: A A+1 A+2 A+3 +----+----+----+----+ y: | 00 | 00 | 04 | d2 | +----+----+----+----+ LE: A+3 A+2 A+1 A

En un sistema little-endian, el byte direccionado es el byte menos significativo, que en este caso es 0xd2 ( 210 sin signo, -46 signo).

En pocas palabras, está imprimiendo la representación decimal firmada de ese solo byte.

En cuanto a la pregunta más amplia, el tipo de la expresión *p es char y el tipo de la expresión *j es int ; el compilador simplemente va por el tipo de expresión. El compilador realiza un seguimiento de todos los objetos, expresiones y tipos a medida que traduce su fuente al código de máquina. Entonces, cuando ve la expresión *j , sabe que se trata de un valor entero y genera el código de máquina de manera apropiada. Cuando ve la expresión *p , sabe que se trata de un valor char .

  1. Es cierto que casi todos los sistemas de escritorio modernos que conozco usan las mismas representaciones para todos los tipos de punteros, pero para plataformas más extrañas incrustadas o de propósito especial, eso puede no ser cierto.
  2. § 6.2.5, inciso 28.


Primero lea la advertencia que dice advertencia: inicialización desde un tipo de puntero incompatible [habilitado por defecto] char * p = & y;

lo que significa que debe realizar una conversión de texto explícita para evitar un comportamiento indefinido de acuerdo con el estándar §7.21.6.1, subcláusula 9 (señalado por @john Bode) como

chat *p = (char*)&y;

y

int y =1234;

aquí y es la local variable y se almacenará en la sección de stack de RAM . En Linux, los enteros de la máquina se almacenan en la memoria de acuerdo con el formato little endian . Suponga que 4 bytes de memoria reservada para y es de 0x100 a 0x104

------------------------------------------------- | 0000 0000 | 0000 0000 | 0000 0100 | 1101 0010 | ------------------------------------------------- 0x104 0x103 0x102 0x101 0x100 y p j

Como se indicó anteriormente, j y p apuntan a la misma dirección 0x100 pero cuando el compilador realizará *p ya que p es un signed character pointer con sign bit por defecto, comprobará el sign bit y aquí el sign bit es 1 significa que una cosa es segura de que la salida se imprimirá Es un número negativo .

Si el sign bit es 1 es decir, el número negativo y los números negativos se almacenan en la memoria como complemento de 2, entonces

actual => 1101 0010 (1st byte) ones compliment => 0010 1101 +1 ------------ 0010 1110 => 46 and since sign bit was one it will print -46

mientras imprime si está utilizando el especificador de formato %u que es para imprimir el equivalente unsigned , not verificará el sign bi t, finalmente, cualquier dato que haya en 1 byte se imprimirá.

finalmente

printf("%d/n",*j);

En la declaración anterior al desreferenciar j que es un signed pointer por defecto y es un puntero int por lo que verificará el bit 31 para el signo , que es 0 significa que la salida será positive no positive y eso es 1234.


Si tienes algo como

int x = 1234; int *p = &x;

Si hace referencia al puntero p , leerá correctamente bytes enteros. Porque lo declaraste como puntero a int . Sabrá cuántos bytes leer por el operador sizeof() . En general, el tamaño de int es de 4 bytes (para plataformas de 32/64 bits), pero depende de la máquina, por eso utilizará el operador sizeof() para conocer el tamaño correcto y lo leerá.

Para su código

int y = 1234; char *p = &y; int *j = &y;

Ahora, el pointer p apunta a y pero hemos declarado que es un puntero a un char por lo que solo leerá un byte o el byte que sea. 1234 en binario se representaría como

00000000 00000000 00000100 11010010

Ahora, si su máquina es little endian, almacenará los bytes invirtiéndolos

11010010 00000100 00000000 00000000

11010010 está en la address 00 Hypothetical address , 00000100 está en la address 01 y así sucesivamente.

BE: 00 01 02 03 +----+----+----+----+ y: | 00 | 00 | 04 | d2 | +----+----+----+----+ LE: 00 01 02 03 +----+----+----+----+ y: | d2 | 04 | 00 | 00 | +----+----+----+----+ (In Hexadecimal)

Entonces, si desreferencia el pointer p , solo leerá el primer byte y la salida será ( -46 en el caso de signed char y 210 en caso de unsigned char , de acuerdo con el estándar C, la firma de char simple es "implementación definida". ) como Byte read sería 11010010 (porque señalamos signed char (en este caso es signed char ).

En su PC, los números negativos se representan como Complemento de 2, por lo que el most-significant bit es el bit de signo. El primer bit 1 denota el signo. 11010010 = –128 + 64 + 16 + 2 = –46 y si desreferencia el pointer j leerá completamente todos los bytes de int ya que declaramos que es puntero a int y la salida será 1234

Si declara el puntero j como int *j entonces *j leerá sizeof(int) aquí 4 bytes (depende de la máquina). Lo mismo ocurre con char o cualquier otro tipo de datos que el puntero apuntado a ellos leerá tantos bytes de los que su tamaño sea, char es de 1 byte.

Como otros han señalado, debe convertir explícitamente a char* como char *p = &y; es una violación de restricción: char * e int * no son tipos compatibles, en su lugar escriba char *p = (char *)&y .


(Tenga en cuenta que esta respuesta se refiere a la forma original de la pregunta, que preguntaba cómo el programa sabía cuántos bytes leer, etc. Lo mantengo sobre esa base, a pesar de que la alfombra se ha sacado de debajo).

Un puntero se refiere a una ubicación en la memoria que contiene un objeto particular y debe incrementarse / decrementarse / indexarse ​​con un tamaño de zancada particular, reflejando el tamaño del tipo puntiagudo.

El valor observable del puntero en sí (por ejemplo, a través de std::cout << ptr ) no necesita reflejar ninguna dirección física reconocible, ni ++ptr necesita incrementar dicho valor en 1, sizeof(*ptr) , o cualquier otra cosa. Un puntero es solo un identificador de un objeto, con una representación de bits definida por la implementación. Esa representación no importa y no debería importar a los usuarios. Lo único para lo que los usuarios deberían usar el puntero es ... bueno, señalar cosas. Hablar de su dirección no es portátil y solo es útil en la depuración.

De todos modos, simplemente, el compilador sabe cuántos bytes leer / escribir porque se escribe el puntero, y ese tipo tiene un sizeof definido, representación y mapeo a direcciones físicas. Entonces, según ese tipo, las operaciones en ptr se compilarán con las instrucciones apropiadas para calcular la dirección de hardware real (que, una vez más, no necesariamente corresponde al valor observable de ptr ), lea el ptr correcto del número de ''bytes'' de memoria, suma / resta el número correcto de bytes para que apunte al siguiente objeto, etc.