¿Pueden las matrices C contener relleno entre elementos?

7 minutos de lectura

avatar de usuario
EM

Escuché un rumor de que, en C, las matrices que están contenidas dentro de las estructuras pueden tener relleno agregado entre los elementos de la matriz. Ahora, obviamente, la cantidad de relleno no puede variar entre ningún par de elementos o calcular el siguiente elemento en una matriz no es posible con la aritmética simple de punteros.

Este rumor también indicó que se garantiza que las matrices que no están contenidas en estructuras no contienen relleno. Sé que al menos esa parte es verdad.

Entonces, en código, el rumor es:

{
    // Given this:
    struct { int values[20]; } foo;
    int values[20];

    // This may be true:
    sizeof(values) != sizeof(foo.values);
}

estoy bastante seguro de que sizeof(values) siempre será igual sizeof(foo.values). Sin embargo, no he podido encontrar nada en el estándar C (específicamente C99) que confirme o niegue esto explícitamente.

¿Alguien sabe si este rumor se aborda en algún estándar C?

editar: entiendo que puede haber relleno entre el final de la matriz foo.values y el final de la estructura foo y que el estándar establece que no habrá relleno entre el inicio de foo y el comienzo de foo.values. Sin embargo, ¿alguien tiene un cita de o referencia a el estándar donde dice que no hay relleno entre los elementos de foo.values?

  • No tengo ninguna referencia para esto, pero estoy bastante seguro de que he leído que el compilador es libre de alinear estructuras como quiera. En general, esto probablemente se hará de la manera que describe Cletus.

    – Emilio H.

    30 de junio de 2009 a las 23:59

  • Parece que mucha gente extraña que la comparación es entre las dos matrices y no entre matriz/estructura. sizeof(valores) != sizeof(foo); esto es no lo que hace, pero lo que la gente parece pensar en algunas respuestas

    – Joakim Elofson

    1 de julio de 2009 a las 0:18

avatar de usuario
Chris Arguin

No, nunca habrá relleno entre los elementos de una matriz. Eso específicamente no está permitido. El estándar C99 llama a los tipos de matriz “Un tipo de matriz describe un conjunto de objetos no vacíos asignados de forma contigua…”. Por el contrario, una estructura se asigna “secuencialmente”, no “contiguamente”.

Puede haber relleno antes o después de una matriz dentro de una estructura; ese es otro animal completamente. El compilador podría hacer eso para ayudar a alinear la estructura, pero el estándar C no dice nada al respecto.

  • Depende de lo que quieras decir. Un compilador puede agregar relleno al final de la estructura (ver mi ejemplo). Si toma una estructura de este tipo y crea una matriz con ella, hay bytes sin usar entre los bytes usados, pero cada elemento de la matriz siempre tiene el tamaño de (T) bytes después del anterior. Pero sizeof(Ta) + sizeof(Tb) + … puede no ser igual a sizeof(T).

    – Tánatos

    1 de julio de 2009 a las 0:08

  • “El tamaño de la comparación que está haciendo podría detectar eso” ¿No? comparar está entre las dos matrices, no entre la matriz y la estructura

    – Joakim Elofson

    1 de julio de 2009 a las 0:09

  • @Thanatos: básicamente estoy de acuerdo, excepto que si el compilador agrega bytes al final de la estructura, eso se incluye en el tamaño de. Sizeof está en un tipo, no en una instancia de ese tipo, y el tipo no sabe si se usa en una matriz o no.

    – Chris Arguin

    1 de julio de 2009 a las 0:22

  • Gracias por la cotización Viene de la sección 6.2.5 párrafo 20 para cualquier persona interesada en buscarlo.

    – EM

    1 de julio de 2009 a las 0:24

Cuidado aquí. El relleno se puede agregar al final de la estructura, pero no se agregará entre los elementos de la matriz como indica en su pregunta. Las matrices siempre harán referencia a la memoria contigua, aunque una matriz de estructuras puede tener relleno agregado a cada elemento como parte de la estructura misma.

En tu ejemplo, el values y foo.values las matrices tendrán el mismo tamaño. Cualquier relleno será parte de la estructura. foo en lugar de.

