Pergunta

Durante uma revisão de código eu me deparei com um código que define uma estrutura simples como o seguinte:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
}

Por outro lado, uma matriz desses objetos é definido:

foo listOfFoos[SOME_NUM];

Mais tarde, as estruturas são matéria-copiado em um buffer:

memcpy(pBuff,listOfFoos,3*SOME_NUM);

Este código baseia-se nas premissas que:... A) O tamanho de foo é 3, e sem preenchimento é aplicado, e b) Uma matriz de estes objectos é embalado sem preenchimento entre eles

Eu tentei com GNU em duas plataformas (64B RedHat, Solaris 9), e funcionou em ambos.

As suposições acima são válidas? Se não, em que condições (por exemplo, mudança no OS / compilador) podem eles falham?

Foi útil?

Solução

Uma matriz de objectos é necessário para ser contígua, de modo que nunca é preenchimento entre os objectos, apesar de preenchimento pode ser adicionada à extremidade de um objecto (a produção de quase o mesmo efeito).

Uma vez que você está trabalhando com carvão de, os pressupostos são provavelmente direito mais frequentemente do que não, mas o C ++ padrão certamente não garante isso. Um compilador diferente, ou mesmo apenas uma mudança nas bandeiras passados ??para o compilador atual poderia resultar em estofamento sendo inserido entre os elementos da estrutura ou após o último elemento da estrutura, ou ambos.

Outras dicas

Seria definitivamente ser mais seguro para fazer:

sizeof(foo) * SOME_NUM

Se você copiar sua matriz como este que você deve usar

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

Isso sempre vai trabalhar enquanto você alocado pBuff para o mesmo tamanho. Desta forma, você está fazendo nenhuma suposição sobre o preenchimento e alinhamento em tudo.

A maioria dos compiladores alinhar uma estrutura ou classe para o alinhamento necessário da maior tipo incluído. No seu caso de caracteres que significa que não há alinhamento e preenchimento, mas se você adicionar um curto, por exemplo, sua classe seria 6 bytes grande com um byte de preenchimento adicionado entre o último caractere e seu short.

Penso que a razão que isso funciona porque todos os campos da estrutura são CHAR qual se alinhar. Se existe, pelo menos, um campo que não alinha 1, o alinhamento da estrutura / classe não será um (a vontade alinhamento depende da ordem do campo e alinhamento).

Vamos ver alguns exemplos:

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    unsigned short i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

Quando executado, o resultado é:

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

Você pode ver que Bar e F_B tem alinhamento 2 de modo que seu campo i será devidamente alinhados. Você também pode ver que Tamanho do Bar é 6 e não 5 . Da mesma forma, o tamanho do B_F (5 de Bar) é 30 e não 25 .

Então, se você é um código rígido em vez de sizeof(...), você vai ter um problema aqui.

Espero que isso ajude.

Tudo se resume ao alinhamento de memória. máquinas de 32 bits típicos ler ou escrever 4 bytes de memória por tentativa. Esta estrutura está a salvo de problemas, porque ele cai sob que 4 bytes facilmente, sem problemas confundindo preenchimento.

Agora, se a estrutura era como tal:

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

Sua lógica colegas de trabalho provavelmente levaria a

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 de Char = 3 bytes, 2 ints = 2 * 4 bytes, de modo 3 + 8)

Infelizmente, devido a acolchoar a estrutura realmente ocupa 12 bytes. Isso é porque você não pode caber três caractere do e um int em que a palavra de 4 bytes, e por isso há um byte de espaço acolchoado lá que empurra o int em seu próprio texto. Isto torna-se mais e mais um problema dos mais diversos tipos de dados tornam-se.

Para situações onde o material como este é usado, e eu não posso evitá-lo, eu tento fazer a ruptura de compilação quando os pressupostos já não detêm. Eu uso algo como o seguinte (ou Boost.StaticAssert se a situação o permitir):

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}

Eu teria sido seguro e substituiu o número mágico 3 com um reckon sizeof(foo) I.

Meu palpite é que o código otimizado para arquiteturas de processador futuro provavelmente irá introduzir alguma forma de preenchimento.

E tentando rastrear esse tipo de erro é uma dor real!

Como já foi dito, usando sizeof (foo) é uma aposta mais segura. Alguns compiladores (especialmente os esotéricos do mundo incorporado) irá adicionar um cabeçalho de 4 bytes de classes. Outros podem fazer truques de memória de alinhamento funk, dependendo de suas configurações do compilador.

Para uma plataforma mainstream, provavelmente você está certo, mas não é uma garantia.

Há ainda pode ser um problema com sizeof () quando você está passando os dados entre dois computadores. Em um deles, o código pode compilar com estofamento e na outra, sem, caso em que sizeof () daria resultados diferentes. Se os dados de matriz é transmitida de um computador para o outro ele vai ser mal interpretada, porque os elementos da matriz não será encontrado onde esperado. Uma solução é ter certeza de que #pragma pack (1) é usado sempre que possível, mas isso pode não ser suficiente para as matrizes. Melhor é prever o problema e utilização estofo para um múltiplo de 8 bits por elemento de matriz.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top