sirven que punteros puntero para los lenguaje funciones declaracion con comparar cadenas aritmetica c casting undefined-behavior

que - C: ¿Cuándo el lanzamiento entre tipos de punteros no es un comportamiento indefinido?



punteros a cadenas (4)

Básicamente:

  • un T * se puede convertir libremente en un void * y viceversa (donde T * no es un puntero de función), y obtendrá el puntero original.
  • un T * se puede convertir libremente a un U * y viceversa (donde T * y U * no son punteros de función), y obtendrá el puntero original si los requisitos de alineación son los mismos. Si no, el comportamiento es indefinido.
  • un puntero de función se puede convertir libremente a cualquier otro tipo de puntero de función y viceversa, y obtendrá el puntero original.

Nota: T * (para punteros que no son de función) siempre cumple con los requisitos de alineación para char * .

Importante: ninguna de estas reglas dice nada acerca de lo que sucede si convierte, digamos, una T * a una U * y luego intenta desreferenciarla. Esa es una área completamente diferente de la norma.

Como recién llegado a C, estoy confundido acerca de cuándo lanzar un puntero está realmente bien.

Según tengo entendido, puedes lanzar prácticamente cualquier tipo de puntero a cualquier otro tipo, y el compilador te permitirá hacerlo. Por ejemplo:

int a = 5; int* intPtr = &a; char* charPtr = (char*) intPtr;

Sin embargo, en general, esto invoca un comportamiento indefinido (aunque funciona en muchas plataformas). Dicho esto, parece haber algunas excepciones:

  • puedes lanzar desde y hacia el void* libremente (?)
  • puedes lanzar desde y hacia char* libremente (?)

(Al menos lo he visto en código ...).

Entonces, ¿qué conversión entre tipos de puntero no es un comportamiento indefinido en C?

Editar:

Intenté buscar en el estándar C (sección "6.3.2.3 Punteros", en http://c0x.coding-guidelines.com/6.3.2.3.html ), pero realmente no lo entendí, aparte del bit sobre el void* .

Edit2:

Solo para aclarar: explícitamente solo pregunto sobre los punteros "normales", es decir, no sobre los punteros de función. Me doy cuenta de que las reglas para enviar punteros a funciones son muy restrictivas. Como cuestión de hecho, ya he preguntado sobre eso :-): ¿Qué sucede si lanzo un puntero de función, cambiando el número de parámetros?


En general, si como es habitual en la actualidad los punteros tienen las mismas propiedades de alineación, el problema no es la conversión propiamente dicha, sino si puede o no acceder a los datos a través del puntero.

La conversión de cualquier tipo T* a void* y viceversa está garantizada para cualquier objeto T : esto garantiza que le devuelva exactamente el mismo puntero. void* es el tipo de puntero de captura de todos los objetos.

Para otros lanzamientos entre tipos de objetos no hay garantía, el acceso a un objeto a través de dicho puntero puede causar todo tipo de problemas, como alineaciones (error de bus), representaciones de enteros de trampas. No se garantiza que los diferentes tipos de punteros tengan el mismo ancho, por lo que, en teoría, incluso podría perder información.

Sin embargo, un elenco que siempre debería funcionar es (unsigned char*) . A través de dicho puntero, puede investigar los bytes individuales de su objeto.


Es un comportamiento indefinido, cuando se convierte a un tipo con un tamaño diferente. Por ejemplo, lanzar desde un char a un int. Una char es de 1 byte de largo. Los enteros tienen una longitud de 4 bytes (en un sistema Linux de 32 bits). Entonces, si tiene un puntero a un personaje y lo convierte en un puntero a un int, eso provocará un comportamiento indefinido . Espero que esto ayude.

Algo como lo siguiente a continuación, causaría un comportamiento indefinido:

#include <stdio.h> #include <stdlib.h> int main() { char *str = "my str"; int *val; val = calloc(1, sizeof(int)); if (val == NULL) { exit(-1); } *val = 1; str = (char) val; return 0; }

EDIT: Lo que dijo Oli sobre los punteros void * es correcto, por cierto. Puedes lanzar entre cualquier puntero vacío y otro puntero.


La excelente respuesta de Oli Charlesworth enumera todos los casos en los que un puntero a un puntero de un tipo diferente da un resultado bien definido.

Además, hay cuatro casos donde lanzar un puntero da resultados definidos por la implementación :

  • Puede lanzar un puntero a un tipo entero suficientemente grande (!). C99 tiene los tipos opcionales intptr_t y uintptr_t para este propósito. El resultado está definido por la implementación. En las plataformas que abordan la memoria como un flujo de bytes contiguo ("modelo de memoria lineal", utilizado por la mayoría de las plataformas modernas), generalmente devuelve el valor numérico de la dirección de memoria a la que apunta el puntero, por lo tanto, simplemente un conteo de bytes. Sin embargo, no todas las plataformas utilizan un modelo de memoria lineal, por lo que esto está definido por la implementación :-).
  • A la inversa, puede convertir un entero en un puntero. Si el número entero tiene un tipo suficientemente grande para intptr_t o uintptr_t y se creó al lanzar un puntero, al volverlo al mismo tipo de puntero le devolverá ese puntero (que, sin embargo, puede que ya no sea válido). De lo contrario, el resultado está definido por la implementación. Tenga en cuenta que, en realidad, la anulación de la referencia del puntero (en lugar de solo leer su valor) aún puede ser UB.
  • Puede lanzar un puntero a cualquier objeto a char* . Luego, el resultado apunta al byte direccionado más bajo del objeto, y puede leer los bytes restantes del objeto incrementando el puntero, hasta el tamaño del objeto. Por supuesto, los valores que realmente obtienes son de nuevo definidos por la implementación ...
  • Puede lanzar libremente punteros nulos, siempre se mantendrán punteros nulos independientemente del tipo de puntero :-).

Fuente: norma C99, secciones 6.3.2.3 "Punteros" y 7.18.1.4 "Tipos de enteros capaces de contener punteros de objetos".

Por lo que puedo decir, todos los demás lanzamientos de un puntero a un puntero de un tipo diferente son comportamientos indefinidos. En particular, si no está lanzando a char o un tipo entero lo suficientemente grande, siempre puede ser UB lanzar un puntero a un tipo de puntero diferente, incluso sin desreferenciarlo.

Esto se debe a que los tipos pueden tener una alineación diferente, y no hay una forma general y portátil de asegurarse de que los diferentes tipos tengan una alineación compatible (excepto en algunos casos especiales, como los pares de tipo entero con signo / sin signo).