You are on page 1of 86

Programacin bsica en

ANSI C




Rafael Menndez de Llano Rozas
Universidad de Cant abria

I
.
.
.
.
.
.
.
.
.






1. PROGRAMACIN EN (ANSI) C: PRIMEROS PASOS.
Introduccin. Estructura de un programa. Declaracin de datos y
sus tipos. Expresiones y sentencias. Entrada / Salida bsica.



2. SENTENCIAS DE SELECCIN EN C.
Operadores de relacin y lgicos. Sentencias condicionales.
Seleccin simple y mltiple.



3. SENTENCIAS DE ITERACIN EN C.
Bucles while, do while y for. Sentencias de control.



4. FUNCIONES y PUNTEROS.
Creacin, utilizacin y tipos de funciones. Paso de parmetros a
una funcin: punteros. mbito de datos y modos de
almacenamiento. Librera de C estndar. Argumentos de main.



5. DATOS ESTRUCTURADOS.
Arrays y punteros. Arrays multidimensionales. Tiras de caracteres:
strings. Registros (struct). Typedef. Datos enumerados.



6. ASPECTOS AVANZADOS.
Uniones: union. Manejo de bits. El preprocesador. Entrada/Salida
con ficheros. Punteros a funciones.



7. APNDICES.
A. Compilador de C en Windows y Linux.
B. Compilador y enlazador.
C. Ensamblador.
D. Otros aspectos del C y del sistema.
Orden de precedencia de los operadores. Declaraciones
complejas. Construccin de un programa sobre Linux.
Gestin de libreras. El editor vi.


ndice

II


Este libro est realizado con la intencin de que sirva
al mayor nmero de estudiantes y sin nimo de lucro.










III



















































Rafael Menndez de Llano Rozas
ISBN de la edicin anterior: 84-89627-26-6, Depsito Legal: SA-650-1996
Queda prohibida su reproduccin fuera de la Universidad de Cantabria para todo uso que no sea el
seguimiento de las asignaturas.



1
Programacin en (ANSI) C: Primeros pasos

El primer tema se dedica a describir la estructura de un programa en lenguaje (ANSI) C, la
declaracin de datos y tipos de datos escalares, las instrucciones ms sencillas y la
construccin de expresiones. Y se termina con las sentencias para realizar la entrada/salida
bsica que permitirn hacer los primeros programas.


Int roducci n
El lenguaje C fue creado por Dennis Ritchie en los laboratorios Bell de AT&T en 1972 para
tener un lenguaje de alto nivel (la mayora de los autores le colocan entre medio y bajo nivel) en el
que poder programar el sistema UNIX. Curiosamente se le llam C por que existan versiones
anteriores (A y B, la B creado por K. Thompson). Es el lenguaje ms empleado en la actualidad
1
(ver
Ilustracin 1), adems sus sucesores se cuentan entre los cinco primeros: C++, Objetive C, C#y ha
tenido influencia sobre J ava, Perl o Python y posee la ventaja de ser fcilmente extensible a nuevas
arquitecturas y como hemos dicho, poder tener acceso a bajo nivel (registros de memoria). Dada su
naturaleza es empleado para programar sistemas POSIX, microcontroladores, procesadores
empotrados o DSP multimedia.


Ilustracin 1. Porcentaje de uso del Lenguaje C.


1
Ranking lenguajes de programacin Noviembre 2013: 1.C, 2.J ava, 3.Objective-C, 4.C++, 5.C#,
6.PHP, 7.(Visual) Basic, 8.Python, 9. Transact-SQL, 10. J avaScript.
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html.
Captulo
1
PROGRAMACIN EN ANSI C INTRODUCCIN



2
La primera especificacin del lenguaje fue escrita en 1978: El lenguaje de programacin C
de Kernighan y Ritchie, de hecho al C especificado se le llam K&R C. Una versin estandarizada de
este lenguaje en el que se evitan algunos de los problemas de la primera versin (poco tipado) y la
proliferacin de libreras, fue creada en 1989 por el comit ANSI y se le llam ANSI C o estndar C.
Hubo una segunda edicin del libro de K&R que cubra esta estandarizacin (ver Ilustracin 2. ANSI
C.). En ella se modific el paso de parmetros a las funciones, se crearon los tipos de datos voi d y
enumerado, el calificador const , una librera estndar, los prototipos de funciones y que stas
puedan devolver estructuras.





En 1990 ANSI C fue adoptado por ISO y se convirti en C90 y en 1999 se cre en nuevo
estndar, el C99, aunque no est implementado completamente en todos los compiladores y tuvo
peor acogida. Las aportaciones fundamentales de este estndar fueron la declaracin de variables
en cualquier sitio, las funciones inline, tipos de datos de 64 bits, arrays de longitud variable,
comentarios de una lnea (// estilo C++) y tipos de datos lgicos y complejos. El C11 es el ms nuevo
estndar (ISO/IEC 9899:2011) que como caracterstica principal da soporte al multithreading.

El lenguaje C tiene la ventaja de que produce cdigo muy compacto y eficiente, pero a costa,
si no se tiene disciplina, de hacer los programas fuentes menos comprensibles de lo que seran en
otros lenguajes como Pascal o Ada. Por eso, cuando se programa en este lenguaje, se debe hacer
un esfuerzo adicional para que los programas resultantes sean claros y fciles de leer. Adems no
soporta excepciones, objetos, polimorfismo o chequeo de rangos.


Est ruct ura de un programa
Un lenguaje de programacin desde el punto de vista lxico (una de las fases de la
compilacin es el anlisis lxico) es un conjunto de tokens (la unidad ms pequea que tiene un
nico significado) dentro de los cuales nos encontramos con los identificadores (definidos por el
programador con las reglas del lenguaje), los smbolos (operadores), las constantes (literales)
numricas o de carcter y las palabras clave o reservadas (usadas por el lenguaje). Con todos ellos
construiremos un programa.

Un identificador ser un nombre compuesto por letras, dgitos y el carcter "_", que deber
comenzar por una letra. No se admitirn letras como la o las vocales acentuadas o caracteres que
hayan sido creados en pases de habla no inglesa. Los identificadores pueden ser de cualquier
longitud pero normalmente slo son significativos los 31 primeros caracteres (esto depender de la
implementacin, antes eran 8). Las maysculas y minsculas son distintas.

Habr identificadores reservados que no podrn utilizarse porque ya tienen un significado
propio, son las palabras reservadas (keywords). El conjunto de stas depende del lenguaje que
utilicemos y algunas veces del compilador en particular. Tambin habr otros identificadores que ya
tienen un significado predefinido, y que si les usamos perderemos su funcin, este sera el caso de
incluir una librera matemtica con una funcin para calcular la arcotangente de un nmero y definir
un identificador como por ejemplo atan.
Ilustracin 2. ANSI C.
PROGRAMACIN EN ANSI C INTRODUCCIN



3

Para el ANSI C estndar las palabras reservadas son:

f or whi l e do i f
el se swi t ch case def aul t
br eak cont i nue got o char
i nt shor t l ong unsi gned
f l oat doubl e st r uct uni on
t ypedef aut o ext er n r egi st er
st at i c r et ur n si zeof si gned
voi d const vol at i l e enum

Tambin hay que tener en cuenta que la librera estndar de C usa otros identificadores que
no deberan ser usados o redefinidos. Estos identificadores son los nombres de las funciones de esta
librera.

En la construccin de un programa tambin se usan delimitadores que separen
identificadores, un delimitador podr ser un espacio en blanco, un tabulador, un retorno de carro, un
comentario (algo no compilable que se utiliza para aclarar o explicar algo) o una combinacin de los
anteriores.

Desde el punto de vista de la estructura, un programa en C se compone de una serie de
declaraciones de datos globales y una serie de funciones. La parte de declaracin est compuesta
de una lista de variables que representan los tipos de datos del programa. La parte de definicin de
las funciones es la que indica lo que tiene que hacer el programa con los datos. Una de las funciones
es la principal, main, dnde se empieza a ejecutar el programa.

Cada funcin est compuesta de dos partes: por un lado la cabecera, donde se incluyen las
instrucciones al preprocesador (ya se ver lo que es) y el nombre de la funcin seguida entre
parntesis por los posibles argumentos que puede tener; y por otro lado est el cuerpo de la misma
(bloque), que estar encerrado entre llaves ({}) y podr contener declaraciones de datos locales a la
funcin y las sentencias que definen su funcionamiento.

Esta estructura se puede ver con el siguiente ejemplo:

#i ncl ude <st di o. h> / * f i cher o de cabecer a par a el pr epr ocesador */
#def i ne CONVERSI ON 166. 386 / * const ant e del pr epr ocesador */

i nt eur os = 4; / * var i abl e ent er a i ni ci al i zada */

mai n ( ) / * Pasa 4 eur os a peset as */
{
i nt peset as; / * var i abl e l ocal a mai n */

peset as = CONVERSI ON * eur os; / * conver si ones de t i po */
pr i nt f ( "Hay %d peset as en %d eur os \ n", peset as, eur os) ; / *sal i da*/
pi nt a( ) ; / * l l amada a f unci n ext er na */
}

pi nt a( ) / * cabecer a de f unci n */
{
pr i nt f ( "Sol o hago est o \ n") ;
}

En azul estaran las instrucciones para el preprocesador (antecedidas del smbolo #). En
verde la declaracin de datos globales. En rojo la funcin principal (dentro sus propias declaraciones
locales). Y en morado otra funcin. Destacados en marrn los comentarios que estn limitados por
los smbolos /* y */ .

En otros lenguajes las funciones de un programa pueden estar anidadas (Pascal o Ada), en el
caso del C todas las funciones estn al mismo nivel (ver Ilustracin 3. Anidamiento de funciones.). En
el programa de ejemplo tambin se observa que hay lneas que empiezan por #, estas
PROGRAMACIN EN ANSI C INTRODUCCIN



4
instrucciones no sern tratadas por el compilador si no por el preprocesador (ver apndice A), que es
el que entrega el cdigo fuente al compilador.

Ilustracin 3. Anidamiento de funciones.

Un programa en C podra escribirse en una sola lnea largusima, si el editor nos dejara, pero
sera difcil de entender y de escribir, por eso, al desarrollar un programa tenemos que adoptar cierta
disciplina: como el sangrado indentation (ver Cuadro 1), es decir, dejar unos espacios antes de
escribir una lnea que pertenece a un mismo grupo de sentencias entre llaves; usar comentarios para
aclarar los puntos oscuros del programa; usar nombres de variables adecuados y escribir una sola
sentencia por lnea. Si no adoptamos una estrategia de escritura, el programa podra resultar ilegible:

i nt a = 4; mai n ( ) {i nt b; b = 5 * a;
pr i nt f ( "Hay %d peset as en %d eur os\ n", b, a) ; pi nt a( ) ; } pi nt a( ) ;
{pr i nt f ( "Sol o hago est o\ n") ; }

ste, es el mismo programa que el anterior, funcionara de la misma manera, pero sera
mucho ms difcil de entender (pensar en un programa de varios cientos de lneas escritas as). En
l, hemos quitado los comentarios y hemos sustituido los nombres de las variables por letras que no
dicen nada. Adems, no hemos respetado el sangrado.

El uso de comentarios en cada parte que creamos conveniente explicar es muy importante
para entender un programa, estos comentarios debern estar entre los smbolos /* y */ (no se
pueden anidar). Comentarios de tipo lnea como // de C++no son ANSI C (aunque son admitidos
en C99). Hay que tener en cuenta que los ";" del C son terminadores (no separadores), dicen cuando
una instruccin se puede leer. De hecho varios ; constituiran varias sentencias vacas.


Decl aracin de dat os y sus t i pos
En C existen dos clases de datos: constantes y variables. Una constante es un smbolo, que
se asocia a un dato de valor constante y que no se podr cambiar. Una variable es una posicin de
la memoria, referenciada por un nombre identificador donde se almacena el valor de un dato que
se puede cambiar a lo largo del programa. As, en el programa anterior, el factor de conversin era
una constante y el nmero de euros y pesetas eran variables.

Las variables o las constantes pueden almacenar o referenciar distintos tipos de datos, estos
pueden ser numricos, alfabticos o agrupaciones de los anteriores (tipos estructurados). A
diferencia de otros lenguajes, el K&R C slo tena esos dos tipos bsicos de datos. Por ejemplo, no
existan datos de tipo lgico (en C clsico se asocian al dato de tipo entero, 0 para FALSE y distinto
de cero para TRUE), tampoco existan el tipo subrango o el enumerado. Es una de las cosas que ha
solucionado el ANSI C. A cambio de esto, el uso de punteros (direcciones de memoria) es habitual,
de hecho, el nombre de una variable de tipo array es un puntero. O asociando el operador & a una
variable obtenemos su direccin en memoria.

Otra de las caractersticas del C es que nos permite renombrar nuestros propios tipos de
datos, para ello se usa la palabra reservada typedef.
PROGRAMACIN EN ANSI C INTRODUCCIN



5


Cuadro 1. Estilos de sangrado.

En la siguiente figura pueden verse los tipos bsicos de datos:





Un tipo entero es un nmero positivo o negativo que no tiene parte fraccionaria. Dentro del tipo
entero podemos destacar varios subtipos: el short normalmente de un byte (-128..127), el int
(-32768..32767) de dos bytes, y el long (-2147483648..2147483647) de cuatro bytes. La longitud en
bytes de cada tipo de dato depende de la implementacin del compilador sobre la mquina que
Algunos estilos de sangrado:

a) La llave abierta se pone en la misma lnea con la estructura de control y la llave
de cierre va en una lnea a la altura del inicio de la estructura:
i f ( a==1) {
b = 1;
c = 2;
}
b) dem, pero la llave de cierre se dispone un poco a la derecha:
i f ( a==1) {
b = 1;
c = 2;
}
c) La llave abierta va en una lnea sola, al igual que la llave cerrada. Ambas se
disponen a la altura de la estructura que gobierna el bloque esta parece la ms
razonable y es la que sigo siempre:
i f ( a==1)
{
b = 1;
c = 2;
}
d) dem, pero las dos llaves se disponen ms a la derecha y el contenido del bloque
ms a la derecha:
i f ( a==1)
{
b = 1;
c = 2;
}
e) Y an otro, con las llaves a la misma altura que el contenido del bloque:
i f ( a==1)
{
b = 1;
c = 2;
}
No hay un estilo mejor que otro. Aunque decir esto provoca muchas discusiones
entre los desarrolladores de C.
Ilustracin 4. Tipos de datos en C.
PROGRAMACIN EN ANSI C INTRODUCCIN



6
usemos. Siempre podemos averiguar el nmero de bytes de un tipo de dato con el operador sizeof()
(devuelve un dato de tipo unsi gned l ong i nt eger ). Es habitual que el entero tenga una longitud
de palabra, si la palabra en la mquina es de 32 bits, el int ocupar 4 bytes. En mi mquina Linux de
64 bits los valores son 2, 4 y 8.

Tambin hay enteros sin signo poniendo a cada subtipo el calificador unsigned: unsigned
short (0..255) de un byte, unsigned int (0..65535) de dos bytes y unsigned long (4 mil millones) de
cuatro bytes. Como hemos dicho el nmero de bytes depende de la mquina y el compilador.

Cuando se programa se tiene que tener en cuenta al realizar el algoritmo, el rango de los
nmeros que estamos usando para no producir un "overflow", es decir, no sobrepasar la aritmtica
del tipo de nmero escogido (lo mismo para el "underflow").

Un tipo real (flotante) es un nmero decimal. Desde el punto de vista matemtico, entre un
rango de enteros hay un nmero finito de elementos (por eso los datos de tipo entero son escalares),
pero entre dos reales el nmero es infinito, para representar esto con el ordenador se utilizan
aproximaciones usando la notacin cientfica (exponencial). As, una parte de la memoria dedicada a
almacenar el real se encarga del exponente y otra parte a la mantisa. En C hay tres subtipos de
reales: el float, el double y el long double. Su tamao (que implicar rango de nmeros y precisin)
depender de la implementacin. Tambin se deber tener en cuenta que se puedan producir
desbordamientos (overflow o underflow). En mi mquina Linux de 64 bits los valores son 4, 8 y 16.

Un tipo carcter describe un carcter alfanumrico (todos los caracteres de la tabla ASCII).
Como es de suponer este tipo de datos tambin es escalar. Podran existir dudas cuando nos
referimos a un carcter que indica un nmero, para diferenciarlo del dato numrico, todos los
caracteres se escriben entre apstrofes, as, no es lo mismo el nmero 8 que carcter '8'. Los datos
de tipo carcter ocupan 1 byte.

Las constantes literales enteras sern los nmeros que se escriben sin punto decimal, si
queremos que una constante sea de tipo long se pondr una L detrs del nmero y u para unsigned.
Tambin se pueden escribir en distintas bases: para octal tendrn que ser precedidas por un 0 y
para hexadecimal por un 0X.

Larga: 22L Unsigned: 100u Octal: 023 Hexadecimal: 0X43

Los literales reales se pueden escribir de dos maneras: en notacin decimal o en notacin
cientfica (exponencial potencia de 10). En la primera se utiliza el punto para separar la parte entera
de la fraccionaria y en la segunda se da una cifra y una potencia de 10 separadas por una e (E) sin
espacios, tambin se usan letras para decir el tipo de flotante (f (F) para float, y L (l) para long
double):

3.1416 .2f 4e16 .8E-5 100. -4.3e-5L

Las constantes de tipo char se especifican con los apstrofes y sern slo un carcter (byte).
Tambin se pueden escribir refirindose a la tabla ASCII, colocando el cdigo en octal o en
hexadecimal, despus de la barra atrs (secuencias de escape).

'3' 't' '\007' '\x7'

Hay definidas una serie de secuencias de escape para ciertos caracteres de control:

\n nueva lnea \f salto de pgina \0 nulo
\t tabulador \\ barra atrs \v tab vertical
\b retroceso \' apstrofe \a alerta
\r retorno de carro \" comillas

Las \\ \' \" estn indicadas cuando hay que incluirlas en una tira de caracteres o en la
definicin de char y no confundirlo con la delimitacin de las mismas.

PROGRAMACIN EN ANSI C INTRODUCCIN



7
Una caracterstica especial del C es que las variables se pueden inicializar en su definicin
con las apropiadas constantes. Esto nos servir para ver ejemplos de declaraciones de datos:

i nt coef i ci ent e = 1024;
f l oat pi = 3. 1416;
char l et r a1, l et r a2 = ' A' ;
char car = ' \ n' ;

Como se ve, tambin se pueden hacer varias declaraciones de variables del mismo tipo,
separndolas por comas. Si se hace una inicializacin en este caso, slo tendr efecto en la ltima
variable. As, l et r a2 valdr 'A', pero el valor de l et r a1 estar indefinido.

Existe un tipo estructurado, que es la tira (string o array de caracteres terminados en el
carcter '\0') de la cual tambin existen constantes (literales). Para su definicin y delimitacin se
usan las comillas.

"Est o es una const ant e/ l i t er al de t i po st r i ng y se del i mi t a por \ ". \ n"

donde se han utilizado los caracteres especiales '\"' y '\n' para poner unas comillas y una nueva lnea
en el mensaje.

Por ltimo en lenguaje C existen dos maneras de declarar constantes (de cualquier tipo). La
primera y ms clsica es la de utilizar constantes simblicas con el preprocesador (del que se ha
hablado en el apndice A). Para ello se utiliza la instruccin def i ne, que como todas las del
preprocesador (no va a ser leda por el compilador) debe ir precedida del smbolo #. La idea es que
el preprocesador actuar sobre el cdigo fuente buscando el nombre de la constante y lo sustituir
por su valor. Por costumbre para este tipo de constantes se utilizan maysculas, aunque no es
necesario. La segunda es utilizando la palabra reservada const antes de la declacin. Obviamente
si declaramos algo constante no se puede modificar su valor en el programa.

A continuacin vemos unos ejemplos:

const i nt coef i ci ent e = 1024;
const f l oat pi = 3. 1416;
#def i ne LETRA ' A'
#def i ne CANTI DAD 345
#def i ne FRASE Hol a a t odos


Expresi ones y sent encias
Los cuerpos de las funciones estarn compuestos de declaraciones similares a las anteriores
y de sentencias que trabajan con los datos definidos. Habr varios tipos de sentencias, una de las
ms utilizadas es la asignacin, que sirve para cambiar a una variable de valor.

Esta sentencia estar compuesta de dos partes separadas por el operador de asignacin "=".
La parte de la izquierda ser la variable a la que queremos cambiar de valor definida por un
identificador (lexema en trminos de compiladores), la parte de la derecha ser una expresin
compuesta por operandos y operadores que formarn una expresin cuyo resultado debe ser del
mismo tipo que la variable, sino se producir una conversin, no un error. Es conveniente indicar
explcitamente (como veremos posteriormente con typecast) si se producen conversiones por
claridad de cdigo.

La asignacin no es como una ecuacin matemtica, simplemente, la parte de la derecha se
evala y se asigna este valor a la parte de la izquierda, matemticamente, "x =x +1" no tiene
sentido, pero esa sentencia en los lenguajes informticos, incluido el C, significa que el valor actual
de x se ha incrementado en uno respecto al antiguo.

PROGRAMACIN EN ANSI C INTRODUCCIN



8
Adems, en C existe la asignacin mltiple, de tal manera que se puede asignar valor a varias
variables:

a = b = c = 780;

El C no es un lenguaje fuertemente tipado, de tal manera que se pueden hacer asignaciones
de datos de distinto tipo, producindose la consiguiente conversin (mejor indicarlo):

car act er = car act er + 1;

Para construir la expresin se usan los operadores, los aritmticos son:

Operador Smbolo
Adicin +
Substraccin -
Negacin -
Multiplicacin *
Divisin /
Resto %


Los operadores "+", "-", "*", y "/" son la suma, la resta, la multiplicacin y la divisin y pueden
ser de tipo real o entero (se dice que los operadores estn sobrecargados porque tienen el mismo
smbolo lexema para diferentes operadores), dependiendo del tipo de operandos. Hay que tener
en cuenta que la divisin entera trunca el resultado, es decir, 5/3 es 1). El resto es el "%" (5 % 3 =2)
que es entero (obviamente no se usa con flotantes).

Como el C intenta producir cdigo compacto hay cinco combinaciones de operadores
aritmticos de asignacin:

Operador Asignacin Smbolo
Adictivo +=
Substractivo -=
Multiplicativo *=
Divisivo /=
De resto %=

As, es equivalente:

x = x +3; x += 3;

Las expresiones pueden estar formadas por variables y constantes (operandos) y operadores.
Cuando la expresin es compleja e incluye a varios operadores y operandos, hay que introducir unas
normas de evaluacin de la expresin, que son las normas de precedencia y asociatividad. Por
ejemplo si nosotros queremos sumar un operando con otro y el resultado multiplicarlo por un tercero,
no podremos hacer:

val or = oper ando1 + oper ando2 * oper ando3;

ya que la asociatividad del "*" es binaria y tiene mayor precedencia que el "+", por lo tanto afectar al
operando segundo y tercero, y al resultado del producto le sumaremos el primero (el orden de
evaluacin no est definido, es decir que trmino de la suma se evala primero).

Para evitar esto tendramos que haber utilizado:

val or = ( oper ando1 + oper ando2) * oper ando3;

