matrices - matriz dinamica en c
¿Cómo asignar memoria para una matriz y una estructura en una llamada a malloc sin romper el alias estricto? (3)
Creo que el código como está escrito viola las reglas estrictas de alias, cuando el estándar se lee en el sentido más estricto.
Está accediendo a un objeto de tipo int
través de un puntero a una array
tipos no relacionada. Creo que una salida fácil sería utilizar la dirección de inicio de la estructura, y luego convertirla en caracteres * y realizar una aritmética de punteros en ella. Ejemplo:
void* alloc = malloc(...);
array = alloc;
int* p_int = (char*)alloc + sizeof(array);
Cuando asigno memoria para una matriz de tamaño variable, a menudo hago algo como esto:
struct array {
long length;
int *mem;
};
struct array *alloc_array( long length)
{
struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
arr->length = length;
arr->mem = (int *)(arr + 1); /* dubious pointer manipulation */
return arr;
}
Entonces uso el arrray así:
int main()
{
struct array *arr = alloc_array( 10);
for( int i = 0; i < 10; i++)
arr->mem[i] = i;
/* do something more meaningful */
free( arr);
return 0;
}
Esto funciona y se compila sin advertencias. Recientemente, sin embargo, leí sobre el aliasing estricto. A mi entender, el código anterior es legal con respecto al aliasing estricto, porque la memoria a la que se accede a través de int *
no es la memoria a la que se accede a través de la struct array *
. ¿El código de hecho rompe reglas estrictas de alias? Si es así, ¿cómo se puede modificar para no romperlas?
Soy consciente de que podría asignar la estructura y la matriz por separado, pero también tendría que liberarlas por separado, probablemente en algún tipo de función free_array
. Eso significaría que tengo que saber el tipo de memoria que estoy liberando cuando la libero, lo que complicaría el código. También es probable que sea más lento. Eso no es lo que estoy buscando.
La forma correcta de declarar un miembro de matriz flexible en una estructura es la siguiente:
struct array {
long length;
int mem[];
};
Luego, puede asignar el espacio como antes sin tener que asignar nada a mem
:
struct array *alloc_array( long length)
{
struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
arr->length = length;
return arr;
}
Modern C admite oficialmente miembros de matriz flexible . Para que puedas definir tu estructura de la siguiente manera:
struct array {
long length;
int mem[];
};
Y distribúyalo como lo hace ahora, sin la molestia adicional de la dudosa manipulación del puntero. Funcionará fuera de la caja, todo el acceso se alineará correctamente y no tendrá que preocuparse por las esquinas oscuras del idioma. Aunque, naturalmente, solo es viable si tiene uno de esos miembros que necesita asignar.
En cuanto a lo que tiene ahora, dado que el almacenamiento asignado no tiene un tipo declarado (es una pizarra en blanco), no está rompiendo el alias estricto, ya que no le ha dado a esa memoria un tipo efectivo. El único problema es con un posible desorden de alineación. Aunque eso es poco probable con los tipos en su estructura.