sirven sencillos que punteros puntero para operaciones los ejemplos ejemplo dev con cadenas aritmetica apuntadores c pointers casting

sencillos - ¿Cuáles son las reglas para lanzar punteros en C?



punteros c++ (6)

Al pensar en punteros, es útil dibujar diagramas . Un puntero es una flecha que apunta a una dirección en la memoria, con una etiqueta que indica el tipo del valor. La dirección indica dónde buscar y el tipo indica qué llevar. Lanzar el puntero cambia la etiqueta en la flecha pero no donde señala la flecha.

d en main es un puntero a c que es de tipo char . Un char es un byte de memoria, por lo que cuando se desreferencia a d , se obtiene el valor en ese byte de memoria. En el siguiente diagrama, cada celda representa un byte.

-+----+----+----+----+----+----+- | | c | | | | | -+----+----+----+----+----+----+- ^~~~ | char d

Cuando lanzas d a int* , estás diciendo que d realmente apunta a un valor int . En la mayoría de los sistemas actuales, un int ocupa 4 bytes.

-+----+----+----+----+----+----+- | | c | ?₁ | ?₂ | ?₃ | | -+----+----+----+----+----+----+- ^~~~~~~~~~~~~~~~~~~ | int (int*)d

Cuando desreferencia (int*)d , obtiene un valor que se determina a partir de estos cuatro bytes de memoria. El valor que obtienes depende de lo que está marcado en estas celdas ? , y sobre cómo se representa un int en la memoria.

Una PC es little-endian , lo que significa que el valor de una int se calcula de esta manera (suponiendo que abarca 4 bytes): * ((int*)d) == c + ?₁ * 2⁸ + ?₂ * 2¹⁶ + ?₃ * 2²⁴ . Entonces verá que mientras el valor es basura, si imprime en hexadecimal ( printf("%x/n", *n) ), los dos últimos dígitos siempre serán 35 (ese es el valor del carácter ''5'' ).

Algunos otros sistemas son big-endian y organizan los bytes en la otra dirección: * ((int*)d) == c * 2²⁴ + ?₁ * 2¹⁶ + ?₂ * 2⁸ + ?₃ . En estos sistemas, encontrará que el valor siempre comienza con 35 cuando se imprime en hexadecimal. Algunos sistemas tienen un tamaño de int que es diferente de 4 bytes. Unos pocos sistemas excepcionales arreglan int de diferentes maneras, pero es muy poco probable que los encuentre.

Dependiendo de su compilador y sistema operativo, puede encontrar que el valor es diferente cada vez que ejecuta el programa, o que siempre es el mismo, pero cambia cuando realiza incluso pequeños ajustes al código fuente.

En algunos sistemas, un valor int debe almacenarse en una dirección que es un múltiplo de 4 (o 2 u 8). Esto se llama un requisito de alignment . Dependiendo de si la dirección de c está correctamente alineada o no, el programa puede bloquearse.

En contraste con su programa, esto es lo que sucede cuando tiene un valor int y toma un puntero al mismo.

int x = 42; int *p = &x;

-+----+----+----+----+----+----+- | | x | | -+----+----+----+----+----+----+- ^~~~~~~~~~~~~~~~~~~ | int p

El puntero p apunta a un valor int . La etiqueta en la flecha describe correctamente lo que hay en la celda de memoria, por lo que no hay sorpresas al desreferenciarlo.

K & R no lo revisa, pero lo usan. Traté de ver cómo funcionaba escribiendo un programa de ejemplo, pero no fue tan bien:

#include <stdio.h> int bleh (int *); int main(){ char c = ''5''; char *d = &c; bleh((int *)d); return 0; } int bleh(int *n){ printf("%d bleh/n", *n); return *n; }

Se compila, pero mi declaración de impresión escupe variables de basura (son diferentes cada vez que llamo al programa). ¿Algunas ideas?


El valor de basura es en realidad debido al hecho de que ha llamado a la función bleh () antes de su declaración .