ya que el operador parntesis es el de mxima precedencia.
PROGRAMACIN EN ANSI C INTRODUCCIN



9

En la siguiente tabla aparecen las caractersticas
1
(precedencia -por orden- y asociatividad)
de los operadores aritmticos, teniendo en cuenta que el operador de asignacin es el que tiene la
menor, precedencia. La otra caracterstica de los operadores es su asociatividad, es decir, a que
nmero de operandos afecta. Normalmente hay de dos tipos: unario, slo a uno (mayor precedencia)
y binario a dos.


Operador Smbolo Asociatividad Precedencia
Adicin + Binario 4
Substraccin - Binario 4
Incremento ++ Unario 2
Decremento - - Unario 2
Signo - Unario 2
Multiplicacin * Binario 3
Divisin / Binario 3
Resto % Binario 3
Parntesis ( ) Primario 1
Asignacin = += -= *= /= %= Binario 5


En la tabla aparecen dos nuevos operadores que son el incremento "++" y el decremento
"--", que son unarios y por tanto tienen mayor precedencia que los binarios. Su misin es incrementar
o decrementar en una unidad el operando. Estos operadores se pueden usar de dos maneras, por
un lado anteponindolos al operando (prefijo) o posponindolos (sufijo). Cuando se usan en una
expresin con otros trminos hay que tener en cuenta su precedencia y su orden de evaluacin:

Prefijo Sufijo
izda =3 * ++dcha; izda =3 * dcha++;

dcha =dcha +1;
izda =3 * dcha;
izda =3 * dcha;
dcha =dcha +1;

La precedencia nos dice donde se colocaran los parntesis por defecto, no el orden de
evaluacin, en el ltimo ejemplo a "dcha", no a "3 * dcha", el uso (prefijo o sufijo) nos dice el orden de
evaluacin. Adems en ambos casos dcha estar incrementada.

Hay que tener mucho cuidado al utilizar estos operadores ya que cuando se utiliza el mismo
operando en una expresin, no se garantiza que se hace primero:

r espuest a = num/ 2 + 3 * ( 1 + num++) ;

Es decir, al evaluar la expresin anterior no est garantizado que se realice primero "num / 2"
que el otro trmino "3 * (1 +num++)", con lo cual podemos tener "num" incrementado o no y el
resultado puede ser diferente (para num =5 podra ser 20 21, lo normal es que sea 20 si se evala
de izquierda a derecha). Por lo tanto para usarlos hay que tener las siguientes precauciones:

1. No emplearlo en expresiones con el mismo operando.
2. No se emplean en argumentos de funciones con el mismo operando.


1
En el captulo sptimo hay una tabla completa de precedencia de todos los operadores.
PROGRAMACIN EN ANSI C INTRODUCCIN



10
Hay otro operador que da el tamao en bytes de una variable o un tipo y tiene la misma
precedencia que los anteriores, siendo tambin unario, su nombre es sizeof. El valor que devuelve
es de tipo unsigned long int. Se puede usar con un tipo o una variable (con variable con o sin
parntesis):
si zeof ( i nt ) si zeof di as si zeof ( di as)

Existe otro operador que es el de moldeado (type cast) para hacer conversiones explcitas de
tipo (poniendo el tipo resultante entre parntesis). Como hemos dicho esto siempre es conveniente
hacerlo explcitamente como buena metodologa de programacin. Es de la misma precedencia que
los unarios, en el ejemplo siguiente el resultado sera 1:

( i nt ) 1. 6

Cuando se hacen conversiones implcitas o se combinan operandos de diferentes tipos hay
que tener cuidado ya que se pueden hacer promociones o prdidas de rango segn el siguiente
esquema de mayor a menor: long double -> double -> float -> long -> int -> short -> char.

En el primer caso no hay problemas adicionales, en el segundo se pueden dar resultados
insospechados, hay que recordar que el compilador no dar error! al hacer cosas como sta (es
necesario indicar que siempre podemos decirle al compilador que sea ms exhaustivo y nos indique
errores como estos):

ch = f l ot ant e;
ch = 123. 3e10;

Cuando se combinan en una expresin operandos de distintos tipos, antes de la evaluacin
se produce una promocin de rango del menor para que el resultado sea del tipo del mayor y la
operacin se produzca entre operandos del mismo tipo.

Hay que tener cuidado si las operaciones se realizan entre datos del mismo tipo para luego
ser promocionados:

i nt t ot al = 500;
i nt numer o = 12;
f l oat medi a = 0. 0;
medi a = t ot al / numer o;

Sabiendo esto podras decir cunto valdra media?



Ent rada y sal i da bsi ca
Los lenguajes de programacin siempre dan un mtodo por el cual el ordenador recibe o
devuelve informacin a travs de una unidad de entrada/salida de una forma legible por una
persona. En C se utiliza una librera que proporciona funciones para realizar esta Entrada/Salida (E/S
o I/O). Se considera que es de uso habitual, por lo que no hay que incluirla al compilar (est includa
en libc), pero lo que tenemos que hacer es declarar la forma de estas funciones (se ver en el tema
correspondiente que son propotipos).

Para ello se debe incluir un fichero de cdigo fuente (ya realizado y llamado stdio.h), donde
est la forma de esas funciones (prototipos), as como constantes simblicas que se pueden usar en
nuestros programas en relacin a la E/S. Para incluir este fichero (fichero de cabecera .h) se utiliza
otra instruccin del preprocesador que es include (los ngulos <> le dicen al preprocesador que
busque el fichero en el directorio por defecto). As en casi todos nuestros programas deberemos
empezar con una lnea como esta:

#i ncl ude <st di o. h>

PROGRAMACIN EN ANSI C INTRODUCCIN



11
Una de las funciones que realiza la salida es printf (imprime formateado). Tiene la siguiente
estructura:

printf (control, lista de variables);

Est compuesta de dos partes, por un lado est una tira (mensaje) de control donde estn
encerrados entre comillas los caracteres a imprimir y los conversores y por otro lado est la lista de
variables que se deben pintar, que es opcional.

Los conversores indican el lugar y el formato en la tira de control de las variables a imprimir,
por lo que su nmero deber ser igual al nmero de variables en la lista. La lista de las cosas que
queremos escribir, tendr que ir separada por comas y podr ser cualquier expresin, variable o
literal.

La salida podr ser formateada de diferente manera de acuerdo al conversor, de los que hay
varios tipos:

Conversor Variable Salida Conversor Variable Salida
%d %i int short int %f Float double double (punto)
%u int Unsigned int %e %E Float double
double
(exponencial)
%o, %x %X int Octal, hexadecimal %g %G Float double
double
(conveniencia)
%c char char %s %p String puntero string puntero


Ejemplos son (sentencia resultado):

printf("el valor de entero es %d .\n", entero); el valor de entero es 23 .
printf("%c%d\n", '$', entero); $23

Los tipos de variables no tienen porque coincidir con los conversores (de ah su nombre), es
decir, que por ejemplo se puede pintar un carcter en forma de entero (nmero ASCII), o cualquier
otra combinacin con otros tipos de datos.

Si dentro de la tira de control se quiere imprimir el smbolo "%", para no confundirlo con un
conversor se deber duplicar:

pr i nt f ( "el 10%%de 100 es 10\ n") ;

Existen unos modificadores de salida que se sitan entre el conversor y el "%". Hay varios
tipos:
Significado Modificador Ejemplo
J ustificacin izquierda - %-d
Anchura de campo nmero %4f
Precisin .nmero %.2f
tipo short h %hd
tipo long / double %ld / %lf
tipo long double L %Lf

Ejemplo:

printf("el valor de pi es %.4f\n",pi); el valor de pi es 3.1416
PROGRAMACIN EN ANSI C INTRODUCCIN



12

Hay que tener en cuenta que la justificacin por defecto es la derecha y que si ponemos una
anchura insuficiente de campo se ignorar.

Al igual que existe una instruccin de salida, tambin existe una de entrada, scanf que
normalmente se realizar desde teclado. La estructura es la misma que la anterior y el formato lo
proporciona el usuario al escribir la entrada, que debe coincidir con lo especificado. En la lectura no
tiene sentido poner mensajes en la tira de control.

Las variables de la lista tienen que ser de tipo puntero (se vern posteriormente), por ahora
esto significa que delante de las variables que no sean tiras o arrays (tambin se vern
posteriormente) habr que poner el smbolo "&", que como veremos despus es el operador de
direccin de tipo unario.

scanf ( "%d %f ", &edad, &suel do) ;
scanf ( "%s", t i r a) ;

Cuando hay varios elementos en la lista de variables, deben ir separados por comas. Cuando
tecleemos los valores para las variables tenemos que separarlos con una lnea, un espacio o un
tabulador, salvo cuando se lea un carcter, en cuyo caso se leer lo que haya de forma literal, lo que
suele plantear problemas, ya que si hay un espacio entre la lectura de dos nmeros y leemos un
carcter, el carcter ledo ser el espacio que hemos dejado como separador entre los nmeros*. As
para la entrada anterior se puede responder:

23
21312. 21
una_t i r a_de_car act er es
o tambin
23 21312. 21 una_t i r a_de_car act er es

o cualquiera de las posibles combinaciones.

A pesar de que exista el conversor %c, para la entrada y salida de caracteres, estas
funciones combinadas suelen dar problemas* ya que filtran los caracteres espacios, retornos y
tabuladores. Por ello es mejor limitar la entrada con scanf a nmeros.

En C existen otras funciones que estn en la librera stdio.h para este fin. Estas funciones son
getchar() y putchar(). getchar() no tiene argumentos y devuelve un carcter, putchar() tiene como
argumento un carcter.

As podramos hacer un programa:

#i ncl ude <st di o. h>
mai n( )
{
char ch;
ch = get char ( ) ;
put char ( ch) ;
}

que escriba el primer carcter que se introduzca (doble eco). Se pueden abreviar las dos sentencias
hacindolas "ms C" (es mala programacin ya que el cdigo se hace menos legible):

put char ( get char ( ) ) ;

El comportamiento de las funciones ser distinto si el sistema posee o no buffer de entrada
(donde cabe una lnea). Si tiene buffer los caracteres no se leern hasta que no se pulse la tecla
ENTER, leyndose todos de golpe. Si no se tiene buffer (con eco) los caracteres se leern
inmediatamente sin esperar al ENTER.


PROGRAMACIN EN ANSI C INTRODUCCIN



13
Un ejemplo con los dos tipos de entrada/salida sera:

Con sistema de buffer: Esto es una prueba
Esto es una prueba

Sin sistema de buffer: EEssttoo eess uunnaa pprruueebbaa












La E/S se puede producir en tres canales lgicos: el de entrada, el de salida y el de errores.
Estos canales se definen en stdio.h con tres variables ya definidas: stdin, stdout y stderr. Cuando
usamos printf usamos la salida stdout, con scanf la entrada stdin y para la salida de errores el fprintf
(ya puedes imaginar que es lo que hace una llamada a printf):

f pr i nt f ( st der r , "el val or de ent er o es %d . \ n", ent er o) ;

La diferencia entre el canal de salida normal y el de errores es que el de salida en buffereado.
Esto significa que una lnea slo sale en pantalla si es terminada con \n, si no se quedar en el buffer
de salida, y el de errores no (sale cada carcter inmediatamente). Esto es muy importante a la hora
de depurar a mano, ya que para saber el valor de las variables en un momento dado siempre
tendremos que terminar la lnea con la secuencia de escape /n.

En la librera stdio, tambin se define una forma de parada de lectura que es la macro EOF
(End Of File), normalmente definido como -1, con lo cual habra que hacer la entrada entera y
conocer las sentencias de control y de iteracin del siguiente tema.

Como avance podemos poner:

#i ncl ude <st di o. h>
mai n( )
{
i nt ch;
whi l e ( ( ch = get char ( ) ) ! = EOF)
put char ( ch) ;
}
















Existen otras dos formas de entrada que no estn en el estndar ANSI de C, pero
que son utilizadas cuando no se quiere esperar al retoro de carro (cuando no se
quiere eco), estas son getch() y getche(), cuya salida sera:

Sin sistema de buffer: Esto es una prueba

Cuadro 2. Entrada / Salida no estndar.
PROGRAMACIN EN ANSI C INTRODUCCIN



14






15
Sent encias de seleccin en C

En este captulo y en el siguiente se describen las sentencias clave para la realizacin de
cualquier programa, como son las sentencias de seleccin y de iteracin que permitirn ir
avanzando en el grado de dificultad de los programas realizados, bien haciendo preguntas
en el programa, bien repitiendo un conjunto de sentencias.


Operadores de rel aci n y l gi cos
Hasta ahora hemos visto que las sentencias se ejecutan secuencialmente segn se han
escrito, pero es muy habitual que una sentencia se ejecute o no, segn una condicin anterior. Para
ello deberemos tener la capacidad de hacer preguntas en el programa y de obtener una respuesta
del tipo s o no. Raro es el algoritmo (instrucciones o reglas bien definidas, ordenadas y finitas que
permite realizar una actividad mediante pasos sucesivos que no generen dudas a quien deba
realizar dicha actividad, en trminos coloquiales la receta) que no incluye alguna seleccin.

Algo quizs nuevo para algunos lectores es que en C cualquier
expresin tiene un valor. Por ejemplo, una expresin de asignacin tiene el
valor de la variable de la parte izquierda. Esto unido a que en C no existen
operadores booleanos
1
, ya que como hemos dicho son enteros, siendo 0
falso, y cierta cualquier cosa distinta de 0 (normalmente 1), hace que las
condiciones o las preguntas que se pueden hacer en el programa sean ms
generales (en C99 se incluy el tipo de dato bool pero no forma parte del
ANSI C). Como curiosidad prueba:

pr i nt f ( "El val or es f al se y t r ue es %d %d \ n", 3>4, 2<3) ;

Las preguntas se hacen en forma de expresiones que tienen un valor
y cuya respuesta es verdadera o falsa. Una expresin puede ser:

1. Una variable (incluida una asignacin como exotismo).
2. Dos expresiones entre un operador relacional.
3. Dos expresiones entre un operador lgico.

Por lo tanto, para realizar preguntas debemos emplear operadores especiales que pueden ser
de dos tipos: operadores relacionales y operadores lgicos.


1
El nombre procede del padre de la lgica George Boole (ver retrato) que fue el primero en definir un
sistema algebraico de lgica que define las funciones AND, OR, y NOT.
Captulo
2
PROGRAMACIN EN ANSI C SELECCIN



16
Los operadores relacionales sirven para comparar y tienen una precedencia media entre los
operadores aritmticos y la asignacin (que es la de menor precedencia). As, si definimos B como
una variable entera podemos hacer: B =4 >5; lo cual es falso, por lo tanto, B valdr 0.

Los operadores relacionales son los siguientes:

== != >= <= > <

No hay que confundir el operador relacional "==" y el operador de asignacin "=". Uno sirve
para comparar dos operandos, el otro cambia el valor de uno de ellos. El "!=" es el distinto (en otros
lenguajes el <>) y los dems tienen el significado obvio: mayor o igual, menor o igual, mayor y
menor. Hay que tener en cuenta que siempre se debern comparar cosas iguales, es decir, enteros
con enteros, flotantes con flotantes y caracteres con caracteres (es mala prctica y no tiene sentido
matemtico comparar con "==" flotantes).

El otro tipo de operadores son los lgicos (los definidos por Boole), hay tres:

AND (^) con && OR (v) con || NOT (~) con !

con ellos se forman las tablas de verdad, que nos dirn el resultado de utilizar uno de estos
operadores. Las tablas son (T es TRUE y F FALSE):


OP 1 OP 2 AND OR OP NOT
F F F F F T
F T F T T F
T F F T
T T T T


Los operadores "&&" y "||" son binarios (dos operandos) y el "!" es unario (un operando). El "!"
es el que tiene la precedencia ms alta (correspondera con el signo menos unario), despus le
sigue el "&&" (que est entre los operadores de relacin y la asignacin) y el "||" entre el "&&" y la
asignacin "=".

La tabla de precedencia (mayor arriba) es la siguiente
1
:


NOT !
Relacionales < <= >= >
Relacionales == !=
AND &&
OR | |
asignacin =


El orden de evaluacin no est garantizado en expresiones aritmticas, pudiendo hacerse
antes un trmino u otro (manzanas =(5+3) * (9+6)), pero en las expresiones lgicas se garantiza que
el orden es de izquierda a derecha:

( ch = get char ( ) ) ! = EOF && ch ! = ' \ n'

primero se asignar valor a ch (por los parntesis, si no, no funcionara) y despus se comparar con
EOF y '\n'.



1
Hay una tabla completa de precedencia en el ltimo tema.
PROGRAMACIN EN ANSI C SELECCIN



17
Y en cortocircuito (si es 0 en AND y 1 en OR se para la evaluacin):

numer o ! = 0 && 12 / numer o == 2

por la precedencia no necesitamos poner parntesis a los lados del && ya que los relacionales tienen
mayor precedencia, adems se garantiza que primero se va a evaluar numero !=0 y al ser en
cortocircuito, si numero es igual a 0 no se producir la divisin por 0 ya que la expresin completa
ser 0 (0 && cualquier cosa es 0).


Sent enci as condi cionales
En cualquier programa normal es necesario tomar decisiones sobre si se ejecuta una cosa u
otra. En C, la estructura que permite este control se llama if. La sentencia if, nos permitir ejecutar
una instruccin dependiendo del valor de una expresin, la estructura es:

if ( expresin )
ejecuta sentencia 1;

Si la expresin es cierta (distinto de 0), ejecutar la sentencia 1, si no,
no la ejecutar y pasar a la siguiente sentencia. Si queremos que ejecute
ms de una sentencia las tendremos que poner en un cuerpo/bloque (entre
llaves).

Otra estructura muy comn de seleccin es el if con else. Es similar a la anterior, pero en este
caso, si la expresin se evala a 0, en vez de realizarse la siguiente instruccin despus del if, se
realizar la instruccin a continuacin del else. La estructura sera:


if ( expresin )
ejecuta sentencia 1;
else
ejecuta sentencia 2;

Tambin aqu, si se quiere realizar un grupo de instrucciones, tanto en el if como en el else se
utilizarn los marcadores de bloque: las llaves.

En C se pone un punto y coma despus de cada sentencia (acta como terminador no como
separador), por lo tanto la sentencia 1 debe terminar en punto y coma (a diferencias de otros
lenguajes como Pascal).

Veamos el ejemplo de la resolucin de una ecuacin de segundo grado (se aade la librera
matemtica math.h para obtener la raz cuadrada, una referencia de esta librera se encuentra en el
captulo sexto):

#i ncl ude <st di o. h>
#i ncl ude <mat h. h> / * hay que compi l ar con - l m */
mai n( )
{
i nt coef 1, coef 2, coef 3; / * decl ar aci ones */
f l oat di scr i mi , sol 1, sol 2;

pr i nt f ( "I nt r oduce l os coef i ci ent es de l a ecuaci on \ n") ;
pr i nt f ( "Coef i ci ent e A : ") ;
scanf ( "%d", &coef 1) ;
pr i nt f ( "Coef i ci ent e B : ") ;
scanf ( "%d", &coef 2) ;
pr i nt f ( "Coef i ci ent e C : ") ;
scanf ( "%d", &coef 3) ;

PROGRAMACIN EN ANSI C SELECCIN



18
di scr i mi = coef 2 * coef 2 - 4 * coef 1 * coef 3;
i f ( di scr i mi >= 0. 0)
{
sol 1 = ( - coef 2 + sqr t ( di scr i mi ) ) / 2 / coef 1;
sol 1 = ( - coef 2 - sqr t ( di scr i mi ) ) / 2 / coef 1;
pr i nt f ( "La sol uci n pr i mer a es : %f ", sol 1) ;
pr i nt f ( "La sol uci n segunda es : %f ", sol 2) ;
}
el se
{
sol 1 = - coef 2 / 2 / coef 1;
sol 2 = sqr t ( - di scr i mi ) / 2 / coef 1;
pr i nt f ( "La par t e r eal es : %f ", sol 1) ;
pr i nt f ( "La par t e i magi nar i a es : %f ", sol 2) ;
}
}

Vemos como, dependiendo del valor del discriminante, tomamos la decisin de resolver la
ecuacin de forma real o de forma compleja, adems, despus de cerrar un bloque, no hace falta
poner un punto y coma.

No hay ninguna restriccin en cuanto a las instrucciones que se pueden introducir dentro de
una sentencia if, de hecho se pueden introducir sentencias if dentro de sentencias if (anidamiento, en
el ANSI C se permiten al menos 15 niveles), el factor limitativo ser la legibilidad del programa que se
produzca. Cuantos ms if anidados se utilicen, ms enmaraado y difcil de entender se har el
programa.

Cuando se anidan muchos if con else, tenemos que saber con seguridad a que if corresponde
el else. El sangrado es algo que escribimos nosotros y que al compilador le resulta indiferente, por lo
tanto la regla que se sigue es que el else siempre pertenece al if que tenga ms cercano,
empezando por el ms interno. Otra manera de estar seguros de la correspondencia es poniendo
estructuras de bloque {}, aunque slo haya una sentencia interna, en los lugares adecuados.

Veamos un ejemplo de esto con el siguiente trozo de cdigo:

i f ( numer o > 6 )
i f ( numer o < 12 )
pr i nt f ( "Cal i ent e ! \ n") ;
el se
pr i nt f ( "Lo si ent o has per di do! \ n") ;

Cundo pintar "Lo siento has perdido"?, cuando numero sea <=que 6 o cuando sea >=que
12, es decir, a qu if pertenece el else? La respuesta es al segundo, da igual el sangrado que se
utilice. Si quisiramos que fuera al primero habra que hacer:

i f ( numer o > 6 )
{
i f ( numer o < 12 )
pr i nt f ( "Cal i ent e ! \ n") ;
}
el se
pr i nt f ( "Lo si ent o has per di do! \ n") ;

Algunas veces podemos evitar anidar los if, por ejemplo, si tenemos las sentencias:

i f ( condi ci on1 )
i f ( condi ci on2 )
i f ( condi ci on3 )

podemos convertirla en una sola sentencia if:

i f ( condi ci on1 && condi ci on2 && condi ci on3)

PROGRAMACIN EN ANSI C SELECCIN



19
Existe otra forma ms de seleccin que es la estructura if else if. Ocurre cuando en la parte
else se utilizan sentencias if.

La estructura sera:

i f ( expr esi n )
ej ecut a sent enci a 1;
el se i f ( expr esi n)
ej ecut a sent enci a 2;
el se
ej ecut a sent enci a 3;

Un ejemplo de esta sentencia sera:

#i ncl ude <st di o. h>
mai n( ) / * r eci bo de l a l uz */
{
f l oat ki l o;
f l oat r eci bo;

pr i nt f ( "I nt r oduce el gast o en ki l owat i os") ;
scanf ( "%f ", &ki l o) ;
i f ( ki l o < 500. 0)
r eci bo = 5. 0 * ki l o;
el se i f ( ki l o < 1000. 0)
r eci bo = 4. 0 * ki l o;
el se
r eci bo = 3. 0 * ki l o;
pr i nt f ( "La cuent a por %f kw. es %f \ n", ki l o, r eci bo) ;
}

Existe en C un operador, el operador condicional, que realiza sentencias if de manera
abreviada y que sirve cuando se tienen que utilizar dos alternativas para calcular una expresin.

Est formado por tres partes, en la primera se hace una pregunta, en la segunda se ejecuta la
sentencia si fuera la respuesta cierta y en la tercera lo contrario. Estas tres partes van separadas por
" ? : ".

As, para asignar el valor absoluto de un nmero se puede hacer:

x = ( y < 0) ? - y : y; <=> i f ( y < 0) x = - y; el se x = y;



Sel ecci n si mpl e y ml t i pl e
Cuando tenemos que elegir entre muchas alternativas, es poco eficiente y claro utilizar
estructuras if else if else..., para ello el C tiene una sentencia switch que permite elegir entre distintas
alternativas. La forma general de esta sentencia es:

swi t ch ( expr esi n )
{ case pr i mer o : sent enci as;
br eak; / * opci onal */
case segundo : sent enci as;
br eak; / * opci onal */
. . .
case l t i mo : sent enci as;
br eak; / * opci onal */
def aul t : sent enci as;
br eak; / * opci onal */
}

PROGRAMACIN EN ANSI C SELECCIN



20
La expresin que selecciona las sentencias que se ejecutarn debe ser de tipo escalar
discreto, ya que hay que escoger con seguridad uno de ellos, normalmente un char o un entero
(posteriormente se vern los tipo enumerados que tambin son vlidos). Los valores que puede
tomar esta expresin son los casos que aparecen en el esquema anterior (en el estndar ANSI C al
menos 257 casos), por supuesto tienen que coincidir en tipo con la expresin selectora. La sentencia
default, que es opcional, se ejecutar siempre que ningn valor de los casos coincida con el valor de
la expresin de seleccin.

En cuanto a esta sentencia hay que considerar los siguientes puntos:

1. Dentro de un caso pueden aparecer distintos valores del tipo de la expresin selectora,
tendrn que estar especificados como case primero : case segundo : ...
2. Los casos no tienen por qu estar en orden, pero no se pueden repetir.
3. Dentro de un caso se pueden ejecutar varias sentencias, incluso otras sentencias switch
(al menos 15 en ANSI C).
4. La sentencia break es opcional y sirve para terminar de ejecutar un caso. Si no se pone,
se ejecutarn, para una seleccin dada, todas sus sentencias y las sentencias de los
casos inferiores hasta que se encuentre un break.
5. La opcin default tambin es opcional, si no se pone y no coincide, se ejecutar la
sentencia posterior al switch.

Para verlo vamos a poner un ejemplo sencillo:

#i ncl ude <st di o. h>
mai n( )
{
char ch;
pr i nt f ( "Dame una l et r a y t e dar e una mar ca de coches: ") ;
i f ( ( ch = get char ( ) ) >= ' a' && ch <= ' z' )
swi t ch ( ch)
{
case ' s' : case w : pr i nt f ( "seat \ n") ;
br eak;
case ' p' : pr i nt f ( "peugot \ n") ;
case ' c' : pr i nt f ( "t ambi en ci t r oen") ;
br eak;
def aul t : pr i nt f ( "r enaul t \ n") ;
br eak;
}
}

De tal manera que si pulsamos 'p' se ejecutarn dos sentencias printf hasta llegar al break de
la opcin 'c', y si no es la 'p', 's', o 'c' se ejecutar el default al que no es necesario poner un break.

En C hay otras sentencias de control, por un lado est la propia break, que no slo se puede
utilizar en el switch, sino en las estructuras de iteracin (bucles) que veremos en el captulo siguiente,
y sirve para interrumpir el control de una instruccin y pasar a la siguiente.

Por otro lado est continue, que se utiliza en los bucles de la misma forma que break pero
slo para una iteracin.

Tambin est la instruccin maldita goto, que va en contra de la programacin estructurada, y
que produce un salto incondicional all donde indique la etiqueta asociada. No recomendable.

Y por ltimo esta la sentencia exit y que produce la terminacin del programa ejecutado. El
argumento que se le da indica situaciones de error. Si es cero es una salida sin error, si es distinto de
cero con error (la codificacin es libre del programador).




21
Sent encias de it eracin en C

En este captulo se describen las sentencias para realizar la repeticin de un conjunto
instrucciones. Son las sentencias de iteracin, que permitirn ir avanzando en el grado de
dificultad de los programas realizados.


Bucl es: whi le, do whi l e y for
En el captulo anterior vimos que la secuencialidad en la ejecucin puede ser modificada con
sentencias de control del tipo if, if / else y switch / case. Existen otras instrucciones, tambin de
control, que permiten ejecutar repetidamente un grupo de sentencias, son los denominados bucles.
Existen tres tipos en C: while, do while y for.

El bucle while es la sentencia ms frecuente para controlar la repeticin, de hecho con slo
esta instruccin y el if sera suficiente para programar estructuradamente. La estructura de la
sentencia es la siguiente:

while ( expresin )
sentencia(s);

Esto significa que mientras (while) una expresin sea distinta de 0 (TRUE) se ejecuten las
siguientes instrucciones. Si el bucle tiene que realizar slo una sentencia no ser necesario
encerrarla entre marcadores de bloque (llaves).

El funcionamiento es el siguiente: nada ms entrar a ejecutar la
sentencia while, se evaluar la expresin, si resulta cierta (distinta de
0) se ejecutan las sentencias internas al bucle (el cuerpo del while), si
resulta falsa se salta la ejecucin del mismo, pasndose a la siguiente
sentencia. En el primer caso y una vez ejecutadas todas las
sentencias internas, se vuelve a evaluar la condicin de entrada,
repitindose el proceso. Esto quiere decir, que en el cuerpo del bucle
tiene que haber alguna instruccin que modifique en algo la evaluacin
de la expresin para que en un momento dado se puede salir de la
ejecucin del bucle.

El bucle while es til cuando queremos repetir un grupo de instrucciones un nmero
indeterminado de veces que no conocemos a priori, por ejemplo, si queremos dividir un entero por
otro repetidamente, hasta que el resultado sea menor que el divisor (calcular los restos), Cuntas
veces tendremos que realizar esto?, en principio no se sabe, depender del nmero a dividir y del
divisor, por lo tanto el nmero de iteraciones vendr determinado por la condicin resultado <divisor.
Captulo
3
PROGRAMACIN EN ANSI C ITERACIN



22

Para ver cmo funciona el bucle while, veamos el ejemplo anterior de la divisin, al que
aadiremos que nos imprima los restos y adems cuente el nmero de divisiones hechas:

#i ncl ude <st di o. h>
mai n( )
{
i nt di vi dendo, di vi sor , r est o;
i nt cont ador = 0;
pr i nt f ( "I nt r oduce di vi dendo y di vi sor : \ n") ;
scanf ( "%d %d", &di vi dendo, &di vi sor ) ;
whi l e ( di vi dendo >= di vi sor ) / * sol o si go si es mayor */
{
r est o = di vi dendo %di vi sor ; / * hago l a di vi si on */
di vi dendo = di vi dendo / di vi sor ;
pr i nt f ( "%9d", r est o) ; / * a l a der echa */
cont ador ++; / * aument o cont ador de di vi si ones */
}
pr i nt f ( "\ nEl di vi dendo es %d\ n", di vi dendo) ;
pr i nt f ( "Numer o de di vi si ones : %d \ n", cont ador ) ;
}

Lo primero que hacemos en el programa es inicializar la variable contador, ya que nada ms
ejecutar un programa todas sus variables tienen un valor aleatorio (dependiendo de como este la
memoria del computador), es la nica que inicializamos, ya que las dems toman un valor dado en el
programa (dividendo y divisor en el scanf y resto al hacer el %).

Es muy frecuente al empezar a programar, dejar sin inicializar las variables, con lo que al
utilizar instrucciones como: contador =contador +1; se producen errores difciles de detectar.

Una vez introducidos los datos, entro en el bucle while y realizo la primera evaluacin de la
expresin, si el dividendo es menor (hemos usado mayor o igual!) que el divisor se saltar hasta la
siguiente instruccin fuera del bucle (printf) si no se realizar la primera divisin, calculando el resto y
modificando el dividendo para la nueva evaluacin del bucle, despus se suma el contador y se
vuelve a realizar la evaluacin de la expresin de entrada, para ver si se hace una nueva iteracin.

Para realizar iteraciones cuando no se conoce el nmero de ellas
a priori, existe otro bucle que es el while de salida o do-while (en otros
lenguajes es el repeat-until pero la condicin es de permanencia no de
salida). El bucle while se distingue del do-while en que la expresin se
evala al comienzo de la iteracin, con lo cual se puede salir del bucle sin
que este se haya ejecutado su cuerpo, sin embargo, en el bucle do-while
la expresin se realiza despus de ejecutarse el cuerpo, con lo cual
siempre se entra al menos una vez en l. La estructura del bucle es:

do
sentencia(s);
while ( expresin );

Anteriormente dijimos que con el bucle while tenamos suficiente para programar una
iteracin, entonces Por qu en C existen dos tipos de bucles? La respuesta es que en algunos
casos resultar ms conveniente entrar directamente en el bucle y hacer la evaluacin de la
expresin de permanencia despus de haber ejecutado una vez el cuerpo del bucle, que realizarla
antes de entrar.

Veamos esto con un ejemplo, queremos leer una lista de nmeros positivos y sumarles hasta
que se introduzca un nmero negativo, realicemos esta tarea usando los dos tipos de bucles. Vemos
como el bucle do-while, en este caso, no funciona, porque lo que nos interesa antes de realizar la
suma, es efectuar el chequeo del nmero introducido. Si introducimos como primer nmero uno
PROGRAMACIN EN ANSI C ITERACIN



23
negativo, en el primer caso no realiza ninguna sentencia del bucle, en el segundo caso, hace una
suma errnea antes de darnos el resultado.

#i ncl ude <st di o. h> #i ncl ude <st di o. h>
mai n( ) mai n( )
{ {
i nt numer o, suma = 0; i nt numer o, suma = 0;
scanf ( "%d", &numer o) ; do {
whi l e ( numer o >= 0) { scanf ( "%d", &numer o) ;
suma += numer o; suma += numer o;
scanf ( "%d", &numer o) ; }
} whi l e ( numer o >= 0) ;
pr i nt f ( "La suma es : %d\ n", suma) ; pr i nt f ( "La suma es : %d\ n", suma) ;
} }

En este ltimo ejemplo hemos utilizado el operador de la asignacin aditiva. Lo mismo que
para la suma, existen operadores para la resta, multiplicacin, divisin y resto, son binarios y su
precedencia es la de la asignacin:

a =a - 20; <=> a -=20; a =a / 20; <=> a /=20;
a =a * 20; <=> a *=20; a =a % 20 <=> a %=20;

Existe un tercer bucle que es el bucle for. Consta de tres campos separados por puntos y
comas: por una lado est la inicializacin, por otro est la expresin de permanencia (condicin) y
por ltimo est la ejecucin para la siguiente iteracin (incremento). Su estructura es:

Realmente es un bucle while encubierto que puede hacer la labor de l en un paso, su
funcionamiento se compara con el while con este ejemplo:

cont = 5; <=> f or ( cont = 5; cont <= numer o; cont ++)
whi l e ( cont <= numer o) pr i nt f ( "hol a\ n") ;
{
pr i nt f ( "hol a\ n") ;
cont ++;
}

La variable contadora al principio tomar el valor 5 (paso 1), despus se comprobar la
condicin de permanencia (paso 2), si es cierta se ejecutar la(s) sentencia(s) interior (paso 3),
cuando se termine, se proceder a ejecutar la tercera parte del bucle, el incremento de cont (paso 4)
y se volver a comprobar la permanencia (y as sucesivamente). En algn momento determinado la
condicin de permanencia ser FALSE y se pasar a la siguiente sentencia fuera del for.

En algunos casos es til adems de inicializar, declarar la variable (contadora) del for en el
propio bucle. Eso significa que el mbito de esa variable ser el bucle y no puede usarse fuera de l.

f or ( i nt cont = 1; cont <= numer o; cont ++)




1
2,5,8
3,6,9
4,7,10
PROGRAMACIN EN ANSI C ITERACIN



24
Se suele decir que un buen estilo de programacin es colocar las variables lo ms cerca
posible de donde se usan, aunque en C K&R esto no se hace as (se colocan al principio) en ANSI C
y posteriores si se puede hacer, pero se debe tener en cuenta que el mbito de la variable (dnde se
puede usar) es slo el del bloque ({}) donde est declarada.

A diferencia de otros lenguajes, el bucle for de C tiene una gran flexibilidad y existen muchas
maneras de utilizarlo:

1. Se puede utilizar para hacer bucles ascendentes y descendentes:

f or ( n = 10; n > 0; n- - )

2. Para hacer bucles con iteraciones de paso a paso:

f or ( n = 2; n < 75; n += 15)

3. Con cualquier tipo de dato:

f or ( ch = ' a' ; ch <= ' z' ; ch++)

4. Con progresin geomtrica de la variable (no siempre aritmtica):

f or ( d = 1; di vi sas < 1500; di vi sas = di vi sas * 10)

5. De carcter general (con una expresin compleja):

f or ( x = 1; y <= 43; y = 4 * x++ + 11)

6. Debido a que la sentencia nula es cierta se pueden hacer bucles infinitos o dejar alguna
parte del bucle vaca:

f or ( ; ; ) f or ( x = 0; x ! = 22; )

7. Por ltimo se puede utilizar cualquier expresin en cada parte:

f or ( pr i nt f ( "i ni ci o\ n") ; scanf ( "%d", &n) ; pr i nt f ( "cuent o\ n") )


Todava se puede ampliar ms el bucle utilizando el operador coma (no es el que vimos en la
declaracin de variables), es binario y su precedencia es la ms baja de todos los operadores,
incluyendo la asignacin:

f or ( g = 5, f = 4; g <= 50; g += 5, d += 7) ;

Veamos un ejemplo clsico de la suma de Zenn, es decir, 1 +1/2 +1/4 +... Esto se podra
hacer en C con un bucle for:

f or ( suma = 0. 0, x = 1. 0, cont = 1; cont <= 20; cont ++, x *= 2. 0)
suma += 1. 0 / x;

En este ejemplo se ve la filosofa del lenguaje C, que trata de compactar al mximo las
sentencias usadas (normalmente a costa de la legibilidad del programa).

Hay que tener en cuenta con respecto a otros lenguajes como el Pascal, el BASIC o el
Fortran, que su bucle FOR clsico se puede implementar de la siguiente manera:

f or ( i = 1; i <= l i mi t e ; i ++)

y que ahora se puede cambiar la variable contadora dentro del bucle (aunque no es aconsejable),
cosa que antes era sumamente arriesgada.
PROGRAMACIN EN ANSI C ITERACIN



25

En los tres tipos de bucles que hemos descrito, se pueden incluir otros bucles dentro de los
primeros (anidamiento) en al menos 15 niveles segn el estndar ANSI.


Sent enci as de cont rol
Como vimos en el captulo anterior hay otras sentencias de control que se pueden utilizar en
los bucles. Por un lado est break, que no slo se puede utilizar en el switch, y que sirve para
interrumpir el control de una instruccin y pasar a la siguiente. Por lo tanto se puede utilizar para
abandonar un bucle (si fuera interno se saldra slo del interno):

whi l e ( ( ch = get char ( ) ) ! = EOF)
{
i f ( ch == ' \ n' )
br eak;
put char ( ch) ;
}

En este caso, para abandonar el bucle hay dos condiciones, que se alcance el EOF (ver final
del tema 1) o que el carcter ledo sea una nueva lnea. Tambin se puede hacer lo mismo
complicando la expresin del bucle:

whi l e ( ( ch = get char ( ) ) ! = EOF && ch ! = ' \ n' )

Por otro lado est continue, que se utiliza en los bucles de la misma forma que break, pero en
vez de salirse definitivamente slo se salta una iteracin.

whi l e ( ( ch = get char ( ) ) ! = EOF)
{
i f ( ch == ' \ n' )
cont i nue;
put char ( ch) ;
}

En este caso se podra sustituir en el cuerpo del bucle por:

i f ( ch ! = ' \ n' )
put char ( ch) ;














PROGRAMACIN EN ANSI C ITERACIN



26


27
Funciones y punt eros

En el cuarto captulo se describe cmo realizar programacin modular. En C se realiza a
travs de funciones, que como sabemos por main, tienen sus propias variables (locales) y
su propio cdigo. Las funciones, a diferencia del main, sern llamadas por otras funciones
y se las pueden pasar datos (argumentos), por lo tanto, tambin se ver como se puede
pasar informacin a estos subprogramas, para lo cual tambin se ver un nuevo tipo de
datos que es el puntero.


Creaci n, ut i l i zaci n y t i pos de funci ones
Uno de los principios bsicos de la programacin estructurada es la divisin del programa en
mdulos conceptualmente distintos, lo que se llaman subprogramas o subrutinas (en C funciones).
La divisin:

1. Hace a un programa ms fcil de escribir, entender y corregir ( ). programacin estructurada
Es el clsico principio de divide y vencers, pudindose testear los mdulos
independientemente a travs de un programa driver (programa que exclusivamente pasa
argumentos al mdulo a testear para comprobar su correcto funcionamientos).

2. Acorta los programas, ya que cada vez que se quiera utilizar el cdigo de la subrutina, no
habr que repetir ese cdigo, simplemente habr que llamarla ( ). reusabilidad

3. Permite crear libreras de subprogramas de utilidades que pueden ser usadas por otros
programadores en otros programas ( ) y ampliar las funciones reusabilidad y modularidad
que tiene el C (como la librera matemtica que hemos usado en el captulo segundo y que
veremos posteriormente o las de entrada/salida).

4. Permite dividir un programa grande (varios miles de lneas) en varias partes y para varios
programadores ( ). Para ello las cabeceras de los subprogramas y lo que divisin del trabajo
realizan deben estar claros desde un principio.

5. Permite concebir el programa como un conjunto de cajas negras, que se pueden probar
separadas y que se comunican datos entre ellas (a travs de sus argumentos y del tipo de
dato que devuelven), permitiendo que detalles internos de las funciones queden ocultos
( ). abstraccin

Captulo
4
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


28
En C ya hemos usado una funcin que est definida por defecto y que es la primera que se
ejecuta, la funcin main
1
. Despus estn las funciones definidas por el usuario y las predefinidas o
funciones de librera. Las primeras requerirn su definicin, declaracin de uso y llamada desde otra
funcin, las segundas slo esto ltimo, ya que los dos primeros puntos estn realizados al hacer el
#include del correspondiente fichero de cabecera .h.

En lo que se refiere a la definicin de la funcin, en el primer captulo veamos que la
estructura de una funcin, ya sea la principal u otra, constaba de dos partes: cabecera y cuerpo. En
la cabecera se hacan declaraciones hacia el preprocesador y se defina el nombre de la funcin
(aunque no tenga argumentos siempre habr que poner parntesis). En el cuerpo, encerrado entre
llaves, se declaraban los datos propios (locales) de la funcin y sus sentencias.

La definicin de una funcin depende de si sta tiene argumentos o no y del tipo de funcin
que es. Si no tiene argumentos y es de tipo entero bastar en C con poner su nombre y una pareja
de parntesis (es buena prctica de programacin, aunque sea redundante, definir explcitamente el
tipo de dato de la funcin). Si la funcin tiene argumentos, stos hay que declararlos como otro tipo
de datos ms, pero fuera del cuerpo de la funcin (entre llaves) e inmediatamente despus del
nombre. Se puede hacer de dos maneras: fuera de los parntesis (C K&R) (parte izquierda) o dentro
de ellos de la funcin (ANSI C) (parte derecha):

f unci on ( ar g) i nt f unci on( i nt ar g)
i nt ar g; {. . . }
{. . . }

Si una funcin tiene varios argumentos habr que separar estos entre comas. As, una funcin
que tenga dos argumentos uno de tipo entero y uno de tipo flotante se tendra que declarar:

ot r a_f unci on( i , f ) ot r a_f unci on( i nt i , f l oat f )
i nt i ; {. . . }
f l oat f ;
{. . . }

Una vez realizada la definicin de la funcin, llega la segunda parte, que es la declaracin
dentro de la otra funcin que la vaya a usar (llamar). Una funcin se puede utilizar como una variable,
por eso mismo las funciones tienen tipo. El tipo por defecto es el entero, ya se ha comentado que en
estas funciones no es necesario (si conveniente) poner tipo; si es otro, habr que declararlas antes
de usarlas.

Esta declaracin se llama el prototipo de la funcin y puede ser ms o menos compleja
(incluyendo los tipos e identificadores de argumentos), dependiendo de las comprobaciones que
queramos que haga el compilador, lo normal es slo poner los tipos de datos de los argumentos, con
lo cual el compilador, a la hora de probar el cdigo, revisar el nmero de argumentos y su tipo. Si
hay discrepancia dar error (en C clsico no existan los prototipos y si los argumentos no coincidan
se produca una coercin de argumentos):


Declaracin prototipo float funcion ();
Con tipos de argumentos float funcin (int , float)
Hasta con nombres float funcin (int i, float f)


A diferencia de otros lenguajes, en C no existen dos tipos de subrutinas (por ejemplo en
Pascal los procedimientos [procedures] y las funciones), slo hay funciones, pero podemos hacer
que esas funciones devuelvan algo o no, o mejor dicho, podemos tener en cuenta lo que devuelven

1
La funcin main() se diferencia del resto, a parte del nombre, en que en ella comienza la ejecucin del
programa, y que sus argumentos ya estn definidos de antemano. Es sumamente extrao, pero una funcin
tambin puede llamar a main().
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


29
o no. En estos casos se utiliza la palabra reservada "void", para sealar algo que no interesa, como
el tipo de funcin que no devuelve nada o que no tiene argumentos.

Esto se puede ver en el ejemplo de ANSI C:

#i ncl ude <st di o. h>
mai n ( )
{
voi d ast er i scos( voi d) ; / * decl ar aci on o pr ot ot i po */

ast er i scos ( ) ; / * l l amada o uso*/
pr i nt f ( "Aqui pongo un mensaj e\ n") ;
ast er i scos ( ) ; / * l l amada */
}

voi d ast er i scos ( voi d) / * def i ni ci on */
{ / * cuer po */
i nt cont ; / * var i abl e l ocal a ast er i scos */

f or ( cont = 1; cont < 40; cont ++)
put char ( ' *' ) ;
put char ( ' \ n' ) ;
}

Este sera el caso de la funcin asteriscos, que es de tipo entero (por defecto) pero de la cual
no se utiliza el valor que devuelve. El flujo de control es como todos los lenguajes, es decir, cuando
se llama a asteriscos(), se empieza a ejecutar la primera sentencia de la misma, y cuando termina,
se devuelve la ejecucin a la funcin que la llam, main(), en la sentencia posterior a la llamada.

Algo que llama fuertemente la atencin es que, independientemente de los argumentos
definidos en la funcin, en la llamada se puede utilizar otro nmero distinto de argumentos. Si este
nmero es menor, parte de los argumentos quedarn indefinidos, si es mayor sern ignorados.
Como veremos, he aqu una de las ventajas de declarar los prototipos de las funciones
completamente, ya que el compilador nos lo advertir.

A diferencia de otros lenguajes, en C las funciones se pueden poner en cualquier orden y no
se pueden anidar, es decir, no se pueden declarar y crear funciones dentro de otras. Normalmente
se sigue un estilo de programacin que es poner primero la funcin principal y despus el resto de
funciones por orden de llamada. Si usamos prototipos, este orden no presentar ningn error, si no
deberamos definir las funciones de acuerdo a su uso y dejar la main() para el final.

