22
UNIVERSIDADE DE BRASÍLIA DEPARTAMENTO DE CIÊNCIA DA COMPUTAÇÃO PROGRAMAÇÃO SISTEMÁTICA Prof. Francisco A. C. Pinheiro Padrão de Codificação e Estilo para a Linguagem C (adotado para os trabalhos da disciplina) VERSÃO 2008-2

Padrão de Codificação e Estilo para a Linguagem C · se eles apontarem para o mesmo vetor. 15 Ptr-5. Use índices em vez de ponteiros para acessar elementos de vetores. 16 Ptr-6

Embed Size (px)

Citation preview

UNIVERSIDADE DE BRASÍLIA

DEPARTAMENTO DE CIÊNCIA DA COMPUTAÇÃO

PROGRAMAÇÃO SISTEMÁTICA

Prof. Francisco A. C. Pinheiro

Padrão de Codificação e Estilopara a Linguagem C

(adotado para os trabalhos da disciplina)

VERSÃO 2008-2

Sumário

Doc-1. Use nomes que auxiliem a interpretação do elemento nomeado. 3Doc-2. Diferencie as classes dos elementos através da grafia dos identificadores 3Doc-3. Adote um estilo de formatação e documentação bem definido. 4Doc-4. Documente completamente a interface dos módulos. 5Doc-5. Documente a implementação dos módulos com o objetivo de facilitar a manutenção

do código.6

Mod-1. Use arquivos de cabeçalho e implementação para cada módulo. 6Mod-2. Evite inclusão múltipla dos arquivos de cabeçalho. 7Mod-3. Identifique cada módulo de forma única. 7Mod-4. Evite o uso de variáveis globais. 8Mod-5. Diminua a complexidade das interfaces. 8Mod-6. Use um único ponto de entrada e um único ponto de saída para módulos (e fun-

ções).8

TD-1. Utilize definição de tipos com indicação de tamanho e sinalização para os tiposbásicos.

9

TD-2. Use protótipos de função. 9TD-3. Use typedef para todas as estruturas (struct) e uniões (union), colocando a definição

de tipo antes da declaração.10

TD-4. Use o qualificador const para declarar as variáveis que não são modificadas, inclu-sive as paramétricas e os ponteiros.

10

TD-5. Limite as conversões de tipo. 11TD-6. Não use o mesmo identificador em múltiplas declarações. 12Mem-1. Minimize alocação dinâmica de memória. 12Mem-2. Use funções de memória que possibilitem a definição de limites de atuação. 13Mem-3. Inicie variáveis e ponteiros antes do uso. 14Ptr-1. Teste os ponteiros para valores válidos antes do uso. 14Ptr-2. Só use um ponteiro para acessar elementos de vetor se ele tiver sido declarado

como ponteiro para este vetor.14

Ptr-3. Só use aritmética de ponteiros com ponteiros que referenciam vetores ou elementosde vetores.

15

Ptr-4. 04. Só realize operações entre ponteiros, aritméticas ou relacionais (<, >, <=, >=),se eles apontarem para o mesmo vetor.

15

Ptr-5. Use índices em vez de ponteiros para acessar elementos de vetores. 16Ptr-6. Use ponteiros ao declarar grandes estruturas como argumentos em chamadas de

função.16

Ctr-1. Minimize a complexidade do fluxo de controle. 17Ctr-2. Um programa não deve conter elementos não utilizados, caminhos intransitáveis,

código morto ou não realizável.17

Ctr-3. O valor retornado por uma expressão que não tenha efeitos colaterais, ou por umafunção cujo valor de retorno seja void, deve ser utilizado.

17

Ctr-4. Não use funções com um número variável de argumentos. 18Ctr-5. Não use expressões como argumentos em chamadas a funções ou macros. 18

1

Err-1. Assegure que as expressões aritméticas produzirão os resultados com a acurácia eprecisão esperadas.

19

Err-2. Verifique o resultado de operações que podem resultar em erros ou exceções. 19Err-3. Verifique a ocorrência de erros de domínio e de imagem. 20Err-4. Use funções-envelope para realizar verificação não realizada pelas funções da bi-

blioteca padrão.20

Err-5. Teste a validade dos argumentos de entrada ao iniciar e a validade dos argumentosde saída ao finalizar a execução de cada função.

21

Err-6. Trate erros e exceções localmente. 21

2

1 Documentação e estilo

Recomendação Doc-1. Use nomes que auxiliem a interpretação do elemento nomeado.

RAZÃO: Favorece o entendimento, facilita a manutenção.COMENTÁRIO: O nome deve auxiliar a interpretação do elemento. Se o elemento for exportado, privilegia-se a inter-pretação; evita-se o uso de abreviações ou siglas que comprometam o entendimento de quem interpreta. Se o elementofor local, privilegia-se a escrita; pode-se usar abreviações e nomes curtos, já que o uso estará documentado.

• Nomes de variáveis devem ser expressões substantivadas.

• Variáveis e funções boolianas devem refletir o significado do valor verdade (true).

• Procedimentos devem ser nomeados pelo que eles fazem.

• Funções devem ser nomeadas pelo que elas retornam.

