¿Se define memcpy (& a + 1, & b + 1, 0) en C11?

Esta pregunta sigue a esta pregunta anterior sobre la definición de memcpy(0, 0, 0) , que se ha determinado de manera concluyente como un comportamiento indefinido.

Como muestra la pregunta enlazada, la respuesta depende del contenido de la cláusula 7.1.4: 1 de C11.

Cada una de las siguientes declaraciones se aplica a menos que se indique explícitamente lo contrario en las descripciones detalladas que siguen: Si un argumento a 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 progtwig) o un puntero nulo, […]) […] el comportamiento no está definido. […]

La función estándar memcpy() espera que los punteros se void y memcpy() , así:

 void *memcpy(void * restrict s1, const void * restrict s2, size_t n); 

Vale la pena plantear la pregunta solo porque hay dos nociones de punteros “válidos” en la norma: hay punteros que se pueden obtener válidamente a través de la aritmética de punteros y se pueden comparar válidamente con < , > a otros punteros dentro de la misma objeto. Y hay punteros que son válidos para la desreferenciación. La primera clase incluye punteros de “un pasado”, como &a + 1 y &b + 1 en el siguiente fragmento, mientras que la última clase no incluye estos como válidos.

 char a; const char b = '7'; memcpy(&a + 1, &b + 1, 0); 

En caso de que el fragmento de memcpy() anterior se considere comportamiento definido, a la luz del hecho de que los argumentos de memcpy() se escriben como punteros a void todos modos, la cuestión de sus respectivas validaciones no puede ser sobre la falta de referencia a ellos. ¿O deberían &a + 1 y &b + 1 ser considerados “fuera del espacio de direcciones del progtwig”?

Esto me importa porque estoy en el proceso de formalizar los efectos de las funciones C estándar. Había escrito una condición memcpy() de memcpy() como requires \valid(s1+(0 .. n-1)); , hasta que se señaló a mi atención que GCC 4.9 había comenzado a optimizar agresivamente tales llamadas de función de biblioteca más allá de lo que se expresa en la fórmula anterior (de hecho ). 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 apuntado por s2 en el objeto apuntado por s1”.

&a + 1 sí es un puntero válido para la sum de enteros, pero &a + 1 no es un puntero a un objeto, por lo que la llamada invoca un comportamiento indefinido.

Si bien la respuesta “correcta” de acuerdo con el estándar parece estar en desacuerdo, puedo encontrar que no es 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); 

debe 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 conteo pero no de las direcciones. ¡Es el mismo extremo del área copiada!

Personalmente, me inclino por definir que memcpy (0,0,0) también sea válido (con la justificación de solo exigir punteros válidos pero no objetos), pero al menos es un caso singular, mientras que el caso del “final de la matriz” es un excepción real a un patrón de otro modo regular para copiar un área al final de una matriz.