Tambin hay que tener en cuenta que en buen estilo de programacin, la funcin main()
debera ser muy pequea y ser prcticamente una serie de llamadas a otras funciones (esto tendra
que hacerse en cualquier tipo de lenguaje).

Veamos un ejemplo de utilizacin de funciones. Se trata de hacer una funcin que calcule el
factorial de un nmero, el cdigo en C normal (en ANSI C habra que cambiar la declaracin de las
funciones y la definicin de sus cabeceras) puede ser:

#i ncl ude <st di o. h>
mai n( )
{
l ong f act or i al ( ) ; / * decl ar aci on */
i nt i ;
f or ( i = 1; i < 15; i ++)
{
i f ( f act or i al ( i ) == 0)
pr i nt f ( "Er r or en l a f unci on\ n") ;
el se
pr i nt f ( "El f act or i al de %d es: %l d\ n", i , f act or i al ( i ) ) ;
}
}
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


30


l ong f act or i al ( i nt ar gu)
{
l ong r esul t ado;

i f ( ar gu > 12) / * excede l a capaci dad de cuat r o byt es*/
{
pr i nt f ( "Er r or en ar gument o") ;
r et ur n ( 0L) ; / * asi gnaci on de val or a f unci on */
}
el se / * caso cor r ect o */
{
r esul t ado = 1;
whi l e ( ar gu ! = 1) / * cal cul o f act or i al */
{
r esul t ado = r esul t ado * ar gu;
ar gu - = 1;
}
r et ur n ( r esul t ado) ; / * asi gnaci n de val or a f unci on */
}
}

En el programa hemos visto: como hemos creado una funcin de tipo long para utilizar
nmeros ms grandes (hemos supuesto 4 bytes, aunque ahora son 8); como para cualquiera de las
dos salidas de la funcin, (al utilizar el if else) hemos tenido que asignar valores a la misma para que
no quede indeterminada, esto se hace con la sentencia return; y como hemos podido usarla en el
printf como si fuera una variable. La sentencia return provocar la terminacin de la funcin, si se usa
un argumento en la misma, ste ser el valor que devuelve la funcin.

De hecho, en la funcin main() tambin deberamos usar la sentencia return. Ya que la
funcin main() es por defecto de tipo entero pero no devuelve nada, se suele usar return(0) para
terminarla o en muchas ocasiones exit(numero), donde numero suele ser un cdigo de error (cero si
no lo hay).

Una peculiaridad de los lenguajes modernos es la recursividad (incluyendo el C), es decir, que
una subrutina se pueda llamar a s misma. La funcin que calcula el factorial podra modificarse
utilizando la definicin recursiva de factorial:

n! = n x ( n- 1) !

sin comprobar el rango de los datos del argumento que deben ser menores que 13, la funcin
podra quedar:

l ong f act or i al ( i nt ar gu)
{
i f ( ar gu == 1)
r et ur n( 1) ; / * est a es l a sal i da */
el se
/ * n! = n x ( n- 1) ! */
r et ur n ( f act or i al ( ar gu- 1) * ar gu) ;
}

Vemos como la funcin se llama a s misma hasta que el factorial valga 1, si no pusiramos
esa condicin, la funcin se llamara a s misma continuamente hasta que el argumento fuera
negativo y muy pequeo para la mquina o hasta que ocurra un error por falta de memoria (cada
llamada a una funcin llena la pila (el stack) del programa con sus argumentos y dato devuelto).

Ya hemos visto como se define una funcin, su tipo, su lugar (antes o despus de ser usada
con prototipo), y como se llama. Pero a una funcin, incluida la main(), se la pueden pasar
argumentos, y podemos hacer que estos sean modificados o no en la funcin. Para lo primero
usaremos los datos de tipo puntero.

PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


31
Paso de parmet ros a una funci n. Punt eros
Hemos dicho que en una funcin se pueden declarar variables locales, Qu ocurre con los
identificadores que se utilizan?, Se conoce una variable declarada en una funcin en otra? La
respuesta es que en C una variable declarada en una funcin (declaracin local) no es conocida por
las dems funciones (se almacenan en la pila o stack, una zona especial de la memoria al
llamarse a la funcin y se destruyen al terminar). Adems como todas las funciones tienen la misma
"categora", es decir, no hay un programa principal (como en Pascal o Ada), las variables declaradas
en la funcin main tampoco son conocidas por las dems funciones.

Otro "tipo" de variables son las globales, tambin llamadas en C externas, que son las
declaradas fuera de las funciones y sern conocidas por todas las funciones posteriores de ese
programa (en ese mismo fichero). Se puede tener acceso a cualquier variable externa anterior o
posteriormente declarada, incluso fuera de ese fichero, utilizando el calificador extern. Por tanto las
variables declaradas en el funcin main() no son variables globales.

El no poder anidar funciones y la independencia del lugar de declaracin, simplifican mucho
las reglas de visibilidad y mbito de variables (donde son conocidas), de tal manera que podemos
decir que slo existen tres:

1. Las variables locales slo son conocidas en las funciones que las declaran y crean.
Si son variables dentro de un bloque, slo son conocidas en ese bloque.

2. Las variables globales pueden ser conocidas por todas las funciones (siempre en
conveniente utilizar extern, para declarar que esa variable es global, no local).

3. Las funciones podrn ser llamadas por todas las funciones, independientemente de
donde se declaren (siempre usando el prototipo).

Un buen estilo de programacin, independientemente del lenguaje, es usar el mnimo nmero
de variables globales, ya que stas van a ser cambiadas en las funciones y puede que por varios
programadores con posibles conflictos. Supongamos que tenemos que hacer un programa extenso
entre varias personas, si dividimos el trabajo por funciones (mtodo normal) y no tenemos ninguna
variable global; cada uno, en sus funciones, es libre de poner los identificadores adecuados a sus
datos, sin tener en cuenta los que han puesto los dems programadores (no hay interaccin al ser
declaraciones locales), con lo cual, se evitarn muchos fallos inesperados: yo pensaba que estaba
cambiando mis variables... y resulta que era la global...

Como hemos dicho, una variable global puede ser conocida y por lo tanto se puede leer y
cambiar en todas las funciones, y adems, podemos pasar a la funcin argumentos, por lo que
podemos decir que existen tres maneras de transferir datos a una funcin:

1. Utilizando variables globales. No es conveniente salvo que se declaren extern.

2. Pasando argumentos por valor, es decir, copiando el valor que tiene en ese
momento la variable que usamos de argumento ( ) al argumento de parmetro actual
la funcin que estamos llamando ( ). De esta forma si cambiamos el parmetro formal
valor en la funcin slo cambiamos el valor de la copia pero no el del original (es la
forma que hemos visto hasta ahora).

3. Pasando los argumentos por referencia. No se pasa el valor (no se hace copia) de la
variable sino su direccin en memoria. Esto quiere decir que los parmetros actuales
y formales son la misma (misma direccin) y si se cambia su valor en la funcin se
cambia el valor de la variable que ha sido colocada como argumento en la llamada.

En otros lenguajes, como el Fortran, el paso de argumentos es siempre por referencia, en
otros como el Pascal hay que utilizar la palabra reservada var delante de un argumento para decir
que ste ha sido pasado por referencia. En C existe otro mecanismo para el paso por referencia que
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


32
es la utilizacin de punteros (los dos primeros puntos: variables globales y paso por valor ya se han
visto).

Podemos entender un tipo de dato puntero como una identificacin simblica, es decir una
variable, que contiene una direccin. Antes tenamos variables que contenan un valor y que estaban
almacenadas en una determinada posicin y ahora tenemos un tipo de variable que puede contener
en vez de datos direcciones.

Para manejar direcciones y valores de variables, el C nos proporciona dos operadores, el de
direccin "&" y el de indireccin "*". Los dos son de tipo unario y de alta precedencia. El primero
usado delante de una variable nos dar la direccin de la misma y el segundo delante de un puntero
nos dar el valor que contiene la direccin almacenada en ese puntero.

Para declarar punteros tendremos que utilizar el asterisco, que designa al identificador como
un puntero (ojo con la confusa nomenclatura, no es el operador indireccin):

i nt *ent er o; / * est o es un punt er o ent er o */
f l oat *r eal ; / * est o es un punt er o f l ot ant e */
i nt var i abl e; / *var i abl e nor mal de t i po ent er o */

Aunque todos los punteros sean una direccin, es decir, algo parecido a un entero unsigned,
en la direccin que contienen puede haber distintos tipos de datos, por lo que en la declaracin del
puntero tambin se pone ese tipo de dato y se dice para abreviar que ese puntero es del tipo de dato
apuntado. As, se hablar de punteros a enteros, flotantes, etc., dependiendo de lo que contenga la
direccin que est en el propio puntero.

La utilizacin de los operadores ser la siguiente (ejemplos):

var i abl e = 23; / * supongamos que est en l a di r ecci n 5000 */
ent er o = &var i abl e; / * al macenado en 5004 per o cont i ene 5000 */
var i abl e = *ent er o; / * equi val ent e a l a pr i mer a */

En el primer caso hacemos una asignacin normal de una variable entera (supongamos que
esa variable est almacenada en la direccin $5000, en el segundo caso estamos asignando la
direccin de la variable ($5000) a travs del operador "&", al puntero entero (el puntero podra estar
almacenado en la direccin $5004 direccin consecutiva y despus de la asignacin contener el
valor $5000) y en el tercer caso, estamos asignando el valor de lo apuntado por un puntero, operador
"*", a una variable normal; el puntero que est en $5004, contiene la direccin $5000, en esta
direccin est el valor 23, y es este valor el que se asigna a "variable" de nuevo). En el siguiente
captulo veremos otras operaciones (operandos) que se pueden utilizar con punteros dada su
equivalencia con los arrays.

Direccin Memoria Direccin Memoria

...
$5000 (variable) 23 $5000 (variable) 23

$5004 (entero) xx $5004 (entero) $5000

$5008 xx $5008 xx
...
primera asignacin segunda y tercera asignacin

Hay que recordar, y esto es importante, que para usar un puntero antes hay que asignarle una
direccin vlida, no se puede trabajar con punteros no asignados, porque contendrn direcciones
inesperadas, normalmente cero.

Con el puntero se pueden hacer llamadas por referencia, simplemente haciendo que los
argumentos de la misma sean de este tipo. Supongamos que queremos crear una funcin que nos
intercambie dos enteros, como una funcin slo devuelve un valor, tendremos que pasar uno de los
&

*

PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


33
argumentos (o los dos) por referencia, para ello haremos los argumentos de tipo puntero y en la
llamada utilizaremos tambin las direcciones de las variables (por ejemplo usando el operador
direccin "&"):

i nt er cambi a( i nt *u, i nt *v) / * decl ar aci on de punt er os a ent er os */
{
i nt t emp; / * se necesi t a una var i abl e l ocal t empor al de buf er */

t emp = *u; / * l o apunt ado por u se pone en t emp */
*u = *v; / * l o apunt ado por v se pone en l o apunt ado por u */
*v = t emp; / * el val or de t emp se pone en l a di r ecci n v */
}

La llamada a esta funcin sera (falta el resto del cdigo):

i nt ent er o1 = 5;
i nt ent er o2 = 4;

i nt er cambi a( &ent er o1, &ent er o2) ;


Si no hubiramos puesto los argumentos de tipo puntero, el cambio slo se habra realizado
en el interior de la funcin (en la pila del programa, que una vez terminada la llamada se
sobreescribe con otras cosas), despus de ella los valores de entero1 y entero2 seran los mismos
que antes de la llamada.

mbi t o de dat os y modos de al macenami ent o
Hemos visto en el apartado anterior que existen dos tipos de variables, las locales y las
globales. Adems en C podemos controlar el modo de almacenamiento de las mismas, dado que es
un lenguaje de medio nivel, relativamente cercano al computador.

Hay cuatro tipos de adjetivos que se pueden dar a las variables:

AUTOMTICAS: es el modo por defecto, y pertenecen a este tipo, todas las variables
locales que hemos visto hasta ahora. Se declararn con la palabra reservada auto delante,
aunque todas son auto por defecto. Las variables automticas se crearn cuando la
funcin se llame y se destruirn cuando la funcin termine de ejecutarse. Su alcance ser
el del bloque donde estn encerradas. Aunque normalmente siempre se declaren las
variables al comienzo de la funcin (despus de la llave), no hay ningn problema en crear
dentro de las funciones otros bloques con {}y declarar dentro de ellos otras variables. No
estn inicializadas por defecto, pero se pueden inicializar con cualquier cosa que tenga ya
un valor, incluso una llamada a una funcin.

f unci on ( i nt ar g1, char ar g2)
{
aut o char l ocal 1; / * no ser i a necesar i o el aut o */
i nt l ocal 2; / * var i abl e l ocal a f unci on */
l ocal 1 = ' \ n' ;
l ocal 2 = 32;
{ / * ot r o ambi t o por l as l l aves */
i nt l ocal 2; / * l ocal es al nuevo ambi t o ocul t a l a ant er i or */
i nt l ocal 3;
l ocal 2 = 15; / * nos r ef er i mos a l a i nt er i or */
l ocal 3 = 4;
}
/ * ahor a l ocal 2 val dr a 32 y no 15, ya que es l a de f uer a */
/ * Est o no se puede hacer , l ocal 3 no exi st e y dar i a er r or es*/
l ocal 3 = 232;
}
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


34

EXTERNAS: es la forma de acceder explcitamente a las variables globales ya creadas,
utilizndose para ello la palabra reservada extern (hemos dicho que cuando accedemos a
una variable global en una funcin es muy conveniente declararla con extern para saber
con seguridad que es global y que se puede cambiar en la funcin). Con esta declaracin
no se crean variables globales, sino que slo se accede a ellas, por lo tanto no se pueden
inicializar en declaracin. Se supone que una variable externa est inicializada a cero.
Adems la podemos inicializar con una constante (se hace antes de ejecutar el programa).

int fuera; fun ()
main () {
{ extern int fuera;
extern int fuera; ...
... }
}

int fuera;
main () fun ()
{ {
extern int fuera; ...
... }
}

int fuera;
main () fun ()
{ {
int fuera; int fuera;
... ...
} }

En el primer caso se crea una variable global, que por lo tanto tendr validez durante toda
la ejecucin del programa y se declara explcitamente en las dos funciones, slo hay una
variable. En el segundo caso no existe la declaracin en la segunda funcin, pero como
est declarada antes que la funcin, es conocida implcitamente y puede ser utilizada por
su nombre, tambin slo hay una variable. En el tercer caso, se declaran dos variables
locales en las funciones con el mismo identificador, por lo que tenemos tres variables en
total y adems no podremos acceder a la variable externa (puede ser origen de errores
difciles de detectar si creemos que cambiamos la global en vez de la local o viceversa por
no haber usado un extern) ya que hemos utilizado su identificador localmente
(accederemos a las variables locales no a la global).

Cuando se habla del mbito de validez de una variable global, hay que tener en cuenta
tres aspectos: primero en que lugar del programa est declarada, ya que slo las
funciones declaradas despus pueden acceder a ella de forma implcita, segundo el
fichero en que est declarada (un programa puede estar confeccionado en varios ficheros),
ya que slo se la conocer implcitamente en ese fichero y tercero si se la utiliza
explcitamente con el adjetivo extern, siempre tendremos acceso a ella, est como est
declarada. Esta declaracin se puede hacer dentro de una funcin, para tener acceso a la
variable en esa funcin, o fuera de ella, para tener acceso a la variable en todas las
funciones posteriores (no recomendado).

ESTTICAS: Este adjetivo o atributo se refiere a la permanencia de las variables en el
programa, por lo que puede haber variables estticas locales o globales. Para ello se
utilizar la palabra reservada static. Las estticas locales permanecern despus de la
terminacin de las funciones que las declaran, por lo cual, conservarn su valor para
posteriores llamadas. Las estticas globales ya son permanentes de por s, por lo que en
este aspecto no cambian. La inicializacin es igual que las externas, pero adems se
inicializan slo lo hacen una vez, ya que esto dentro de una funcin podra dar problemas.

PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


35
REGISTROS: Las variables declaradas de esta forma son iguales que las automticas,
slo se le pide al compilador, que si puede, las almacene en los registros internos del
procesador. Si no puede hacerlo, simplemente ser almacenada de la forma usual. La
efectividad de esta declaracin depende en gran manera de la mquina en cuestin, por lo
cual puede haber grandes restricciones en su uso. La palabra reservada para ello es
register. Se suele hacer si consideramos que esa variable es muy importante y muy usada,
con lo cual ganaremos rendimiento en la ejecucin del programa.


Li brera de C est ndar
Ahora ya sabemos crear nuestras propias funciones, pero debemos recordar que en C existen
un gran nmero de funciones agrupadas en libreras, cuyos prototipos (variables, macros y tambin
constantes) se pueden incluir en un programa a travs de la instruccin del preprocesador #include
con su correspondiente fichero de cabecera (.h).

Hay una librera especial que es la librera estndar de C (ANSI C Standard Library, ISO C
library) o libc, que es una recopilacin de ficheros cabecera y bibliotecas con rutinas estandarizadas
por un comit de la Organizacin Internacional para la Estandarizacin (ISO), que implementan
operaciones comunes, tales como las de entrada y salida o el manejo de cadenas. En algunos casos
ser necesario incluir alguna parte en el enlazador al compilar un programa (como la matemtica),
en otros ya estar en libc por defecto. Lo que si deberemos hacer es incluir el fichero de cabecera
correspondiente en nuestro cdigo como hemos dicho.

En el estndar hay 15 ficheros de cabecera
1
:


<assert.h> Contiene la macro assert (asercin), utilizada para detectar errores lgicos y otros
tipos de fallos en la depuracin de un programa sobre todo cuando se hacen
llamadas al sistema.
<ctype.h> Contiene funciones para clasificar caracteres segn sus tipos o para convertir
entre maysculas y minsculas independientemente del conjunto de caracteres
(tpicamente ASCII o alguna de sus extensiones).
<errno.h> Para analizar los cdigos de error devueltos por las funciones de biblioteca. errno.
Nos da los cdigos y mensajes de error estndar.
<float.h> Contiene la definicin de constantes que especifican ciertas propiedades de la
biblioteca de coma flotante, como valores mximos y mnimos de precisin o
rango. Tambin una serie de macros que nos dan los lmites que podemos
alcanzar con los flotantes y los dobles.
<limits.h> Contiene la definicin de constantes que especifican ciertas propiedades de los
tipos enteros (en general desde char a long) como por ejemplo el rango.
<locale.h> Para la funcin setlocale() y las constantes relacionadas. Se utiliza para
seleccionar el entorno local apropiado (configuracin regional) de la mquina,
como la moneda.
<math.h> Contiene las funciones matemticas comunes.
<setjmp.h> Declara las macros setjmp y longjmp para proporcionar saltos de flujo de control
de programa no locales.
<signal.h> Para controlar algunas situaciones excepcionales como la divisin por cero.
<stdarg.h> Posibilita el acceso a una cantidad variable de argumentos pasados a una
funcin.
<stddef.h> Para definir varios tipos de macros de utilidad.
<stdio.h> Proporciona el ncleo de las capacidades de entrada/salida del lenguaje C. Tnato
sobre consola como sobre ficheros.
<stdlib.h> Para realizar ciertas operaciones como conversin de tipos, generacin de
nmeros pseudo-aleatorios, gestin de memoria dinmica, etc.

1
http://www.csse.uwa.edu.au/programming/ansic-library.html
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


36
<string.h> Para manipulacin de cadenas de caracteres. Prximo tema.
<time.h> Para tratamiento y conversin entre formatos de fecha y hora.


De las cuales, las ms importantes (en rojo negrita) son la conocida stdio.h, la de caracteres
ctype.h, la math.h (para funciones matemticas que despus veremos), la librera estndar stdlib.h, y
la de manipulacin de cadenas string.h.

Las ms importantes son
1
:

1. Funciones de caracteres: En el estndar ANSI estn en la librera ctype.h. Las
funciones/macros ms importantes son: isalpha(ch), para saber si un carcter es
alfanumrico, isdigit(ch) si es dgito, islower(ch), si es letra minscula, isspace(ch), si
espacio y si es mayscula isupper(ch).

2. Funciones matemticas. Estn definidas en la librera math.h y las principales funciones
son: valor absoluto fabs(x), raz cuadrada sqrt(x), potencia base exponente pow(x,y),
techo ceil(x), suelo floor(x), exponente e exp(x), logaritmo neperiano log(x) y decimal
log10(x). Las trigonomtricas son: coseno cos(x), seno sin(x), tangente tan(x),
arcocoseno acos(x), arcoseno asin(x), arcotangente atan(x), arcotangente de y/x
atan2(y,x), coseno hiperblico cosh(), seno hiperblico sinh() y tangente hiperblica
tanh(). Todas ellas son de tipo double. Adems habr que linkar con la librera
matemtica (-lm) ya que la inclusin de math.h slo introduce las definiciones de
constantes, tipos de datos y prototipos, pero no los cuerpos de las funciones.

3. Funciones de strings, string.h. Las ms importantes se vern en el siguiente tema.

4. Conversin de caracteres y otras funciones: Se incluyen en la librera stdlib.h y son de
uso cotidiano.

Las de conversin como de tira a entero atoi(tira), de tira a flotante atof(tira), etc.
Matemticas como valor absoluto abs(), o la de generacin aleatoria rand().
Entorno como la de salida exit(), llamadas al sistema system(), entorno de ejecucin
como getenv().
Peticin de memoria dinmica. Como hemos repetido, un puntero necesita
inicializarse antes de ser usado, esto se puede hacer dndole la direccin de alguna
variable (con "&") o array (prximo captulo) o tambin pidiendo memoria dinmica al
sistema
2
. Para hacerlo, existen dos funciones definidas en la librera estndar
stdlib.h:

punt er o = mal l oc( l ongi t ud) ;
punt er o = cal l oc( uni dades, byt es por uni dad) ;
punt er o = r eal l oc( punt er o, nueva_l ongi t ud) ;

Y despus del uso liberarla:

f r ee( punt er o) ;

Existen otros ficheros estndar posteriores (NA1 del aadido del 95, C99 del nuevo estndar
del 99 y C11 del reciente C de 2011):

<complex.h> Conjunto de funciones para manipular nmeros complejos (nuevo en C99).
<fenv.h> Para controlar entornos en coma flotante (nuevo en C99).

1
http://www.acm.uiuc.edu/webmonkeys/book/c_guide/
2
Cuando realizamos un programa, lo compilamos y lo cargamos en el computador para ejecutarlo,
estamos reservando suficiente memoria para almacenar en ella todas las variables del programa (variables
externas o globales) y un espacio de pilas (stack) para ir creando y destruyendo las variables locales. Existe un
tercer "tipo" de memoria, la memoria dinmica o montn (heap) que se busca en tiempo de ejecucin a travs
de llamadas del lenguaje que tienen correspondencia directa con llamadas del sistema operativo.
PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


