21. Guia d'estil de programació en C
1. Introducció
L'objectiu d'aquest mòdul és fixar un conjunt de pautes de programació en C per a l'assignatura Fonaments de Programació. Cal destacar que actualment no existeix un "estàndard" d'estil de programació en C. Malgrat tot, considerem que és necessari establir unes normes comunes ja que aquestes:
- Ajudaran als estudiants a adquirir un bon estil de programació, evitant d'aquesta manera desenvolupar mals hàbits.
- Reduiran el nombre d’errors. Cal recordar que és preferible detectar errors en temps de compilació/link-edició, que no en temps d’execució.
- Generaran un codi més llegible, tant pel propi autor del codi com per a la resta de persones que no siguin les autores (per exemple, els professors).
És important aclarir que és possible que els professors de l'assignatura tinguin adoptades unes normes de programació lleugerament diferents (en part o en la seva totalitat) a les presentades en aquest document. Això es pot apreciar a les solucions propostes d'exercicis d’examen o de pràctiques. En qualsevol cas, a efectes de correcció, les normes que es tindran en compte seran les d'aquest mòdul.
2. Consideracions generals
2.1 Fitxers font
Els programes escrits en C tenen extensió .c.
2.2 Estructura
Un fitxer de codi C segueix la següent estructura general:
- Capçalera. Tots els fitxers han de començar amb un comentari que contingui: el nom del fitxer, l'autor, la data de creació i una breu descripció del contingut.
- Includes. Fitxers de capçalera (.h) precedits per la directiva #include. Primer es situen els del sistema i després els propis de l’aplicació (si n’hi ha).
- Definició de constants simbòliques (#define) i de tipus definits per l’usuari (typedef).
- Predeclaració. Definició de prototipus d’accions i funcions locals.
- Main. En cas d’existir, la implementació de la funció main.
- Implementació d’accions/funcions. Dins de qualsevol funció/acció (inclosa la funció main), en primer lloc es declaren les variables locals i, a continuació, el cos de l’acció/funció.
[Exemple 21_01]
* File: Example.c
* Author: Nom Cognom
* Date: 18-06-2018
* Description: Salutació universal
*/
/* System header files */
#include <stdio.h>
/* Application header files */
#include "example.h"
/* Symbolic constants */
#define MAX 10
/* User defined types */
/* Predeclaration of actions and functions */
/* Main function */
int main() {
printf("\n Hello world!");
return 0;
}
/* Implementation of actions and functions */
3. Definició de constants
En general, els valors numèrics no s'han d'utilitzar directament al codi, ja que donen lloc al que es coneix com "números màgics". Valors com 3.14159 o 42 no són auto-descriptius i, a més, són difícils de mantenir. Per aquest motiu, és més adequat utilitzar constants amb noms simbòlics.
Per a la definició de les constants, habitualment utilitzarem define en lloc de const.
Amb const s'emmagatzema el valor de la constant en un espai de memòria, i permet al compilador realitzar una comprovació de tipus minimitzant la possibilitat d’error. Amb define en canvi, el preprocessador realitza una substitució literal del nom de la constant pel valor assignat, i ens permet una major versatilitat a l'hora d'utilitzar-se amb determinades estructures com són les taules.
[Exemple 21_02]
#define PI 3.14159
#define ANSWER 42
/* Declarant constants amb const*/
const float PI = 3.14159;
const int ANSWER = 42;
/* Correcte : utilitzar constants */
if (n > PI/2) {
printf("\n Correct");
}
/* A evitar : utilitzar valors constants */
if (n > 3.14159/2) {
printf("\n Correct");
}
4. Noms
Per tal d’identificar els elements del codi (constants, tipus, funcions, accions i variables) es recomana escollir noms que siguin auto-descriptius. En el cas de les variables, els noms curts augmenten la seva llegibilitat i facilitat d’ús.
En general, es recomana seguir les següents convencions:
- Els noms de les constants s’escriuen exclusivament amb lletres majúscules. Si el nom està format per més d’una paraula, aquestes es separen utilitzant el símbol "_".
[Exemple 21_03]
const int MAX = 1000;
- Els noms dels tipus definits per l’usuari comencen habitualment amb la lletra "t", i van seguits immediatament de les paraules que descriuen el tipus. Si el nom està format per més d’una paraula, la primera lletra de cadascuna de les paraules s’escriu en majúscules, la resta en minúscules i no es deixa cap separació entre elles.
[Exemple 21_04]
typedef struct {
int idSubject;
int numberOfStudents;
int room;
} tSubject;
typedef tSubject tSubjectsSet [MAX_SUBJECTS];
- Els noms de funcions, accions i variables s’escriuen amb lletres minúscules. De la mateixa manera que els tipus definits per l’usuari, si el nom està format per més d’una paraula, la primera lletra de cadascuna de les paraules s’escriu en majúscules, la resta en minúscules i no es deixa cap separació entre elles.
[Exemple 21_05]
Es recomana evitar l’ús de noms negats per a les variables de tipus lògic (boolean).
[Exemple 21_06]
/* Correcte : */
bool found = false;
/* A evitar : */
bool notFound = true;
Per a les funcions o accions s’aconsella seguir les següents convencions:
- Incluen verbs en forma imperativa.
[Exemple 21_07]
void deleteRepeatedElements(tVector v);
- Les funcions de tipus lògic (boolean) comencen habitualment per "is".
[Exemple 21_08]
bool isOdd(int n);
bool isEmpty(tVector v);
/* També existeixen altres possibilitats : */
bool belongs(int a, tVector v);
5. Consideracions del codi
A continuació es descriuen les normes i recomanacions per organitzar correctament el codi:
- Cada línia de codi conté una única instrucció bàsica:
[Exemple 21_09]
i = 0;
j = 1;
/* A evitar : */
i = 0; j = 1;
- Si alguna de les tres parts de la construcció d’un for està buida, o si és necessari modificar la variable de control d’un for dins el cos del bucle, aleshores és millor utilitzar un while.
[Exemple 21_10]
/* Returns the first position of v that contains x or -1 if v does not contain x */
/* Correcte : */
int searchPosition(int x, tVector v) {
bool found = false;
int i = 0;
int position = 0;
while (!found && i < MAX) {
if (v[i] == x) {
found = true;
} else {
i__;
}
}
if (found) {
position = i;
} else {
position = -1;
}
return position;
}
/* A evitar : */
int searchPosition(int x, tVector v) {
bool found = false;
int i = 0;
position = 0;
for (; (!found && i < MAX); ) {
if (v[i] == x) {
found = true;
} else {
i__;
}
}
if (found) {
position = i;
} else {
position = -1;
}
return position;
}
- Evitar escriure codis de funcions o accions molt llargs. En cas d'obtenir un codi molt llarg, es recomana crear una nova acció o funció per delegar part del codi, augmentant d'aquesta manera la seva llegibilitat. D'altra banda, es recomana evitar moltes crides niuades.
- Evitar comparacions d'igualtat o desigualtat entre nombres reals, especialment si aquests són resultat d'una sèrie d'operacions. Això és degut a que petits errors d'arrodoniment podrien fer fallar el programa.
- Una variable de tipus lògic, no s'ha de comparar amb un true o false directament.
[Exemple 21_11]
/* Correcte : */
if (found) {
/* ... */
}
if (!found) {
/* ... */
}
/* A evitar : */
if (found == true) {
/* ... */
}
if (!found == true) {
/* ... */
}
- En aquesta assignatura, considerem que les variables globals estan prohibides.
- Els paràmetres d'una acció s'han de col·locar en el següent ordre: entrada, entrada/sortida, sortida.
6. Línies en blanc
És convenient separar amb línies en blanc les diferents parts (fitxers de capçalera, constants, tipus, accions i funcions) d'un fitxer C (veure Exemple 21_01). Dins de cada part és convenient separar cada element amb el mateix número de línies en blanc, excepte els tipus definits per l'usuari, que s'escriuen sense separació (veure Exemple global, apartat 12).
En trossos de codi llarg, es recomana col·locar una línia en blanc que separi blocs conceptuals.
7. Indentació
S'aconsella indentar amb quatre espais per nivell. Evitar l'ús de tabuladors, configurar l'editor per a desactivar-los (la "desactivació" dels tabuladors consisteix en realitat a inserir espais en el seu lloc).
Si un tros de codi té masses indentacions, és aconsellable utilitzar una funció/acció auxiliar.
8. Espais
Com a norma general, s'aconsella deixar un espai de separació entre paraules clau, instruccions, literals, etc. Les excepcions importants són:
- Els punts i coma han d'estar enganxats al que continguin a la part esquerra (veure Exemple 21_12).
- La part interna d'un parèntesi no es separa d'allò que contingui (veure Exemple 21_13).
- El parèntesi esquerre d'una funció/acció s'escriu enganxat al seu nom (veure Exemple 21_13).
- Les claus per accedir a una posició d'un vector o string no contenen espais (veure Exemple 21_13).
- Els operadors binaris *, /, %, - (canvi de signe) s'escriuen sense espais per emfatitzar la seva precedència.
[Exemple 21_12]
int a;
a = x _ y*z;
/* A evitar : */
a = x _ y * z;
/* A evitar : */
a = x_y*z;
/* Correcte : */
a = -b;
/* A evitar : */
a = - b;
- Els operadors __ (increment) i –- (decrement) no contenen espais.
- El caràcter & (per indicar el pas per referència) s'escriu immediatament a la dreta del tipus al qual està associat (veure Exemple global, apartat 12).
[Exemple 21_13]
int anotherFunction(tVector v) {
int i;
int j = 0;
int a;
for (i = MAX - 5; i >= -4; i--) {
a = i*i _ factorial(a) _ v[j];
j__;
}
return a;
}
9. Claus
La varietat de maneres en què es poden col·locar les claus és molt diversa. En aquesta assignatura es recomana la següent forma:
[Exemple 21_14]
/* ... */
}
while (condition2) {
/* ... */
}
for (inicialization; condition; "step") {
/* ... */
}
Tal com es pot observar, no es col·loca res a la dreta d’una clau, ja sigui la que obra com la que tanca. En el cas de if’s niats, on les condicions són excloents entre sí (és a dir, que serà necessari avaluar-les una rere l’altra (estructura del tipus: if else if else if ... )) es recomana escriure-ho de la següent manera:
[Exemple 21_15]
/* ... */
} else {
if (condition2) {
/* ... */
} else {
if (condition3) {
/* ... */
} else {
/* ... */
}
}
}
[Exemple 21_16]
/* ... */
/* c corresponds to each char of the input */
if (c >= 'a' && c <= 'z') {
printf("\n lower case letter");
} else {
if (c >= 'A' && c <= 'Z') {
printf("\n capital letter");
} else {
if (c >= '0' && c <= '9') {
printf("\n digit");
} else {
printf("\n unknown");
}
}
}
10. Parèntesis
Tal com s'ha comentat anteriorment:
- Les paraules clau es separen dels parèntesis.
[Exemple 21_17]
/* ... */
}
- La part interna d'un parèntesis no es separa d'allò que conté.
[Exemple 21_18]
/* ... */
}
- El parèntesi esquerra d'una funció o acció s'escriu a continuació del seu nom.
[Exemple 21_19]
/* ... */
}
- No es col·loquen parèntesis al voltant del que retorna una funció.
[Exemple 21_20]
return found;
/* A evitar : */
return (found);
11. Comentaris
És necessari documentar el codi en la mesura justa. En concret, és necessari explicar en poques línies què fa cada funció/acció. En els casos en què això no sigui trivial, s'aconsella agregar un breu comentari explicatiu.
[Exemple 21_21]
/* Return n! */
int factorial(int n) {
int result = 0
if (n == 0) {
result = 1;
} else {
result = n*factorial(n - 1);
}
return result;
}
Col·locar comentaris redundants pot ser tant perniciós com ometre comentaris importants, tal com es mostra en el següent exemple.
[Exemple 21_22]
i__; /* Increments the value of i in one unit */
12. Exemple global
A continuació es mostra un exemple que il·lustra gran part de les normes vistes al llarg d'aquest document:
[Exemple 21_23]
** File: Example.c
** Author: Professorat FP
** Date: 01-08-2009
** Description: Exemple global
*/
/* System header files */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* Symbolic constants */
#define END_SEQ -1
/* User defined types */
typedef int tNatural;
/* Predeclaration of actions and functions */
bool isEvenNumber(tNatural num);
/* Main function */
int main() {
tNatural num;
tNatural sum = 0;
/* Read natural number */
scanf("%d", &num);
while (num != END_SEQ) {
if (isEvenNumber(num)) {
sum = sum _ num;
}
scanf("%d", &num);
}
printf("%d ", sum);
return 0;
}
/* Implementation of actions and functions */
bool isEvenNumber(tNatural num) {
bool isEven;
if (num%2 == 0) {
isEven = true;
} else {
isEven = false;
}
return isEven;
}
13. Referències
- Consejos y normas de estilo para programar en C
https://pascua.iit.comillas.edu/palacios/cursoc/estiloC.pdf
- Kernighan, B., Ritchie, D. The C programming Language.
https://hikage.freeshell.org/books/theCprogrammingLanguage.pdf
- Mañas, José A. Estilo de programación
https://web.dit.upm.es/~pepe/doc/adsw/base/doc/estilo.htm
- Roura, S. Normes de programació de P1 (2007).
http://www.lsi.upc.edu/~prap/prap/normesP1.pdf