Como usar os tipos padrão C99 para máxima portabilidade E eficiência na maioria das plataformas?
Primeiro, aqui está o que eu entendo e penso o que é verdade para a questão.
Use tipos de dados rápidos para variáveis únicas, como contadores oufor
índices de loop. Por exemplo:
#define LOOP_COUNT (100U)
uint_fast8_t index;
for(index = 0; index < LOOP_COUNT; index++){
/* Do something */
}
Eu suponho que o tipo mais adequado aqui éuint_fast8_t
Desde aindex
nunca pode exceder 255 e esta será a implementação mais rápida para todas as plataformas. Se eu usasseunsigned int
em vez disso, será mais rápido em plataformas> = 16 bits, mas será mais lento em plataformas de <16 bitsint
é de no mínimo 16 bits por padrão. Além disso, se eu usasseuint8_t
será mais lento em plataformas de 8 bits, pois o compilador adicionaAND 0xFF
instruções para verificar o estouro de cada incremento (meu compilador ARM7 faz isso mesmo na otimização de velocidade total).size_t
também não é uma opção, pois pode ser maior que o tamanho inteiro nativo.
Lado ruim (?) Disso, se um estouro é esperado para 8 bits, isso não vai acontecer. Programador deve verificar se há overflow manualmente (como ele / ela deve IMHO), o que pode resultar em código buggy se esquecido. Além disso, o compilador (e até mesmo o PC-Lint para minha surpresa) não dará nenhum aviso / problema se LOOP_COUNT "acidentalmente" for configurado para um valor maior que 255 em plataformas> 8 bits, mas o aviso será gerado em uma plataforma de 8 bits, que reduzirá a portabilidade e introduzirá bugs, mas isso pode ser evitado com#if
Verificações.
Use menos tipos de dados, tanto quanto possível, se o uso de memória é motivo de preocupação, como em matrizes ou estruturas. Por exemplo:
uint_least8_t array[100];
É a maneira mais portátil e eficiente de declarar matrizes se o uso da memória for uma preocupação. Esse tipo fornecerá uma matriz de bytes se o acesso a bytes for possível na plataforma e fornecerá a menor matriz inteira de largura acessível, caso contrário. Além disso, menos tipos podem ser usados em estruturas se tivermos arrays da estrutura.
O tipo mínimo também pode sofrer os problemas que os tipos rápidos fazem, já que a largura das variáveis pode ser alterada em diferentes plataformas para ambos os casos.
Evite o máximo possível de tipos de dados com largura fixa, pois eles podem nem existir em algumas plataformas, exceto acesso a registradores de hardware, mapeamento de protocolos de comunicação, etc., onde precisamos saber os bits exatos da variável. Por exemplo:
typedef struct {
uint8_t flags;
uint8_t length;
uint8_t data[100];
uint16_t crc;
} __attribute__((packed)) package_t;
Usualmente__attribute__((packed))
(ou algo semelhante) deve ser usado para garantir que nenhum preenchimento seja inserido para esses casos, pois isso pode ser um problema por si só.
Agora, se a minha compreensão for verdadeira, acho que menos tipos de dados são mais propensos a serem usados em matrizes ou estruturas, os tipos de dados rápidos são mais propensos a serem usados para variáveis individuais e tipos de dados fixos são improváveis de serem usados para atingir o máximo portabilidade e eficiência. Mas digitar "rápido" e "mínimo" toda vez não é animador. Então, penso em um tipo definido da seguinte forma:
typedef [u]intN_t os_[u|s]exactN_t;
typedef [u]int_fastN_t os_[u|s]N_t;
/* I couldn't come up with a better name */
typedef [u]int_leastN_t os_[u|s]minN_t;
/* These may change */
typedef uint_least8_t os_byte_t;
typedef uint_least16_t os_word_t;
/* ... */
Primeiro e a questão importante é, meu entendimento é verdadeiro?Qual seria a maneira mais portátil e eficiente de usar os tipos padrão C99 e como você os declararia se isso não fosse verdade?O meu conjunto de tipos faz sentido ou é provável que ele produza código com bugs?Além disso, eu ficaria feliz em saber como e onde você usa os tipos padrão C99.