37
<inttypes.h> Para operaciones de conversin con precisin entre tipos enteros (nuevo
en C99).
<iso646.h> Para utilizar los conjuntos de caracteres ISO 646 (nuevo en NA1).
<stdalign.h> Para alimiamiento de datos (del C11).
<stdatomic.h>Para operaciones atmicas entre threads (del C11).
<stdbool.h> Para el tipo booleano (nuevo en C99).
<stdint.h> Para definir varios tipos enteros (nuevo en C99).
<stdnoreturn.h>Especfico para funciones que no devuelven nada (del C11).
<tgmath.h> Contiene funcionalidades matemticas de tipo genrico (type-generic)
(nuevo en C99).
<threads.h> Para manejar threads y sus mecanismos de sincronizacin (del C11).
<wchar.h> Para manipular flujos de datos anchos y varias clases de cadenas de
caracteres anchos (2 o ms bytes por carcter), necesario para soportar caracteres de
diferentes idiomas (nuevo en NA1).
<uchar.h> Para manejar caracteres del Unicode (del C11).
<wchar.h> Define funciones para manejar cadenas de caracteres largas (del NA1).
<wctype.h> Para clasificar caracteres anchos (nuevo en NA1).


Aunque el nombre y las funciones incluidas en cada librera pueden cambiar, uno de los
intentos del estndar ANSI C es fijar las libreras definidas en todos los sistemas.


Argument os de main
La funcin principal main() es diferente a las otras ya que normalmente no es llamada por
nadie y se ejecuta automticamente al inicio del programa. Entonces para qu usar argumentos en
ella? La respuesta es sencilla, los argumentos de main son los argumentos del propio programa
(pensad en lnea de comandos sobre todo en sistemas Linux). As, cuando ejecutamos un programa
desde el sistema y le pasamos unos argumentos debe existir una manera de obtenerlos dentro del
programa.

shel l si st ema$ pr ogr ama ar gument o1 ar gument o2

Para ello main toma dos argumentos clsicos (incluso un tercero que no mencionamos): una
variable de tipo entero que nos indica el nmero de argumentos que nos han pasado desde el
sistema y una variable que es un array de cadenas (strings) de caracteres con los valores de los
argumentos. En el ejemplo la primera variable valdra 2 y las cadenas seran argumento1 y
argumento2.

De arrays y cadenas trata el siguiente tema.





PROGRAMACIN EN ANSI C FUNCIONES Y PUNTEROS


38





39
Dat os est ruct urados

En el quinto captulo se avanza en la declaracin de datos, viendo nuevos tipos de datos
estructurados: primero el array como conjunto de datos del mismo tipo (tanto escalares
como estructurados), haciendo nfasis en su equivalencia con los punteros y en un array
especial que es el de caracteres o string (cadena); y segundo las estructuras (struct) como
conjunto de datos de diferente tipo. Se termina con los enumerados.

Ar rays y punt eros
Los arrays surgieron por la necesidad de agrupar un conjunto de elementos del mismo tipo, de
una forma eficiente. Vamos a ver esto con un ejemplo, supongamos que estamos haciendo un
programa que trabaja con los litros por metro cuadrado cados en un ao, si no dispusiramos de los
arrays, tendramos que definir doce variables de tipo flotante a las que tendramos que asignar
valores, veamos en un fragmento de programa como se hara con y sin arrays:

f l oat l l uvi a[ 12] ; f l oat l l u_1, l l u_2, l l u_3, l l u_4,
i nt i nd; l l u_5, l l u_6, l l u_7, l l u_8, l l u_9,
l l u_10, l l u_11, l l u_12;
f or ( i nd = 1; i nd <= 12; i nd++)
scanf ( "%f ", l l uvi a[ i nd] ) ; scanf ( "%f ", &l l u_1) ;
. . . . . scanf ( "%f ", &l l u_2) ;
scanf ( "%f ", &l l u_3) ;
. . . . .

Se ve claramente que la segunda forma es muy poco prctica, imaginemos lo que tendramos
que hacer si nos interesara la lluvia cada cada da en un ao.

Podemos definir a los datos estructurados como una reunin de tipos simples caracterizados
por un mtodo de estructuracin particular. En concreto, el array es un grupo de elementos del
mismo tipo a los que se les da un nombre comn, y donde los datos individuales de los que est
compuesto pueden ser accedidos por medio de un ndice.

Los arrays se declaran como cualquier tipo de variable vista anteriormente, salvo que habr
que encerrar entre corchetes el nmero de elementos que queremos almacenar. A diferencia del
Fortran, en C los arrays comienzan en la posicin 0 y a diferencia del Pascal, en C slo se da este
valor en el array y no el rango entre los que puede variar el ndice, la declaracin sera (en C99 se
pueden definir arrays dinmicos siempre y cuando la variable del tamao tenga ya un valor):

i nt ar r ay_de_ent er os[ 100] ;
i nt ar r ay_de_ent er os[ n] ; / * par a C99, n t i ene que t ener ya val or */
Captulo
5
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



40
En este caso se est declarando un array de 100 enteros. Si queremos acceder a un
elemento del array, simplemente pondremos el nombre del array seguido entre corchetes del ndice
del elemento en cuestin:

ar r ay_de_ent er os[ 99] = 10; / * at enci on es el ul t i mo el ement o */

El ndice de la variable siempre ser entero y el tipo de variable de que est compuesto el
array puede ser cualquier otro tipo.

Es un error muy comn acceder al ltimo elemento de array repitiendo el nmero de
elementos (en el ejemplo el elemento 100). Esto es incorrecto, ya que el array empieza en cero, y
ese dato no existe. An la situacin puede ser peor: que el compilador no nos diga nada, y al
ejecutar el programa puede que funcione, pero debemos saber que ese elemento est machacando
alguna otra variable, con lo que el error puede ser difcil de encontrar.

En ANSI C los arrays se pueden inicializar sin necesidad de ser estticos (a diferencia del C
K&R), si lo son valdrn cero. Adems para inicializarlos explcitamente, habr que poner entre llaves
los valores de cada elemento separados por comas:

i nt di as[ ] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

Si no ponemos todos los elementos, los restantes valdrn cero si es esttico, si ponemos
demasiados el compilador nos dar un error. No siempre es necesario dar un lmite a los arrays, ste
se puede tomar de la inicializacin (como en el ejemplo anterior, en el cual se poda poner 12).

Un aspecto diferenciador de otros lenguajes es que los arrays por definicin tambin son
punteros (constantes de tipo puntero), su nombre es un puntero al primer elemento del array, por lo
tanto es verdad que:

ar r ay == &ar r ay[ 0] ;

Por lo tanto, el uso de arrays y punteros es equivalente, siempre que tengamos en cuenta que
un array es un puntero (una direccin) constante que no puede cambiar de valor. Las operaciones
que se pueden realizar con punteros:

1. La asignacin "=" de dos punteros igualar las direcciones a las que apuntan.

2. La comparacin "==" dice si las direcciones a las que apuntan son iguales.

3. La indireccin "*" nos da el valor de la direccin que contienen.

4. La direccin "&" nos da la direccin donde est guardado el puntero, no la direccin a la
que apunta.

5. El incremento o decremento de punteros "+" "-" "++" "--" aumenta o reduce la direccin a
la que apunta en tantas unidades como bytes ocupe el tipo al que pertenece, es decir, si
tenemos un puntero de tipo float y en nuestro sistema el tipo float ocupa cuatro bytes, un
incremento del puntero en una unidad aadir cuatro bytes a la direccin a la que apunta:

punt er o_f l ot ant e <=> di r ecci n
punt er o_f l ot ant e + 1 <=> di r ecci n + 4

6. La diferencia "-" de dos punteros nos dar en nmero de unidades que les separan
(normalmente dentro de un array). Esto significa que como un array es un puntero, si le
sumamos algo, lo que estaremos haciendo es trasladarnos por sus elementos:

ar r ay + 2 == &ar r ay[ 2]

Para ver esto se puede hacer el siguiente programa:

PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



41
#i ncl ude <st di o. h>
#def i ne TAM 3
mai n ( )
{
i nt f echas[ TAM] , *pun, i ndi ce;
doubl e f act ur as[ TAM] , *punf ;

pun = f echas; / * asi gnaci on de punt er os */
punf = f act ur as;
pr i nt f ( "t amaos %l u %l u \ n", si zeof ( i nt ) , si zeof ( doubl e) ) ;
f or ( i ndi ce = 0; i ndi ce < TAM; i ndi ce++)
pr i nt f ( "punt er os + %d: %10p %10p \ n", i ndi ce,
pun + i ndi ce, punf + i ndi ce) ;
}

Que producira una salida parecida a sta:

t amaos 4 8
punt er os + 0: 0x7f f f 3df 925d0 0x7f f f 3df 925e0
punt er os + 1: 0x7f f f 3df 925d4 0x7f f f 3df 925e8
punt er os + 2: 0x7f f f 3df 925d8 0x7f f f 3df 925f 0

Esta equivalencia entre punteros y arrays tambin tiene sus consecuencias en las llamadas a
funciones, ya que los arrays siempre se pasarn por referencia, es decir, pasaremos su direccin (el
propio nombre). As, si hacemos una funcin que calcule la media de un array de enteros:

medi a( i nt ar r ay[ ] , i nt n)
{
i nt i ndi ce;
i nt suma;
f or ( i ndi ce = 0, suma = 0; i ndi ce < n; i ndi ce++)
suma += ar r ay[ i ndi ce] ;
r et ur n( suma/ n) ;
}

Puede ser fcilmente convertida a punteros:

medi a( i nt *pa, i nt n)
{
i nt i ndi ce;
i nt suma;
f or ( i ndi ce = 0, suma = 0; i ndi ce < n; i ndi ce++)
suma += *( pa + i ndi ce) ;
r et ur n( suma/ n) ;
}

Se observa que cuando se declara el argumento array en la funcin no hace falta dar su
tamao, ya que ste es tomado de la llamada a la funcin. Y como la representacin de arrays o
punteros es la misma, de hecho se podra usar cualquiera de las dos funciones sin necesidad de
cambiar la llamada. Tambin es de destacar que la operacin *(pa +indice) lo que hace es
desplazar el puntero segn el tipo de elemento al que apunta y despus obtener su valor, es
equivalente a obtener los valores de los distintos elementos de un array. Hay que tener en cuenta
que cuando se utilizan punteros no se chequea que nos hayamos salido del array que apuntamos, lo
cual puede ser muy peligroso y que slo se pueden incrementar los punteros variables como pa
pero no los constantes como puede ser el nombre de una array, se puede ver ms claro con la
siguiente equivalencia imposible:

ar r ay = ar r ay + 3; 5 = 5 + 3;

es tan absurdo intentar cambiar el valor de la constante entera "5" como la direccin del array.
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



42
Ar rays mul t i di mensionales
Al igual que con otros lenguajes, en C se pueden definir arrays de varias dimensiones,
simplemente declarando cada dimensin entre corchetes:

i nt mat r i z[ 4] [ 5] ;

a diferencia del FORTRAN su organizacin es por filas, el orden de los elementos para el ejemplo
anterior ser:

00, 01, 02, 03, 04, 10, 11, 12. . .

En C una array de dos dimensiones es una array de arrays de una dimensin, es decir, un
array a punteros (es equivalente pero con distintas formas) y por eso, para acceder a un elemento no
se escribe matriz[3,4], sino matriz[3][4]. La equivalencia sera:

i nt *mat r i z_punt er os[ 4] ;

Pero a diferencia de la forma de array multidimensional donde se reservan 4 x 5 enteros, en
este caso slo se crea espacio para cuatro punteros para los que se necesita crear espacio e
inicializar un array de 5 enteros con las peticiones de memoria dinmica vistas en el anterior captulo.








Al igual que los unidimensionales, los multidimensionales se pueden inicializar separando
cada dimensin por llaves o directamente teniendo en cuenta su almacenamiento (para una de 2x2):

i nt mat r i z[ 2] [ 2] = { {1, 2}, {2, 4} };
i nt mat r i z[ 2] [ 2] = { 1, 2, 2, 4 };

Debido al mtodo de almacenamiento, tambin se puede trabajar con ellos como si fueran
punteros, as, si tengo un array de enteros de 4x2 y un puntero a enteros con la misma direccin, es
equivalente irse a la tercera fila segundo elemento, que al almacenado en la sexta posicin:

punt er o + 5 == ar r ay[ 2] [ 1]

Otra caracterstica de los arrays multidimensionales es que la omisin de una dimensin nos
indica una agrupacin interna de la misma (slice), es decir, si tenemos un array de dos dimensiones
(5x8) y slo utilizamos un corchete, nos estamos refiriendo a las filas. Esto significa, como hemos
dicho, que un array de dos dimensiones es equivalente a un array de punteros (a los que hay que
definir la segunda dimensin) o a un puntero de punteros al que hay que definir las dos dimensiones:

mat r i z[ 1] == &mat r i z[ 1] [ 0] / * l a f i l a 1 */
. . .

i nt mat r i z[ 5] [ 8] i nt *punt er o[ 5] / * como ar r ay de punt er os*/
f or ( i =0; i <5; i ++) / *pet i ci on de memor i a par a l a segunda di mensi on */
pun[ i ] = ( i nt *) cal l oc( 8, si zeof ( i nt ) ) ;
. . .
pun[ 2] [ 2] = 28; / *ej empl os de acceso como mat r i z o punt er o*/
*( pun[ 2] +2) = 28;
. . .










Malloc / Calloc

PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



43
i nt mat r i z[ 5] [ 8] i nt **punt er o / * como punt er o a punt er o */
/ * habr i a que pedi r memor i a par a l os punt er os, mas compl ej o */
dobl e = ( i nt **) cal l oc( FI L, si zeof ( i nt *) ) ; / * pr i mer a di m*/
f or ( i =0; i <FI L; i ++)
dobl e[ i ] = ( i nt *) cal l oc( COL, si zeof ( i nt *) ) ; / * segunda di m*/
. . .
dobl e[ 2] [ 2] = 39; / *ej empl os de acceso como mat r i z o punt er o*/
*( dobl e[ 2] +2) = 28;
. . .

Por ltimo, cuando se llama a una funcin con arrays multidimensionales, siempre hay que
decirle el tamao de la fila (al menos), es decir el nmero de columnas, ya que lo que hacemos es
pasarle a la funcin realmente un puntero a un array de filas como la segunda versin del ejemplo
anterior (es responsabilidad del programador no pasarse con el nmero de filas al recorrerlas dentro
de la funcin).

As, estas definiciones son equivalentes:

f unci n( i nt mat r i z [ 2] [ 3] ) {. . . } / * con l as dos */
f unci n( i nt mat r i z [ ] [ 3] ) {. . . } / * con una di mensi n */
f unci n( i nt ( *mat r i z) [ 3] ) {. . . } / * punt er o a ar r ay */

En el primer caso le estamos pasando un array de seis elementos (la matriz es de 2x3); en el
segundo le estamos diciendo que el nmero de elementos por fila es 3, con lo cual ya se sabe la
organizacin completa (no pasarse con las filas); en el tercero un puntero a un array de 3 enteros
(como una fila), se usan los parntesis ya que el operador [ ] tiene ms precedencia que el
operador *. No confundir con i nt *mat r i z [ 3] que sera un array de 3 punteros.



Ti ras de caract eres: st ri ngs
En C no existe como tipo el string o tira de caracteres (un mensaje o sucesin de caracteres
con una determinada longitud), simplemente se asocia este tipo de dato al array de caracteres
terminados en el carcter nulo '\0' que se usa para conocer la longitud del mismo (funciona como
terminador y ocupa una posicin):

char t i r a[ 20] ;

Las tiras, como los arrays, son tambin punteros (constantes), pero para inicializarlos y no
tener que trabajar directamente con el carcter nulo, se usan las comillas, las cuales lo incluyen
automticamente:

char t i r a[ 20] = "est o es un mensaj e";
char *pun_t i r a = "Car ay";

En la inicializacin, todos los caracteres sin asignar sern puestos a nulos. Siempre hay que
poner una longitud igual o mayor que la necesaria, ya que si no estaramos cometiendo un error. Por
eso, tambin se pueden declarar sin poner la longitud, con lo cual se cogera como longitud la del
mensaje ms el carcter nulo.

char t i r a[ ] = "Est e es ot r o mensaj e";

que tendr una longitud de 21 (20 del mensaje y uno del nulo).

Se puede acceder a los caracteres individuales utilizando corchetes:

t i r a[ 12] = ' s' ;
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



44

Para la entrada y salida se utiliza el conversor %s, al ser la tira por definicin un puntero, no
hay que leerlo con el operador direccin &, actuando la propia tira de caracteres encerrada entre
comillas como un puntero:

pr i nt f ( "%s\ n", "Ot r o mensaj e") ;
scanf ( "%s", t i r a) ;
pr i nt f ( "%s, %u, %c \ n", "Me", "oyes", *"bi en") ;

en el ltimo caso estamos pintando la tira "Me", la direccin de "oyes" (ser una direccin del propio
cdigo del programa) y lo que contiene la direccin de "bien" (es decir bien) como carcter (el
primero, o sea, b).

Tambin existe la posibilidad de tener tiras de varias dimensiones en forma de array de
punteros a char :

char *ar r ay[ 5] = {"a", "b", "c", "d", "e"};

define cinco punteros a caracteres. As:

*ar r ay[ 0] == "a", *ar r ay[ 1] == "b", . . . .

Esta definicin es equivalente a definir un array multidimensional de caracteres:

char ar r ay [ 5] [ 8] = {"a", "b", "c", "d", "e"};

pero en el caso anterior, el tamao de los arrays era variante y se tomaba de la longitud del mensaje
de inicializacin (en todos los casos 1) y en este la longitud es fija y es igual a 8.

En C existen varias funciones definidas para trabajar con tiras de caracteres dentro de la
librera estndar. El primer grupo de funciones corresponde a la entrada / salida. Al igual que con los
caracteres, en los que podamos usar las funciones printf y scanf o getchar y putchar, existen para
las tiras las mismas funciones que se conocen como gets y puts.

El uso del scanf es el que ya hemos mencionado, slo hay que tener en cuenta tres
precauciones:

1. Si queremos leer una tira de caracteres con scanf, no podemos declararla como
puntero a char, ya que en esta declaracin no decimos en ninguna parte el tamao de
la tira, recordad que se coga de la inicializacin.

2. Si utilizamos el formato de array de caracteres, hay que tener en cuenta que no se
podrn usar dentro de la tira los caracteres espaciadores como retorno de carro,
espacio o tabulador, ya que la funcin lo considerar como final de entrada.

3. El scanf no quita del buffer de lectura el \n introducido para leer los datos, con lo que
permanecer para la siguiente lectura.

Debido a esto, el scanf slo se utiliza cuando se quieren leer varios datos de distinto tipo a la
vez, cuando slo se quieren leer tiras, es mucho ms cmodo y compacto el uso de gets().

El gets() toma caracteres desde la entrada hasta que se pulsa el enter (\n) y una vez
encontrado lo convierte al carcter nulo y lo pone al final de la tira. Su declaracin est hecha en
<stdio.h>.

Como dijimos, hay que tener en cuenta que siempre se deben usar punteros con direcciones
vlidas, por eso lo normal es utilizar un array:

char t i r a[ 81] ;
get s( t i r a) ;
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



45

pero tambin se puede usar el valor devuelto que ser un puntero a carcter, con lo cual tendramos:

char t i r a[ 81] ; / * decl ar aci on de ar r ay */
char *pt r ; / * decl ar aci on de punt er o a car ct er */
pt r = get s( t i r a) ; / * l ect ur a de dos f or mas */

Adems, si se encuentra el carcter EOF o ha habido algn error en la lectura, la funcin
devolver el carcter nulo que se puede especificar en el programa con la constante NULL, definida
en la librera stdio.h, y por lo tanto hacer los chequeos de entrada convenientes:

whi l e ( get s( t i r a) ! = NULL)
. . .

Para realizar la salida se utiliza puts(), que lo nico que necesita es una tira:

char t i r a[ ] = "Hol a";
put s( t i r a) ;
put s( &t i r a[ 2] ) ;

Empezar a pintar todos los caracteres que encuentre hasta encontrar un nulo (Qu problema como
no lo encuentre! ya que no parar, llenando la pantalla de tonteras), el cual convertir en retorno de
carro y pintar la tira. La diferencia con printf es que esta ltima no incorpora automticamente el
retorno de carro y hay que ponerlo explcitamente.

A parte de las funciones de entrada/salida, existen definidas en la librera estndar (usar
string.h) cuatro funciones para trabajar con tiras de caracteres:

strlen(): toma una tira como argumento y devuelve un entero con su longitud sin contar
el carcter nulo.

strcat(): toma como argumentos dos tiras de caracteres y devuelve en la primera la
unin de ambas. Para ello debe asegurarse que en la primera tira caben las dos
unidas.

strcmp(): toma como argumentos dos tiras de caracteres y compara sus contenidos,
no sus direcciones. Devuelve un entero que indica si las tiras son iguales o no. En el
primer caso devolver un 0, en el segundo un entero positivo o negativo dependiendo
de si la primera tira es mayor o menor que la segunda. El valor absoluto ser la
diferencia de cdigo ASCII.

strcpy(): toma como argumentos dos tiras de caracteres y copia la segunda en la
primera, es decir sus contenidos, no sus direcciones. Hay que comprobar que la
primera tiene espacio suficiente.

En el siguiente ejemplo se muestra el uso de las funciones de strings para hacer una
bsqueda de uno, en un array de string:

#i ncl ude <st di o. h>
#i ncl ude <st r i ng. h>
#def i ne TAM 5
mai n ( )
{
i nt i ;
char nombr e[ 81] ;
char agenda[ TAM] [ 81] = {"Ant oni o", "I nes", "J uan", "Pepe"};

put s( "Escr i be el nombr e : ") ;
get s( nombr e) ;


PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



46
f or ( i =0; i <TAM; i ++)
{
i f ( st r cmp( nombr e, agenda[ i ] ) == 0)
{
pr i nt f ( "%s ", nombr e) ;
put s( "encont r ado") ;
br eak;
}
}
i f ( i ==TAM)
put s( "No exi st e") ;
}


Regi st ros: st ruct
Hemos visto en el apartado anterior, que los arrays sirven para tratar conjuntos de datos del
mismo tipo. Cuando queremos trabajar con grupos de elementos de distinto tipo se utiliza el struct
(estructura o registro). A los elementos se les llama miembros y pueden ser de cualquier otro tipo,
incluidos arrays y structs.

Los registros son conceptualmente como las fichas que se utilizan en las bases de datos:
personas (nombre, apellidos, ao de nacimiento, profesin, etc.); coches (matrcula, marca, modelo,
ao de fabricacin, etc.); libros de una biblioteca (ttulo, autor, editorial, ejemplares...) y cualquier otro
tipo de objeto que pueda ser descrito por sus partes o caractersticas, como un nmero complejo
(parte real e imaginaria).

La declaracin de este tipo de dato estructurado es la siguiente:

st r uct est r uct ur a
{
char nom_mi embr o1;
i nt nom_mi embr o2; / * cual qui er t i po */
. . . . .
char ar r ay[ 81] ;
} ;

Esto forma un patrn (tag) para despus declarar variables de este tipo. En la declaracin de
la variable, hay que escribir la palabra reservada struct seguida del nombre del patrn y del nombre
de la variable:

st r uct est r uct ur a mi a;

Como en el caso de las variables escalares, se pueden definir varias de golpe separndolas
por comas:

st r uct est r uct ur a mi a, t uya;