int valorTotal;int totalizaValores; // Desconformebool opcaoOK;bool opcaoInvalida; // Desconformevoid calcular_saldo() {...void saldo_conta() {... // Desconformefloat salario_semanal() {...float calcular_salario_semanal() {... // Desconformebool tamanho_valido() {...bool verificar_tamanho() {... // Desconforme

FONTE: [gnu, 5.4] [misra], [nureg, 2.4.1.2].

Recomendação Doc-2. Diferencie as classes dos elementos através da grafia dos identificadores

RAZÃO: Favorece o entendimento, facilita a manutenção.COMENTÁRIO: É desejável que as classes dos elementos possam ser diferenciadas graficamente sem, entretanto, pre-judicar a legibilidade (o que ocorre quando se usa afixos crípticos para indicar a classe). A grafia dos identificadoresdeve ser, antes de tudo, consistente: use um mesmo estilo sempre.

Variáveis, funções, parâmetros e membros de estruturas euniões.

Inicial minúscula.

Nomes de macros, valores constantes e constantes enume-radas.

Todos os caracteres em maiúsculas.

Tipos declarados (typedef). nomes terminados com o sufixo "_t",inicial maiúscula, outodos os caracteres em maiúsculas.

Tags de estruturas, uniões e enumerações. Como variáveis ou inicial maiúscula.Nomes usados como prefixo, por exemplo, rótulos identifi-cadores de módulos.

Todos os caracteres em maiúsculas.

Separe as palavras de um nome com sublinhado ou usandouma letra maiúscula inicial.

grafe_os_nomes_assim ougrafeOsNomesAssim.

EXEMPLO: O código abaixo ilustra algumas das grafias sugeridas:

3

extern ES_imprime_detalhe();#define TAXA (1.016)enum Escapes {BELL = ’\a’, RETRO = ’\b’, TAB = ’\t’, NL = ’\n’,

TABV = ’\v’, RET = ’\r’};enum Escapes valor_escape;const float PI = 3.1416;int valor_tx_mensal;typedef reg_a Registro_aluno;typedef reg_a registro_aluno_t;struct reg_a {int matricula;char *nome;

}Registro_aluno reg2_aluno;registro_aluno_t reg2_aluno;void imprime_media_alunos(int lista_matricula[]) ...

FONTE: [misra], [nureg].

Regra Doc-3. Adote um estilo de formatação e documentação bem definido.

RAZÃO: Favorece o entendimento, facilita a manutenção.COMENTÁRIO:

• Use chaves para definir blocos.

• Adote um único estilo para o uso de chaves: 1TBS ou 2TBS

• Os comentários deve ter a mesma indentação que o objeto sendo comentado.

• Indente comandos aninhados e estruturas de controle.

• Use um espaço entre operadores e operandos.

• Quebre expressões longas de forma que um operador inicie a próxima linha.

EXEMPLO: O estilo 2TBS é o estilo adotado pelo padrão GNU; é o estilo padrão da ferramenta indent. O estilo 1TBS(one true brace style) é também conhecido como estilo do kernel ou estilo K&R; pode ser obtido com o argumento-kr no programa indent.

4

#include <stdio.h>#include <math.h>

/*** \brief Exemplo formatacao 1TBS

*/int fun_1TBS(int arg){int res = 0, ind = 0, aux = 0;float tx_ano = 0, vr = 0;switch (arg) {case 1:case 5:

aux = aux + 2;/* segue fluxo */

case 7:/* Comentario no mesmo nivel

que o item comentado */res = res * aux;break;

default:res = 0;break;

}

if (res > 0) {/* espaço entre operadores */if (aux == -34 * tx_ano / 2) {

/* operador iniciandopróxima linha */

vr = (ind + sqrt(res + 2.06)/ tx_ano) / (aux+ ln(ind * aux));

} else {/* else em if’s aninhados */

}}

}

#include <stdio.h>#include <math.h>/*** \brief Exemplo formatacao 2TBS

*/intfun_2TBS (int arg){int res = 0, ind = 0, aux = 0;float tx_ano = 0, vr = 0;switch (arg){case 1:case 5:aux = aux + 2;/* segue fluxo */

case 7:/* Comentario no mesmo nivel

que o item comentado */res = res * aux;break;

default:res = 0;break;

}

if (res > 0){/* espaço entre operadores */if (aux == -34 * tx_ano / 2){/* operador iniciando

próxima linha */vr = (ind + sqrt (res + 2.06)

/ tx_ano) / (aux+ ln (ind * aux));

}else{/* else em if’s aninhados */

}}

}

FONTE: [gnu, 5.1], [misra], [nureg, 4.4.1.1].

Regra Doc-4. Documente completamente a interface dos módulos.

RAZÃO: Permitir o entendimento e o uso dos módulos sem a necessidade de acesso à sua implementação.COMENTÁRIO: A interface de um módulo é a única parte do módulo acessada por quem vai utilizá-lo. Deve, portanto,permitir o completo entendimento do seu funcionamento. Todos os elementos da interface de um módulo devem sercompletamente documentados, incluindo os elementos importados pelo módulo.EXEMPLO: O código abaixo ilustra a documentação de uma função usando as "tags"do doxygen:

