c language-lawyer undefined-behavior memcpy

¿Es un comportamiento indefinido de memcpy desde una variable no inicializada?



language-lawyer undefined-behavior (3)

¿Está utilizando una variable sin inicializar como src para memcpy comportamiento indefinido en C?

void foo(int *to) { int from; memcpy(to, &from, sizeof(from)); }


(Anteriormente: no se ha notificado la dirección de from ). No, eso no causará un comportamiento indefinido, solo eso tiene un valor indeterminado. En la medida en que no pretenda usar el valor de la variable no inicializada , el programa tendrá un comportamiento bien definido.

(Como asignar espacio y no inicializar variables no es UB ).


El comité C propuso una respuesta al informe de defectos 451: la inestabilidad de las variables automáticas sin inicializar es:

La respuesta a la pregunta 3 es que las funciones de la biblioteca mostrarán un comportamiento indefinido cuando se utilicen en valores indeterminados.

La pregunta en el defecto había buscado una exención para memcpy y fwrite si este era el caso diciendo:

[...] El hecho de que uno quiera poder copiar bytes de relleno no inicializados en estructuras usando memcpy sin un comportamiento indefinido es la razón por la que usar el valor de un objeto no inicializado no es un comportamiento indefinido. Esto parece sugerir que un fwrite de una estructura con bytes de relleno no inicializados no debe mostrar un comportamiento indefinido.

Esta parte de la respuesta propuesta parece estar dirigida a esa preocupación sobre el relleno no inicializado:

El comité también observa que los bytes de relleno dentro de las estructuras son posiblemente una forma distinta de representación "tambaleante".

Podemos ver el informe de defectos de formularios 338: C99 parece excluir el valor indeterminado de ser un registro no inicializado, esto es un cambio de las expectativas pasadas. Dice entre otras cosas:

[...] Creo que la intención de excluir el tipo unsigned char de tener representaciones de trampa fue permitir que se use para copiar (a través de memcpy) memoria arbitraria, en el caso de que la memoria pueda contener representaciones de trampa para algunos tipos. .]

La publicación del blog La lectura de contenidos indeterminados también podría estar indefinida, cubre la evolución de la lectura de valores indeterminados en C bien y da más sentido a los cambios que mencioné anteriormente.

Vale la pena tener en cuenta que esto difiere de C ++ en el que leer un valor indeterminado de un carácter angosto no firmado no es un comportamiento indefinido y el informe de defectos 240 señala esta diferencia:

El comité C está lidiando con un problema similar en su DR338. De acuerdo con este análisis, planean adoptar casi el enfoque opuesto al descrito anteriormente al aumentar la descripción de su versión de la conversión de valor-a-valor. El CWG no consideró que el acceso a un personaje sin firma todavía podría quedar atrapado si se asigna en un registro y necesita reevaluar la resolución propuesta de esa manera. Véase también el número 129.


Este es un comportamiento definido con respecto a la acción de copiar, excepto si int tiene una representación de trampa en su sistema. La memoria se asignó en la pila cuando se definió int from . El contenido de este int es lo que sucedió en ese lugar en la pila en ese momento. Por lo tanto, el resultado final, el valor del int que se está copiando a no está definido (indeterminado).

Otras respuestas tienen citas del estándar C que indica que se produce un comportamiento indefinido cuando se "usa" el valor de una variable sin inicializar. Lo que obviamente no se aplica si no usas el valor. Hay otra mención en el comportamiento indefinido estándar de C11 al copiar / asignar variables sin inicializar:

6.3.2.1p2

Si el lvalue designa un objeto de duración de almacenamiento automático que podría haberse declarado con la clase de almacenamiento de registro (nunca se tomó su dirección), y ese objeto no está inicializado (no se declaró con un inicializador y no se realizó ninguna asignación antes de su uso) ), el comportamiento es indefinido.

Esto tampoco afecta su código porque la dirección de from se toma cuando llama a memcpy

Otra parte relevante del estándar C11 es 6.2.6.1

Ciertas representaciones de objetos no necesitan representar un valor del tipo de objeto. Si el valor almacenado de un objeto tiene una representación de este tipo y es leído por una expresión lvalue que no tiene tipo de carácter, el comportamiento no está definido. Si una representación de este tipo es producida por un efecto secundario que modifica todo o parte del objeto por una expresión lvalue que no tiene tipo de carácter, el comportamiento no está definido) Dicha representación se llama representación de trampa.

Algunos procesadores muy antiguos podrían tener una representación de trampa para un bit de paridad visible por software o "cero negativo" en arquitecturas que no sean complementarias. Los procesadores x86, por ejemplo, no tienen representaciones de trampas para int .