Programação de sistemas embarcados: inteiros padrão e mistura de tipos inteiros

Todos os subconjuntos de linguagem C mais seguros, como MISRA-C:2012, têm uma classe inteira de regras para aritmética de inteiros. Curiosamente, essas são geralmente as regras mais frequentemente violadas em quase qualquer base de código. Então, o que torna a aritmética inteira tão complicada?

A aritmética inteira parece ser uma matéria do ensino fundamental. Mas na programação, você absolutamente deve não subestimá-lo. Na verdade, todos os subconjuntos de linguagem C mais seguros (por exemplo, MISRA-C:2012 [1]) tem uma classe inteira de regras para aritmética inteira. Curiosamente, essas são geralmente as regras mais frequentemente violadas em quase qualquer base de código. Então, o que torna a aritmética inteira tão complicada? Quais são as regras da linguagem C? Como evitar algumas das armadilhas mais comuns? Por favor, assista a vídeo lição nº 11 para descobrir:

Lição 11 – Inteiros padrão e mistura de tipos inteiros

Por que os números inteiros são complicados?

Os números inteiros em matemática são simples porque existe apenas um tipo: números ilimitados que podem durar indefinidamente de -∞ a +∞. Mas na programação, os números têm um faixa limitada, como 8 bits, 16 bits, 32 bits ou 64 bits. Cada faixa requer tratamento específico. Além disso, os números podem ter diferentes assinatura atribuído a eles (assinado vs não assinado). Isso significa que os mesmos padrões de bits na representação digital podem ser intérprete de forma diferente dependendo do intervalo e da assinatura atribuída ao número. Por exemplo, um padrão de bits 0xFFF6 pode ser interpretado como um valor sem sinal 65526 ​​de 16 bits, um valor com sinal de 16 bits -10 ou um valor com sinal de 32 bits 65526. (Consulte Lição 1 para refrescar sua memória sobre a representação do complemento de dois.) Agora considere as implicações de misturado todos esses diferentes tipos de inteiros em expressões!

Padrão Todo

O primeiro passo para simplificar o problema dos inteiros é conhecimento o intervalo e a assinatura atribuídos a um determinado número. Infelizmente, os tipos inteiros “nativos de C”, como “int” e “char”, bem como os qualificadores “short”, “long” e “unsigned”, são intencionalmente ambíguos. Por exemplo, ‘int’ pode ter um intervalo de 16 bits em um processador de 8 ou 16 bits, um intervalo de 32 bits em um processador de 32 bits e um intervalo de 64 bits em um processador de 64 bits.

Essa ambiguidade tem sido um grande problema há décadas, especialmente na programação embarcada. Mas, eventualmente, o problema foi formalmente abordado no C99 [2]quem apresentou o Padrão tipos inteiros definidos no arquivo de cabeçalho . Alguns exemplos de inteiros incluem: uint8_t, int16_t ou uint32_t. Tenho certeza que qualquer um pode adivinhar o alcance e a assinatura de cada tipo.

Para programadores embarcados, é sem dúvida o recurso mais valioso do C99 por causa dos nomes padronizados e, mais importante, porque agora o compilador vendedor é responsável por fornecer tipos inteiros portáteis com um intervalo e assinatura conhecidos. Mas para apreciá-lo, você tem que usar o todo No lugar simples ‘int’, ‘curto’ ou ‘char.’ Inventar seus próprios nomes de tipo inteiro de casa é contraproducente. (Veja também MISRA-C:2012 Diretiva 4.6 [1].)

Misturando tipos de inteiros e regras de promoção de inteiros em C

Enquanto inteiros certamente ajudam, para entender como uma expressão será avaliada você ainda precisa saber o regras de promoção de inteiros em C [2], que são dependentes do processador. Isso é especialmente verdadeiro ao misturar diferentes tipos de inteiros em expressões. o vídeo lição 11 apresenta algumas das regras mais importantes e as ilustra com exemplos, então não vou repeti-las aqui.

Constantes inteiras assinadas e não assinadas em C

Constantes inteiras simples em C representam sinal todo. Por exemplo, a constante 0 significa sinal inteiro zero (estritamente falando, zero octal assinado). Mas você também pode fazer explicitamente Não assinado constantes fornecendo o sufixo “U” ou “u”. Por exemplo, 0U representa um Não assinado inteiro zero. Outros exemplos são 1U, 5u, 0xDEADBEAFU. Aplicar sufixos “U” (ou “u”) a constantes inteiras não assinadas é uma maneira simples de aprimorar suas expressões inteiras. (Os sufixos “U” ou “u” também são exigidos pela regra 7.2 do MISRA-C:2012 [1].)

Notas finais

A aritmética de números inteiros é um terreno fértil para bugs em C e, apesar de minhas décadas de experiência, ainda cometo erros com números inteiros. O assunto é complexo, e esta lição #11 mal arranha a superfície. Para saber mais, recomendo que você leia o padrão C99 [2] e diretrizes MISRA-C [1], especialmente a Seção 8.10, “Modelo de Tipo Essencial”. Usando um ferramenta de análise estática também é uma técnica muito eficaz para reduzir bugs com números inteiros. Ao executar a análise estática, você também deve habilitar a verificação MISRA-C.

Finalmente, se você quiser testar sua compreensão da aritmética inteira, uma abordagem oportuna quiz acaba de ser publicado em EmbeddedRelated.com. Até agora, mais de 90% dos participantes estão errados, o que prova meu ponto de vista. Vamos ver como você se sai…

[1] Padrões MISRA-C

[2] Norma ISO/IEC 9899:TC3 (C99)


Dr. Miro M. Samek é o criador do QP em tempo real de código aberto
Estruturas incorporadas e design baseado no modelo gráfico QM freeware
ferramenta. Ele também é o fundador e CEO da saltos quânticos — o fornecedor de software incorporado moderno baseado em objetos ativos e máquinas de estado hierárquico, bem como ferramentas para modelagem visual, geração automática de código e teste de unidade de software profundamente incorporado. Miro ensina o popular curso em vídeo do YouTube “Programação de Sistemas Embarcados Modernos” no qual esta série de artigos se baseia.

Conteúdo Relacionado:

Para mais embutidos, assine a newsletter semanal da Embedded.