Su intuición sobre "por qué no usar una matriz de tamaño 1" es perfecta.
El código está haciendo el "C struct hack" incorrecto, porque las declaraciones de arrays de longitud cero son una violación de restricción. Esto significa que un compilador puede rechazar su truco inmediatamente en el momento de la compilación con un mensaje de diagnóstico que detiene la traducción.
Si queremos perpetrar un truco, debemos escabullirlo más allá del compilador.
La forma correcta de hacer el "truco struct C" (que es compatible con dialectos C volviendo a 1989 ANSI C, y probablemente mucho antes) es el uso de una matriz perfectamente válida de tamaño 1:
struct someData
{
int nData;
unsigned char byData[1];
}
Además, en lugar de sizeof struct someData
, el tamaño de la parte antes de byData
se calcula usando:
offsetof(struct someData, byData);
para asignar un struct someData
con espacio para 42 bytes en byData
, entonces debería utilizar:
struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);
Tenga en cuenta que este cálculo offsetof
es de hecho el cálculo correcto, incluso en el caso del tamaño de la matriz es cero. Usted ve, sizeof
toda la estructura puede incluir relleno. Por ejemplo, si tenemos algo como esto:
struct hack {
unsigned long ul;
char c;
char foo[0]; /* assuming our compiler accepts this nonsense */
};
El tamaño de struct hack
es posiblemente acolchada para la alineación debido a la miembro de ul
. Si unsigned long
tiene cuatro bytes de ancho, muy posiblemente sizeof (struct hack)
es 8, mientras que offsetof(struct hack, foo)
es casi con seguridad 5. El método offsetof
es la forma de obtener el tamaño exacto de la parte anterior de la estructura justo antes de la matriz.
Así que esa sería la forma de refactorizar el código: hacerlo conforme al clásico y altamente portable struct hack.
¿Por qué no utilizar un puntero? Porque un puntero ocupa espacio extra y debe inicializarse.
Existen otras buenas razones para no utilizar un puntero, a saber, que un puntero requiere un espacio de direcciones para que sea significativo. El struct hack es externalizable: es decir, hay situaciones en las que dicho diseño se ajusta al almacenamiento externo, como áreas de archivos, paquetes o memoria compartida, en el que no se desean punteros porque no son significativos.
Hace varios años, utilicé el hack struct en una interfaz de pase de mensajes de memoria compartida entre kernel y espacio de usuario. No quería punteros allí, porque solo habrían sido significativos para el espacio de direcciones original del proceso que generaba un mensaje. La parte del kernel del software tenía una vista hacia la memoria usando su propia asignación en una dirección diferente, y así todo estaba basado en cálculos de compensación.
Este es el "struct hack", descrito en la pregunta 2.6 de [comp.lang.c FAQ] (http://www.c-faq.com/). Dennis Ritchie lo llamó "amistad sin justificación con la implementación C". C99 introdujo una nueva función de lenguaje, el "miembro de matriz flexible", para reemplazar el hack de estructura. Incluso el compilador de Microsoft, que se destaca por su falta de compatibilidad con C99, admite miembros de matriz flexibles. –
NO agregue la etiqueta 'c' a esta pregunta.Las reglas de C++ para esto son bastante diferentes de las reglas de C. –
@BenVoigt La respuesta aceptada es código C puro, así que supongo que su edición es incorrecta. c hack se aplica tanto a c como a C++ de la misma manera –