¿Se define memcpy(& a+1, & b+1, 0) en C11?
strcpy (2)
Esta pregunta sigue a esta pregunta previa sobre la definición de memcpy(0, 0, 0)
, que se ha determinado de manera concluyente como un comportamiento indefinido.
Como muestra la pregunta vinculada, la respuesta depende del contenido de la cláusula 7.1.4 de C11: 1
Cada una de las siguientes afirmaciones se aplica a menos que se indique explícitamente lo contrario en las descripciones detalladas que siguen: Si un argumento de una función tiene un valor no válido (como un valor fuera del dominio de la función o un puntero fuera del espacio de direcciones del programa, o un puntero nulo, [...]) el comportamiento no está definido. [...]
La función estándar memcpy()
espera que los punteros se void
y const void
, así:
void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
La pregunta vale la pena solo porque hay dos nociones de punteros "válidos" en el estándar: hay punteros que pueden obtenerse válidamente a través de aritmética del puntero y se pueden comparar válidamente con <
, >
con otros punteros dentro del mismo objeto. Y hay punteros que son válidos para desreferenciar. La primera clase incluye punteros "one-past" como &a + 1
y &b + 1
en el siguiente fragmento, mientras que la última clase no los incluye como válidos.
char a;
const char b = ''7'';
memcpy(&a + 1, &b + 1, 0);
Si el fragmento de arriba se considera comportamiento definido, a la luz del hecho de que los argumentos de memcpy()
se escriben como punteros para void
todos modos, por lo que la pregunta sobre sus respectivas validez no puede ser sobre desreferenciarlos. ¿O debería &a + 1
y &b + 1
considerarse "fuera del espacio de direcciones del programa"?
Esto me importa porque estoy en el proceso de formalizar los efectos de las funciones C estándar. Había escrito una precondición de memcpy()
como requires /valid(s1+(0 .. n-1));
, hasta que me llamó la atención que GCC 4.9 había comenzado a optimizar aggressively tales llamadas a la función de la biblioteca más allá de lo expresado en la fórmula anterior (de indeed ). La fórmula /valid(s1+(0 .. n-1))
en este lenguaje de especificación particular es equivalente a true
cuando n
es 0
, y no captura el comportamiento indefinido en el que se basa GCC 4.9 para optimizar.
C11 dice:
(C11, 7.24.2.1p2) "La función memcpy copia n caracteres del objeto señalado por s2 en el objeto apuntado por s1."
&a + 1
sí mismo es un puntero válido para la suma entera pero &a + 1
no es un puntero a un objeto, por lo que la invocación invoca un comportamiento indefinido.
Si bien la respuesta "correcta" de acuerdo con la norma parece estar en desacuerdo, puedo encontrarla poco sincera después de int a [6]; int b [6]; todo
memcpy(a+0, b+0, 6);
memcpy(a+1, b+1, 5);
memcpy(a+2, b+2, 4);
memcpy(a+3, b+3, 3);
memcpy(a+4, b+4, 2);
memcpy(a+5, b+5, 1);
debería ser válido (y copiar un área que termina al final de las matrices) mientras
memcpy(a+6, b+6, 0);
es válido a la luz del recuento pero no de las direcciones. ¡Es el mismo final del área copiada!
Personalmente, me inclino por definir que memcpy (0,0,0) también sea válido (con la justificación de simplemente exigir punteros válidos pero no objetos), pero al menos es un caso singular mientras que el caso "end of array" es un excepción real a un patrón de lo contrario regular para copiar un área al final de una matriz.