uint8_t vs unsigned char

Qual é a vantagem de usar uint8_t sobre unsigned char em C?

Eu sei que em quase todos os sistemas uint8_t é apenas um typedef para unsigned char , então por que usá-lo?

Ele documenta sua intenção – você armazenará números pequenos, em vez de um caractere.

Também parece melhor se você estiver usando outros typedefs como uint16_t ou int32_t .

Apenas para ser pedante, alguns sistemas podem não ter um tipo de 8 bits. Segundo a Wikipedia :

Uma implementação é necessária para definir tipos inteiros de largura exata para N = 8, 16, 32 ou 64 se, e somente se, houver algum tipo que atenda aos requisitos. Não é necessário defini-los para nenhum outro N, mesmo se ele suportar os tipos apropriados.

Portanto, não é garantido que o uint8_t exista, embora seja para todas as plataformas onde 8 bits = 1 byte. Algumas plataformas incorporadas podem ser diferentes, mas isso está ficando muito raro. Alguns sistemas podem definir os tipos de char como 16 bits e, nesse caso, provavelmente não haverá um tipo de 8 bits de qualquer tipo.

Além disso (menor) questão, a resposta do @Mark Ransom é o melhor na minha opinião. Use aquele que mostra mais claramente para o que você está usando os dados.

Além disso, estou assumindo que você quis dizer uint8_t (o typedef padrão de C99 fornecido no header stdint.h ) em vez de uint_8 (não faz parte de nenhum padrão).

O objective é escrever código independente de implementação. unsigned char não unsigned char não é garantido como um tipo de 8 bits. uint8_t é.

Como você disse, ” quase todo sistema”.

char é provavelmente um dos menos propensos a mudar, mas quando você começa a usar uint16_t e amigos, usar uint8_t combina melhor e pode até ser parte de um padrão de codificação.

Na minha experiência, existem dois lugares onde queremos usar uint8_t para significar 8 bits (e uint16_t, etc) e onde podemos ter campos menores que 8 bits. Ambos os locais são onde o espaço é importante e geralmente precisamos examinar um dump bruto dos dados durante a debugging e precisamos determinar rapidamente o que ele representa.

O primeiro está nos protocolos de RF, especialmente em sistemas de banda estreita. Nesse ambiente, podemos precisar include o máximo de informações possível em uma única mensagem. O segundo é em armazenamento flash, onde podemos ter um espaço muito limitado (como em sistemas embarcados). Em ambos os casos, podemos usar uma estrutura de dados compactados, na qual o compilador cuidará da embalagem e do desempacotamento para nós:

 #pragma pack(1) typedef struct { uint8_t flag1:1; uint8_t flag2:1; padding1 reserved:6; /* not necessary but makes this struct more readable */ uint32_t sequence_no; uint8_t data[8]; uint32_t crc32; } s_mypacket __attribute__((packed)); #pragma pack() 

Qual método você usa depende do seu compilador. Você também pode precisar suportar vários compiladores diferentes com os mesmos arquivos de header. Isso acontece em sistemas embarcados em que dispositivos e servidores podem ser completamente diferentes – por exemplo, você pode ter um dispositivo ARM que se comunica com um servidor x86 Linux.

Há algumas advertências com o uso de estruturas compactadas. A maior pegadinha é que você deve evitar desreferenciar o endereço de um membro. Em sistemas com palavras alinhadas com mutibyte, isso pode resultar em uma exceção desalinhada – e um coredump.

Algumas pessoas também se preocupam com o desempenho e argumentam que o uso dessas estruturas compactadas irá desacelerar seu sistema. É verdade que, nos bastidores, o compilador adiciona código para acessar os membros de dados não alinhados. Você pode ver isso observando o código assembly em seu IDE.

Mas como as estruturas compactadas são mais úteis para comunicação e armazenamento de dados, os dados podem ser extraídos em uma representação não compactada ao trabalhar com ela na memory. Normalmente, não precisamos estar trabalhando com todo o pacote de dados na memory.

Aqui está alguma discussão relevante:

pacote pragma (1) nem __attribute__ ((alinhado (1))) funciona

O pacote __attribute __ ((packed)) / #pragma do gcc é inseguro?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html

Há pouco. Do ponto de vista da portabilidade, o char não pode ser menor que 8 bits e nada pode ser menor que o char , portanto, se uma determinada implementação C tiver um tipo inteiro de 8 bits sem sinal, ele será char . Alternativamente, pode não ter nenhum, e nesse ponto qualquer truque de typedef é discutível.

Ele poderia ser usado para documentar melhor seu código, no sentido de que é claro que você precisa de bytes de 8 bits e nada mais. Mas na prática é uma expectativa razoável em praticamente qualquer lugar já (existem plataformas DSP nas quais isso não é verdade, mas as chances de seu código rodar lá são pequenas, e você pode errar usando uma afirmação estática no topo do programa em tal plataforma).

Em quase todos os sistemas que conheci uint8_t == unsigned char, mas isso não é garantido pelo padrão C. Se você está tentando escrever código portátil e importa exatamente o tamanho da memory, use uint8_t. Caso contrário, use um caracter não assinado.

Isso é muito importante, por exemplo, quando você está escrevendo um analisador de rede. Os headers de pacotes são definidos pela especificação do protocolo, não pelo modo como o compilador C de uma plataforma em particular funciona.