5

/*** \brief Insere novo aluno

** \param mat Matrícula do novo aluno

* \param nome Nome do novo aluno

* \param tel Telefone do novo aluno

* \param ee Endereço eletrônico do novo aluno

** Insere o aluno, ajustando os apontadores de posição

* de modo a manter o arquivo ordenado pela matrícula do

* aluno. Se já existir um aluno com a mesma

* matrícula, a função retorna sem inserir.

*/voidARQ_A_inserir_aluno(int32_t mat, char_t nome[], char_t tel[], char_t ee[]);

FONTE: [misra], [nureg, 4.4.1.3].

Regra Doc-5. Documente a implementação dos módulos com o objetivo de facilitar a manutenção do código.

RAZÃO: A documentação da implementação é dirigida a quem irá manter o módulo. Pode omitir as descrições quesão óbvias a partir do código.COMENTÁRIO: As pessoas que irão manter o código devem ser capazes de entender a linguagem na qual ele estáescrito. Pode-se evitar documentação das partes óbvias:

• É importante documentar soluções de projeto que auxiliem o entendimento da implementação como um todo.

• É necessário documentar soluções não triviais.

FONTE: [misra], [nureg, 4.4.1.3].

2 Modularização

Regra Mod-1. Use arquivos de cabeçalho e implementação para cada módulo.

RAZÃO: Promove o encapsulamento, facilita a manutenção.COMENTÁRIO:

Arquivo cabeçalho Arquivo implementação

• Deve conter apenas os elementos exportadospelo módulo.

• Todos os elementos devem ser declarações detipos ou declarações extern.

• Deve conter todas as definições.

• Todos os elementos usados apenas no módulodevem ser declarados static.

EXEMPLO:

6

/* prog_mod1.h

* MOD1: ARQUIVO CABEÇALHO */

#ifndef MOD1#define MOD1#include "tipos.h"typedef struct tx_mensais taxa_t;struct tx_mensais{char_t cod;float64_t v1;

};extern taxa_t val_tx;extern const float32_t veloc;extern uint16_t salario_medio(void);

#endif

/* prog_mod1.c

* MOD1: ARQUIVO IMPLEMENTAÇÃO */

/* inclusão cabeçalhos do sistema */#include <stdio.h>/* inclusão cabeçalhos locais */#include "prog_mod1.h"

/* Definições globais */taxa_t val_tx;const float32_t veloc = 34E6;

/* Declarações e definições locais */typedef struct nodo_desc nodo_t;struct nodo_desc{int8_t matr;nodo_t *prox;

};

/* Declarações de funções locais *//* (protótipos) */static void calcular_salario(void);

/* Definições das funções */uint16_tsalario_medio() { ... }static voidcalcular_salario() { ... }

FONTE: [misra, 2-03-01, 2-03-02, 2-03-03], [nureg].

Regra Mod-2. Evite inclusão múltipla dos arquivos de cabeçalho.

RAZÃO: Facilita a manutenção e o desenvolvimento, evitando os erros decorrentes de múltiplas definições de elemen-tos contidos nos cabeçalhos.EXEMPLO:

/* inicio do arquivo */#ifndef idModulo#define idModulo/* conteúdo do arquivo */

#endif/* fim do arquivo */

/* inicio do arquivo */#if !defined(idModulo)#define idModulo/* conteúdo do arquivo */

#endif/* fim do arquivo */

FONTE: [misra, 2-03-01, 2-03-02, 2-16-02], [nureg].

Regra Mod-3. Identifique cada módulo de forma única.

7

RAZÃO: A unicidade do rótulo (e do nome) facilita a identificação do módulo no qual um elemento é definido.COMENTÁRIO: O nome de um módulo deve identificar sua função dentro do sistema. O rótulo de um módulo, contendoaproximadamente três letras maiúsculas, é usado para compor a identificação dos seus elementos exportados. Observeque nem todos os elementos exportados por um módulo precisam ter o rótulo do módulo em seu nome. O importanteé manter a clareza do significado do nome.EXEMPLO: No exemplo abaixo o nome ES é usado como rótulo.

/* prog_ent_sai.h

* ES: Módulo de entrada e saída */#ifndef $$MOD_ES#define $$MOD_ES#include "tipos.h"#define MAX_LIN (200)extern bool_t ES_estado_sumario;extern void ES_imp_totais(void);...

#endif

FONTE: [misra], [nureg].

Recomendação Mod-4. Evite o uso de variáveis globais.

RAZÃO: Variáveis globais aumentam o acoplamento e dificultam a manutenção.COMENTÁRIO: Quando necessário, deve-se manter as definições das variáveis globais em um único arquivo de imple-mentação, com suas declarações no arquivo cabeçalho correspondente.FONTE: [misra], [nureg].

Recomendação Mod-5. Diminua a complexidade das interfaces.

RAZÃO: Diminui o acoplamento e facilita a manutenção.COMENTÁRIO: Interfaces complexas dificultam o entendimento, além de contribuirem para aumentar o acoplamentoentre módulos. Em geral deve-se diminuir o acoplamento entre os módulos (e funções):

• Evitando o uso de variáveis globais.

• Diminuindo o número de parâmetros.

• Usando vetores ou estruturas.

• Evitando expressões nas listas de argumentos.

FONTE: [misra], [nureg, 4.1.1.3, 4.4.2.2], [iso, 6.7.5.3].

Recomendação Mod-6. Use um único ponto de entrada e um único ponto de saída para módulos (e funções).

RAZÃO: O uso de pontos únicos de entrada e saída facilita a análise e o teste do código, adere à definição usual demódulo, além de melhorar a compreensão do fluxo de controle e evitar valores de retorno indefinidos.COMENTÁRIO: Embora C não possua mecanismo para múltiplos pontos de entrada (explícitos), é possível chamarqualquer endereço de memória atribuindo-se um inteiro a um ponteiro para função.EXEMPLO: No código abaixo o valor de retorno de aux é indefinido quando a é diferente de zero.

8

#include <stdio.h>int aux(int);

intmain(int nargs, char *args[]){printf("%d\n", aux(0));printf("%d\n", aux(3));

}intaux(int a){if (a == 0)return 123;

}

FONTE: [misra, 2-06-06-05], [nureg, 4.1.2.4].

3 Tipos de dados

Regra TD-1. Utilize definição de tipos com indicação de tamanho e sinalização para os tipos básicos.

RAZÃO: Os tipos básicos em C/C++ têm seus tamanhos e implementações dependentes do compilador e da plataformaalvo da compilação. O padrão da linguagem estabelece apenas limites básicos.EXEMPLO: O uso de definição de tipos para os tipos básicos favorece a portabilidade. Os valores abaixo são baseadosna implementação do GNU GCC para o cabeçalho <stdint.h>.

#ifndef DECL_TIPOS#define DECL_TIPOS#include <stdbool.h>typedef bool bool_t;typedef char char_t;typedef unsigned char uint8_t;typedef unsigned short uint16_t;typedef unsigned int uint32_t;typedef unsigned long uint64_t;

typedef signed char int8_t;typedef signed short int16_t;typedef signed int int32_t;typedef signed long int64_t;typedef float float32_t;typedef double float64_t;typedef long double float128_t;#endif

FONTE: [misra, 2-03-09-02], [nureg, 4.1.2.6], [iso, 7.18].

Regra TD-2. Use protótipos de função.

RAZÃO: O uso de protótipos torna possível a verificação de tipos dos parâmetros.COMENTÁRIO: O tipo de uma função que não retorna valor e a lista de parâmetros de uma função sem parâmetrosdevem ser declarados void.FONTE: [misra], [nureg, 4.1.2.5], [iso, 6.5.2.2, 6.7.5.3].

9

Regra TD-3. Use typedef para todas as estruturas (struct) e uniões (union), colocando a definição de tipo antes dadeclaração.

RAZÃO: Favorece o entendimento, eliminando a necessidade das palavras extras struct e union na declaração detipos.EXEMPLO: O uso de typedef também permite a declaração circular de tipos.

typedef struct um um_t;typedef struct dois dois_t;

struct um{int num_a;dois_t *elem;

};

struct dois{int num_b;um_t *elem;

};

FONTE: [misra], [nureg].

Regra TD-4. Use o qualificador const para declarar as variáveis que não são modificadas, inclusive as paramétricase os ponteiros.

RAZÃO: Se uma variável não deve ser modificada, ela deve ser declarada de modo a assegurar este comportamento.EXEMPLO:

#include <stdio.h>

void funcao(int , int *, const int, int const, const int *, int * const);

intmain(int nargs, char *args[]){int p1 = 1, p2 = 2, p3 = 3, p4 = 4, p5 = 5, p6 = 6;printf("%d %d %d %d %d %d\n", p1, p2, p3, p4, p5, p6);funcao(p1, &p2, p3, p4, &p5, &p6);printf("%d %d %d %d %d %d\n", p1, p2, p3, p4, p5, p6);return 0;

}voidfuncao(int p1, /* p1 é cópia (não é constante) */

int *p2, /* p2 é ponteiro (não é constante) */const int p3, /* p3 é cópia (constante) */int const p4, /* p4 é cópia (constante) */const int *p5, /* p5 é ponteiro (conteúdo constante) */int *const p6) /* p6 é ponteiro (constante) */

{printf("funcao: %d %d %d %d %d %d\n", p1, *p2, p3, p4, *p5, *p6);p1 = 11;

*p2 = 22;/* p3 = 33; atribuição inválida *//* p4 = 44; atribuição inválida */p5 = p2;

10

/* (*p5) = p4; atribuição inválida *//* p6 = p2; atribuição inválida */(*p6) = p4;printf("funcao: %d %d %d %d %d %d\n", p1, *p2, p3, p4, *p5, *p6);

}

FONTE: [misra, 2-07-01, 2-08-04-03], [nureg].

Recomendação TD-5. Limite as conversões de tipo.

RAZÃO: As regras para conversões de tipo, incluindo as promoções inteiras, são complicadas e podem gerar resultadodiferente do pretendido.COMENTÁRIO: Limite as conversões explícitas de tipos e elimine as conversões implícitas ou automáticas. Eviteoperações cujos operandos possuem múltiplos tipos de dados. Também evite misturar em uma mesma expressãovariáveis sinalizadas e não sinalizadas.EXEMPLO: No programa à esquerda, para realizar a comparação o inteiro i é convertido para um valor não sinalizado,fazendo com que assuma o maior valor possível para este tipo. Uma conversão semelhante ocorre no programa àdireita, o valor 2147483647 é impresso em vez do 0.

#include <stdio.h>intmain(int nargs, char *args[]){int i;unsigned int ui;i = -1;ui = 2;if (i > ui){printf("negativo maior que positivo\n");

}else{printf("negativo não maior que positivo\n");

}}

#include <stdio.h>intmain(int nargs, char *args[]){int i, resultado;unsigned int ui;i = -1;ui = 2;resultado = i/ui;printf("%d\n", resultado);

}

A imposição de tipos também pode não funcionar quando o programa é compilado com otimização, em virtude dasregras de identificação (aliasing rules) usadas pelo compilador. No trecho a seguir a primeira impressão produz 11111111 ou 2222 2222, dependendo do nível de otimização utilizado na compilação. A última impressão pode resultarem 49 ou 50.

#include <stdio.h>intmain(int nargs, char *args[]){short vetor[2];vetor[0] = 0x1111;vetor[1] = 0x1111;

*(int *)vetor = 0x22222222;printf("%x %x\n", vetor[0], vetor[1]);

double a = 0.5;

11

double b = 0.01;double c = a / b;printf("%f / %f = %f\n", a, b, c);printf("%f / %f = %d\n", a, b, (int)c);

}

FONTE: [misra, 2-05-00], [nureg, 4.1.2.6].

Regra TD-6. Não use o mesmo identificador em múltiplas declarações.

RAZÃO: Evita erros de interpretação e resultados inesperados devido a conversões não pretendidas.COMENTÁRIO: Múltiplas declarações com tipos compatíveis tornam o programa confuso; com tipos incompatíveis, ocomportamento não é definido.EXEMPLO: O programa abaixo gera um resultado inesperado, decorrente da impressão de um vetor como se fosse uminteiro.

#include <stdio.h>int b;intmain(int nargs, char *args[]){printf("%d\n", b);

}

/* -------------------------------a variável b é declarada em outroarquivo como:--------------------------------- */

char b[4] = {’a’, ’b’, ’c’, ’d’}

FONTE: [misra, 2-02-10], [nureg, 4.1.2.6].

4 Memória

4.1 Alocação e atribuição de valores

Recomendação Mem-1. Minimize alocação dinâmica de memória.

RAZÃO: Reduz a ocorrência de problemas normalmente associados com a alocação dinâmica de memória.COMENTÁRIO: A alocação dinâmica de memória, embora torne os programas mais eficientes, pode causar problemassérios, exigindo atenção redobrada no desenvolvimento e manutenção dos programas. Alguns dos problemas:

• Alocar memória sem liberá-la após o uso.

• Tentativa de usar memória não alocada ou que já tenha sido liberada.

• Tamanho insuficiente de memória.

Também é possível que funções de alocação de memória exibam diferentes comportamentos dependendo do tipodos argumentos. Por exemplo, a função void *realloc(void *pv, size_t tam) possui diferentes com-portamentos:

• A função reduz o bloco de memória alocado, liberando o espaço desnecessário, se:

– se o novo tamanho é menor do que o antigo.

12

• A função aloca novo bloco de memória se:

– se o novo tamanho é maior do que o antigo e não pode ser alocado a partir de pv.

– pv é NULL e tam é maior que zero (funcionando como malloc(tam)), ou

• A função retorna NULL se:

– a alocação não pode ser realizada, ou

– se pv não é NULL e tam é zero (funcionando como free(pv)).

• A função tem comportamento indefinido se:

– pv não corresponde a um espaço previamente alocado por malloc, calloc, realloc, ou que já tenhasido liberado.

– pv é NULL e tam é zero.

FONTE: [gnu, 4.2], [misra], [nureg 4.1.1.1], [iso, 7.20.3.4].

Regra Mem-2. Use funções de memória que possibilitem a definição de limites de atuação.

RAZÃO: O uso de funções sem a verificação dos limites de atuação pode causar gravação em (ou acesso a) endereçosnão pretendidos.COMENTÁRIO: As seguintes funções agem sobre um número definido de caracteres:

char *strncat(char *, char *, size_t),int strncmp(char *, char *, size_t),int strncpy(char *, char *, size_t),void *memmove(char *, char *, size_t), evoid *memcpy(char *, char *, size_t).

As seguintes funções não permitem limitar a atuação e dependem, portanto, do conteúdo dos objetos que manipu-lam: strcat, strcmp e strcpy.EXEMPLO: Observe, entretanto, que mesmo funções que permitem a checagem dos limites de atuação podem oferecerriscos.

#include <stdio.h>#include <string.h>#define TAM1 (10)#define TAM2 (20)intmain(int nargs, char *args[]){char str1[TAM1], str2[TAM2], str3[TAM2];

strcpy(str1,"vt com dez"); /* ultrapassa str1 */strncpy(str1,"vt com dez", TAM1); /* Falta ’\0’ */strncpy(str1,"vt com dez", TAM1 - 1); /* Falta ’\0’ */strncpy(str2, str1, TAM2 + 5); /* Ultrapassa str2 */memmove(str1, str2, TAM2); /* ultrapassa str1 */memcpy(str1, str2, TAM2); /* ultrapassa str1 (indefinido) */return 0;

}

FONTE: [misra, 2-18-00-05], [nureg, 4.1.1.5, 4.1.1.6], [iso, 7.21].

13

Regra Mem-3. Inicie variáveis e ponteiros antes do uso.

RAZÃO: Favorece a legibilidade e facilita a manutenção.COMENTÁRIO: Variáveis automáticas não são iniciadas. Variáveis globais podem ou não ser iniciadas. As variáveisestáticas são sempre iniciadas, mas a iniciação explícita favorece a legibilidade.

• Inicie as variáveis locais no início da função.

• Inicie as variáveis globais uma única vez.

• Use um procedimento específico para iniciar as variáveis globais.

FONTE: [misra, 2-08-05-01], [nureg, 4.1.2.2].

4.2 Referência (ponteiros)

Regra Ptr-1. Teste os ponteiros para valores válidos antes do uso.

RAZÃO: Diminue a imprevisibilidade.COMENTÁRIO: Ponteiros não iniciados, ou com valores inválidos, são particularmente perigosos.EXEMPLO: O trecho de código à esquerda resulta em comportamento imprevisível.

#define VALOR (14L)long *ptr;...

*ptr = VALOR;...

#define VALOR (14L)long *ptr = NULL;long valor;...ptr = &valor;...if (ptr != NULL){

*ptr = VALOR;}

FONTE: [misra], [nureg, 4.1.2.2].

Regra Ptr-2. Só use um ponteiro para acessar elementos de vetor se ele tiver sido declarado como ponteiro para estevetor.

RAZÃO: Evita o acesso indevido a posições de memória que não sejam elementos de vetor.EXEMPLO: No código à esquerda a variável vet não é um ponteiro para vetor. Observe que nos dois códigos épossível chamar a função passando um ponteiro para um inteiro. Entretanto, o código à direita facilita a revisão, poisespecifica mais claramente a intenção do programador.

14

voidfuncao_desconforme(const int *vet){printf("%d\n", *(vet + 3));printf("%d\n", *(vet++));printf("%d\n", vet[2]);

}

voidfuncao_conforme(const int vet[]){printf("%d\n", *(vet + 3));printf("%d\n", *(vet++));printf("%d\n", vet[2]);

}

FONTE: [misra, 2-05-00-16], [nureg].

Regra Ptr-3. Só use aritmética de ponteiros com ponteiros que referenciam vetores ou elementos de vetores.

RAZÃO: A adição e subtração de ponteiros que não apontem para vetores resulta em comportamento indefinido.EXEMPLO: No código à esquerda a variável vet não é um ponteiro para vetor.

voidfuncao_desconforme(const int *vet){printf("%d\n", *(vet + 3));printf("%d\n", *(++vet));

}

voidfuncao_conforme(const int vet[]){printf("%d\n", *(vet + 3));printf("%d\n", *(++vet));

}

FONTE: [misra, 2-05-00-16], [nureg].

Regra Ptr-4. 04. Só realize operações entre ponteiros, aritméticas ou relacionais (<, >, <=, >=), se eles apontarempara o mesmo vetor.

RAZÃO: As operações que envolvem ponteiros que apontam para vetores diferentes resultam em comportamento in-definido.EXEMPLO: Os resultados das operações da função à esquerda são indefinidos.

voidfuncao_desconforme(const int ori[],

const int dst[]){int res;int *p1 = (ori + 1);int *p2 = (dst + 1);res = p1 - dst;res = ori - p2;res = p1 - p2;res = p2 > ori;res = ori >= p2;

}

voidfuncao_conforme(const int ori[],

const int dst[]){int res;int *p1 = (ori + 1);int *p2 = (dst + 1);res = p1 - ori;res = p2 - dst;res = p1 - p1;res = p1 > origem;res = dst >= p2;

}

15

FONTE: [misra, 2-05-00-17], [nureg].

Regra Ptr-5. Use índices em vez de ponteiros para acessar elementos de vetores.

RAZÃO: Favorece o entendimento, facilita a manutenção.EXEMPLO: O código à direita é mais claro que o equivalente à esquerda.

voidfuncao_desconforme(const int vet[]){printf("%d\n", *(vet + 3));printf("%d\n", *(vet + 1));printf("%d\n", *vet);

}

voidfuncao_conforme(const int vet[]){printf("%d\n", vet[3]);printf("%d\n", vet[1]);printf("%d\n", vet[0]);

}

FONTE: [misra, 2-05-00-15], [nureg].

Regra Ptr-6. Use ponteiros ao declarar grandes estruturas como argumentos em chamadas de função.

RAZÃO: Diminui o acoplamento e previne estouro da pilha de chamada (stack overflow).COMENTÁRIO: Em C/C++ vetores (arrays) são sempre passados por referência (usando ponteiros), mas estruturas euniões podem ser passadas por referência ou como valores da pilha de chamada (stack).EXEMPLO: O programa à esquerda terá a pilha de execução estourada bem antes que o da direita.

#include <stdio.h>#include <stdlib.h>

typedef struct grande grande_t;struct grande{long int qtd;long int valores[5000];

};void teste(grande_t);intmain(int nargs, char *args[]){grande_t estrutura;estrutura.qtd = 0;teste(estrutura);return 0;

}voidteste(grande_t estrutura){estrutura.qtd++;printf("%d ", estrutura.qtd);teste(estrutura);

}

#include <stdio.h>#include <stdlib.h>

typedef struct grande grande_t;struct grande{long int qtd;long int valores[5000];

};void teste(grande_t *);intmain(int nargs, char *args[]){grande_t estrutura;estrutura.qtd = 0;teste(&estrutura);return 0;

}voidteste(grande_t *estrutura){estrutura->qtd++;printf("%d ", estrutura->qtd);teste(estrutura);

}

16

FONTE: [misra], [nureg, 4.1.1.3], iso[6.7.5.3].

5 Fluxo de controle

Recomendação Ctr-1. Minimize a complexidade do fluxo de controle.

RAZÃO: Quanto mais complexo é o fluxo de controle, mais difícil é o desenvolvimento e a manutenção.COMENTÁRIO: Em geral deve-se priorizar o entendimento e a legibilidade:

• Use switch em vez de if’s aninhados.

• Use cláusula default no comando switch e cláusula else no comando if.

FONTE: [misra, 2-06-04], [nureg, 4.1.2.2].

Regra Ctr-2. Um programa não deve conter elementos não utilizados, caminhos intransitáveis, código morto ou nãorealizável.

RAZÃO: Estas situações indicam ou um esquecimento por parte do programador ou um erro de lógica.COMENTÁRIO: Um código não realizável nunca será executado, por construção. Um caminho intransitável existesintaticamente mas a semântica da aplicação faz com que nunca seja executado. Um código morto é aquele cujaexecução não afeta o processamento.EXEMPLO:

voidexem(int a){int b = 34; // elemento não usadoswitch (a - a){printf("codigo nao realizavel\n");

case ’1’:printf("opcao 1\n"); // caminho intransitávelbreak;

default:a = a + 2; // código mortoprintf("opcao padrao\n");break;

}return;printf("codigo nao realizavel");

}

FONTE: [misra, 2-00-01], [nureg, 4.1.2.2].

Regra Ctr-3. O valor retornado por uma expressão que não tenha efeitos colaterais, ou por uma função cujo valorde retorno seja void, deve ser utilizado.

RAZÃO: Se um valor não é utilizado sua produção não precisa existir.COMENTÁRIO: No caso de funções, se a chamada é feita por conta dos efeitos colaterais, o correto é codificar essesefeitos em um procedimento (função sem valor de retorno).

17

Pode-se, em alguma situações, descartar explicitamente o valor de retorno, impondo o valor void, como, porexemplo, em (void)funcao(3).FONTE: [misra, 2-00-01-07], [nureg].

Regra Ctr-4. Não use funções com um número variável de argumentos.

RAZÃO: Argumentos variáveis dificultam a manutenção e verificação da função.COMENTÁRIO: Funções com número variável de argumentos são difíceis de serem verificadas. A chamada a funçõescom número variável de argumentos e cujo protótipo não tenha uma lista de parâmetros terminada em reticências,produz um comportamento não especificado.EXEMPLO:

#include <stdio.h>#include <stdarg.h>

void aux(int, char, ...);intmain(int nargs, char *args[]){aux(1, ’x’);aux(5, ’t’, ’e’, ’s’, ’t’, ’e’);

}

voidaux(int a, char b, ...){va_list ptr;va_start(ptr, b);printf("%c", b);while (--a > 0){b = va_arg(ptr, int);printf("%c", b);

}printf("\n");va_end(ptr);

}

FONTE: [misra, 2-08-04-01], [nureg, 4.2.1.5].

Regra Ctr-5. Não use expressões como argumentos em chamadas a funções ou macros.

RAZÃO: O uso de expressões pode produzir comportamento não especificado ou indesejado.COMENTÁRIO: O ordem de avaliação dos argumentos não é determinada. Em particular os operadores unários ++ e-- são problemáticos.EXEMPLO: Os trechos à esquerda podem produzir resultados inesperados.

Versão errada Versão correta

funcao(i, i++); a = i; i++;funcao(a, i);

#define MAX(x, y) ((x > y) ? x : y)...limite = MAX(++i, j);

#define MAX(x, y) ((x > y) ? x : y)...++i;limite = MAX(i, j);

18

FONTE: [misra], [nureg, 4.1.2.5].

6 Erros e exceções

Regra Err-1. Assegure que as expressões aritméticas produzirão os resultados com a acurácia e precisão esperadas.

RAZÃO: Erros de precisão e acurácia devido a conversões de tipos e imprecisão na representação dos valores e opera-ções com comportamento não definidos são comuns.COMENTÁRIO: As operações aritméticas, em particular as de ponto flutuante, são naturalmente imprecisas. Deve-se:

• Usar o tipo double.

• Evitar a comparação de igualdade com valores de tipo flutuante.

• Garantir que o valores resultantes de imposição de tipos podem ser representados no tipo alvo.

• Controlar o truncamento das divisões inteiras.

• Controlar a ordem de precedência usando parêntesis.

• Assegurar que o valor da expressão não depende da ordem de avaliação.

• Separar atribuição e avaliação

FONTE: [misra, 2-00-03, 2-00-04, 2-01-00], [nureg, 4.1.2.7, 4.1.2.8].

Regra Err-2. Verifique o resultado de operações que podem resultar em erros ou exceções.

RAZÃO: Evita a ocorrência de comportamento imprevisível ou inesperado.COMENTÁRIO: Operações de entrada e saída, operações que utilizam ponteiros, operações matemáticas, etc. podemresultar em erros que devem ser verificados.EXEMPLO:

#include <stdio.h>intmain(int nargs, char *args[]){FILE *fp;fp = (FILE *)NULL; /* inicie o ponteiro */fp = fopen("id_arquivo","r"); /* atribua valor */if (fp != (FILE *)NULL) /* verifique validade */{/* ... */

}if (fp != (FILE *)NULL) /* verifique validade */{close(fp);fp = (FILE *)NULL; /* libere o ponteiro */

}return 0;

}

19

FONTE: [misra, 2-00-03], [nureg, 4.2.2.1].

Regra Err-3. Verifique a ocorrência de erros de domínio e de imagem.

RAZÃO: Evita resultados errados.COMENTÁRIO: Os erros de domínio ocorrem quando os dados de entrada estão fora da faixa de representação definidapara a operação. Os erros de imagem ocorrem quando os dados de saída estão fora da faixa de representação dosvalores que podem ser produzidos. As chamadas a funções da biblioteca padrão podem produzir estes tipos de erros,que devem ser verificados através da variável errno.

#include <stdio.h>#include <math.h>#define PI (3.14159)intmain(int nargs, char *args[]){double x = 4, res;res = acos(x);if ((res >= 0) && (res <= PI/2)){printf("quadrante I\n");

}else{printf("quadrantes "

"II, III ou IV\n");}

return 0;}

#include <stdio.h>#include <math.h>#include <errno.h>#define PI (3.14159)intmain(int nargs, char *args[]){double x = 4, res;res = acos(x);if (errno == EDOM){printf("Erro de dominio\n");

}else{if ((res >= 0) && (res <= PI/2)){printf("quadrante I\n");

}else{printf("quadrantes "

"II, III ou IV\n");}

}return 0;

}

OBSERVAÇÕES: O padrão MISRA (2-19-03-01) determina o não uso de errno.FONTE: [misra, 2-00-03, 2-19-03-01], [nureg, 4.2.2.1], [iso, 7.12].

Recomendação Err-4. Use funções-envelope para realizar verificação não realizada pelas funções da bibliotecapadrão.

RAZÃO: Minimiza o risco de erros ou comportamento indefinido.COMENTÁRIO: As funções-envelope (wrapping functions) são uma forma conveniente de encapsular a verificação desituações não detectadas pelas funções da biblioteca padrão.FONTE: [misra, 2-00-03], [nureg, 4.1.1.8].

20

Recomendação Err-5. Teste a validade dos argumentos de entrada ao iniciar e a validade dos argumentos de saídaao finalizar a execução de cada função.

RAZÃO: Evita erros que podem comprometer a integridade do sistema.COMENTÁRIO: A instrumentação assegura que a função receberá e produzirá apenas os valores esperados.FONTE: [misra, 2-00-03, 2-08-04-06], [nureg, 4.1.2.5].

Regra Err-6. Trate erros e exceções localmente.

RAZÃO: O tratamento local de erros facilita a interpretação das causas e a manutenção do programa.COMENTÁRIO: A propagação de erros através de vários níveis de chamadas pode ocultar a natureza da falha, dificul-tando a interpretação do que ocorreu.

É aceitável a existência de um procedimento unificado para tratamento de erros semelhantes que podem ocorrerem vários pontos do programa.FONTE: [misra], [nureg, 4.2.2.1].

Referências

[nureg] Hecht, H., M. Hecht, S. Graff, W. Green, D. Lin, S. Koch, A. Tai and D. Wendelboe. Review Guideli-nes on Software Languages for Use in Nuclear Power Plant Safety Systems, Technical Report, NUREG CR-6463, US Nuclear Regulatory Commission, 1996. (www.nrc.gov/ about-nrc/ regulatory/ research/ digital/ tech-reference.html)

[iso] ISO. C Programming Language Standard, ISO/IEC 9899:1999, Joint Technical Committee ISO/IEC JTC 1,Information technology, Committee Draft, 2005. (www.open-std.org/ jtc1/ sc22/ wg14)

[misra] MISRA. Misra C++: Guidelines for the use of C++ language in critical systems, Draft for comment, TheMotor Industry Software Reliability Association, 2007. (www.misra.org.uk)

[gnu] Stallman, Richard, et al. GNU Coding Standards, GNU Project, Free Software Foundation, October, 2007.(www.gnu.org/ prep/ standards)

21