Pergunta

Eu estou escrevendo C biblioteca multi-plataforma, mas, eventualmente, eu tenho erro em meus UnitTests, mas apenas em máquinas Windows. Eu rastreou o problema e descobriu que está relacionado com o alinhamento das estruturas (estou usando matrizes de estruturas para armazenar dados para vários objetos semelhantes). O problema é:. Memset (sizeof (struct)) e estruturas de fixação membros, um por um produzir diferentes resultado byte-to-byte e, portanto, memcmp () retorna "não igual" resultado

Aqui, o código para ilustração:

#include <stdio.h>
#include <string.h>

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

S1 s1, s2;

int main()
{
    printf("%d %d\n", sizeof(S1), sizeof(S2));

    memset(&s1, 0xFF, sizeof(S1));
    memset(&s2, 0x00, sizeof(S1));

    s1.a = 0LL; s1.b = 0;

    if (0 == memcmp(&s1, &s2, sizeof(S1)))
        printf("Equal\n");
    else
        printf("Not equal\n");

    return 0;
}

Este código com MSVC 2003 @ o Windows produto seguinte resultado:

16 8
Not equal

Mas o mesmo código com GCC 3.3.6 @ Linux funciona como esperado:

12 8
Equal

Isso faz com que a minha unidade de testes muito difícil.

Am eu entendi que MSVC usa tamanho do maior tipo nativo (long long) para determinar o alinhamento de estrutura?

Alguém pode me dar conselhos como eu posso mudar meu código para torná-lo mais robusto contra este problema de alinhamento estranho? No meu código real eu estou trabalhando com matrizes de estruturas via ponteiros genéricos para executar memset / memcmp e eu sou geralmente não sabem tipo exato, só tenho sizeof valor (struct).

Foi útil?

Solução

O que temos feito é usado o pacote # pragma para especificar o tamanho dos objetos deve ser:

#pragma pack(push, 2)

typedef struct {
    long long      a;
    int            b;
} S1;

typedef struct {
    long           a;
    int            b;
} S2;

#pragma pack(pop)

Se você fizer isso, as estruturas será o mesmo tamanho em ambas as plataformas.

Outras dicas

expectativa do seu teste de unidade é errado. Ele (ou o código que testa) não deve verificar a memória intermédia de estrutura de byte-por-byte. Para os dados de byte-preciso o código deve criar um tampão de byte explicitamente em pilha ou em pilha e preenchê-lo com os extractos de cada membro. Os extractos podem ser obtidos na forma de CPU-independente de ordenação usando a operação de deslocamento para a direita contra os valores inteiros e conversão do resultado pelo tipo de bytes, tal como (char não assinado).

BTW, o trecho escreve s2 passado. Você pode corrigir isso alterando esta

memset(&s2, 0x00, sizeof(S1));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S1)))

para isso,

memset(&s2, 0x00, sizeof(S2));

s1.a = 0LL; s1.b = 0;

if (0 == memcmp(&s1, &s2, sizeof(S2)))

Mas o resultado é tecnicamente "indefinido" porque o alinhamento dos membros nas estruturas é específico do compilador.

Manual GCC:

Note-se que o alinhamento de qualquer estrutura ou tipo de união é exigido pela norma ISO C para ser, pelo menos, um múltiplo perfeito do menor múltiplo comum dos alinhamentos de todos os membros da estrutura ou união em questão.

Além disso, este tipicamente introduz um elemento de estofo (isto é, enchimento bytes para ter a estrutura alinhados). Você pode usar o #pragma com um argumento de packed. Nota, #pragmas NÃO são uma forma portátil de trabalhar. Infelizmente, esta é também a única maneira de trabalhar no seu caso.

Referências: GCC Aqui em alinhamento estrutura. MSDN alinhamento estrutura.

Note que este não é um problema de alinhamento 'estranha'. MSVC foi escolhida para assegurar que a estrutura é alinhada de um limite de 64 bits, uma vez que tem um membro de 64 bits de modo que adiciona algum acolchoamento no final da estrutura para assegurar que as matrizes desses objectos terão cada elemento adequadamente alinhados. Na verdade, estou surpreso que o GCC não fazer o mesmo.

Estou curioso o que você está testando unidade faz que atinge um obstáculo com isso - a maior parte do alinhamento de tempo de membros da estrutura não é necessário a menos que você precisa para corresponder a um formato de arquivo binário ou um protocolo de fio ou você realmente precisa para reduzir a memória usada por uma estrutura (especialmente utilizados em sistemas embarcados). Sem saber o que você está tentando fazer em seus testes Eu não acho que uma boa sugestão pode ser dada. Embalagem a estrutura pode ser uma solução, mas ele vem com algum custo - desempenho (especialmente em plataformas não-Intel) e portabilidade (como embalagem struct está configurado é pode ser diferente do compilador para compilador). Estes não podem importa para você, mas pode haver uma maneira melhor para resolver o problema no seu caso.

Você pode fazer algo como

#ifdef _MSC_VER
#pragma pack(push, 16)
#endif

/* your struct defs */

#ifdef _MSC_VER
#pragma pack(pop)
#endif

Para dar um compilador directiva forçando alinhamento

Ou ir para as opções de projeto e alterar o alinhamento struct padrão [no Código Generation]

estofamento Estrutura para valores de 64 bits é diferente em diferentes compiladores. Eu vi diferenças entre mesmo entre alvos gcc.

Note que explicitamente estofo para alinhamento de 64 bits só vai esconder o problema. Ele vai voltar se você começar estruturas ingenuamente nidificação, porque os compiladores ainda discordam sobre o alinhamento natural das estruturas internas.

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