10 Organização do código fonte
Abelson e Sussman (1996) escreveram uma vez que “programas devem ser escritos para que humanos os leiam e para que, somente como consequência, máquinas os executem”1. Em outras palavras, compiladores não ligam para como os programas são escritos, para os nomes das variáveis nem para o bom ou mau gosto do programador. Programas apenas são compilados e executados.
10.1 Documentação
A primeira etapa para se ter um bom código fonte passa longe do código C em si. Ela consiste em ter, no início do código, um comentário com a descrição do conteúdo do arquivo.
Esse comentário estabelece a documentação do código e deve conter uma descrição de seu propósito e de seu contexto, bem como as condições necessárias para sua utilização e execução. Seguem, a título de exemplo, documentações encontradas no Github2.
Arquivo sysctl.c
3:
/*
* sysctl wrapper for library version of Linux kernel
* Copyright (c) 2015 INRIA, Hajime Tazaki
*
* Author: Mathieu Lacage <mathieu.lacage@gmail.com>
* Hajime Tazaki <tazaki@sfc.wide.ad.jp>
*/
Arquivo ubsan.c
4
// SPDX-License-Identifier: GPL-2.0-only
/*
* UBSAN error reporting functions
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
*/
Arquivo seg6.py
5:
/*
* SR-IPv6 implementation
*
* Author:
* David Lebrun <david.lebrun@uclouvain.be>
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
Arquivo writing_a_custom_training_loop_in_tensorflow.py
6 (em Python):
"""
Title: Writing a training loop from scratch in TensorFlow
Author: [fchollet](https://twitter.com/fchollet)
Date created: 2019/03/01
Last modified: 2023/06/25
Description: Writing low-level training & evaluation loops in TensorFlow.
Accelerator: None
"""
O conteúdo e o nível de detalhe de cada comentário depende do programador, das regras da empresa em que trabalha ou da equipe envolvida, entre outros muitos fatores.
Neste livro, os programas são sempre precedidos por um comentário contendo:
- O propósito do programa;
- As pré e pós-condições a que o código atende.
Segue um exemplo simples desta forma de documentação.
/*
Cálculo de juros compostos
Requer: o capital investido, a taxa de juros (a.m.) e o tempo
de investimento em meses
Assegura: o montante final da transação
*/
Com base nessa descrição, que é relativamente simples, não é necessário olhar os comandos do programa para se saber a que ele se propõe, o que ele requer para ser executado e qual o resultado que apresentará ao final.
10.2 Organização visual
O código fonte de um programa é escrito para que outros programadores (ou seja, pessoas) leiam e entendam os comandos e a intenção do programador.
Segue um programa em C válido. Sua qualidade, entretanto, é discutível e é apenas uma nova versão do código que implementa o Algoritmo 1.1.
/*
Cálculo e apresentação das raízes reais de uma equação de segundo grau na
forma ax^2 + bx + c = 0
Requer: Os coeficientes a, b e c da equação
Assegura: as raízes reais da equação; ou mensagem que a equação é
inválida; ou mensagem que não há raízes reais
*/
#include <stdio.h>
#include <math.h>
int main(void) { char entrada[160];printf(
"Digite os valores de a, b e c da equação: "); fgets(entrada, sizeof
entrada
, stdin); double a, b, c; sscanf(entrada
, "%lf%lf%lf" , & a, &b, &
);
cif(a == 0) printf( "Não é equação do segundo grau (a = 0).\n" )
; else { double discriminante = pow
(
b,
2) - 4 * a * c; if(discriminante <
0) printf("A equação não possui raízes reais.\n");
else
if
(discriminante < 1.0e-10) { double x = -b / (2* a
); printf("Uma raiz: %g.\n", x); } else { double x1 = (-b -
(discriminante)) / (2 * a); double x2 = (-b +
sqrt(discriminante))
sqrt/ (2 * a); printf("Raízes reais: %g e %g.\n", x1
,
);}}return
x20;}
-2 10 13
Digite os valores de a, b e c da equação: Raízes reais: 6.07071 e -1.07071.
Para ilustrar a dificuldade que o código, mesmo correto na sua lógica e sintaxe, proporciona quando é mal escrito, considere o problema de localizar a vírgula na expressão pow(b, 2)
que calcula \(b^2\) para o discriminante.
Se a ordem dos comandos estiver correta, o compilador entende o que tem que ser feito e gera o código executável sem problemas ou dificuldades. Porém, caso o compilador aponte um erro como seguinte, qual seria a dificuldade de encontrar o problema?
main.c: In function ‘main’:
main.c:28:62: warning: unused variable ‘x1’ [-Wunused-variable]
28 | ); printf("Uma raiz: %g.\n", x); } else { double x1 = (-b -
| ^~
main.c:31:59: error: ‘x1’ undeclared (first use in this function);
| did you mean ‘x2’?
31 | / (2 * a); printf("Raízes reais: %g e %g.\n", x1
| ^~
| x2
main.c:31:59: note: each undeclared identifier is reported only once for each
function it appears in
main.c: At top level:
main.c:35:8: error: expected identifier or ‘(’ before ‘return’
35 | x2);}}return
| ^~~~~~
main.c:36:4: error: expected identifier or ‘(’ before ‘}’ token
36 | 0;}
| ^
O erro acima foi causado apenas por um }
inserido no lugar errado. Todas as outras mensagens são decorrentes desta falha na interpretação, a qual impede a sequência da análise e gera uma cascata de erros.
Em suma, os programas são para as pessoas lerem.
No ?sec-guia-de-estilo são apresentadas as orientações de escrita seguidas neste livro, mas as seções seguintes introduzem os pontos iniciais, justificando suas importâncias.
A escrita de um bom programa em C recai, necessariamente, na organização visual do código, ou seja, pela disposição espacial dos comandos, instruções e pontuações ao longo do código.
10.3 Indentação
A indentação é o espaço dado da margem esquerda até o comando e ela serve para indicar a hierarquia dos comandos. Esse recurso visual é essencial para um bom programa e tem sido consistentemente usado em todos os exemplos de programas dados.
Segue o exemplo simples de um programa que faz conversões de unidades de distância.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanfdouble em_centimetros = em_metros * 100;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printfdouble em_pes = em_metros * 3.281;
("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printfdouble em_jardas = em_metros * 1.094;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printfdouble em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printfreturn 0;
}
O código acima é apresentando sem indentações e isso dificulta enxergar os comandos e até a abrangência do main
. O acréscimo da indentação indica um nível para os comandos contidos no bloco da função principal e isso leva à versão seguinte do programa.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanfdouble em_centimetros = em_metros * 100;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printfdouble em_pes = em_metros * 3.281;
("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printfdouble em_jardas = em_metros * 1.094;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printfdouble em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printfreturn 0;
}
A indentação tem que ser constante para um mesmo nível de comandos, o que leva a que comandos de mesmo nível sempre se iniciem na mesma coluna.
No exemplo seguinte há problemas com a declaração da variável entrada
, que deveria estar indentada, e também com o conjunto de comandos que realizam as conversões. Neste último caso, estes comandos parecem estar de alguma forma “subordinados” aos comandos anteriores, o que não o caso.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanfdouble em_centimetros = em_metros * 100;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printfdouble em_pes = em_metros * 3.281;
("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printfdouble em_jardas = em_metros * 1.094;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printfdouble em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printfreturn 0;
}
Segue, por fim, um último péssimo exemplo de indentação, no qual transparece o desleixo do programador.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanfdouble em_centimetros = em_metros * 100;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printfdouble em_pes = em_metros * 3.281;
("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printfdouble em_jardas = em_metros * 1.094;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printfdouble em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printfreturn 0;
}
10.4 Linhas de espaçamento
Outro ponto que auxilia uma melhor visualização do código é o uso de linhas em branco, cuja função é separar as diferentes tarefas que o programa tem que cumprir.
É apresentado na sequência o programa de conversão de unidades de distância com o acréscimo de linhas em branco.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanf
double em_centimetros = em_metros * 100;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printf
double em_pes = em_metros * 3.281;
("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printf
double em_jardas = em_metros * 1.094;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printf
double em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printf
return 0;
}
Uma linha em branco deve ser incluída para separar as linhas com #include
do início da função main
, o que dá destaque a esta função.
Foram introduzidas linhas em branco dentro do main
para isolar, primeiramente, os comandos que fazem a leitura da distância em metros, incluindo mensagem, leitura, declaração da variável e conversão para double
. Todos estes comandos são, em essência, “obtenha a distância em metros”.
Outros blocos que são agrupados são os de cada uma das conversões, com declaração de variável e a escrita do resultado.
Por último aparece o indicador de sucesso da execução: return 0
, também destacado dos demais comandos.
As linhas em branco devem ser, via de regra, apenas uma, devendo ser evitadas duas ou mais linhas separando os grupos de comandos.
O agrupamento de comandos depende do programador, de forma que o mesmo código possa ter sua organização conforme o exemplo seguinte.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanf
double em_centimetros = em_metros * 100;
double em_pes = em_metros * 3.281;
double em_jardas = em_metros * 1.094;
double em_polegadas = em_metros * 39.37;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printf("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printf("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printf("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printf
return 0;
}
Neste caso, o agrupamento se refere à leitura, seguida pelos cálculos e finalizada pelas apresentações dos resultados.
10.5 Um comando por linha
Uma regra básica de clareza é separar os comandos de forma que cada um fique em sua própria linha.
Como exemplo, o programa de conversão de distâncias foi reescrito de forma a violar essa regra e é apresentado na sequência.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite uma distância em metros: "); fgets(entrada,
printfsizeof entrada, stdin);
double em_metros; sscanf(entrada, "%lf", &em_metros);
double em_centimetros = em_metros * 100; double em_pes = em_metros *
3.281; double em_jardas = em_metros * 1.094; double em_polegadas
= em_metros * 39.37;
("> %.1lfm = %.1fcm\n> %.1lfm = %.1f pés\n", em_metros,
printf, em_metros, em_pes);
em_centimetros("> %.1lfm = %.1f jardas\n", em_metros, em_jardas); printf(
printf"> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
return 0;
}
Essa versão coloca alguns comandos e declarações individuas em uma mesma linha, o que está sintaticamente correto, mas dificulta a visão dos limites entre os diversos elementos.
É importante lembrar que quando os problemas se tornam mais complexos, o código será mais longo e com mais nuances, assim como terá maior número de variáveis. Nesta situação, mais preciosa essa recomendação se torna.
10.6 Padronização de identificadores
Os identificadores usados para as variáveis e demais elementos da linguagem devem ser significativos. Além disso, também devem seguir um mesmo padrão ao longo do código.
O entendimento de um programa pode ser prejudicado se houver uma variável chamada salario_inicial
e o salário final for indicado por outra com nome s_final
. O uso de salario_final
é, sem dúvidas, mais consistente.
Da mesma forma, em um mesmo programa há variáveis como taxa_juros
, montante_final
e saldo_atual
juntamente com outras como tx_br
, tx_liq
, sf
e p
, por exemplo. As abreviações excessivas não estabelecem um padrão minimamente compatível em contraposição aos nomes mais explícitos.
Nada disso implica em que não possa haver abreviações, desde que atendem à legibilidade. Se em um aplicação há variáveis para diversas taxas diferentes, o uso de tx_inicial
e tx_inercial
são razoáveis para o contexto. Porém todas devem prefixadas com tx_
, sem misturas o prefixo taxas_
.
10.7 Localização da declaração de variáveis
Embora uma variável possa ser declarada em inúmeras localizações dentro de um código fonte e ainda mantê-lo correto e coerente, é preciso reforçar que o local em que as declarações ocorrem favorecem o entendimento do propósito do programa e de sua lógica. Este tema é abordado de maneira rápida na Seção 9.1.
Por hábitos “ancestrais”, de quando a linguagem C não admitia a mistura entre declarações e código, ainda persiste a prática de declarar todas as variáveis juntas no início do bloco. Assim, nas versões iniciais da linguagem os programas deveriam necessariamente declarar todas suas variáveis antes da primeira linha que tivesse um comando ou chamada de função. Segue um exemplo.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
double em_metros, em_centimetros, em_pes, em_jardas, em_polegadas;
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgets(entrada, "%lf", &em_metros);
sscanf
= em_metros * 100;
em_centimetros = em_metros * 3.281;
em_pes = em_metros * 1.094;
em_jardas = em_metros * 39.37;
em_polegadas
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printf("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printf("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printf("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printf
return 0;
}
1.0
Digite uma distância em metros:
> 1.0m = 100.0cm
> 1.0m = 3.3 pés
> 1.0m = 1.1 jardas > 1.0m = 39.4 polegadas
O impacto para programas simples não é, em geral, grande. À medida que o número de linhas cresce, juntamente à complexidade da solução implementada, o “amontoado” de declarações no começo do programa prejudica a legibilidade. Muitas vezes, por exemplo, é preciso verificar se uma variável tem o tipo necessário e fazer as adequações pode trazer impactos não previstos. Por exemplo, ao alterar uma declaração de int
para long int
pode afetar não somente a variável de interesse, mas outras declaradas juntas.
10.8 Alinhamento dos finais de bloco
As chaves da função main
estabelecem o bloco da função principal e inclui as declarações e os comandos que formam o programa. Um bloco de comandos, na forma de um comando composto, é muitas vezes necessário para incluir mais que um comando dentro de um condicional, por exemplo.
Não é incomum que, em programas em C, venha uma sequência de vários fechamentos de bloco, ou seja, vários }
. Segue um programa que calcula as raízes de uma equação \({ax^2 + bx + c = 0}\) (\({\forall a, b, c}\)), mesmo que a equação se reduza a forma linear.
/*
Determinação das raízes reais de uma equação ax^2 + bx + c = 0, sem restrições
aos coeficientes a, b, ou c
Pré-condições: Os valores dos coeficientes a, b e C
Pós-condições: uma ou duas raízes reais ou mensagem indicando que não há
raízes reais
Quando a é zero, a equação é tratada como linear; caso contrário, é
quadrática. Divisões por zero são tratadas pela divisão de double, ou
seja, podendo resultar em 'not a number' ou +-inf.
*/
#include <stdio.h>
#include <math.h>
int main(void) {
char entrada[160];
("Considere uma equação ax^2 + bx + c = 0.\n"
printf"Digite os valores a, b e c da equação: ");
(entrada, sizeof entrada, stdin);
fgetsdouble a, b, c;
(entrada, "%lf%lf%lf", &a, &b, &c);
sscanf
if (a == 0) {
// Equação do primeiro grau
double x = -c / b;
("Raiz: %g.\n", x);
printf}
else {
// Equação do segundo grau
double discriminante = pow(b, 2) - 4 * a * c;
// Determina o número de raízes reais
if (discriminante < 0)
("Não há raízes reais.\n");
printfelse if (discriminante < 1e-5) { // quase zero
// Raíz única
double x = -b / (2 * a);
("Raíz: %g.\n", x);
printf}
else {
// Duas raízes distintas
double x1 = (-b - sqrt(discriminante)) / (2 * a);
double x2 = (-b + sqrt(discriminante)) / (2 * a);
if (x2 < x1) { // ordem não decrescente
double temporario = x1;
= x2;
x1 = temporario;
x2 }
("Raízes: %g e %g.\n", x1, x2);
printf}
}
return 0;
}
Considere uma equação ax^2 + bx + c = 0.-6 5 28
Digite os valores a, b e c da equação: Raízes: -1.7834 e 2.61673.
É necessário que todos os fechamentos de bloco sejam, de alguma forma, alinhados a sua abertura. Esse alinhamento é essencial para localizar, por exemplo, um }
que foi esquecido.
Nos programas apresentados neste livro, os blocos de comandos compostos são sempre iniciados na linha da estrutura que o utiliza, como um if
, por exemplo.
if(a > 0) {
// Instruções...
}
else {
// Outras instruções...
}
Desta forma, o fim do bloco fica com a mesma indentação da estrutura que o iniciou.
Por questões de estilo (?sec-guia-de-estilo), há programadores que preferem que as chaves sejam abertas na linha consecutiva. Isso naturalmente não é um problema, desde que o padrão seja mantido para todo o código. Na sequência são apresentadas variações do posicionamento das chaves, porém em todas o alinhamento de indentação é mantido.
if(a > 0)
{
// Instruções...
}
else
{
// Outras instruções...
}
if(a > 0) {
// Instruções...
} else {
// Outras instruções...
}
Como contra exemplo, na sequência é apresentado o programa anterior usando uma “indentação alternativa”, que alinha o }
com seu respectivo }
, qualquer que seja sua posição. É preciso observer que o resultado quebra a visualização do código e dificulta exatamente saber onde termina cada bloco.
/*
Determinação das raízes reais de uma equação ax^2 + bx + c = 0, sem restrições
aos coeficientes a, b, ou c
Pré-condições: Os valores dos coeficientes a, b e C
Pós-condições: uma ou duas raízes reais ou mensagem indicando que não há
raízes reais
Quando a é zero, a equação é tratada como linear; caso contrário, é
quadrática. Divisões por zero são tratadas pela divisão de double, ou
seja, podendo resultar em 'not a number' ou +-inf.
*/
#include <stdio.h>
#include <math.h>
int main(void) {
char entrada[160];
("Considere uma equação ax^2 + bx + c = 0.\n"
printf"Digite os valores a, b e c da equação: ");
(entrada, sizeof entrada, stdin);
fgetsdouble a, b, c;
(entrada, "%lf%lf%lf", &a, &b, &c);
sscanf
if (a == 0) {
// Equação do primeiro grau
double x = -c / b;
("Raiz: %g.\n", x);
printf}
else {
// Equação do segundo grau
double discriminante = pow(b, 2) - 4 * a * c;
// Determina o número de raízes reais
if (discriminante < 0)
("Não há raízes reais.\n");
printfelse if (discriminante < 1e-5) { // quase zero
// Raíz única
double x = -b / (2 * a);
("Raíz: %g.\n", x);
printf}
else {
// Duas raízes distintas
double x1 = (-b - sqrt(discriminante)) / (2 * a);
double x2 = (-b + sqrt(discriminante)) / (2 * a);
if (x2 < x1) { // ordem não decrescente
double temporario = x1;
= x2;
x1 = temporario;
x2 }
("Raízes: %g e %g.\n", x1, x2);
printf}
}
return 0;
}
Considere uma equação ax^2 + bx + c = 0.-6 5 28
Digite os valores a, b e c da equação: Raízes: -1.7834 e 2.61673.
A maioria dos editores de programas atualmente já provê, ao digitar, uma alinhamento adequado das chaves, o que facilita a digitação. É preciso cuidar para que a própria edição do código não acabe com o alinhamento inadvertidamente.
O auto-formatador é um recurso muito comum aos IDEs de programação. O uso desse recurso pode ser amplamente empregado!
10.9 Comentários
Comentários devem ser esparsos e minimalistas no programa. Um programa que precisa de muitos comentário é porque o código não está bem escrito. A versão seguinte do programa do peso ideal inclui apenas dois comentários considerados pertinentes. Eles apenas guiam o humano que lê o código para que localize facilmente o significado dos dois cálculos.
/*
Cálculo estimado da massa ideal de uma pessoa baseada em seu sexo biológico
e sua altura
Requer: o sexo biológico (masculino ou feminino) e a altura em metros
Assegura: a massa ideal da pessoa apresentada (kg)
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite o sexo (M ou F) e a altura em quilogramas: ");
printf(entrada, sizeof entrada, stdin);
fgetschar sexo;
double altura;
(entrada, "%c%lf", &sexo, &altura);
sscanf
double massa_ideal;
if(sexo == 'F')
= 62.1 * altura - 44.7; // feminino
massa_ideal else
= 72.7 * altura - 58; // masculino
massa_ideal
("Massa ideal: %.1fkg.", massa_ideal);
printf
return 0;
}
Embora a comparação sexo == 'F'
seja suficiente, não custa nem atrapalha indicar a diferença entre as duas fórmulas.
Em nível próximo ao exagero, considerando-se um programa curto como esse, ainda é possível ter comentários indicando a razão dos agrupamentos de comandos.
/*
Cálculo estimado da massa ideal de uma pessoa baseada em seu sexo biológico
e sua altura
Requer: o sexo biológico (masculino ou feminino) e a altura em metros
Assegura: a massa ideal da pessoa apresentada (kg)
*/
#include <stdio.h>
int main(void) {
char entrada[160];
// Obtenção de sexo biológico e altura
("Digite o sexo (M ou F) e a altura em quilogramas: ");
printf(entrada, sizeof entrada, stdin);
fgetschar sexo;
double altura;
(entrada, "%c%lf", &sexo, &altura);
sscanf
// Cálculo da massa ideal
double massa_ideal;
if(sexo == 'F')
= 62.1 * altura - 44.7; // feminino
massa_ideal else
= 72.7 * altura - 58; // masculino
massa_ideal
// Apresentação do resultado
("Massa ideal: %.1fkg.", massa_ideal);
printf
return 0;
}
É importante não “supercomentar” um programa. O excesso de comentários tende a tornar o código ilegível.
O exemplo seguinte pertence à classe “não faça assim”.
/*
Cálculo estimado da massa ideal de uma pessoa baseada em seu sexo biológico
e sua altura
Requer: o sexo biológico (masculino ou feminino) e a altura em metros
Assegura: a massa ideal da pessoa apresentada (kg)
*/
// Inclusão dos arquivos de cabeçalho
#include <stdio.h> // funções de entrada e saída
// Programa principal
int main(void) {
// Variável para a leitura da linha
char entrada[160];
// Obtenção de sexo biológico e altura
("Digite o sexo (M ou F) e a altura em quilogramas: ");
printf(entrada, sizeof entrada, stdin); // leitura da linha digitada
fgets// Declaração das variáveis de entrada
char sexo; // para o sexo biológico
double altura; // para a altura da pessoa
// Conversão da linha para obtenção dos valores
(entrada, "%c%lf", &sexo, &altura);
sscanf
// Declaração da massa ideal
double massa_ideal;
// Verificação do cálculo conforme feminino ou masculino
// (se o sexo for diferente de 'F', assume que seja 'M')
if(sexo == 'F')
// Realização do cálculo para o sexo feminino
= 62.1 * altura - 44.7;
massa_ideal else
// Realização do cálculo para o sexto masculino
= 72.7 * altura - 58;
massa_ideal
// Apresentação do resultado
("Massa ideal: %.1fkg.", massa_ideal); // massa ideal em kg
printf
// Indicação de término do programa com sucesso
return 0;
}
Nesta versão, há comentários óbvios (como em double altura; // para a altura da pessoa
) e até a visualização da estrutura do if
, que é simples, fica um tanto comprometida com o excesso de informações.
10.10 Organização consistente do código
A organização do código deve ser consistente. As conversões de distância podem usar um programa correto, porém inconsistente quanto a sua organização. Essa é uma situação que deve ser firmemente evitada.
/*
Conversões de distância de metros para centímetros, pés, jardas e polegadas
Requer: uma distância em metros
Assegura: O valor equivalente em centímetros, pés, jardas e polegadas
*/
#include <stdio.h>
int main(void) {
char entrada[160];
double em_centimetros;
("Digite uma distância em metros: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble em_metros;
(entrada, "%lf", &em_metros);
sscanf
= em_metros * 100;
em_centimetros double em_pes = em_metros * 3.281;
("> %.1lfm = %.1fcm\n", em_metros, em_centimetros);
printf("> %.1lfm = %.1f pés\n", em_metros, em_pes);
printf
double em_jardas = em_metros * 1.094, em_polegadas;
("> %.1lfm = %.1f jardas\n", em_metros, em_jardas);
printf
= em_metros * 39.37;
em_polegadas ("> %.1lfm = %.1f polegadas\n", em_metros, em_polegadas);
printf
return 0;
}
10
Digite uma distância em metros:
> 10.0m = 1000.0cm
> 10.0m = 32.8 pés
> 10.0m = 10.9 jardas > 10.0m = 393.7 polegadas
Neste programa pode ser até complicado localizar em que momento houve a declaração da variável em_polegadas
, por exemplo.
Tradução livre de “programs must be written for people to read, and only incidentally for machines to execute”.↩︎
Github: https://github.com.↩︎
https://github.com/libos-nuse/net-next-nuse/blob/46e2206969943ba3fb87441dee0b433624daf35c/arch/lib/sysctl.c.↩︎
https://github.com/KSPP/linux/blob/e0756cfc7d7cd08c98a53b6009c091a3f6a50be6/lib/ubsan.c.↩︎
https://github.com/linux-wpan/linux-wpan-next/blob/107bc0aa95ca572df42da43c30a2079266e992e4/net/ipv6/seg6.c.↩︎
https://github.com/keras-team/keras/blob/419973ee15ecd0e2d085e077399ce3bd5437df15/guides/writing_a_custom_training_loop_in_tensorflow.py.↩︎