Aquí está la explicación en cuanto a por qué una estructura puede necesitar relleno entre sus miembros o incluso después de su último miembro, y por qué una matriz no:

Diferentes tipos pueden tener diferentes requisitos de alineación. Algunos tipos deben alinearse en límites de palabras, otros en límites de palabras dobles o incluso cuádruples. Para lograr esto, una estructura puede contener bytes de relleno entre sus miembros. Es posible que se necesiten bytes de relleno finales porque la ubicación de la memoria directamente después de una estructura también debe cumplir con los requisitos de alineación de la estructura, es decir, si bar es de tipo struct foo *luego

(struct foo *)((char *)bar + sizeof(struct foo))

produce un puntero válido para struct foo (es decir, no falla debido a una desalineación).

Como cada ‘miembro’ de una matriz tiene el mismo requisito de alineación, no hay razón para introducir relleno. Esto también es válido para las matrices contenidas en estructuras: si el primer elemento de una matriz está alineado correctamente, también lo están todos los elementos siguientes.

avatar de usuario
Tánatos

Sí, algo así. Las variables a menudo se alinean con algún límite, dependiendo de la variable. Tome lo siguiente, por ejemplo:

typedef struct
{
    double d;
    char c;
} a_type_t;

double y char son 8 y 1 bytes, en mi sistema, respectivamente. Total de 9. Esa estructura, sin embargo, será de 16 bytes, por lo que los dobles siempre estarán alineados en 8 bytes. Si solo hubiera usado ints, chars, etc., entonces la alineación podría ser 1, 2, 4 u 8.

Para algún tipo T, sizeof(T) puede o no ser igual sizeof(T.a) + sizeof(T.b) + sizeof(T.c) ... etc

En general, esto depende completamente del compilador y la arquitectura. En la práctica, nunca importa.

avatar de usuario
cleto

Considerar:

struct {
  short s;
  int i;
} s;

Suponiendo que los cortos son de 16 bits y usted está en 32 bits, el tamaño será probablemente ser de 8 bytes, ya que cada miembro de la estructura tiende a alinearse con un límite de palabra (32 bits en este caso). Digo “probablemente” porque es un comportamiento específico de la implementación que puede modificarse mediante indicadores del compilador y similares.

Vale la pena enfatizar que este es un comportamiento de implementación que no necesariamente está definido por el estándar C. Al igual que el tamaño de los cortos, enteros y largos (el estándar C simplemente dice que los cortos no serán más grandes que los enteros y los largos no serán más pequeños que los enteros, lo que puede terminar como 16/32/32, 16/32/64 , 32/32/64 u otras configuraciones).

  • En una máquina basada en ARM funcionará, pero también es muy peligroso, ya que ARM no realiza comprobaciones de alineación.

    – Ignas Limanauskas

    30 de junio de 2009 a las 23:59

  • En realidad, creo que en las máquinas x86 y amd64, que tendrán un tamaño de 6, para los compiladores gcc: no hay ningún beneficio para que un carácter esté alineado en 8 bytes. Pero como mencionas, esto depende del compilador.

    – Tánatos

    1 de julio de 2009 a las 0:04

  • El tamaño es 8 en máquinas x86 y amd64 con gcc. Acepto que esto depende del compilador; en el ejemplo de esta respuesta, el problema es la alineación natural de un int.

    – janm

    21 de febrero de 2010 a las 8:53

  • En una máquina basada en ARM funcionará, pero también es muy peligroso, ya que ARM no realiza comprobaciones de alineación.

    – Ignas Limanauskas

    30 de junio de 2009 a las 23:59

  • En realidad, creo que en las máquinas x86 y amd64, que tendrán un tamaño de 6, para los compiladores gcc: no hay ningún beneficio para que un carácter esté alineado en 8 bytes. Pero como mencionas, esto depende del compilador.

    – Tánatos

    1 de julio de 2009 a las 0:04

  • El tamaño es 8 en máquinas x86 y amd64 con gcc. Acepto que esto depende del compilador; en el ejemplo de esta respuesta, el problema es la alineación natural de un int.

    – janm

    21 de febrero de 2010 a las 8:53

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad