¿Violación de alias estricta o infracción de alineación con una estructura con miembro de matriz flexible?

Imagina una estructura como esta:

struct test_s { int i; size_t namelen; char name[]; }; 

Ahora me gustaría devolver una matriz de tales estructuras al código de usuario, también usando una función genérica que toma un puntero void * :

 size_t test_read(void *buf, size_t bufsize); 

Cada uno de los elementos de la matriz debería alinearse con la alineación de la struct s_s . En cada uno de los elementos de la matriz, el namelen se ajusta, por lo que es mayor o igual que strlen(name) , pero con bytes “no utilizados” adicionales entre los miembros de la matriz, de modo que el siguiente miembro de la matriz comienza en el banco de memoria divisible por alignof(struct s_s) .

Así que podemos saber / afirmar que al tener cualquier struct test_s *t es cierto que t->namelen % alignof(struct test_s) == 0 y t->namelen == 0 || t->namelen >= strlen(t->name) t->namelen == 0 || t->namelen >= strlen(t->name) .

Ahora la imagen que test_read devuelve 4 de struct test_s . Podemos iterar a través de ellos, pero agregando a cada bucle el sizeof(struct test_s) y agregando el t->namelen del miembro actual ya procesado pnt += sizeof(struct test_s) + t->namelen . Los bytes de “relleno” “no utilizados” entre los miembros de la matriz y las cadenas se dejan sin inicializar y la intención es que nunca se pueda acceder a ellos.

 #include  #include  #include  #include  #include  #include  #include  // out structure struct test_s { int i; size_t namelen; char name[]; }; // stores an struct test_s object into buf and after it stores string as pointed to by name // returns sizeof(struct test_s) + strlen(name) size_t test_init(void *buf, size_t bufsize, int i, const char name[]) { assert(buf != NULL); assert(bufsize > sizeof(struct test_s)); size_t namelen; // if (t->namelen == 0) { that means that t->name is empty. } if (name) { namelen = strlen(name) + 1; assert(bufsize > sizeof(struct test_s) + namelen * sizeof(*((struct test_s*)0)->name)); } else { namelen = 0; } struct test_s * const t = buf; t->i = i; t->namelen = namelen; memcpy(t->name, name, namelen); return sizeof(*t) + namelen * sizeof(*t->name); } // works as test_init, but returned value is incremented to // make the `buf + returned value` aligned to struct test_s size_t test_init_aligned(void *buf, size_t bufsize, int i, const char name[]) { const size_t copied = test_init(buf, bufsize, i, name); // align the end of the struct to be aligned with struct test_s struct test_s * const t = buf; const size_t tmp = copied % alignof(struct test_s); const size_t to_aligned = tmp ? alignof(struct test_s) - tmp : 0; t->namelen += to_aligned; const size_t aligned_size = copied + to_aligned; return aligned_size; } // returns multiple struct test_s objects and stores them in buf // returns number of bytes written size_t test_read(void *buf, size_t bufsize) { char * const outbeg = buf; char *out = buf; char * const outend = &out[bufsize]; out += test_init_aligned(out, outend - out, 1, "first element"); out += test_init_aligned(out, outend - out, 2, "second element"); out += test_init_aligned(out, outend - out, 3, "third element"); out += test_init_aligned(out, outend - out, 4, NULL); return out - outbeg; } int main() { // our read buffer char alignas(struct test_s) buf[1024]; // read structs test_s with strings into our buffer const size_t readlen = test_read(buf, sizeof(buf)); const struct test_s *test = NULL; const char *pnt = NULL; for (pnt = &buf[0]; pnt namelen * sizeof(*test->name)) { test = (const struct test_s *)pnt; printf("i=%d len=%2zu name='%s'\n", test->i, test->namelen, test->namelen != 0 ? test->name : "" ); } } 
  1. ¿Este código tiene alguna violación estricta de aliasing?
  2. ¿Este código puede desalinear acceso a algún objeto?
  3. ¿Este código tiene algún comportamiento indefinido?
  4. Este código se basa en el ejemplo en man 7 inotify y pretendía que fuera muy similar. El ejemplo allí itera sobre la struct inotify_event s usando el mismo principio. ¿Este ejemplo tiene algún comportamiento indefinido?

Yo digo que no hay una violación estricta aliasing. Nunca struct test_s* un objeto subyacente utilizando diferentes punteros, los punteros siempre son struct test_s* y char* , aunque la aritmética se realiza utilizando los punteros char* .

Versión en vivo del código disponible en onlinegdb .

La pregunta se origina en los comentarios de esta pregunta .