Tambin se pueden declarar en un bloque el tag y la variable, poniendo el nombre de esta
justo despus de la definicin del tag:

st r uct est r uct ur a {. . . } mi a;

Como en el caso de los arrays, en ANSI C las estructuras se pueden inicializar en la definicin
(en la declaracin de la variable, no en el tag):

st r uct est r uct ur a mi a = {' a' , 23, "pepe"};


PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



47
Para acceder a cada uno de los miembros se tendr que utilizar un operador nuevo que es el
punto ".", este operador es binario y tiene la ms alta prioridad:

mi a. nom_mi embr o1;

Tambin se pueden declarar arrays de estructuras (para formar por ejemplo bases de datos),
estructuras de estructuras y punteros de estructuras:

st r uct est r uct ur a mi a[ 100] ; / * ar r ay de est r uct ur as */

st r uct f echa / * est r uct ur a con est r uct ur as */
{
i nt di a, mes, agno;
} ;

st r uct hombr e
{
st r uct f echa naci mi ent o;
char nombr e[ 81] ;
} manuel ;

st r uct hombr e *t u; / * punt er o a est r uct ur as */

y utilizarlas:

mi a[ 32] . nom_mi embr o1 = ' 2' ;
manuel . naci mi ent o. di a = 13;
t u = &manuel ;

