Instituto de Ingeniería Eléctrica, Facultad de Ingeniería.
Universidad Mayor de la República. Montevideo, URUGUAY.
Abril, 1998.
Este documento define un estilo de codificación en el lenguaje de programación C a través de un conjunto de normas y recomendaciones. Se pretende que este estilo ayude a construir programas correctos, entendibles y fáciles de mantener.
En este documento, una "norma" es una regla que debe cumplirse obligatoriamente; una "recomendación" es una regla válida en general, pero que puede admitir o requerir excepciones.
Este documento trata exclisivamente del estilo de programación en lenguaje C en cuanto a codificación. No trata cuestiones de diseño, organización funcional ni otras que hacen a la buena construcción de los programas.
Se usarán las siguientes convenciones de nombres de archivos:
Norma: Todos los archivos tendrán leyenda de derechos de autor.
Todos los archivos de código fuente tendrán la siguiene
leyenda de derechos de autor:
/* Copyright, 1998, Instituto de Ingenieria Electrica */
Recomendación: una función no ocupará más
de 2 páginas.
Cuando una función ocupe más de dos páginas, debe
considerarse su separación en varias funciones, o colocar las subfunciones
en módulos separados. Funciones cortas y relacionadas pueden estar
en un mismo archivo.
Norma: largo de línea <= 80 caracteres.
Las líneas de código no deben exceder de 80 caracteres.
Las líneas largas deben ser separadas en varios renglones, de modo
que al imprimir el código resulte legible. Se recomienda que las
líneas de continuación sean indentadas. El largo de línea
incluye espacios al principio y comentarios en la misma línea del
código.
Norma: Orden de las secciones.
El orden de las secciones de un programa es el siguiente:
Prólogo.
Debe indicar lo que hay en el archivo: propósito de los objetos
contenidos, sean éstos funciones, declaraciones de datos externos,
definiciones u otros. La descripción debe ser breve y específica;
no debe tomar más trabalo leerla que la revisión del código.
Inclusiones.
Archivos de encabezados incluídos. Si la inclusión no
es obvia, debe indicarse la razón con un comentario. Los archivos
de inclusión del sistema, como stdio.h, deben preceder los archivos
de inclusión del usuario.
Definiciones.
Todas las definiciones que afectan todo el archivo.
Definiciones globales.
Declaraciones de datos globales (externos).
Funciones.
Las funciones se colocan al final en el orden más significativo
posible.
Recomendación: organización de archivos de encabezado.
Todas las funciones en un mismo archivo de encabezado deben estar relacionadas
a una misma función general. Dentro de un archivo de encabezado,
la funciones que realicen tareas relacionadas deben estar agrupadas en
una misma sección.
Recomendación: nombres de los archivos de encabezado.
Evitar nombres de archivos privados de encabezado que coincidan con
nombres de archivos públicos de encabezado. No usar nombes absolutos
para archivos de encabezado; colocarlos en su ubicación estándar
o en el directorio corriente. La opción de compilación relativa
a inclusión de bibliotecas ("include-path") es la mejor
forma de manejar bibliotecas privadas extensas.
Norma: archivo de encabezado en el archivo de definición de
la función.
Los archivos de encabezado que declaran funciones o variables externas
deben incluírse en el archivo que define la función o la
variable para permitir verificación de tipos.
Recomendación: no anidar archivos de encabezado.
Los archivos de encabezado no deben anidarse. El prólogo de
cada archivo de encabezado debe describir que otros archivos de encabezado
deben incluírse para que el actual archivode encabezado funcione.
En casos extremos pueden incluírse todas las sentencias de inclusión
comunes en un nuevo archivo de inclusión.
Norma: nombres de variables.
Todas las variables deben comenzar con letra minúscula. En los
nombres compuestos por varias palabras, cada palabra excepto la primera
deben comenzar com nayúscula, así: una_Variable.
Los nombres de punteros deben empezar con "p_", así: p_UnaVariable.
Los nombres de variables globales deben comenzar por "g_", así:
g_UnaVariable.
Los nombres definidos deben ir en mayúsculas separados por subrraya,
así:
#define DIAS_SEMANA 7
Recomendación: no usar variables globales.
Se recomienda enfáticamente no usar variables globales. Excepcionalmente,
se admite el uso de variables globales para descargar las invocaciones
a funciones. Todo uso de variable global dentro de una función debe
ser indicado en el prólogo.
Norma: variables globales declaradas al principio del archivo.
Las variables globales deben declararse al principio del archivo, antes
de las declaraciones de funciones. Las variables que son globales sólo
para las funciones de un mismo archivo deben ser declaradas estáticas.
Norma: variables globales al principio de la función.
Las variables dentro de una función deben declararse al comienzo
de la función
Norma: usar #define para constantes simbólicas.
Todas las cantidades que permanezcan constantes deben nombrarse con
#define y escribirse en mayúsculas, así:
#define MAX_CANALES 4096
Las constantes simbólicas que se usen en una única unidad
de compilación deben definirse al principio de esa unidad de compilación.
Las constantes simbólicas de alcance más general deben definirse
y documentarse en archivos de encabezado de alcance adecuado.
Norma: declaración explícita de +1 en largo de cadenas
para '\0'.
El largo de arreglos de caracteres usados como cadenas y terminados
por un caracter nulo, deben definir su largo indicando explícitamente
el "+1" para el carater nulo de terminación.
#define LARGO_NOMBRE 20 + 1
Norma: declaración de estructuras.
Cada campo de una estructura debe declararse en un renglón separado. La
estructura debe tener un nombre en mayúsculas. La asignacion de
una estructura a una variable debe hacerse en una sentencia separada, así:
struct LIBRO {
char nombre[LARGO_NOMBRE];
char autor[LARGO_AUTOR];
long numero;
}
Recomendación: macros.
Los nombres de macros deben escribirse en mayúsculas. Incluír
un comentario en el mismo renglón de declaración de la macro.
Las definiciones de macros deben estar al principio del archivo, o en un
archivo de encabezado. Colocar paréntesis en torno a los parámetros
en el texto de reemplazo, y en torno a todo el texto si es posible, así:
#define SIGUIENTE(p) ((p)->_next)
Norma: valor de retorno declarados explícitamente.
La función debe devolver "void" si no se devuelve
ningún valor.
Norma: lista de parámetros.
Si la lista de parámetros no cabe en un renglón, los
renglones de continuación deben estar indentados hasta el lugar
donde comienza la lista de parámetros en el primer renglón.
Una función que devuelve información por vía de sus
parámetros debe devolver en su nombre solamente información
de estado.
Cada parámetro pasado a una función debe ocupar un renglón
separado en la declaración de la función, seguido de un corto
comentario que describa su función.
Norma: función estática.
Toda función que sólo sea llamada desde otras funciones
en el mismo archivo debe ser declarada estática.
Patrón para declaración de funciones.
void nom_func (int param1, /* comentario parámetro 1 */
long param2, /* comentario parámetro 2 */
double param3, /* comentario parámetro 3 */
char param4, /* comentario parámetro 4 */
{
printf("%d %ld %f %c\n", param1, param2, param3, param4);
return;
}
Norma: cuerpo de la función.
La llave de apertura "{" del cuerpo de la función
debe estar a la izquierda en un renglón separado o al final del
renglón que introduce el bloque. El resto de la función hasta
la llave de cierre "}" debe estar indentada un paso.Un paso de
indentación es 2 o 3 espacios; no usar tabuladores.
En la función debe haber una única sentencia return
con un parámetro si la función no es void. Aún si
la función no devuelve ningún valor, es buena práctica
incluir una sentencia return.
La llave de cierre "}" debe estar a la izquierda y en un renglón
separado.
Norma: prototipo de la función.
Deben crearse prototipos para todas las funciones. Si los prototipos
de las funciones residen en un archivo separado, este archivo debe tener
extensión ".fp".
Recomendación: patrón para encabezado de archivos y
funciones.
Si el archivo contiene funciones relacionadas de tal modo que la modificación
de una requerirá probablemente la modificación de varias
de las otras, se colocará al principio del archivo el siguiente
encabezado, y al principio de cada función un comentario breve que
la describa.
/*================================================================= | Archivo: nombre de archivo | Propósito: | Documentación: (si corresponde) | | Revisiones: | Fecha Nombre Revisión | -------- --------------- ---------------------------------------- | 02-03-98 Juan Perez Creado | ===================================================================*/
Si las funciones del archivo no están particularmente relacionadas, se colocará el siguiente encabezado al principio de cada función.
/*================================================================= | Función: nombre de función | Propósito: | Método: (si corresponde) | | Revisiones: | Fecha Nombre Revisión | -------- --------------- ---------------------------------------- | 02-03-98 Juan Perez Creado | ===================================================================*/
Recomendación: comentarios en las funciones.
Los comentarios dentro del cuerpo de las funciones deben restringirse
a bloques de comentario que precedan bloques cohesivos. Describirán
los propósitos del bloque en el cumplimiento de una tarea cohesiva.
El uso de nombres significativos en variables y funciones minimizan la
necesidad de comentarios.
Norma: bloques de comentario.
Los bloques de comentario deben estar precedidos y seguidos por un
renglón en blanco. En bloques de comentario de varios renglones,
los renglones siguientes al primero deben tener un caracter barra vertical
"|" al comienzo del renglón. Tal como es requerido, el
primer renglón comenzará con "/*" y el último
finalizará con "/*". El bloque de comentario debe indentarse
al mismo nivel que el bloque de código que comenta.
/*=================================================================== | Este es un ejemplo de bloque de comentario. Nótense las líneas de | guiones en el primer y último renglón y las barras verticales al | principio de cada línea de texto. Recuérdese además que deba haber | un renglón en blanco antes y otro después del bloque de comentario. =====================================================================*/
Norma: una sentencia por renglón.
Cada sentencia debe ocupar un renglón. "a = 2; b =3;"
no es permitido.
Recomendación: no usar sentencias goto ni
continue.
Se recomienda enfáticamente no usar sentencias goto
ni continue. El uso de break debe limitarse a las sentencias
switch, cuando se requiera. Se prefiere el siguiente código
estado = EXITO;
i = 0;
while ((cadena[i] != NULL && (estado == EXITO)) {
transmitCar(cadena[i], &estado);
}
en lugar de
estado = EXITO;
i = 0;
while (cadena[i] != NULL) {
transmitCar(cadena[i], &estado);
if (estado != EXITO) break;
}
Recomendación: evitar la sentencia exit.
Se recomienda evitar el uso de la sentencia exit, excepto
para el manejo de errores.
Recomendación: máximo 4 niveles de anidamiento en estructuras
de control.
El anidamiento de sentencias if, for, while,
etc., no debe superar los 4 niveles. Si se requieren más deberá
considerarse el uso de una función en alguno de los niveles superiores.
Norma: sentencias nulas.
Las sentencias nulas requieren comentario.Por ejemplo, si el caso default
de una sentencia case no hace nada, colocar igualmente el rótulo
default y un comentario tal como
/* no hay acción */
seguido de la sentencia break.
El cuerpo nulo de un for o un while debe estar en un renglón separado
y contener un comentario que asegure que el cuerpo es intencionalmente
nulo, así
while (*destino++, *origen++) ; /* vacío */
Norma: comentario para falta de break en switch.
Cuando se pretende que un caso de una sentencia case caiga
en el siguiente caso, la falta de break en la sentencia anterior
debe ser notada con un comentario.
switch (queIsotopo) {
case PU239: /* misma acción que PU240, sin break */
case PU240:
procesoPlutonio();
break;
default:
break; /* si no es PU239 ni PU240 no hay acción */
}
Norma: bloques de sentencias.
La llave de apertura "{" de los bloques de código
debe estar al final de la sentencia de control del bloque o a renglón
seguido alineado con la primera letra de la setnencia de control. El cuerpo
del bloque debe estar indentado un paso desde la sentencia de control.
La llave de cierre "}" debe estar en renglón aparte con
la misma indentación que la sentencia de contro. Por ejemplo:
if(primero < ultimo) {
resultUno = primero / 2;
resultDos = ultimo / 2;
}
else {
resultUno = ultimo / 2;
resultDos = primero /2;
}
do
{
estado = haceAlgo();
} while (estado == EXITO);
Recomendación: bloque con sentencia única.
Se aconseja encerrar entre llaves "{...}" aún las
sentencias únicas de las sentencias de control:
if (unaCondicion == CIERTO) {
variable Uno = variableDOS;
}
Norma: operadores decremento e incremento sólo en sufijo.
Usar los operadores de incremento "++" y decremento "--"
sólo como sufijo, y no como parte de otra sentencia. Se prefiere
esta forma
while (cadena[i] != NULL) {
haceAlgo();
i++;
}
en lugar de
while (cadena[i++] != NULL) {
haceAlgo();
}
Norma: desvío en llamado a función.
Cuando un desvío se base en el resultado de un llamado a función,
el llamado a función debe estar un renglón aparte. La forma
p_FileHandle = fopen("unArchivo", READ_ONLY);
if (p_FileHandle == NULL) {
printf("No pudo abrirse archivo; fin de programa");
terminaAplicacion()
}
else {
haceAlgo();
}
resulta más fácil de entender que
if ((fileHandle = open("unArchivo", READ_ONLY)) == NULL) {
printf("No pudo abrirse archivo; fin de programa");
terminaAplicacion()
}
else {
haceAlgo();
}
Norma: evitar efectos secundarios.
Las expresiones no deben producir internamente efectos secundarios.
Evitar sentencias tales como while (cadena[i++] != 0).
Norma: no usar el valor verdadero por defecto en las pruebas.
No usar el valo no nulo por defecto. Usar comparaciones explícitas
aún si el valor de comparación no cambia nunca. La forma
if (func() != FALSO)
es preferible a
if (func())
Norma: usar espacios entre operadores binarios.
Todos los operadores que tomen dos parámetros deben tener un
espacio antes y otro después del operador.
Norma: no poner espacio después de un operador unario.
No debe haber espacio que separe un operador unario del objeto afectado.
Recomendación: evitar el uso del operador de comparación
condicional.
Se recomienda evitar el uso del comparador condicional ternario "?";
se lo permite en macros.
if (abc > xyz) {
zUno = abc;
}
else {
zUno = xyz;
}
es más fácil de leer que
zUno = (abc > xyz) ? abc : xyz
Norma: usar paréntesis para evitar ambigüedades de precedencia.
Deben usarse paréntesis para eliminar ambigüedades que
puedan surgir por desconocimiento de la precedencia de operadores. Por
ejemplo, al incrementar la variable apuntada por el puntero p_NumVeces,
escribir (*p_NumVeces)++ asegura que se está incrementando el contenido
de la dirección y no el puntero.
Recomendación: uso de espacios en blanco para mejorar la legilibilidad.
Usar a discreción los espacios en blanco horizontales y verticales
para hacer más legible el código. Los renglones en blanco
y la indentación deben reflejar la estructura de bloques del código.
Norma: operadores condicionales en renglones separados.
Una lista de varios operadores condicionales debe separarse en renglones
diferentes. El código
if (nodo->sigte == NULL && totalCuenta < pedido
&& pedido <= MAXLOTE && servActivo(esteIngreso)) {
...
se escribe mejor así:
if (nodo->sigte == NULL
&& totalCuenta < pedido
&& pedido <= MAXLOTE
&& servActivo(esteIngreso))
{
...
Análogamene, los lazos for complicados deben separarse en líneas diferentes:
for (corr = *listp, rastro = pList;
corr != NULL;
rastro = &(corr->sigte), corr = corr->sigte) {
haceAlgo();
haceOtraCosa();
}
Norma: espaciado de paréntesis.
Las palabras claves seguidas de expresiones entre paréntesis
no deben quedar separadas del paréntesis izquierdo de la expresión.
Debe colocarse un espacio después de la coma en las listas de argumentos.
Norma: nombres de constantes simbólicas en mayúscula.
Las constantes simbólicas deben escribirse en mayúscula.
Por ejemplo, CIERTO.
Recomendación: consistencia en la definición de constantes.
Las constantes deben definirse en forma consistente con su uso; por
ejemplo, escribir 540.0 en lugar de asumir una conversión forzada
de 540.
La compilación condicional sólo debe usarse para controlar la compilación de código dependiente de máquina, fijando opciones de tiempo de compilación, o para depuración.
Norma: compilación condicional por defecto.
La compilación condicional de código dependiente de máquina
debe llevar, por defecto, a error. La compilación condicional de
código destinado a optimización debe llevar, por defecto,
a código no optimizado.
Recomendación: compilación condicional.
Siempre que sea posible, colocar el código de compilación
condicional en un archivo de encabezado en lugar de en el programa principal.
Las unidades de compilación condicional deben encerrarse entre llaves
de a una característica.
Norma: código de máquina en un archivo separado.
Colocar todo el código de máquina en un archivo separado
del código independiente de máquina. El código dependiente
de máquina debe estar puesto con #ifdef para que salga
un mensaje de error significativo si el código es compilado en una
máquina diferente.
Recomendación: uso del código de máquina.
El código de máquina debe usarse solamente cuando sea
estrictamente necesario. Intentar escribir como independientes de máquina
todas las rutinas que soporten código dependiente de máquina.
Recomendaciones generales para la portabilidad.
Recordar que los largos de los tipos nativos puede variar entre plataformas,
especialmente punteros y enteros. La presición y formato de almacenamiento
del tipo float también puede variar entre plataformas.
No asumir que el programa será siempre ejecutado en la máquina
para la cual fue diseñado originalmente.
Norma: código compatible ANSI.
Todo el código debe conformar el estándar de ANSI para
lenguaje C.
Norma: verificar siempre el valor de retorno de las funciones.
Verificar siempre el valor de retorno de las funciones que devuelven
valores especiales en caso de error.
Recomendación: usar funciones de biblioteca siempre que sea
posible.
Usar funciones de biblioteca siempre que sea posible. No reinventar
la rueda.
Las recomendaciones y normas expuestas en esta guía deben seguirse de buena fé, recordando que siempre se está trabajando, aunque sea potencialmente, en un grupo de trabajo.