En el caso de c ++ obtendrá un error de compilación, pero en c, el compilador supone que el tipo de retorno de la función es int , mientras que su función devuelve un puntero a entero.

Consulte esto para obtener más información: http://www.geeksforgeeks.org/g-fact-95/


Los punteros de lanzamiento generalmente no son válidos en C. Hay varias razones:

  1. Alineación. Es posible que, debido a consideraciones de alineación, el tipo de puntero de destino no pueda representar el valor del tipo de puntero fuente. Por ejemplo, si int * estuviera inherentemente alineado a 4 bytes, la conversión de char * a int * perdería los bits más bajos.

  2. Aliasing. En general, está prohibido acceder a un objeto, excepto a través de un valor l del tipo correcto para el objeto. Hay algunas excepciones, pero a menos que las entiendas muy bien, no quieres hacerlo. Tenga en cuenta que el aliasing es solo un problema si realmente desreferencia el puntero (aplica los operadores * o -> a él, o lo pasa a una función que lo desreferenciará).

Los principales casos notables en los que los punteros de casting están bien son:

  1. Cuando el tipo de puntero de destino apunta al tipo de carácter. Se garantiza que los punteros a los tipos de caracteres pueden representar cualquier puntero a cualquier tipo, y lo retrotraerán con éxito al tipo original si lo desea. Puntero a vacío ( void * ) es exactamente lo mismo que un puntero a un tipo de carácter, excepto que no tiene permitido desreferenciarlo ni hacer aritmética en él, y lo convierte automáticamente a otros tipos de puntero sin necesidad de un molde, por lo que los punteros para anular son generalmente preferibles sobre los punteros a los tipos de caracteres para este propósito.

  2. Cuando el tipo de puntero de destino es un puntero al tipo de estructura cuyos miembros coinciden exactamente con los miembros iniciales del tipo de estructura originalmente apuntada a. Esto es útil para varias técnicas de programación orientadas a objetos en C.

Algunos otros casos oscuros están técnicamente bien en términos de los requisitos de idioma, pero son problemáticos y es mejor evitarlos.


Sospecho que necesitas una respuesta más general:

¡No hay reglas para lanzar punteros en C! El idioma le permite lanzar cualquier puntero a cualquier otro puntero sin comentarios.

Pero la cosa es: ¡no hay conversión de datos o lo que sea que se haga! Es su responsabilidad exclusiva que el sistema no malinterprete los datos después del lanzamiento, lo que generalmente sería el caso, lo que llevaría a un error en el tiempo de ejecución.

Por lo tanto, cuando lo elijas totalmente, ten en cuenta que si los datos se utilizan desde un puntero emitido, ¡los datos son compatibles!

C está optimizado para el rendimiento, por lo que carece de reflexividad en tiempo de ejecución de punteros / referencias. Pero eso tiene un precio: usted como programador debe cuidar mejor de lo que está haciendo. Tienes que saber en ti mismo si lo que quieres hacer es "legal"


Usted tiene un puntero a un char . Entonces, como su sistema sabe, en esa dirección de memoria hay un valor de sizeof(char) espacio sizeof(char) . Cuando lo transfiera a int* , trabajará con datos de sizeof(int) , por lo que imprimirá su char y algo de memoria después como un entero.


char c = ''5''

Un char (1 byte) se asigna en la pila en la dirección 0x12345678 .

char *d = &c;

d = 0x12345678 la dirección de c y la almacena en d , por lo que d = 0x12345678 .

int *e = (int*)d;

Fuerza al compilador a suponer que 0x12345678 apunta a un int , pero un int no es solo un byte ( sizeof(char) != sizeof(int) ). Puede tener 4 u 8 bytes según la arquitectura o incluso otros valores.

Por lo tanto, cuando imprime el valor del puntero, el entero se considera tomando el primer byte (que era c ) y otros bytes consecutivos que están en la pila y que son simplemente basura para su intención.