Cuando son punteros a estructuras, existe un nuevo operador (es una atajo) que accede
directamente a los miembros de la estructura, para evitar complicar la nomenclatura de C. Se
debera poner ( *t u) . naci mi ent o no *t u. naci mi ent o (el . Tiene ms precedencia que el *).
Ese atajo es el de pertenencia a estructura "->", es binario y tiene mxima prioridad (equivalente al "."
pero para punteros:

t u - > naci mi ent o <=> manuel . naci mi ent o

Las operaciones vlidas sobre estructuras son: copiarlas (asignarlas como una unidad), tomar
su direccin (&), y tener acceso a sus miembros (. o ->). La primera operacin implica que se
pueden pasar como argumentos a funciones y que adems stas pueden devolver estructuras.
Advirtase que se pasa a la funcin toda la estructura, por lo que suele ser mucho ms eficiente
pasar un puntero a esa estructura que la estructura entera.

#i ncl ude <st di o. h>
#i ncl ude <st r i ng. h>

st r uct punt o
{
char nombr e[ 80] ;
i nt x; ;
i nt y;
} gl obal ;

mai n( voi d)
{
st r uct punt o A = {"pepe", 11, 15}; / en i ni ci al i zaci n */
st r uct punt o B;
st r uct punt o suma( st r uct punt o, st r uct punt o) ; / * pr ot ot i po */

st r cpy( B. nombr e , "j uan") ; / *no se puede hacer = j uan, es const ant e */
B. x=4; / * i ni ci al i zaci n con asi gnaci n */
B. y=0;

gl obal =suma( A, B) ; / * l l amada a f unci n */
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



48

pr i nt f ( "Ahor a val e %s %d %d \ n", gl obal . nombr e, gl obal . x, gl obal . y) ;
}

/ *def i ni ci n de l a f unci n de t i po punt o */
st r uct punt o suma ( st r uct punt o uno, st r uct punt o dos)
{
uno. x += dos. x;
dos. y += dos. y;
r et ur n uno;
}

Un prototipo con punteros a estructuras quedara como:

st r uct punt o suma( st r uct punt o *, st r uct punt o *) ; / * pr ot ot i po */

Cuando sumamos un puntero a una estructura, lo que estamos haciendo es movernos tantas
posiciones de memoria como bytes ocupe la estructura. Pero hay que tener en cuenta que ese
nmero de bytes no tiene porque ser la suma que ocupan todos sus miembros ya que algunos de
ellos pueden estar alineados a posiciones pares o mltiplos de palabra y dejar huecos en la
estrucura.


t ypedef
Vimos en el tema uno la palabra reservada typedef que nos dejaba renombrar tipos de datos,
esto puede ser til para definir estructuras de forma abreviada. Pero tambin puede servir para
cualquier tipo de dato y aumentar la claridad del cdigo:

t ypedef i nt Longi t ud;
t ypedef char * Cadena;
t ypedef st r uct punt o Punt o;
t ypedef st r uct Compl ej o {f l oat r eal ; f l oat i mag; };
t ypedef st r uct punt o * Punt oer o;

hemos usado la inicial en mayscula para diferenciarlas del resto (punto es distinto de Punto).

Usar este tipo de datos es ms sencillo y claro, incluso se pueden usar en typecast:

Longi t ud l ongi ;
Cadena mensaj e;
Punt o geomet r i co;
Punt oer o i ni ci al ;
mensaj e = ( Cadena) mal l oc( 80) ;


Dat os enumerados
El ANSI C ha creado un nuevo tipo de dato no existente en C K&R llamado enumerado, para
ello se emplea la palabra reservada enum seguida del identificador del patrn de constantes y de
una lista de valores constantes entre llaves que la variable puede tomar (es parecido al tag de las
estructuras):

enumcol or es = {ver de, r oj o, amar i l l o, vi ol et a, azul , nar anj a};

Los valores se almacenan en enteros, de tal manera que verde vale 0, rojo 1, amarillo 2, etc.
Estos valores se pueden alterar ponindolos en la declaracin:

enumni vel es = {baj o = 100, medi o = 500, al t o = 2000};
PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



49

Si una de esas constantes se especifica, las siguientes tomaran valores consecutivos (medio
ser 101 y alto 102):

enumni vel es = {baj o = 100, medi o, al t o};

Tambin son vlidos los caracteres al ser anlogos a enteros:

enumcont r ol es = {campana= \ a , l nea = \ n , t abul ador = \ t };


Los operadores vlidos para este tipo de operandos son la asignacin simple, la comparacin
(slo igualdad y desigualdad) y los aritmticos para constantes. No se pueden usar como ndices de
un array.

Como en el caso de las estructuras, una vez que hemos definido el tipo de datos (tag)
tendremos que crear la variable correspondiente:

enumni vel es est e_ni vel ;


PROGRAMACIN EN ANSI C DATOS ESTRUCTURADOS



50


51
Aspect os avanzados

En el sexto captulo se describen otros aspectos avanzados del lenguaje como un tipo
especial de estructura llamada unin, el manejo de bits, el preprocesador, la entrada/salida
con ficheros y los punteros a funciones.


Uni ones (uni on)
Hay otro tipo de estructuras que son las uniones (unions). Tambin estn formadas por
campos, pero en este caso todos los campos estn en la misma posicin de memoria, reservando el
compilador memoria suficiente para almacenar el mayor de los campos (miembros).

La declaracin de las mismas es idntica a la de las estructuras:

uni on var i os
{
i nt numer o;
f l oat r eal ;
char car act er ;
} ;

Y su uso tambin, ya que se utilizar el operador ".", para variables y el operador "->" para
punteros a uniones (en el ejemplo anterior la union tendra el tamao del float).

Es responsabilidad del programador obtener el tipo de variable adecuado que est
almacenado en la estructura. No se puede almacenar un char e intentar obtener un float. Adems si
se inicializa, slo puede ser con el tipo del primer miembro de la unin.

Normalmente este tipo de datos se usan para almacenar variables que pueden ser de
distintos tipos o en cuestiones de bajo nivel (nivel hardware).


Manej o de bi t s
El C es un lenguaje de medio nivel que se realiz para sustituir, si es posible, al ensamblador,
por lo que debe permitir controlar casi todos los aspectos de la mquina y proporcionar herramientas
para poder trabajar en el mbito del bit.

Captulo
6
PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



52
Estos bits slo se pueden usar en bytes o palabras de la mquina donde est implementado
el compilador, por lo que slo se pueden utilizar en los tipos de datos char (byte) e int (palabra).

Los operadores de bits del C son los siguientes:

Operador Significado Ejemplo
~ Negacin ~(11111111) ==(00000000)
& AND 23 & 11 ==3
| OR 65 | 54 ==119
^ XOR 62 ^75 ==117
<< Desplazamiento a la izquierda (10010101) <<2 ==(01010100)
>> Desplazamiento a la derecha (10010101) >>2 ==(00100101)

Tendremos las operacin bsicas (no confundir con las operaciones lgicas) y adems
desplazamientos en ambos sentidos, para lo cual se introducen ceros en el sentido contrario.

Otra forma de trabajar con bits es construyendo estructuras donde cada miembro, llamado
campo, es una agrupacin de bits. Detrs de l se pone su dimensin en bits:

st r uct {
unsi gned enabl e : 1;
unsi gned st r obe : 2;
unsi gned al l ow : 1; } r egi st r o;

Una variable que se defina como registro (podra ser el registro de un controlador) se
almacenar en un entero (palabra natural), pero ocupar 4 bits: enable 1, strobe 2 y allow 1. Se
puede acceder a los bits con el operador pertenencia "." pero slo se podrn utilizar valores binarios
(0 1):

r egi st r o. al l ow = 0;
r egi st r o. st r obe = 3; / * es 11*/

Tambin se pueden poner etiquetas vacas para separar bits y etiquetas nulas para pasar al
siguiente entero (es til cuando accedemos a registros hardware de ese formato):

st r uct {
unsi gned enabl e : 1;
: 2; / * hueco de 2 bi t s */
unsi gned st r obe : 2;
: 0; / * nul o: si gui ent e ent er o */
unsi gned al l ow : 1; } r egi st r o;

en el primer byte habr 3 bits tiles (el 0 y el 3,4) y en el segundo slo 1 (el 0).


El preprocesador
El proceso de compilacin de un programa C, en trminos generales (ver apndices), consta
de dos fases: por un lado est la accin del preprocesador, y por otro, la propia compilacin. En la
primera, se transforma el cdigo fuente para que pueda ser compilado, fundamentalmente
sustituyendo constantes simblicas, aadiendo o quitando cdigo segn unas determinadas
condiciones y aadiendo los ficheros de cabecera. El carcter "#" es tpico de las instrucciones del
preprocesador y es antepuesto en todas ellas.

Para la definicin de constantes se utiliza la instruccin del preprocesador #define, que consta
de dos partes, por un lado los caracteres que se tienen que encontrar en el cdigo fuente y por otro
lado por lo que se tienen que sustituir (como estilo se suelen usar las maysculas para definir las
PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



53
constantes simblicas del preprocesador y las minsculas para el cdigo C). Un ejemplo de
definicin sencillo sera:

#def i ne MAXI MO 100

El analizar el cdigo y siempre que se encuentre MAXIMO lo sustituir por 100 antes de la
compilacin. Hay una excepcin a esta regla y es cuando esos caracteres estn encerrados entre
comillas en el cdigo, en tal caso no se producir dicha sustitucin.

Como no son instrucciones de C, no tienen que terminar en punto y coma, aunque
acompandolas se pueden usar comentarios y operaciones (aunque dichas operaciones no se
realizarn en el preprocesado).

Si la definicin a sustituir es muy larga, se puede utilizar el carcter \ para continuar en otra
lnea. Adems esa sustitucin se puede realizar en funcin de otros #define anteriores.

#def i ne DOS 2
#def i ne CUATRO Dos * Dos
#def i ne Mensaj e "Est o ser i ncor por ado"
#def i ne Px pr i nt f ( "x es %d. \ n", x)

Tambin se pueden definir macros con los #define para ello se dan los argumentos de las
mismas a continuacin:

#def i ne CUADRADO( x) x * x

de tal manera que cuando se encuentre cuadrado se sustituir el argumento por el
argumento * argumento:

z = CUADRADO( x) ; - > z = x * x;
z = CUADRADO( 2) ; - > z = 2 * 2;

Y tambin se pueden hacer indefiniciones de los #define con el comando #undef, sobre todo
para grandes programas en los que se tienen que redefinir temporalmente determinadas constantes
simblicas.

Otro comando muy til, que ya hemos utilizado, es el que se usa para hacer inclusiones de
ficheros. Cuando el preprocesador se encuentra el comando #include, tomar el fichero que viene a
continuacin y lo incluir en el cdigo fuente. De tal manera que podemos hacer un #include de un
fichero que a su vez tenga #define dentro.

El fichero se puede delimitar por <>que indicar que se busque en el directorio definido por
defecto en el proceso de compilacin o por " " que lo buscar en el propio directorio.

Tambin se puede realizar la inclusin o exclusin de cdigo, que ser muy til en la
depuracin de un programa y en la adaptacin a distintos compiladores. Se utilizan los comandos del
preprocesador: #if, #else, y #endif, e #ifdef, #ifndef.

Las tres primeras instrucciones son sentencias condicionales con la misma funcin que las del
lenguaje, pero se diferencian de ellas en que la expresin ser una constante definida por el
preprocesador con #define (que puede valer 0 o algo distinto de 0). Si es 0, el cdigo C entre ellas no
se compilar. Las dos ltimas son idnticas a las anteriores, con la diferencia que slo comprueban
la existencia de la definicin de la constante simblica y no su valor:

#i f COMPI LADOR == "ANSI C" #i f def COMPI LADOR
#i ncl ude "est e. f i cher o" #i ncl ude "dat os. h"
#endi f #el se
#i ncl ude "ot r osdat os. h"
#endi f

PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



54
En ANSI C hay algunas macros predefinidas (ntese los dos subrayados):

Macr o Descr i pt i on
__DATE__ The cur r ent dat e as a char act er l i t er al i n "MMM DD YYYY" f or mat
__TI ME__ The cur r ent t i me as a char act er l i t er al i n "HH: MM: SS" f or mat
__FI LE__ Thi s cont ai ns t he cur r ent f i l ename as a st r i ng l i t er al .
__LI NE__ Thi s cont ai ns t he cur r ent l i ne number as a deci mal const ant .
__STDC__ Def i ned as 1 when t he compi l er compl i es wi t h t he ANSI st andar d.


Podemos considerar un fichero como una zona de almacenamiento permanente,
normalmente en disco, que se ve lgicamente a travs de un nombre (se supone que el lector est
familiarizado con el manejo de ficheros en otros lenguajes). Pero lo que nos interesa aqu es como
estn


Ent rada / sal i da con fi cheros
Podemos considerar un fichero como una zona de almacenamiento permanente,
normalmente en disco, a la que nos referimos a travs de un nombre (se supone que el lector est
familiarizado con el manejo de ficheros). Para el lenguaje C internamente los ficheros son una
estructura (struct) que se declara en la librera stdio.h, en la cual, se hace un #define de la estructura,
al identificador FILE (tambin hay otros sistemas que definen el FILE como un typedef). Este
identificador ser usado para definir una variable de tipo puntero a FILE, que es la que usaremos en
el programa para referirnos al fichero.

Las operaciones que se pueden hacer con los ficheros son las habituales. As, tendremos
operaciones para abrir y cerrar ficheros, para leer y escribir en ellos distintos tipos de datos y para
poder movernos (acceso aleatorio) en su informacin.

La funcin para abrir ficheros es la fopen, que ser de tipo puntero a FILE (puntero a struct),
pero que no hay que declarar donde se utilice porque ya est hecho en stdio.h. Esta funcin admite
dos argumentos: el nombre del fichero y el modo de apertura, ambos como tira de caracteres (los
dos pueden ser punteros a char). El modo puede ser: "r" para slo lectura, "w" para escritura
(borrando el fichero si existe) y "a" para escritura pero aadiendo datos, no borrando el fichero
existente. Existen otros modos menos usuales como "r+", "w+", "a+" para hacer ambas cosas.
Tambin existen los modos equivalentes para ficheros binarios: "rb", "wb", "ab" y, "r+b", "w+b", "a+b".

En todos los modos, si la operacin falla se devolver el valor NULL (definido en stdio.h). La
utilizacin es:

FI LE *f i cher o;
f i cher o = f open( "nombr e", "r ") ;

La funcin para cerrar ficheros es la fclose, que tomar como argumento un puntero a fichero.
Es una funcin entera, por lo tanto, si la operacin es correcta, devolver un 0 y si falla un -1.

f cl ose( f i cher o) ;

Estas funciones trabajan con ficheros con buffer o stream (almacenamiento intermedio en
memoria), de tal manera que al cerrarlo, se vaca el buffer al fichero
1
. Cuando un programa termina
normalmente (final de main o llamada a exit) se cierran todos sus ficheros automticamente, pero
esto no ocurre as cuando el programa termina anormalmente, por lo que la informacin del buffer
asociado al fichero sin cerrar se pierde, por eso siempre es conveniente cerrarlos cuando se dejen
de usar en el programa.


1
Se hace as por eficiencia, si tratamos de leer o escribir datos de longitud pequea, el manejo
asociado a esas operaciones puede ser costoso en tiempo y es ms eficaz escribir en memoria estas variables
y despus pasar de un solo golpe toda esa memoria al fichero.
PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



55
Al igual que existen funciones para la entrada/salida por los canales estndar, debido a la
equivalencia entre dispositivos y ficheros dada por el sistema operativo, tambin existen funciones
para la estrada/salida por ficheros, de hecho son las mismas con distintos nombres (la entrada
estndar es stdin, la salida es stdout, y la de error stderr).

Para la salida (escritura) de distintos tipos de datos est la equivalente a printf que se llama
fprintf. Esta funcin tiene tres argumentos, el primero es el puntero a fichero, el segundo la tira de
control y el tercero las variables a escribir.

f i cher o = f open( "nombr e", "w") ;
i nt edad = 7;
f pr i nt f ( f i cher o, "Pedr o t i ene %d meses \ n", edad) ;

Para la lectura de ficheros, como se intuye, est la funcin fscanf, con la cual hay que tener las
mismas precauciones que con scanf en lo que se refiere a la lectura de strings y caracteres. Tambin
lo que se lee con ella son punteros a variables, no las propias variables, por lo que se deber utilizar
el operador de direccin "&".

f i cher o = f open( "nombr e", "r ") ;
i nt edad, *punt _ent er o;
f scanf ( f i cher o, "%d", &edad) ;
f scanf ( f i cher o, "%d", punt _ent er o) ;

Por la misma razn que existen las funciones getchar y putchar, tambin existen sus
equivalentes getc y putc para ficheros, a las cuales habr que darlas como ltimo argumento el
puntero a fichero.

ch = get c( f i cher o) ;
put c( ch, f i cher o) ;

Por ltimo, al igual que la entrada/salida de strings, tambin existe la equivalencia para
ficheros; estas funciones son fgets y fputs. La primera tiene como primer argumento el puntero a
carcter (tira de caracteres) donde se quiere realizar la lectura, despus el nmero de caracteres que
se quieren leer y por ltimo el puntero a fichero. La segunda tiene como primer argumento la tira
(puntero) de caracteres que se quiere escribir y como ltimo argumento el puntero a fichero.

Como ejemplo de utilizacin de ficheros, vamos a ver como con las dos funciones de
caracteres podemos ya hacer un vuelca el contenido de un fichero (el fichero si es de lectura debe
existir):

#i ncl ude <st di o. h>
mai n ( )
{
FI LE *ent r ada; / * f i cher o de ent r ada */
i nt ch;
/ * abr e f i cher o par a l ect ur a que se l l ama nombr e */
i f ( ( ent r ada = f open( "nombr e", "r ") ) ! = NULL)
{
/ * l ee hast a f i nal de f i cher o */
whi l e ( ( ch = get c( ent r ada) ) ! = EOF)
/ * st dout es l a pant al l a */
put c( ch, st dout ) ;
f cl ose( ent r ada) ;
}
el se
put s( "No se puede abr i r ") ;
}

Adems fgets es de tipo puntero a carcter y devuelve un NULL si encuentra el EOF y a
diferencia de gets no convierte el carcter '\n', y fputs devuelve un EOF si hay un error, quita el nulo
del final, pero no aade el carcter '\n'.

PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



56
Su uso sera:

f i cher o = f open( "nombr e", "r ") ;
( f get s( t i r a, 81, f i cher o) ! = NULL) ;

f i cher o = f open( "nombr e", "a") ;
cont r ol = f put s( "Ya er a hor a", f i cher o) ;

Hay otras funciones menos importantes para trabajar con ficheros:

rewind() coloca el ndice del fichero al comienzo del mismo y toma como argumento
un puntero a fichero.
remove() borra un fichero y tambin toma como argumento el fichero.
rename () toma como argumentos dos tiras de caracteres con el nombre viejo y el
nuevo y cambia el nombre.
feof() nos dice si para el fichero indicado se ha llegado a su final.
ferror() nos dice si para el fichero indicado ha habido algn error.
fflush() para el fichero indicado fuerza que el bufer interno se vuelque a fichero.
fseek() que sirve para moverse por el fichero. Tiene tres argumentos: un puntero a
fichero, un offset de tipo long (distancia donde se busca, positiva o negativa) y un
modo de bsqueda. El modo puede ser 0 para el comienzo del fichero, 1 para la
posicin actual y 2 para el final del fichero, por lo que el offset se toma con referencia
a este modo. La funcin devuelve un 0 si todo va bien.

f seek( f i cher o, 32L, 1) ;



Punt eros a funci ones
Hemos dicho que un puntero es slo una variable que contiene una direccin. Pero la
direccin puede apuntar o bien a un dato o bien a cdigo. Si es cdigo la forma natural de
referenciarlo es con una funcin, de ah que existan punteros a funciones.

La forma de definirlos empieza a ser algo barroca pero sintetizando la podemos resumir en:

<t i po> ( *<i dent i f i cador >) ( <l i st a_de_par met r os>) ;

Ejemplos:

voi d ( *f unc) ( i nt *, i nt *) ; ( 0) / * decl ar aci ones */
i nt ( *pf unci on1) ( ) ; ( 1)
voi d ( *pf unci on2) ( i nt ) ; ( 2)
f l oat *( *pf unci on3) ( char *, i nt ) ; ( 3)
voi d ( *pf unci on4) ( voi d ( *) ( i nt ) ) ; ( 4)
i nt ( *pf unci on5[ 10] ) ( i nt ) ; ( 5)

El ejemplo 0 declara un puntero a funcin func que no devuelve nada y tomas dos
argumentos de tipo puntero a entero. El ejemplo 1 declara un puntero, "pfuncion1" a una funcin que
devuelve un int y no acepta parmetros. El ejemplo 2 declara un puntero, "pfuncion2" a una funcin
que no devuelve valor y que acepta un parmetro de tipo int. El ejemplo 3 a una funcin que
devuelve un puntero a float y admite dos parmetros: un puntero a char y un int. El 4, declara una
funcin "pfuncion4" que no devuelve valor y acepta un parmetro. Ese parmetro debe ser un
puntero a una funcin que tampoco devuelve valor y admite como parmetro un int. El 5 declara un
array de punteros a funcin, cada una de ellas devuelve un int y admite como parmetro un int.


PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



57
El uso de los punteros en una asignacin o una llamada sera (el propio nombre de una
funcin es un puntero):

f unc = i nt er cambi a; / * asi gnaci n de val or */
( *f unc) ( &a, &b) ; / * l l amada */

pf unci on1 = f unci on; / * asi gnaci n de val or */
i nt x = pf unci on1; / * l l amada */






PROGRAMACIN EN ANSI C ASPECTOS AVANZADOS



58


59
El compilador de C

En este apndice se ver el funcionamiento del compilador de C en ambientes
Windows y fundamentalmente GNU-Linux, que es el que se recomienda.


Int roducci n
Existe una secuencia de trabajo para producir una aplicacin ejecutable desde un cdigo
fuente en ANSI C (o cualquier otro lenguaje no interpretado). El cdigo en lenguaje de alto nivel debe
ser traducido a cdigo mquina que pueda ser ejecutado por el computador. Aqu es donde
interviene el compilador, que es ayudado por varias herramientas para su cometido: el ensamblador,
el enlazador y el depurador.

Las fases para producir la aplicacin las podramos resumir en los siguientes puntos:

1. Con la ayuda de un editor escribiremos un programa en alto nivel. Este editor puede
ser muy sencillo y de propsito general o especializado en la realizacin de cdigo
fuente, con lo cual ser sensible a las palabras propias del lenguaje de alto nivel
utilizado, avisarnos de la falta de algn elemento como llaves o puntos y coma e
incluso nos permitir embellecer ese cdigo. En cualquier caso el resultado ser un
cdigo en texto plano.

2. Este cdigo pasa un por una fase de preprocesamiento del texto, que incluir la
sustitucin de determinadas constantes o la inclusin de otro ficheros, por citar los
dos cometidos ms usuales. En cualquier caso se produce otro fichero de texto plano
(modificando el anterior) con formato de cdigo de alto nivel.


3. Este nuevo cdigo ahora es compilado (internamente se pasa por varias fases que
puedes ver en el apndice B). Y se producir un cdigo en ensamblador
normalmente. Puede ocurrir que el cdigo fuente est mal realizado, esto nos lo
advertir el compilador, indicando el tipo de fallo y la lnea de cdigo fuente que falla
(ojo algunas veces puede fallar). Si falla volveremos a editar el cdigo hasta
corregirlo.

4. Una fase que puede ser opcional es el ensamblado del cdigo producido. Si el
compilador produce ensamblador (lenguaje mquina con formato humano), este
ensamblador pasar por otra herramienta que es el ensamblador (ver apndice C
para ms detalles), que produce cdigo objeto en lenguaje mquina. Si el compilador
hace ya esta labor esta fase no ser necesaria.


Apndice
A
CURSO DE LENGUAJ E ANSI C COMPILADOR DE C



60
5. Este cdigo objeto an no es ejecutable, ya que al realizar el cdigo fuente, hemos
utilizado algunas funciones de algunas libreras que ya estaban realizadas y que no
hemos programado e incluido. Por ello hace falta otra fase que es la de enlazado.
Nuestro cdigo objeto se tiene que mezclar con otro cdigo (el de las libreras) para
por fin obtener el cdigo ejecutable. Este trabajo es realizado por otra herramienta
que se llama enlazador (linker).

6. Llegamos a la fase de ejecucin y depuracin, para lo cual daremos nuestra
aplicacin al sistema operativo para la ejecute. En muy raras ocasiones el cdigo que
hemos hecho funciona a la primera, tanto en la fase de compilacin como de
ejecucin, por ello existe otra herramienta llamada depurador para poder ejecutar la
aplicacin de forma controlada y nos que nos d detalles de donde falla. Funciones
tpicas de una herramienta de depuracin ser la ejecucin paso a paso o la
visualizacin del valor de las variables entre otras muchas.

Las fases descritas* se conocen como parte de las fases del ciclo de vida del software. stas
incluyen las descritas en la siguiente tabla:

Ciclo Porcentaje de esfuerzo
Definicin del programa 3%
Especificacin 15%
Codificacin y testeo* 14%
Verificacin 8%
Mantenimiento 60%

Puede ser sorprendente el poco esfuerzo relativo que supone la codificacin y el enorme
esfuerzo que se dedica al mantenimiento, por eso una de las leyes no escritas de la programacin es
que un programa se escribe una vez y se lee cientos, por eso hay que hacer nfasis en la
documentacin del programa y la claridad del mismo (nombres de variables, profusin de
comentarios, cdigo claro, prototipos de funciones, etc.).

Otras de los puntos a tener en cuenta es que cuesta tanto esfuerzo codificar un programa
para resolver un problema como su definicin y especificacin, es decir, antes de empezar a
programar hay que dedicar un esfuerzo equivalente a saber qu es lo que vamos a programar. Lo
podemos resumir en los siguientes pasos:

1. Tener claro el problema a resolver. Esto parece obvio.
2. Saber cules van a ser las entradas del programa y sus salidas.
3. Resolver el problema a mano con ejemplos cortos.
4. Desarrollar un algoritmo (pseudocdigo) para resolver el problema.
5. Codificar el algoritmo en el lenguaje escogido (ANSI C) con explicacin en
comentarios. Estos comentarios pueden incluso, a parte de la explicacin, contener el
nombre del autor, la fecha de inicio y los cambios realizados, o todo lo que se crea
oportuno. Nunca ser cicatero con las explicaciones. Dentro de unos meses ni el
propio autor se puede acordar de lo que ha hecho.
6. Verificacin que el resultado del programa es el del ejemplo corto a mano.



Wi ndows
En Windows existen varias posibilidades de utilizar un compilador de C, pero la mayoras de
ellas son de pago, como Visual C Studio o similares, por ello la opcin que se ha escogido es la de
usar un compilador de C++ (perfectamente vlido para C) gratuito de software libre llamado
Bloodshed Dev C++ (http://prdownloads.sourceforge.net/dev-cpp/devcpp-4.9.9.2_setup.exe) para
aquellos que prefiris usar el entorno Windows.

CURSO DE LENGUAJ E ANSI C COMPILADOR DE C



61
Tiene un entorno de desarrollo completo (editor, compilador, depurador, ejecucin en lnea)
como se puede ver en la siguiente figura:



Otra posibilidad es Geany (http://download.geany.org/geany-1.23.1_setup.exe) un entorno
integrado de desarrollo (IDE) tambin de software libre que existe tanto para Windows como para
Linux con similares caractersticas:





CURSO DE LENGUAJ E ANSI C COMPILADOR DE C



62
GNU-Li nux

La otra posibilidad, que es la recomendada, es que utilicis un entorno GNU-Linux. En este
entorno tambin tenis varias posibilidades que podemos clasificar en dos clases: IDE o las
herramientas clsicas (separadas) de un sistema GNU-Linux.

En el primer caso podis utilizar, dependiendo del sistema de ventanas que usis, Kdevelop
para KDE, o gedit (con plugins) para Gnome, o cualquiera de los IDE existentes como el mencionado
Geany, Anjuta (http://projects.gnome.org/anjuta/downloads.html), NetBeans (http://netbeans.org/) de
Sun o el archiconocido Eclipse con plugins (http://www.eclipse.org/).

En el segundo caso el compilador de C que vamos a utilizar es el gcc (g++). Este compilador
realiza automticamente toda la cadena de operaciones para producir un fichero ejecutable, es decir,
llama al preprocesador, compila el programa, produce un fichero en ensamblador, ensambla el
programa, lo enlaza (linka) y produce el ejecutable (cpp es el preprocesador, ld es el linker, as es el
ensamblador y gdb el depurador ddd es su versin grfica).

El compilador siempre espera que se le d un nombre de fichero que contenga un programa
fuente de lenguaje C, para distinguir estos ficheros de cdigo fuente del resto de ficheros del sistema,
es obligatorio que todos terminen en ".c". Si no hacemos esto el compilador nos responder con: "file
not recognized: File format not recognized". Otras terminaciones comunes son: .C, .cc, y .cxx para
ficheros en c++; .h para ficheros del preprocesador; .s, .S para ficheros en ensamblador; y .o para
ficheros objeto (sin enlazar).

Como cualquier otro comando del sistema, el formato para ejecutarlo ser nombre [-opciones]
y argumentos. La forma ms sencilla de ejecutarlo para producir un fichero ejecutable con nombre
a.out sera la siguiente:

gcc pr ogr ama. c

Como hemos dicho, el compilador es un comando ms, por lo que tambin admite opciones,
stas usualmente se pondrn delante del nombre del fichero fuente y debern estar separadas, por
ejemplo no es lo mismo poner gcc -dv que gcc -d v, dado que hay opciones multiletra. Las opciones
estn divididas en varios grupos: globales, del lenguaje, de alerta, de depuracin, de optimizacin, de
preprocesado, de ensamblador o linkador, de directorios, y de dependencias del hardware.

A continuacin aparecen las opciones ms habituales (las puedes encontrar con man gcc):

Globales:
-c Compila pero no llama al linker, generando un .o.
-o ejecutable Cambia el nombre del fichero ejecutable (por defecto a.out) al expresado en
la opcin.
Del lenguaje:
-ansi Compila siguiendo las reglas del ANSI C.
-E Ejecuta slo el preprocesador.
Del linker:
-llibreria Incluye la librera libreria.
De directorios:
-Ldirectorio Aade el directorio directorio a los directorios donde se buscar lo dado por
-l (anterior opcin).
-Idirectorio Aade el directorio a la lista de directorios de includes.
De alerta:
-w Inhibe los mensaje de warning. Los mensajes de warning indicarn algo a
tener en cuenta, pero que no es un error.
De depuracin:
-g Produce informacin para poder utilizar alguno de los depuradores
(debuggers) del sistema (gdb).
CURSO DE LENGUAJ E ANSI C COMPILADOR DE C



63

Una vez que se ha compilado un programa con la opcin -g, puede ser ejecutado con el
debugger del sistema, que en este caso se llama gdb: gdb ejecutable (como hemos dicho es
altamente recomendable usar la versin grfica ddd). Esto permitir ejecutarle de forma controlada
(paso a paso, lnea a lnea, hasta un punto de ruptura, etc.), ver los valores de las variables, etc. Para
ms informacin se puede utilizar el comando help del mismo. Tambin se puede utilizar el
depurador para obtener informacin de los ficheros cores del sistema. Cuando un programa se
aborta en ejecucin se produce un fichero de nombre core que puede ser analizado con gdb core.








CURSO DE LENGUAJ E ANSI C COMPILADOR DE C



64


65
Compilador y enlazador


En este apndice veremos como trabaja el compilador: estructuras
de datos, fases de compilacin y anlisis sintctico y semntico. Tambin
se ver la funcin del programa enlazador o linker



Un compilador es un programa que traduce un cdigo fuente de un lenguaje de alto nivel a
ensamblador o directamente a cdigo mquina. El compilador puede estar escrito en ensamblador,
en un lenguaje de alto nivel (tendencia actual) como por ejemplo C (ya que es un lenguaje de nivel
medio) o incluso en un lenguaje especial para construir compiladores (compilador de compiladores),
en el caso de UNIX puede ser lex y yacc o bison. No hay que confundirlo con un intrprete que lee
lnea a lnea un programa y lo va ejecutando, por ejemplo los antiguos BASIC.

El funcionamiento de un compilador est dividido fundamentalmente en dos partes: Una de
anlisis del cdigo fuente y otra de sntesis del cdigo objeto, para lo cual, al igual que el
ensamblador, debe construir y analizar varias tablas (ver Figura 1).

























Apndice
B
Figura 1. Proceso de compilacin.
PROGRAMA FUENTE


ANLISIS TABLAS
1. ANALIZADOR LEXICOGRFICO DE SMBOLOS
2. ANALIZADOR SINTCTICO DE CONSTANTES
3. ANALIZADOR SEMNTICO DE BUCLES


PROGRAMA INTERMEDIO


SNTESIS
1. PREPARACIN
2. GENERACIN DE CDIGOS


PROGRAMA OBJ ETO
SISTEMAS OPERATIVOS COMPILADORES Y ENLAZADORES



66

En la fase de anlisis aparecen tres componentes:

1. El analizador lexicogrfico (scanner). Tiene como misin explorar los caracteres del
programa, convertirlo a un formato apropiado para la fase sintctica (quitar partes
innecesarias como comentarios y convertirlo a la notacin adecuada) y construir las
tablas de datos necesarias. Para ello distingue entre varios tipos de componentes
(llamados tokens o unidades lxicas):

Constantes: De tipo numrico o alfabtico.
Identificadores: Nombres dados a entidades propias del programa (variables,
subprogramas, etc.).
Operadores: Los dados en un determinado programa (aritmticos, relacionales,
lgicos, etc.).
Palabras clave: Predefinidas por el lenguaje para poder construir las
sentencias.
Delimitadores: Separadores de los anteriores.

A cada uno de estos grupos se les aplican sus reglas propias, construyndose para
ellos las tablas de smbolos correspondientes (si no lo estn ya, como la de palabras
reservadas). El scanner puede dar errores de escritura.

2. El analizador sintctico. Tomando el cdigo producido por el analizador lexicogrfico, el
sintctico es el encargado de determinar si una sentencia es correcta o no (de acuerdo
a las reglas del lenguaje), para ello se sigue un algoritmo reconocedor o parser. Si
todo es correcto (tendr que seguir las reglas de la gramtica del lenguaje que se
pueden dar con distintas representaciones: BNF, diagramas, etc.) producir un rbol
sintctico reflejo del programa que es usado por el analizador semntico.

3. El analizador semntico. Tiene como misin construir un cdigo intermedio
independiente de la mquina donde se ejecutar el cdigo objeto. Para ello utilizar las
rutinas semnticas que reflejan las leyes de la gramtica.

En la parte de sntesis se deber producir el cdigo objeto a partir de este cdigo intermedio.
Esta parte es similar al anlisis, slo que ahora se genera cdigo por cada una de las partes de la
sentencia que se est evaluando.

Adems en esta fase se puede producir una optimizacin del cdigo objeto resultante, que
puede ser de dos tipos:

Independiente de la mquina.
Reduccin simple: Sustitucin de operaciones aritmticas innecesarias por constantes.
Ordenacin de instrucciones: El resultado es el mismo pero una expresin reordenada
puede producir cdigo ms eficiente al eliminar registros intermedios.
Reduccin de potencia: Sustitucin de operaciones complejas (como potenciacin por
otras ms simples).
Reduccin de invariantes: Quitar de lazos elementos constantes u operaciones
constantes que no dependen de la ejecucin del bucle.

Dependiente de la mquina.
Utilizacin de registros eficientemente. Se pueden usar algoritmos para seleccionar
registros vctima a la hora de hacer cargas y guardas de valores.

El cdigo objeto producido puede ser absoluto o reubicable. En el primer caso el cdigo se va
a ejecutar en una posicin fija de la memoria, en el segundo caso el cdigo puede ejecutarse en
cualquier posicin, pudiendo tener cuatro partes:

Tabla de smbolos. Define las distintas partes lgicas de un programa como las
referencias externas, las reas de datos comunes, etc.
SISTEMAS OPERATIVOS COMPILADORES Y ENLAZADORES



67
Texto. El formado por el propio cdigo y los datos utilizados por l.
Tabla de reubicacin. Define las direcciones que tienen que cambiarse con respecto a la
direccin de ejecucin inicial.
Terminacin. Indica la terminacin del programa.

Tambin hay que tener en cuenta que un programa objeto puede hacer referencia a otros
programas objeto. Estas referencias son solucionadas por una fase posterior realizada por el
programa montador, enlazador o linker, que dependiendo de la complejidad del sistema operativo
puede estar unido al programa cargador o no.


SISTEMAS OPERATIVOS COMPILADORES Y ENLAZADORES



68


69
Ensamblador

En este apndice veremos qu es un ensamblador y como trabaja

Int roducci n

Un programa ensamblador (se llaman ensambladores porque lo que hacen es juntar varias
rutinas en un nico programa) de forma genrica es aquel que traduce un cdigo en lenguaje
ensamblador a lenguaje mquina (binario). En algunos casos se dice que es un programa traductor
que convierte cada lnea de ensamblador en una lnea de cdigo binario, pero esto no es del todo
cierto sobre todo en programas ensambladores en los que se pueden construir macroinstrucciones.

Los lenguajes ensambladores se dividen normalmente (por su funcionamiento) en dos clases:
ensambladores de un paso load-and-go y ensambladores de dos pasos, que primero determinan
los smbolos del programa y despus en el segundo paso los traducen adecuadamente.

Estos smbolos o esta informacin con que trabaja un ensamblador es de tres tipos:

1. Absoluta. Es independiente de la direccin donde vaya a estar cargado el programa,
como por ejemplo cantidades.

2. Externa o global. Son variables que pertenecen a otros subprogramas y que no van a ser
conocidos hasta que todo el programa sea enlazado.

3. Reubicable o localizable. En este caso la direccin depender de donde est localizado el
subprograma en s, pero no de otros subprogramas, por lo que para hacer la direccin
absoluta basta con sumar la direccin relativa (de dentro del programa) a la direccin de
inicio.

A su vez, el ensamblador trabaja con varios tipos de instrucciones:

1. Simblicas. Constan de varios campos: Etiqueta, aadida a una instruccin permite que
se pueda referenciar lgicamente en el programa y su valor ser la direccin que tome la
instruccin; Cdigo de operacin, un smbolo obligatorio que indica la instruccin
propiamente dicha que pertenece al conjunto de instrucciones de la CPU; Operando, es
una direccin de datos cuya complejidad vendr dada por el propio repertorio de
instrucciones de la CPU, puede ser desde un nmero a un smbolo pasando por
operaciones aritmticas; Comentario, algo que se pone de forma explicativa y que no es
traducido a cdigo mquina.

Apndice
C
SISTEMAS OPERATIVOS ENSAMBLADOR



70
2. Pseudo-instrucciones. Son instrucciones que ayudan al proceso de ensamblado del
programa, no traducindose a cdigo mquina. Instrucciones tpicas son las de comienzo
y fin de programa o las de equivalencia de valores o smbolos.

3. Definicin de constantes. Estas pseudo-instrucciones introducen una o varias constantes
a partir de una posicin de memoria que normalmente es referenciada con una etiqueta.

4. Reserva de espacio. Es otra pseudo-instruccin que sirve para reservar espacio en
memoria. Tambin la direccin de final o del principio suele tener asociada una etiqueta.

5. De subprogramas. Son pseudo-instrucciones que indican cuando una variable es local o
global. Por norma slo se utilizan con las variables globales, teniendo una forma para
definirlas y otra para referenciarlas.

6. De origen. Indican al ensamblador donde el programa o los subprogramas se deben
cargar en la memoria, si es que se sabe de antemano.



Proceso de t raducci n

El proceso de traduccin se realiza de lnea en lnea, analizndose para cada una los campos
introducidos y transformando stos en cdigos binarios. Para ello el ensamblador siempre tiene
presentes dos tablas fijas:

1. Tabla de cdigo de operacin. Dnde estn definidas toda las instrucciones de la CPU
con sus correspondientes cdigos binarios.

2. Tabla de pseudo-instrucciones. Aqu estn definidas las instrucciones propias de ese
programa ensamblador.

Adems, el ensamblador construye una tabla variable que es la que define los smbolos
utilizados en ese programa junto con su direccin.

As, el proceso de traduccin en el primer paso del ensamblador consistira en:

1. Lectura de la sentencia.
2. Anlisis (verificacin de correccin)
Etiquetas
Localizacin en la tabla de smbolos y tomar direccin.
Si no est insertarla.
Cdigo de operacin
Localizacin en la tabla de cdigos y escribir cdigo.
Anlisis del operando.
Pseudo-instruccin
Localizacin en la tabla de pseudo-instrucciones.
Tratamiento de la pseudo-instruccin.
Anlisis del operando.

Como se ve el trabajo del primer paso del ensamblador (Analizador, reconocedor o scanner)
es el de trabajar con tablas. Por lo que es fundamental que la bsqueda en las mismas est muy
optimizada, para ello se utilizan varias tcnicas:

1. Bsqueda secuencial. Dada una tabla (formada por k smbolos que sirven de ndice y sus
k traducciones), se va comparando en ella el smbolo a encontrar con los valores ndice
de la tabla secuencialmente. El nmero de intentos medio ser de N/2, donde N es la
SISTEMAS OPERATIVOS ENSAMBLADOR



71
longitud de la tabla. Si se hace un ordenamiento de esta tabla con el nmero de
recurrencias, el promedio puede disminuir.

2. Bsqueda logartmica o binaria. En este caso se ordena la tabla con el valor binario del
smbolo y en vez de hacer una bsqueda secuencial se empieza comparando con la
mitad de la tabla para saber en que parte est, esto se repite con cada nueva mitad hasta
encontrar el ndice.

3. Indexacin alfabtica. No se tiene una tabla normal sino que sta se divide en 26
subtablas una por cada letra del alfabeto, teniendo una tabla general con los orgenes de
cada una de las subtablas. La bsqueda, en este caso, slo se realizar en la subtabla
correspondiente a la inicial del smbolo a encontrar.

4. Bsqueda en forma de rbol. Aqu la tabla tiene forma de rbol binario ordenado que
permita la insercin ordenada y la bsqueda simultneamente.

5. Bsqueda utilizando pilas. Se utiliza cuando un mismo ndice o smbolo tiene varias
traducciones y se utiliza nicamente la ms reciente.

6. Bsqueda utilizando tablas de hash. Es parecido al de indexacin alfabtica, pero en este
caso la divisin en subtablas se hace a travs de una funcin de hash (desmenuzar).

Para completar la funcin del primer paso del ensamblador slo hay que decir que tambin se
encarga de decir de descubrir posibles errores en la introduccin de los smbolos de las instrucciones
(ver s hay alguno que no existe).

Una vez verificado el programa y construidas las tablas de smbolos, se aplica la segunda
fase del ensamblador que fundamentalmente lo que va a hacer en escribir el cdigo binario
resultante. Para ello la funcin que lleva a cabo se puede describir como:

1. Lectura del programa.
2. Tratamiento de las sentencias.
Operacin
Bsqueda en la tabla de cdigos de operacin.
Obtencin de cdigo binario y longitud.
Actualizar contador de direcciones.
Pseudo-instruccin.
Localizacin en la tabla de pseudo-instrucciones.
Tratamiento de la pseudo-instruccin.
Anlisis del operando.
Bsqueda si es smbolo en la tabla.
Obtencin de direccin.
Si no es smbolo se obtiene el valor directamente.
3. Escritura de cdigo objeto.



Ti pos de ensambladores

Hasta aqu se ha visto el ensamblador bsico ms normal, pero existen otros tipos de
ensambladores:

1. Ensambladores de un solo paso o de incremento. Se hacen las dos fases a la vez: la
verificacin y construccin de tablas; y su anlisis y escritura del cdigo. Slo se utilizaban
este tipo de ensambladores cuando la lectura del cdigo era muy lenta (antiguas lectores
de tarjetas).

SISTEMAS OPERATIVOS ENSAMBLADOR



72
2. Macroensambladores. La operacin ser la misma que la vista anteriormente para un
ensamblador de dos pasos, con la diferencia que tendremos que construir una tabla para
las macroinstrucciones (grupo de instrucciones con un nombre que puede ser llamado
desde otras partes del programa) que tienen como particularidad que su tamao es
variable, por lo que suele almacenar esta tabla en una lista encadenada.


3. Ensambladores cruzados. La nica particularidad de stos es que producen cdigo objeto
para otra mquina distinta a la que est ejecutando el ensamblador.







73
Ot ros aspect os de C y del sist ema



Orden de precedenci a de l os operadores
En la tabla que aparece a continuacin estn todos los operadores de C y sus rdenes de
precedencia con ms prioridad los de arriba:


Operadores Significado Sentido
() {} -> . Parntesis, Bloque, Campos de estructuras Izda - Dcha
! ~ ++ --
- (tipo) *
& sizeof
NOT, Negacin bit, Incremento, Decremento,
signo menos, conversin tipo, indireccin,
direccin, tamao
Dcha - Izda
Todos Unarios
* / % Multiplicacin, Divisin, Mdulo Izda - Dcha
+ - Adicin, Sustraccin Izda - Dcha
>> << Desplazamiento de bits Izda - Dcha
> < >= <= Relacionales Izda - Dcha
== != Relacionales Izda - Dcha
& AND bit Izda - Dcha
^ XOR bit Izda - Dcha
| OR bit Izda - Dcha
&& AND Izda - Dcha
|| OR Izda - Dcha
?: Condicional Izda - Dcha
=+=-=*=/=%= Asignacin Dcha - Izda
, Coma Izda - Dcha



Decl araciones compl ej as
La combinacin de los modificadores de declaracin de punteros "*", arrays "[ ]", y funciones
"( )", permite en lenguaje C declaraciones muy sofisticadas pero poco claras y en principio difciles
de interpretar.

Apndice
D

PROGRAMACIN EN ANSI C APNDICES



74
Cuando se combinan estos modificadores hay que tener presente ciertas reglas de
ordenamiento:

1. La prioridad del modificador es mayor cuanto ms prxima est al identificador.
2. Los modificadores [ ] y ( ) tienen mayor prioridad que el *.
3. Se pueden usar parntesis, no confundir con las funciones, para agrupar parte de una
declaracin.

En los siguientes ejemplos se ven declaraciones de datos utilizando estas normas:

Declaracin Significado
char matriz[4][5] Un array doble de 4 x 5 (4 strings de 5 char)
char *string Un puntero a char (un string)
int **puntero Un puntero a puntero de int
char *array[12] Un array de 12 punteros a char (strings)
int (*pun)[3] Un puntero a un array de 3 enteros
int *arr[4][3] Un array de 4 punteros a arrays de 3 enteros
int (*pun)[3][5] Un puntero a una array doble de 3 x 5


En estas declaraciones tambin se pueden incluir las funciones, con lo cual las expresiones
se hacen an ms complicadas:

Declaracin Significado
char *funcion() Una funcin de tipo puntero a char (string)
long (*punf)() Un puntero a una funcin que devuelve un long
int *funci()[3] Una funcin que devuelve una puntero a array de 3 enteros
char *fun[3]() Array de tres punteros a funcin que devuelve un dato de tipo char



Const rucci n de un programa sobre GNU/Li nux - UNIX

Programacin separada (modular)
Hasta ahora hemos visto que todos nuestros programas estaban en un fichero que se edita,
compila y ejecuta. Pero normalmente, en programas grandes, esto no se hace as, sino que se
construye el programa de forma modular en varios ficheros, aplicndose el principio de divide y
vencers, ya que los mdulos del programa sern ms fciles de entender y depurar (algo parecido
a la divisin de un programa en funciones pero a otro nivel ms abstracto). Con esta forma de
trabajar conseguimos algunas ventajas:

1. Obviamente los mdulos tienen una extensin menor que el programa completo. Por lo
tanto, stos sern ms fciles de analizar.
2. Cada mdulo se puede compilar por separado (ms rpido).
3. Cada mdulo ser ms fcil de depurar por separado, ya que no se tendrn que tener en
cuenta influencias externas.
4. La divisin del trabajo entre varios programadores es ms sencilla y limpia de realizar.

Esto conlleva que se tengan que aplicar (conveniente no obligatorio) ciertos criterios a la hora
de construir ese programa utilizando la estructura en rbol de directorios y ficheros:

1. Se puede utilizar un directorio (en vez de un fichero como antes) para contener los
ficheros de los que va a estar constituido el programa.

PROGRAMACIN EN ANSI C APNDICES



75
2. Dentro de ese directorio general se puede crear varios subdirectorios donde se sepa que
se va a encontrar lo que estamos buscando, como por ejemplo:
2.1. Un directorio para los ficheros fuente.
2.2. Un directorio para los ficheros de cabecera del preprocesado (normalmente
"include"). Ver punto 4. Los del sistema estn en el directorio "/usr/include".
2.3. Un directorio de libreras (normalmente "lib").
2.4. Un directorio de ejecutables (normalmente "bin").
2.5. Un directorio de documentacin (normalmente "doc").

3. Los mdulos tienen que ser construidos (divisin del programa) teniendo en cuenta
principios semnticos, ofreciendo servicios a otros mdulos externos (cajas negras), de tal
manera que se garantice el perfecto funcionamiento de los mismos de forma aislada.
Tambin se deber tener en cuenta que
1
:
3.1. Hay partes dependientes del hardware que deben ser sealadas como tal. De
hecho, por definicin no son transportables a otros sistemas y deben estar
separadas del resto del programa.
3.2. Otras dependern de algo especfico como llamadas a un sistema operativo
concreto y debern ser tratadas de la misma forma.

4. Los ficheros de cabecera "*.h" estn destinados normalmente a contener las definiciones
comunes a varios mdulos. En ellos suelen aparecer distintos tipos de informacin:
4.1. Definicin de constantes.
4.2. Definicin de tipos de datos.
4.3. Definicin de prototipos de funciones.
Suele ser una mala prctica de programacin incluir las propias definiciones (reservas de
espacio) de variables.


Gest in de libreras en UNIX
Anteriormente se ha comentado que podemos incluir en nuestro programa, cdigo objeto
realizado en otros lenguajes o en el mismo C, as la lnea de compilacin podra complicarse:

gcc modul o1. c modul o2. c pr epr o. i pr ogr ama. c obj et o. o - o ej e - l m

donde hemos incluido al compilar dos mdulos de cdigo fuente C, un fichero de preprocesado, el
programa principal fuente, un cdigo objeto (no necesariamente C) y una librera, en este caso la
librera matemtica (para ello deberemos haber usado en alguna parte un #include de math.h).

Una pregunta que podemos hacernos es por qu hemos incluido esa librera. La respuesta es
porque con el fichero de cabecera math.h slo hemos incluido definiciones de constantes, tipos de
datos y prototipos de funciones, pero no el cuerpo compilado de estas funciones, que est contenido
precisamente en esa librera (libm.a), ya que estas funciones no son de uso general y no se han
incluido por defecto en la estndar. Ocurre lo contrario con las libreras de manejo de la
entrada/salida o las funciones de strings, que s se incluyen por defecto al realizar los ejecutables
(podemos considerar una librera como un conjunto de ficheros que contienen cdigo objeto, las
propiedades de esos ficheros son asimiladas en la propia librera que los mantiene a travs de un
registro ndice).

Otra pregunta que surge es: Puedo yo crear mis propias libreras? La respuesta es s. Existe
en sistemas Linux (UNIX) el comando ar para ello, que nos permite crearlas (aadir mdulos),
modificarlas o eliminarlas (quitar mdulos).

Como cualquier comando, la sintaxis del mismo incluye opciones y argumentos:

ar - [ opci ones] [ mdul os] l i br er a [ f i cher os]

1
Gran parte de los inconvenientes de el cdigo dependiente se pueden solventar con la compilacin
condicionada que nos proporciona el preprocesador. Referencias ms amplias de l las podemos encontrar en
el libro de Kernigham.
PROGRAMACIN EN ANSI C APNDICES



76

Las opciones ms habituales son:

Opcin Significado
d Borrar mdulos a travs de los ficheros indicados
m Cambia de orden (mueve) un mdulo en la librera
p Pinta en pantalla un mdulo a travs de su fichero
q Aade de forma rpida mdulos (sin registro ndice) al final
r Reemplaza mdulos a travs de su fichero
t Muestra el contenido de la librera
x Extrae mdulos a travs de su fichero
o Preserva la fecha original del mdulo en la extraccin
s Crea o actualiza el registro ndice
u Reemplaza teniendo en cuenta la fecha
Modificador
a Lo coloca detrs de un mdulo existente
b, i Aade delante de un mdulo existente
c Crea una librera
v Modo "verbose"


De esta manera si tenemos una librera que se llama libre.a y tres mdulos mod1.o, mod2.o y
mod3.o podemos hacer:

Ejemplo Accin
ar c libre.a Crea la librera
ar r libre.a mod1.o Aade el mdulo y crea la librera si no existe
ar tv libre.a Muestra el contenido de la librera
ar q libre.a mod2.o mod3.o Coloca al final de forma rpida el mdulo
ar s libre.a Actualiza el registro ndice
ar x libre.a mod3.o Extrae el tercer mdulo

A la hora de usar la librera creada tenemos que tener en cuenta las siguientes reglas:

1. Las libreras se buscarn en los directorios por defecto que son "lib" y "/usr/lib". Si no
ponemos nuestra librera ah, tendremos que utilizar la opcin del compilador -L para
indicarlo.
2. Lo mismo tendremos que hacer con los ficheros incluidos de cabecera, en este caso la
opcin es -I.
3. Todas las libreras que creemos las empezaran con la palabra "lib" a la que seguir el
nombre propio de la librera (en el caso anterior "re").
4. Al compilar tendremos que invocar al enlazador con la opcin -l para que incluya la librera
creada (en el ejemplo -lre).

Existe un comando relacionado con las libreras, comando nm, para ver el contenido de sus
mdulos. La sintaxis es:

nm- [ opci ones] [ f i cher os]

donde el fichero puede ser un mdulo o una librera, en este ltimo caso se puede usar la opcin -s
para ver el ndice.

PROGRAMACIN EN ANSI C APNDICES



77
El edi t or de UNIX vi
En todos los sistemas UNIX y formando parte del mismo como un comando ms, existe una
familia de editores que nos permitirn procesar texto. Esta familia procede del editor ed, que era el
editor original del UNIX, pero que prcticamente ya no se usa.

La familia consta de tres miembros, por un lado el ex que es la versin moderna y mejorada
del ed, el edit que es un subconjunto del ex para principiantes, y el vi, que a diferencia de los otros
editores que eran de lnea, es de pantalla (un editor de lnea slo trabaja con una lnea, por lo que es
muy incmodo de usar, un editor de pantalla nos permite trabajar en todo el texto, aunque slo nos
muestra una ventana del mismo). Este ltimo adems realiza llamadas al ex para realizar
determinadas funciones.

En muchos sistemas operativos existen otros editores, como el emacs o el mismo
WordPerfect, ya que el vi es muy poco amigable (es desagradable de usar), pero tienen la
desventaja que no son universales, cuestin que en el vi est garantizada ya que forma parte del
sistema operativo.

Todos los editores funcionan de igual manera, es decir, a travs de un buffer en memoria.
Cuando se lee un fichero desde el editor, este se carga en memoria en un buffer sobre el cual se
hacen las modificaciones y una vez terminado el trabajo se vuelca el contenido de la memoria en el
fichero. Esto tiene una ventaja y es que si se hace algo mal podemos dejar el trabajo sin salvar
(pasar a disco), pero tambin tiene una gran desventaja, y es que si ocurre una fallo en la corriente o
en el sistema, todo el trabajo realizado sobre la memoria se perder, por eso es muy importante
realizar volcados a disco cada cierto tiempo (hay editores avanzados que lo hacen automticamente
como el WordPerfect).

El editor vi tambin trabaja con un buffer en memoria que conviene salvar cada cierto tiempo.
Para ello tendremos que ponerlo en modo comando y hacer ":w", pero ) qu significa modo comando
? El vi tiene dos modos de trabajo, uno donde introduciremos caracteres que formarn parte del
texto y otro donde esos caracteres son acciones sobre el editor, como veremos en el siguiente
apartado.


Funcionamient o
Lo primero que tenemos que hacer para trabajar con el editor es evidentemente ejecutarlo,
para ejecutar el vi tendremos que poner su nombre seguido opcionalmente por el nombre de un
fichero de texto que queramos editar:

$ vi f i cher o

si el fichero ya existe aparecer en pantalla el mismo, si es nuevo aparecer una tilde "~" en las
lneas que estn sin ocupar, en este caso la primera, donde se realizar la escritura del texto.

Cuando ejecutemos el vi nos lo encontraremos en modo comando, con lo cual esperar que
le introduzcamos una orden (carcter) sobre la que actuar, bien cargar un fichero si es que no se ha
indicado ninguno en la lnea de ejecucin, o bien un comando de paso a modo texto.

Para pasar a modo comando de nuevo se utiliza un carcter especial que es el de escape y
que se puede conseguir con la tecla de escape (no hay ningn problema en pulsar varias veces esa
tecla, a parte de un pitido por cada par de pulsaciones). En la pantalla del vi slo aparece el texto
sobre el que se escribe, por lo que no sabemos (con algn indicativo en pantalla) en que modo
estamos, por lo que se suele pulsar varias veces la tecla escape para garantizar el modo comando,
esto unido a lo anterior, hace de una sesin de varios usuarios de vi, un concierto de pitidos.


PROGRAMACIN EN ANSI C APNDICES



78
Comandos
El vi tiene casi un centenar de comandos, de los que habitualmente slo se utilizan una
docena. Estos comandos se pueden dividir en varios grupos dependiendo de la funcin a realizar.


Movi mi ent o del cursor
Existen ms de 40 comandos de este tipo, pero los principales son cuatro, indicativos de las
cuatro direcciones sobre las que nos podemos mover:
la direccin a la que apuntan es la misma que la posicin que ocupan en el teclado,
restringindose su movimiento a la zona ocupada por el texto (si intentamos salir de esa zona nos
avisar con un pitido). Tambin se podrn utilizar las flechas del teclado.

Hay que incluir dentro de los comandos de movimiento la combinacin de caracteres avance
de lnea (10) y retorno de carro (13), que se consiguen con la tecla return o enter. Esta servir para
dar a una lnea por concluida y pasar a la siguiente en la primera posicin.

Los otros comandos de movimiento menos usados aparecen en la tabla II, donde "^" significa
que se debe pulsar antes del comando la tecla de control (un carcter de control es otro carcter
ms)


Ent radas a modo t ext o
Fundamentalmente hay tres comandos para escribir texto, el a para aadir a partir de la
posicin del cursor, el i para insertar delante del cursor y el o, O para aadir una nueva lnea.

h : izquierda
l : derecha
j : abajo
k : arriba

b : principio palabra (begin)
e : final de la palabra (end)
0 : comienzo de lnea
$ : final de lnea
^d : 12 lneas abajo (down)
^f : 24 lneas abajo (forward)
^u : 12 lneas arriba (up)
^b : 24 lneas arriba (back)
nG : ir a la lnea n (go)
^g : nmero de lnea actual

a : aadir (append)
i : insertar (insert)
o : abrir debajo una lnea
O : abrir arriba una lnea
PROGRAMACIN EN ANSI C APNDICES



79

Hay que recordar que en todo el entorno Unix las maysculas y las minsculas son diferentes,
por eso tambin existen las variantes A y I, la primera aadir texto al final de la lnea actual y la
segunda insertar texto al comienzo de la misma. Si queremos escribir caracteres especiales antes
tendremos que sealarlos con el carcter ^v.

Borrado y al t eraci n
Una vez que hayamos escrito algo, ese texto se puede borrar o alterar pasando a modo
comando. Para borrar se utilizan dos comandos fundamentales (y sus variantes) el x y el d. El x para
borrar el carcter sobre el que estamos y el d (que se suele utilizar modificado) para borrar y pasar a
una zona de memoria especial donde se puede recuperar lo borrado.


El r es el comando de hacer sustituciones de texto, si utilizamos la versin minscula, slo
podremos sustituir un carcter, pasando de nuevo a modo comando, si utilizamos la mayscula nos
pondremos permanentemente en modo sustitucin (hasta que con escape pasemos a modo
comando), con lo que podremos sustituir varios caracteres. El comando c es una combinacin del
comando d y el i, primero borra lo indicado y despus pasa a modo insercin para escribir de nuevo
lo borrado.


Deshacer cambi os
En caso de error, existe un comando para deshacer los cambios realizados, este es el u
(undelete), del cual hay la versin minscula para deshacer un slo cambio o la mayscula para
deshacer todos los de la lnea.

Sal i da
Una vez que hayamos realizado la edicin, normalmente querremos salvar nuestro trabajo en
un fichero y abandonar la edicin. Existen desde el vi varias formas, la ms usual es la que parece a
continuacin:


x : borra un carcter
d : borrar poniendo en el buffer
r : reemplazar un carcter (replace)
R : reemplazar varios caracteres
c : borrar e insertar (change)


u : deshacer un cambio (undelete)
U : deshacer todos los cambios de la lnea

ZZ : salva en el fichero y sale
PROGRAMACIN EN ANSI C APNDICES



80
Separaci n y uni n de l neas
Hay dos comandos fundamentales para cortar y unir lneas, el primero y obvio es el retorno de
carro, el segundo es J (join), observar que se utiliza la mayscula.

Copi ado y recuperaci n
A parte del fichero y del buffer temporal donde se realiza el trabajo de edicin, hay una serie
de buffers temporales auxiliares donde se colocan los ltimos cambios o borrados que hayamos
hecho, y hay una serie de comandos que nos permiten trabajar con estos buffers para hacer copias
de texto.

Hay fundamentalmente tres comandos que nos permiten trabajar con estos buffers, por un
lado est el comando d que ya hemos visto, y con el cual realizamos borrados de texto que
colocamos temporalmente en un buffer. Y por otro lado estn el comando y (yank) y el p (put), el y
copia el texto seleccionado y lo coloca en un buffer, y el p hace lo contrario, coge la informacin del
buffer y la coloca en el texto.



Modificadores de comandos
Hay ciertos comandos, sobre todo los del ltimo apartado, que casi nunca se utilizan solos,
sino que lo hacen con sus modificadores. Hay varios tipos:

Repetir el comando: Esto hace que el comando acte en toda la lnea. As si d borra, dd
borra toda una lnea.
Poner un nmero delante del comando: Hace que ese comando se repita tantas veces
como el nmero indicado. As, 4dd borrar 4 lneas.
Alcances: Hay una serie de caracteres que despus de un comando modifican el alcance
del mismo. Estos alcances son:

e : hasta el final de la palabra.
w : hasta el comienzo de la siguiente palabra.
b : hasta el comienzo de la palabra.
$ : hasta el final de la lnea.
0 : hasta el comienzo de la lnea.
) : hasta el comienzo de la siguiente frase.
) : hasta el comienzo de la frase.
}: hasta el final del prrafo.
{: hasta el comienzo del prrafo.



<enter>: separar dos lneas
J : unir dos lneas (join)

y : copiar dejando en el buffer (yank)
p : poner el buffer en el texto despus (put)
P : poner el buffer en el texto antes
PROGRAMACIN EN ANSI C APNDICES



81
As, de borrar hasta el final de la palabra donde est situado el cursor, o d$ borrar hasta el
final de la lnea.

Cualquiera de estos modificadores puede utilizarse conjuntamente con otros, de esta manera
2dw borrar dos palabras.

Hemos dicho que con estos comandos se utilizan unos buffers especiales donde se colocan
los cambios que estamos realizando, en vi no slo existe un buffer para realizarlos, sino que hay
varios que se nombran con los nmeros y letras del alfabeto, aunque lo normal es utilizar el buffer
por defecto.

Para utilizarlos se pone delante del comando unas comillas, el cdigo del buffer, y por ltimo el
comando (incluso con otros modificadores).

As, "c4dd borra cuatro lneas y las coloca en el buffer c. Para recuperar las cuatro lneas
borradas bastara hacer: "cp. Recordad que si no nos interesa lo recuperado, siempre podemos
volver atrs con el comando u. Esta forma de utilizarlo puede servirnos para ir inspeccionando los
buffers uno a uno.


Llamadas al edit or de lneas ex
El vi est capacitado para utilizar los comandos del editor de lneas ex, de tal manera que
tenemos otro conjunto de comandos a los cuales se accede en modo comando anteponindoles el
carcter ":". De echo se puede pasar de un editor a otro utilizando el comando Q (pasa al ex) y el :
(pasa al vi). En muchos casos estos comandos son tan importantes como los originales del vi.

Adems se pueden utilizar varios comandos juntos como: :r !who que ejecuta el comando
who, lo lee, y lo coloca en el fichero de edicin.

Cabe destacar que los comandos del ex admiten delante de ellos dos nmeros que son el
rango de filas donde actan, as, si escribimos :3,5w fic, estamos haciendo que se escriban las lneas
3, 4 y 5 en el fichero fic. Lo mismo se puede hacer con :co y :m, para :r slo hace falta poner la lnea
detrs de la cual se colocar el fichero ledo. Para indicar todas las lneas del texto se utiliza la g, que
significa que se har globalmente, esta opcin es muy importante en los comandos que se vern
posteriormente.

Los comandos de bsqueda y sustitucin son muy utilizados. Estos se hacen en combinacin
con el ex. Los dos comandos del vi para realizar estas tareas son el / y el ?. El primero busca un
patrn hacia adelante y el segundo hacia atrs. Adems tenemos el comando n que repite la
bsqueda. A continuacin aparece una tabla de los ms utilizados:




Comando Descripcin Ejemplo
:wq Salva el texto en un fichero y sale del editor :wq
:w fichero Escribe el texto en un fichero :w pepe
:q! Sale del editor sin salvar el texto :q!
:r fichero Lee un fichero y lo coloca en el cursor :r pepe
:! comando Ejecuta un comando de la shell desde el editor :! who
:co Copia unas determinadas filas :3,5co7
:m Mueve unas determinadas filas :4,8m9
PROGRAMACIN EN ANSI C APNDICES



82

La estructura de un comando de este tipo es la siguiente:

: di r ecci n/ or den/ par met r os

donde direccin consta de las lneas donde se debe realizar la accin y el patrn (secuencia de
caracteres) a buscar, orden ser lo que se quiere hacer despus de encontrado el patrn, y
parmetros normalmente el nmero de veces que se quiere realizar la operacin. Veamos unos
ejemplos:

:1,7/can /s//perro / Se substituye de la 1 a la 7 can por perro una vez.
:g/can /s//perro /g En todo el fichero y todas las veces.
:/patrn/s/patrn/nuevo/ Busca patrn y sustituye por nuevo
:g/p/s/p/n/ Busca p y lo sustituye por n globalmente

En el primer ejemplo la direccin seran las lneas 1 a 7 y el patrn can , una vez buscado se
dice con la orden s// que se sustituya ese patrn (habra que repetirlo pero por defecto es el mismo,
por lo tanto se escribe //) por el nuevo patrn perro . Si se coloca como parmetro la g se realizar la
sustitucin en todos los encuentros.

Por ltimo hay que decir que desde el vi se pueden editar varios ficheros a la vez (uno detrs
de otro), para ello se deber ejecutar el vi con varios ficheros contiguos: vi f1 f2 f3. Los comandos
son:

:e fichero Editar otro fichero
:n Pasar al siguiente fichero en edicin mltiple


Opciones del edit or
Por ltimo y brevemente, hay que decir que el vi se puede personalizar y ponerle otras
opciones como los espacios por tabulador, o el nmero de columnas de texto. Para ello se utiliza el
comando del ex :set.

Las opciones ms comunes son:

ai identar.
ic ignorar maysculas.
nu numerar las lneas.
redraw reescribir.
sw nmero de espacios en ai.
ts nmero de espacios en tab.
w nmero de lneas de texto.
wm margen derecho (80-x).
all ver todas las opciones.

Un ejemplo de utilizacin de este comando, y quizs el ms til, sera para poner delante del
texto como referencia en nmero de la lnea:

: set nu

You might also like