Fiquei me perguntando se alguém poderia me explicar o que a #pragma pack
declaração do pré - processador faz e, mais importante, por que alguém iria querer usá-la.
Verifiquei a página do MSDN , que ofereceu algumas informações, mas esperava ouvir mais de pessoas com experiência. Eu já vi isso em código antes, embora pareço não encontrar mais onde.
c
c-preprocessor
pragma-pack
Cenoc
fonte
fonte
#pragma
diretivas, elas são definidas pela implementação.A mod s = 0
onde A é o endereço es é o tamanho do tipo de dados; isso verifica se os dados não estão desalinhados.Respostas:
#pragma pack
instrui o compilador a compactar membros da estrutura com um alinhamento específico. A maioria dos compiladores, quando você declara uma estrutura, inserirá preenchimento entre os membros para garantir que eles estejam alinhados aos endereços apropriados na memória (geralmente um múltiplo do tamanho do tipo). Isso evita a penalidade de desempenho (ou erro definitivo) em algumas arquiteturas associadas ao acesso a variáveis que não estão alinhadas corretamente. Por exemplo, dados inteiros de 4 bytes e a seguinte estrutura:O compilador pode escolher colocar a estrutura na memória da seguinte maneira:
e
sizeof(Test)
seria 4 × 3 = 12, mesmo que contenha apenas 6 bytes de dados. O caso de uso mais comum para#pragma
(que eu saiba) é ao trabalhar com dispositivos de hardware nos quais você precisa garantir que o compilador não insira preenchimento nos dados e que cada membro siga o anterior. Com#pragma pack(1)
, a estrutura acima ficaria assim:E
sizeof(Test)
seria 1 × 6 = 6.Com
#pragma pack(2)
, a estrutura acima ficaria assim:E
sizeof(Test)
seria 2 × 4 = 8.A ordem das variáveis em struct também é importante. Com variáveis ordenadas da seguinte forma:
e com
#pragma pack(2)
, a estrutura seria apresentada assim:e
sizeOf(Test)
seria 3 × 2 = 6.fonte
#pragma
é usado para enviar mensagens não portáteis (como neste compilador apenas) para o compilador. Coisas como desativar certos avisos e estruturas de embalagem são razões comuns. Desabilitar avisos específicos é particularmente útil se você compilar com os avisos quando o sinalizador de erros estiver ativado.#pragma pack
especificamente é usado para indicar que a estrutura que está sendo compactada não deve ter seus membros alinhados. É útil quando você tem uma interface mapeada na memória para um pedaço de hardware e precisa controlar exatamente para onde os diferentes membros da estrutura apontam. Notavelmente, não é uma boa otimização de velocidade, pois a maioria das máquinas é muito mais rápida ao lidar com dados alinhados.fonte
Diz ao compilador o limite para alinhar os objetos em uma estrutura. Por exemplo, se eu tiver algo como:
Em uma máquina típica de 32 bits, você normalmente "deseja" ter 3 bytes de preenchimento entre
a
eb
parab
aterrissar em um limite de 4 bytes para maximizar sua velocidade de acesso (e é o que normalmente acontece por padrão).Se, no entanto, você precisar corresponder a uma estrutura definida externamente, deseje garantir que o compilador estabeleça sua estrutura exatamente de acordo com essa definição externa. Nesse caso, você pode dar ao compilador um aviso
#pragma pack(1)
para não inserir nenhum preenchimento entre membros - se a definição da estrutura incluir preenchimento entre membros, você o insere explicitamente (por exemplo, geralmente com membros nomeadosunusedN
ouignoreN
, ou algo sobre isso ordem).fonte
b
em um limite de 4 bytes significa que o processador pode carregá-lo emitindo uma única carga de 4 bytes. Embora dependa um pouco do processador, se estiver em um limite estranho, é bem provável que, para carregá-lo, seja necessário que o processador emita duas instruções de carregamento separadas, use um shifter para juntar essas peças. A penalidade típica é da ordem de 3x a carga mais lenta desse item.Os elementos de dados (por exemplo, membros de classes e estruturas) normalmente estão alinhados nos limites WORD ou DWORD dos processadores de geração atual, a fim de melhorar os tempos de acesso. A recuperação de um DWORD em um endereço que não seja divisível por 4 requer pelo menos um ciclo de CPU extra em um processador de 32 bits. Portanto, se você tiver, por exemplo, três membros de caracteres
char a, b, c;
, eles tendem a ocupar 6 ou 12 bytes de armazenamento.#pragma
permite substituir isso para obter um uso mais eficiente do espaço, à custa da velocidade de acesso ou para obter consistência dos dados armazenados entre diferentes destinos do compilador. Eu me diverti muito com essa transição de código de 16 para 32 bits; Espero que a transferência para código de 64 bits cause os mesmos tipos de dores de cabeça para algum código.fonte
char a,b,c;
normalmente são necessários 3 ou 4 bytes de armazenamento (pelo menos em x86) - isso ocorre porque o requisito de alinhamento é de 1 byte. Se não fosse, então como você lidariachar str[] = "foo";
? O acesso achar
é sempre uma máscara de busca e troca simples, enquanto o acesso a umaint
pode ser uma busca ou uma busca, ou apenas uma busca, dependendo de estar alinhado ou não.int
possui (em x86) um alinhamento de 32 bits (4 bytes) porque, caso contrário, você obteria (digamos) meioint
em umDWORD
e meio no outro, e isso levaria duas pesquisas.O compilador pode alinhar membros em estruturas para alcançar o desempenho máximo em determinada plataforma.
#pragma pack
A diretiva permite controlar esse alinhamento. Normalmente, você deve deixá-lo por padrão para obter o melhor desempenho. Se você precisar passar uma estrutura para a máquina remota, geralmente utilizará#pragma pack 1
para excluir qualquer alinhamento indesejado.fonte
Um compilador pode colocar membros da estrutura em limites de bytes específicos por razões de desempenho em uma arquitetura específica. Isso pode deixar um preenchimento não utilizado entre os membros. O empacotamento da estrutura força os membros a serem contíguos.
Isso pode ser importante, por exemplo, se você precisar que uma estrutura esteja em conformidade com um arquivo ou formato de comunicação específico em que os dados que você precisa estejam em posições específicas dentro de uma sequência. No entanto, esse uso não lida com problemas de endianismo, portanto, embora usado, ele pode não ser portátil.
Também pode sobrepor exatamente a estrutura de registro interno de algum dispositivo de E / S, como um controlador UART ou USB, por exemplo, para que o acesso ao registro seja feito através de uma estrutura e não de endereços diretos.
fonte
Você provavelmente só desejaria usá-lo se estivesse codificando para algum hardware (por exemplo, um dispositivo mapeado para memória) que tivesse requisitos estritos para o pedido e o alinhamento de registros.
No entanto, isso parece uma ferramenta bastante direta para atingir esse objetivo. Uma abordagem melhor seria codificar um mini-driver no assembler e fornecer uma interface de chamada C em vez de se atrapalhar com esse pragma.
fonte
Eu o usei no código antes, embora apenas para interagir com o código legado. Este era um aplicativo Mac OS X Cocoa que precisava carregar arquivos de preferência de uma versão anterior do Carbon (que era compatível com a versão original do M68k System 6.5 ... você entendeu). Os arquivos de preferência na versão original eram um despejo binário de uma estrutura de configuração, que costumava
#pragma pack(1)
evitar ocupar espaço extra e economizar lixo (ou seja, os bytes de preenchimento que estariam na estrutura).Os autores originais do código também usavam
#pragma pack(1)
para armazenar estruturas usadas como mensagens na comunicação entre processos. Acho que o motivo aqui foi evitar a possibilidade de tamanhos de preenchimento desconhecidos ou alterados, pois o código às vezes examinava uma parte específica da estrutura da mensagem contando vários bytes desde o início (ewww).fonte
Vi pessoas usá-lo para garantir que uma estrutura ocupe toda uma linha de cache para impedir o compartilhamento falso em um contexto multithread. Se você tiver um grande número de objetos que serão compactados livremente por padrão, poderá economizar memória e melhorar o desempenho do cache para compactá-los com mais firmeza, embora o acesso à memória desalinhada normalmente diminua a velocidade das coisas, por isso pode haver uma desvantagem.
fonte
Observe que existem outras maneiras de obter consistência de dados que o #pragma pack oferece (por exemplo, algumas pessoas usam o #pragma pack (1) para estruturas que devem ser enviadas pela rede). Por exemplo, consulte o seguinte código e sua saída subsequente:
A saída é a seguinte: sizeof (struct a): 15, sizeof (struct b): 24 sizeof (twoa): 30, sizeof (twob): 48
Observe como o tamanho da estrutura a é exatamente o que a contagem de bytes é, mas a estrutura b possui um preenchimento adicionado (consulte isso para obter detalhes sobre o preenchimento). Ao fazer isso, em oposição ao pacote #pragma, você pode controlar a conversão do "formato de ligação" nos tipos apropriados. Por exemplo, "char two [2]" em um "short int" etc.
fonte
sizeof
retorna umsize_t
que deve ser impresso usando%zu
. Usando o especificador formato errado invoca indefinido comportamentoPor que alguém quer usá-lo?
Para reduzir a memória da estrutura
Por que não se deve usá-lo?
fonte