Está en la página 1de 72

INTRODUCCIN A LA PROGRAMACIN EN C

1. INTRODUCCIN
1.1. Proceso de creacin de un programa ejecutable
1.2. Ingeniera del software
2. INTRODUCCIN AL LENGUAJE C
2.1. Elementos del lenguaje C
2.1.1. Comentarios y separadores
2.1.2. Palabras clave
2.1.3. Identificadores y constantes
2.2. Variables y constantes
2.2.1. Tipos de variables, declaracin e inicializacin
2.2.2. Modo de almacenamiento y mbito de una variable
2.3. Operadores
2.3.1. Aritmticos
2.3.2. Relacionales e Incrementales
2.3.3. Lgicos y de bits
2.3.4. De asignacin
2.3.5. Conversiones implcitas de tipo
2.3.6. Otros operadores
2.3.7. Precedencia y asociatividad
2.3.8. Funciones matemticas
2.4. E/S por consola
2.5. Estilo
2.6. Ejemplos
3. SENTENCIAS DE CONTROL
3.1. Condicionales
3.1.1. If-else
3.1.2. Switch-case
3.2. Bucles
3.2.1. For
3.2.2. While y Do-while
3.3. Alteraciones del flujo
3.3.1. Break y Continue
3.3.2. Goto
3.4. Ejemplos y ejercicios
4. TIPOS AVANZADOS DE DATOS
4.1. Vectores. Inicializacin y acceso a datos
4.1.1. Operaciones con vectores

Programacin en C 1
4.1.2. Ordenacin de un vector
4.2. Matrices. Inicializacin y acceso a datos
4.3. Cadenas de caracteres. Inicializacin, E/S por consola
4.3.1. Operaciones con cadenas
4.3.2. Biblioteca string.h
4.4. Estructuras. Inicializacin y acceso a datos
4.4.1. Copia de estructuras
4.4.2. Vector de estructuras
4.4.3. Estructuras de estructuras y vectores
4.5. Uniones
4.6. Enumeraciones
4.7. Tipos definidos por el usuario
4.8. Ejemplos
5. PUNTEROS
5.1. Declaracin e inicializacin
5.2. Operaciones con punteros
5.2.1. Asignacin
5.2.2. Aritmtica y comparacin
5.3. Punteros a vectores
5.4. Punteros a cadenas de caracteres
5.5. Punteros a estructuras
5.6. Punteros a punteros
5.7. Asignacin dinmica de memoria
6. ESTRUCTURA DE UN PROGRAMA
6.1. Directivas del preprocesador
6.2. Funciones
6.2.1. Declaracin y definicin
6.2.2. Llamada
6.3. mbito de una variable
6.4. Parmetros de una funcin. Paso por valor y referencia
6.4.1. Retorno
6.4.1.1. Retorno vaco (void) y de resultados
6.4.1.2. Retorno booleano
6.4.2. Funciones que llaman a otras funciones
6.4.3. Funciones, vectores y matrices
6.4.4. Funciones y cadenas
6.4.5. Funciones y estructuras
6.4.5.1. Estructuras como parmetros de una funcin
6.4.5.2. Estructuras como retorno de una funcin
6.5. Estructuracin de funciones en ficheros
6.5. Recursividad

Programacin en C 2
6.6. Parmetros de la funcin main
6.7. Ejemplos
7. ENTRADA / SALIDA
7.1. Salida estndar
7.1.1. printf
7.1.2. putchar y puts
7.2. Entrada estndar
7.2.1. scanf
7.2.2. getchar, gets y fflush
7.3. Gestin de ficheros
7.3.1. Abrir y cerrar ficheros. fopen y fclose
7.3.2. Gestin de errores
7.3.3. Operaciones de lectura/escritura
7.3.3.1. fprint y fscanf
7.3.3.2. fputc, fputs, fgetc y fgets
7.3.3.3. fwrite y fread
7.4. Ejemplos
8. PROBLEMAS RESUELTOS
8.1. Mximo comn divisor
8.2. Adivinar un nmero
8.3. Lotera
8.4. Integral
8.5. Nmeros primos
8.6. Fibonacci
8.7. Media y rango de un vector
9. APLICACIN PRCTICA: AGENDA
9.1. Funciones
9.2. Funciones
9.2.1. Gestin de 1 contacto
9.2.2. Gestin de 2 contactos
9.2.3. Gestin de la agenda
9.2.4. Gestin de E/S
9.3. Resultado
BIBLIOGRAFIA

Programacin en C 3
1. INTRODUCCIN
Un lenguaje de programacin es un lenguaje artificial que permite escribir un programa (conjunto de
instrucciones que interpreta y ejecuta un ordenador). Para que un ordenador interprete un programa, sus
instrucciones tienen que estar codificadas en lenguaje mquina (binario).
El humano est acostumbrado al lenguaje natural. Escribir un programa en binario es engorroso. Esta es la
esencia de los lenguajes de programacin. Por eso se les denomina lenguajes simblicos o de alto nivel, en
oposicin a los lenguajes mquina o de bajo nivel. Queda pendiente entonces, la tarea de traducir un
programa de lenguaje simblico a lenguaje o cdigo mquina sigue pendiente. Para ello se usan los
programas intrpretes y compiladores.
El lenguaje de ms bajo nivel es el ensamblador, DATA SEGMENT
parecido al lenguaje mquina con modificaciones
SALUDO DB Hola Mundo",13,10,"$"
nemotcnicas para facilitar su uso.
DATA ENDS
La tabla muestra un ejemplo de cdigo en
ensamblador vlido para procesadores INTEL CODE SEGMENT
80XX/80X86e, que imprime en pantalla el mensaje
ASSUME CS:CODE, DS:DATA, SS:STACK
Hola Mundo.
INICIO:
Los lenguajes de alto nivel son ms cercanos al
lenguaje natural, ms fciles de comprender. MOV AX,DATA
Entre los lenguajes de alto nivel y el ensamblador MOV DS,AX
existen lenguajes, de nivel medio, que combinan las
caractersticas de los primeros con operaciones MOV DX,OFFSET SALUDO
propias del lenguaje ensamblador, como la MOV AH,09H
manipulacin de bits y direcciones de memoria,
entre los que se encuentran C, C++ o Java. El INT 21H
programa siguiente, escrito en C tambin muestra MOV AH,4CH
en pantalla el mensaje Hola Mundo:
INT 21H
#include <stdio.h>
CODE ENDS
void main() { printf (Hola Mundo\n"); }
END INICIO
Comparando ambos programas, se aprecia que el escrito en C es ms corto e inteligible. La programacin
en ensamblador consigue ms eficiencia al ejecutar ms rpido y aprovechar mejor el hw de la mquina.
Sin embargo, son ms largos y hay que aprender un lenguaje especfico para cada procesador. Los
lenguajes de alto nivel penniten escribir programas abstrayendose del procesador concreto de una mquina,
lo que permite que se ejecute en distintas mquinas (portabilidad).
El lenguaje C fue inventado por Dennis Ritchie en Laboratorios Bell en 1972 para ser usado con UNIX.
Deriva del lenguaje B, escrito en 1970 por Ken Thompson para el primer UNIX, que a su vez deriva del
lenguaje BCPL (Martin Richards, aos 60). El nombre C se dio por ser la siguiente letra a B. El siguiente
lenguaje, que a lo mejor se debiera llamar D, se da en llamar C++ (C orientado a objetos, groseramente).
Hasta los 80, aunque existan compiladores para diversas arquitecturas, C sigui ligado a UNIX. En 1989,
se convirti en el estndar ANSI C. La estandarizacin sigui hasta 1999, cuando se public un nuevo
estndar, C99, mantenindose en la vanguardia de los lenguajes de programacin no orientados a objetos.
En la actualidad, C sigue siendo muy popular. Es un lenguaje de propsito general, compacto (32 palabras
reservadas), fcil de aprender, que combina la programacin estructurada con la eficiencia y velocidad del
ensamblador. Su ventaja es permitir programar a diferentes niveles, desde drivers de un dispositivo hw
hasta un SO. UNIX y DOS estn escritos en C. Adems, es buena base para abordar la POO con C++.
1.1. Proceso de creacin de un programa ejecutable
Resolver un problema con un ordenador, mecanizar una solucin o automatizar un proceso, son conceptos
semejantes que suponen codificar una serie de instrucciones que la mquina debe interpretar
correctamente. Es lo que ocurre con el lenguaje humano, incluyendo los gestos.
La mquina slo entiende cdigo maquina, binario. La transformacin del cdigo fuente de un lenguaje de
alto nivel a cdigo maquina se llama construccin (build) y distingue las siguientes etapas:
Preprocesado. El cdigo es un fichero de texto. La mquina no lo entiende. As, lo primero es preprocesar
el cdigo (fuente), realizado tareas como sustituir partes del cdigo por otras equivalentes o analizar
dependencias entre ficheros generando una versin del cdigo tambin en texto.

Programacin en C 4
Compilado. Consiste en transformar el archivo preprocesado en uno binario (cdigo mquina). En general
visible en disco (con extensin .obj en WS y .o en Linux). Estos archivos ya son parte del programa binario
que ejecutara el equipo. Son una parte, no el programa completo.
Enlazado esttico (linkado). Es normal
necesitar cdigo adicional, ya compilado en, por
ejemplo, bibliotecas estticas (.liben en WS y .a
en Linux). El enlazador o linker realiza la
composicin del archivo ejecutable (.exe en
WS, sin extensin en Linux). El cdigo de
biblioteca necesario, queda embebido en el
ejecutable, por lo que no es necesario adjuntar
la biblioteca para su ejecucin.
Enlazado dinmico. Al ejecutar un programa
puede necesitarse ms cdigo no enlazado en
la etapa anterior y que se encuentre en
bibliotecas dinmicas (o compartidas, extensin
.dll en WS y .so en Linux).
Cuando estas bibliotecas se encuentran en el SO (como ocurre a menudo) no es necesario distribuirlas con
el ejecutable. Al enlazar dinmicamente el SO, el programa se convierte ya en un proceso completo en
ejecucin.
Entre los compiladores de C estn el de MS (Visual C), el de Borland (Builder) y el GCC, de entornos Unix.
Para escribir un programa en C puede usarse cualquier editor, como Notepad, el bloc de notas u otros
especficamente creados para escribir y manejar programas fuentes en C.
Los errores de un programa se pueden dividir bsicamente en 2 tipos: errores en tiempo de desarrollo y
errores en tiempo de ejecucin.
Los errores en tiempo de desarrollo se dan cuando el archivo ejecutable no puede llegar a serlo. A su vez
distinguen 2 subtipos: errores de sintaxis y de linkado. Los primeros se refieren a la escritura del programa,
como una falta de ortografa. El compilador lo detecta e informa al usuario. Son fciles de detectar y
corregir. Los errores de linkado se refieren a que siendo el cdigo sintcticamente correcto, falla la creacin
del ejecutable por faltar cdigo (por ejemplo una biblioteca o se ha omitido un archivo). Tambin son
relativamente sencillos de detectar. El compilador puede emitir avisos (warnings) informando de anomalas,
no impiden la creacin del ejecutable, pero pueden suponer errores en ejecucin.
Los errores en tiempo de ejecucin se dan cuando el programa est en funcionamiento. Su deteccin es
ms compleja. Para solucionarlo se usan herramientas de depuracin (debugger) que permiten ejecutar un
programa paso a paso, visualizando su estado en cada momento. Un ejemplo paradigmtico es la
operacin de divisin entre 0.
1.2. Ingeniera del software
La programacin es una parte de lo que se conoce como Ingeniera del Software. La creacin de sw no es
un proceso artstico, no se improvisan soluciones a un problema. La ingeniera del sw establece
metodologas para el desarrollo de cdigo, abarcando reas como el anlisis de requisitos, del dominio del
problema, diseo, codificacin, pruebas, validacin, documentacin, control de versiones, etc.
Al igual que un edificio, un programa necesita un proyecto, el diseo de un arquitecto. El albail no
improvisa, se cie al proyecto y lo ejecuta con un resultado que ser revisado.
Un programa, puede ser manipulado por distintas personas, por tanto, el cdigo tiene que ser legible. Para
ello, son buenas prcticas de programacin, entre otras, incluir comentarios, explicando los detalles, no
obviedades; seguir un estilo aceptado en la organizacin (tabulaciones, saltos de lnea) o usar nombres
significativos en variables y funciones (variable "radio", en vez de "r" o "x").
Tambin es importante buscar programas genricos que particulares, cuando sea posible. Por ejemplo,
podra ser mejor opcin codificar un programa con un vector que maneje un nmero de elementos
indeterminado, en principio, que uno que maneje slo 10 datos. Por fin, que un programa funcione, o lo
parezca, no garantiza que el programa este bien realizado y la solucin sea correcta.

Programacin en C 5
2. INTRODUCCIN AL LENGUAJE C
El cdigo del siguiente programa, es el tpico con el que comenzar el #include <stdio.h>
estudio de un lenguaje de programacin. Lo nico que hace es mostrar
void main ()
en pantalla el mensaje Hola mundo.
{
Para su estudio, se analizar su cdigo lnea a lnea.
printf("Hola mundo\n");
#include <stdio.h>
}
Es la lnea necesaria para usar funciones de entrada / salida estndar.
La E/S estndar es la consola, en la que mostrar mensajes y leer lo que teclea el usuario. El significado
intuitivo de esta lnea de cdigo es que "incluya" (include) el fichero stdio.h (std=estandar, io=input, output).
void main () {
Esta lnea define el punto de entrada al programa, la funcin main () o principal. Un programa en C necesita
la existencia de una funcin main (). Slo una funcin puede ser denominada as. La llave de apertura
define el comienzo de un bloque de sentencias. En este caso el comienzo de la funcin main ().
printf("Hola mundo\n"); }
Realiza la llamada a la funcin printf (), que muestra mensajes de texto en la consola. Se encuentra
declarada en el fichero stdio.h, por lo que se codific la primera linea del programa con #include <stdio.h>.
El final de la sentencia se hace con punto y coma.
La llave de cierre indica el fial del bloque de sentencias, en este caso, main (). Cuando el programa alcanza
el final de la funcin main (), termina su ejecucin. Los programas se ejecutan en secuencia, una tras otra,
de arriba abajo. El programador debe tenerlo en cuenta. Si se omite la palabra void antes de main (), el
compilador lo acepta.
Cada sentencia se delimita con un punto y coma y generalmente se escribe una por lnea, aunque no es
imprescindible, es cuestin de estilo. Un conjunto de sentencias se pueden agrupar en un bloque delimitado
por llaves. El lenguaje C siempre distingue entre maysculas y minsculas. Es case-sensitive.
2.1. Elementos del lenguaje C
2.1.1. Comentarios y separadores
Todo programa debe ser sencillo y claro. Para //comentario de una linea
ello se emplean comentarios descriptivos.
#include <stdio.h>
Texto que no es tenido en cuenta por el
compilador, no es cdigo ejecutable. /*Este es un comentario de varias lineas, explicando lo
que sea necesario*/
Es til para las personas que leen el
programa. En C los comentarios de una lnea void main() //aqui puede ir un comentario
van precedidos de una doble barra (//). Los
comentarios de ms de una lnea se encierran {
entre una barra y asterisco (/* --- */. printf("Hola mundo\n"); //aqui puede ir otro comentario
Un separador es un caracter o conjunto de ellos que separan elementos del lenguaje. Los separadores
puede ser espacios, retornos de carro o tabulaciones. Los comentarios tambin podran considerarse
separadores. Un separador delimita dos elementos diferentes, sin efecto sobre la sintaxis.
Los separadores se usan tpicamente para #include <stdio.h> #include <stdio.h>
hacer ms legible el cdigo. Por ejemplo, el
void main () void main () {
programa anterior poda haber sido escrito de
las siguientes formas, aadiendo o quitando { //aqui comentario //aqui comentario
espacios y retornos de carro, siendo el
ejecutable binario generado al compilar printf("Hola mundo\n"); printf("Hola mundo\n");}
exactamente el mismo. } }
2.1.2. Palabras clave
C dispone 32 palabras clave (reservadas, defmidas en el estndar ANSI C. En la siguiente tabla se
muestran clasificadas por funcin. Las 3 primeras filas se relacionan con los tipos de datos. La ultima tiene
que ver con el control del flujo del programa. Al programar en C hay que indicar al compilador los tipos de
datos que se usarn. Al usar una variable para la edad de una persona, podr ser de tipo entero y se har
con la palabra clave 'int' abreviatura de "integer" (entero).

Programacin en C 6
Para usar una variable que represente la altura de una persona en metros, habr que usar decimales y ser
conveniente definirla con la palabra reservada 'float' o 'double' en funcin de la precisin decimal.
Las sentencias de control indican cmo tratar la ejecucin. Si se precisa tomar una decisin condicional se
usar 'if', por ejemplo. Para repetir un proceso se podrn usar 'for', 'while' o lo que corresponda.
Modificadores auto extern register static const volatile signed unsigned
de tipo
Tipos char double float int long short void
primitivos
Tipos datos enum struct typedef union sizeof
avanzados
Sentencias de break case continue do else if for while
control
return goto default switch
2.1.3. Identificadores y constantes
Un identificador es un nombre simblico referido a un dato o a una parte de un programa. Son nombres
asignados a datos (variables o constantes) y funciones. En C siguen unas reglas:
1. Slo pueden contener caracteres alfanumricos, maysculas o minsculas y el caracter
"underscore" (A-Z, a-z, 0-9, '_') . El alfabeto es ingls (la letra '' est excluida).
2. No pueden contener el caracter espacio ni caracteres especiales o de puntuacin
3. El primer caracter no puede ser un dgito, esto es, debe comenzar por una letra o el caracter ' '.
4. Se distinguen maysculas de minsculas
5. El estndar ANSI C admite un mximo de 31 caracteres de longitud, aunque en la practica a
veces pueden ser ms caracteres.
6. No se pueden utilizar palabras clave reservadas como 'long', 'auto', 'const', etc.
Algunas buenas prcticas que suelen aplicar los programadores son::
1. Usar identificadores o nombres significativos. Por ejemplo para referirse a una altura, usar
altura, mejor que al, ya que el programa ser ms legible.
2. Al usar variables auxiliares o de sentido matemtico, se recomienda usar nombres de
identificadores similares a los matemticos. Por ejemplo, un vector v con subndice i, se puede
poner vi. Usar el identificador i para contadores, repeticiones e indexar vectores. Asimismo, usar
los identificadores 'i', 'j', 'k' para contadores, bucles anidados o ndices. Usar los identificadores 'x' e
'y' para coordenadas espaciales o variables independientes e independiente de una funcin.
Las constantes son datos que no cambian de valor y no se modifican. Pueden ser numricas, caracteres y
cadenas de caracteres. En constantes enteras si el nmero es suficientemente pequeo se usa el tipo int y
long automticamente si el valor es grande. Para indicar formato long se aade una L al final del nmero.
34 constante tipo int 34L constante tipo long
Tambin se pueden usar constantes en formato octal, precediendo el nmero por 0. Para constantes
hexadecimales, precediendo con 0x o 0X.
07 constante octal 0xff constante hexadecimal 0XA1 constante hexadecimal
Las constantes reales, son por defecto de tipo double, a no ser que se diga lo contrario con la letra f F.
Tambin se permite la notacin cientfica con la letra e o E, seguida del exponente (base 10).
2.28 constante double 1.23f constante float 0.234 constante double
.345 constante doubl .67F constante float 123e3 constante double (123000.0)
.23E-2f constante float (0.0023f)
Las constantes de caracteres se representan entre comillas simples:
a constante caracter (letra a) '+' constante caracter (smbolo +)
Las constantes de cadenas de caracteres se limitan por comillas dobles:
"Hola mundo"

Programacin en C 7
2.2. Variables y constantes
Las variables son los datos bsicos de datos en un void main ()
programa. Son un campo de memoria con un
{ const int c=4; /* constante simbolica de tipo entero
nombre identificativo, que puede almacenar
denominada 'c', que vale 4 */
valores diferentes en la ejecucin de un programa.
int a; // variable tipo entero denominada 'a'
Al nombre dado a una variable se le llama
identificador y tiene que cumplir las normas a=3; // 'a' toma el valor 3
explicadas para los identificadores.
a=8; // 'a' toma ahora el valor 8
Las constantes son valores fijos con los que se
a=c; /* 'a' toma el valor de c, 4. La
puede asignar un valor a variables, operar con
constante 'c' no es modificada */
variables y constantes, etc. y en general usarse a
lo largo de la ejecucin de un programa. c=5; // Error! se intenta modificar la constante 'c'
En C existen adems las constantes simblicas, }
establecidas mediante la palabra clave const y
cuyo valor no puede ser modificado en el cdigo.
2.2.1. Tipos de variables, declaracin e inicializacin
Toda variable que vaya a usarse en un programa debe ser declarada antes. Esto es definir su tipo y
asignarle nombre. Opcionalmente se puede inicializar. El tipo indica su dominio (entero, carcter) e
imnplcitamente sus operaciones. Los tipos de dato existen para ahorrar espacio en memoria.
Los tipos de datos enteros son: char, short, int, long. Los reales float y double. Se entiende por enteras las
cantidades sin decimales y reales (decimal o de coma flotante) las que tienen parte entera y decimal. Tienen
punto decimal, aunque esa parte sea 0. Un caracter es un entero que se corresponde a un smbolo ASCII.
Caracteres y cadenas se estudian ms adelante. La sintaxis de
<tipo> <identificador> [=valor inicial];
declaracin de una variable es la mostrada.
Ejemplos de declaracin:
Al declarar una variable, realmente se indica a la int a; // declara una variable de tipo entero
mquina que reserve memoria, a la que se har denominada 'a'
referencia con el nombre de la variable, de forma al
double b=3.1; // declara una variable real de
leer o escribir en la variable, se lea o se escriba en
precision doble y valor inicial 3.1
su posicin de memoria. Entre la memoria
disponible, es el SO el que asigna una posicin a=4; // correcto
libre. El programador no decide en ese proceso.
b=14.567; // correcto
En C, cuando las declaraciones de variables se
c=23; // error de sintaxis, variable 'c' no
realizan en un bloque de sentencias entre llaves, las
deben ubicarse siempre al principio, siendo un error declarada
de sintaxis intentar declararlas en otro lugar. int d; // error de sintaxis,
En cada bloque de sentencias la zona de declaraciones termina con la primera lnea que no es declaracin.
La diferencia entre los tipos de datos enteros y reales es el tamao que ocupan en memoria, el nmero de
bytes que usan para codificar su valor. En funcin de su tamao, el rango de valores varia. Se puede
afirmar que el tamao char<=short<=int<=long y que el tamao float<=double.
En valores enteros se pueden aadir los especificadores signed o unsigned, indicando si las variables
pueden tener signo (enteros) o no (naturales). El tamao y rango de los diferentes tipos de datos depende
de la mquina, el SO y el compilador en cuestin. La siguiente tabla resume, suponiendo un ordenador de
32 bits con WS XP y Visual C 6.0, los tamaos de cada tipo de variable y valores mximos posibles para
cada una de ellas.
Signo Tipo Tam Rango Signo Tipo Tam Rango
signed char 1B -128, 127 unsigned char 1B 0, 255
signed short 2B -32768, 32767 unsigned short 2B 0, 65535
signed int 4B -2147483648, 2147483647 unsigned int 4B 0, 4294967295
signed long 4B -2147483648, 2147483647 unsigned long 4B 0, 4294967295
float 4B -3.4E 38, 3.4E 38 double 8B -1.7E 308, 1.7E 308

Programacin en C 8
Adems del rango, las variables de tipos reales float y double tienen distinta precisin (por su codificacin
3
interna). Segn el formato IE 754, un nmero float de precisin simple ocupa 4 B (32bits) y usa 1 bit para el
signo, 8 para el exponente y 23 para la mantisa. En la prctica esto se traduce a una precisin de unos 7
dgitos significativos. El nmero double ocupa 8 B, 1 para signo, 11 para exponente y 52 para la mantisa, lo
que en la practica significa una precisin de 16 dgitos significativos.
Variables del mismo tipo se pueden
declarar e inicializar en la misma lnea, <tipo> <identificador1}> [=valor1], <identificador2> [=valor2], ;
con la siguiente sintaxis:
El rango de valores no es comprobado por el compilador ni por el computador durante la ejecucin, con lo
que se pueden producir desbordamientos superiores o inferiores. Esto se muestra en el ejemplo de la
izquierda. Para almacenar letras o caracteres se usa el tipo de datos char (o unsigned char). En realidad es
de tipo entero, ya que lo que realmente almacena es el nmero entero del cdigo ASCII del caracter.
void main() void main ()
{ unsigned char c=255; // numero entero {
c=c+1; // c pasa a valer 0 char letra='a'; // letra=97
c=0; char simbolo='+'; //simbolo=43
c=c-1; // c pasa a valer 255 }
}
2.2.2. Modo de almacenamiento y mbito de una variable
Por su modo de int x=3; // la variable x es global
almacenamiento las
void main ()
variables se dividen en
globales y locales. Una { int y=5; // y es local a la funcion main
variable global existe desde
x=145; // correcto; mbito de la variable x
el punto en el que se declara
hasta el final del fichero en { int z=3; // variable z local a este bloque de sentencias
que se declara. Una variable
local existe desde el punto // Ambito de z
en el que se declara hasta el y=17; // se tiene acceso a la variable y
final del bloque de
sentencias en que se }
declara. En general se evita z=14; /* error de sintaxis, ya que el ambito de z se limita al bloque
el uso de variables globales, anterior. Entre estas llaves se define el mbito de y
ya que pueden generar
problemas. Ejemplo. } // Fuera de estas llaves se define el mbito de x

2.3. Operadores
Los operadores son smbolos (+, *) que permiten hacer tareas sobre variables y constantes, produciendo
un resultado fruto de la operacin sobre operandos. Los operadores pueden ser unarios (aplican a un nico
operando), binarios (2 operandos) y ternarios (3 operandos). Los operandos pueden ser numricos o
booleanos. El resultado de la operacin puede ser igualmente numrico o booleano.
2.3.1. Aritmticos
Los operadores aritmticos de C son:
+ suma - resta %
mdulo o resto de la divisin entera
* multiplicacin / divisin
Son operadores binarios que aceptan dos operandos de tipo numrico, devolviendo un valor numrico del
mismo tipo. El operador modulo % solo puede ser aplicado a datos enteros. Aplicarlo a constantes o
variables reales es error de sintaxis. Estos operadores intervienen tpicamente en el lado derecho de una
asignacin. Si un operador est en el lado izquierdo, se debe tener en cuenta que primero se evala el lado
derecho y luego se asigna. Hay que tener en cuenta que los nmeros tienen precisin finita. Por tanto, se
producirn redondeos, por ejemplo en operaciones de nmeros de tipo real, el dgito menos significativo. El
orden de evaluacin (prioridad) es similar al matemtico. Multiplicaciones y divisiones tienen ms prioridad
que sumas y restas. No existe operador para realizar la potencia de un nmero en C. Ejemplos:
int a=3, b=5, c;

Programacin en C 9
double d=3.0, e=5.0, f;
float r;
c=a+b; //c valdr 8, a y b no se modifican c=a*6; // c valdra 18, a no se modifica
c=b/a; //division entera, c valdra 1 c=a/b; // division entera c vale 0
c=b%a; // resto; c valdr 2 c=a*b+2; //c vale 17, a*b=15 15+2=17
f=d/e; // f valdr 0.6 f=d%c; // Error de sintaxis
a=a+b; // a pasa a valer 3+5, 8 r=2.0f/3.0f; // r vale 0.666667
2.3.2. Relacionales e Incrementales
Los operadores relacionales comparan datos numricos (variables y constantes). El resultado es booleano,
es decir, devuelve 0 si es falsa la relacin y 1 si es verdadera. El resultado se puede asignar por tanto a
variables de tipo entero (int, char). Los operadores relacionales son:
< menor que > mayor que <= menor o igual que
>= mayor o igual que == igual a != distinto a
El operador de comparacin de dos variables o constantes es un doble signo igual, que es diferente de la
asignacin, que se hace con slo un signo igual. Ejemplos.
int x=10, y=3, r;
r = (x==y); // se compara si x es igual a y: es falso (0). El resultado se asigna a r. Luego r=0
r = (x>y); // r vale 1 r = (x!=y); // r vale 1 r = (x<=y); // r vale 0 r = (x>=2); // r vale 1
Los operadores incrementales, incrementan o decrementan un nico operando (operador unario) de tipo
numrico (genemlmente entero) en una unidad. Son:
++ incrementa una unidad -- decrementa una unidad

En funcin de la posicin relativa del operador respecto del operando se defmen las siguientes operaciones:
a++; // post incremento ++a; // pre incremento a--; // post decremento --a; // pre decremento
La diferencia es el valor devuelto por el operador en caso en que la expresin deba de ser evaluada en una
sentencia con otras operaciones. En el caso de preincremento y predecremento el valor obtenido es igual al
valor final del operando, ya que primero (pre) se hace la operacin y luego se transfiere el resultado
int a=2, r;
Si se hace r=++a; primero se incrementa 'a', que pasa a valer 3. El resultado se asigna a r: r = 3. Esta
operacin es equivalente a {++a; r=a;} primero se incrementa a y luego se asigna.
Cuando es un postincremento o postdecremento, primero se transfiere el resultado (valor de la variable) y
luego se incrementa dicha variable. En el ejemplo, r=a++; primero se realiza la asignacin, luego r = 2 y
luego se incrementa a: a= 3. Sera una operacin equivalente a { r=a; a++} el orden inverso al anterior.
2.3.3. Lgicos y de bits
Los operadores lgicos implementan operaciones booleanas. Actan sobre operandos booleanos y dan un
resultado booleano. Las operaciones ANO y OR son binarias (2 operandos) mientras que NOT es unaria.
&& AND (Y lgico) || OR (O lgico) ! NOT(negacin lgica)
Los operadores lgicos se usan para combinar operadores relacionales formando expresiones lgicas
complejas
float a=3.0f,b=2.0f; int c=4,d=S,r;
r= (a>b) && (c<d); // como 3.0>2.0 y 4<5, r = 1 r= (a<11.3f) || (d!=5); // 3.0<11.3, r = 1
Tambin es posible que uno o ambos operandos sea una variable o constante numrica. En ese caso se
evaluara como falso (0) si su contenido es 0, y verdadero (1) si su contenido es otro.
int c=4, d=5,r; r= !c; // 'c' es 0 luego c=1 y r no c, luego r=0
Los operadores de bits son operadores binarios, excepto la negacin que es unario. Aplican a variables de
tipo entero, resultando otro nmero entero aplicando las operaciones lgicas correspondientes a los bits de
los operandos.

Programacin en C 10
| OR de bits >> desplazamiento de bits a la derecha ^ XOR (O exclusivo) de bits
& AND de bits << desplazamiento de bits a la izquierda ~ NOT de bits
unsigned char a=8, b=4, c; / /a=000 1000, b=0000 0100
c = a & b; // c=0000 0000, luego c=0 c = a | b; // c= 0001 1000, c=12
c = a << 2; // c=0100 0000, c=32 c = ~a; // c= 1111 0111, c=247
Cuando se desplazan bits a la izquierda, se rellena con 0 por la derecha. Cuando se desplazan bits a la
derecha, se rellena con 0 por la izquierda si el digito ms significativo es 0, y se rellena con 1 si es 1.
Adems el tipo de variable es con signo.
2.3.4. De asignacin
El operador bsico de asignacin es el signo '=', pero se pueden realizar otras operaciones de forma
combinada cuando aparece el mismo operando en el lado izquierdo y derecho de la asignacin.
= asignacin += incremento y asignacin -= decremento y asignacin
%= mdulo y asignacin *= multiplicacin y asignacin /= divisin y asignacin
<<= desplazamiento de bits a >>= desplazamiento de bits a &= AND lgico de bits y
izquierda y asignacin derecha y asignacin asignacin
|= OR lgico de bits y asignacin ^= XOR lgico de bits y asignacin
El signo = debe interpretarse como asignacin. La expresin m=z no se debe leer como "m igual a z" sino
como "se asigna a m el valor de z". Los operadores dobles tipo += o +=, operan la expresin de la derecha
del signo igual con la que aparece a la izquierda del operador. Por ejemplo x*=y equivale a x=x*y. Cuando a
la derecha del = aparece una expresin x*=y+4, significa es x=x*(y+4). Todas asignacin tiene 2 partes, el
lado izquierdo (left value) y el derecho (right value). En el lado derecho puede existir una expresin
aritmtica o lgica, pero en el lado izquierdo slo una variable. Ejemplos.
int a=B, b=3, c;
a=5; // asigna el valor 5 a la variable 'a' a*=4; // multiplica a por 4. Equivale a=a*4, luego a es 20
a+=b; // suma b y a. Equivale a=a+b; a es 23 a/=(b+1); // Equivale a=a/(b+1); division entera. a es 5
2.3.5. Conversiones implcitas de tipo
Si los operandos son de distinto tipo, el de menor precisin promociona a la de mayor precisin. Al realizar
operaciones con datos de distintos tipos, la mquina iguala los rangos para dar un resultado. Los rangos
son (de menor a mayor): char, short, int, long, float y double. Se llama promocin al hecho de elevar el
rango del dato que lo tiene menor. Al caso contrario se le llama prdida. El resultado de la expresin se
reconvierte (promocin o prdida) al tipo de variable a que se asigna.
int a=3, b=5, e; double d=3.0 ,e=5.0, f;
f=a+e; // a se convierte de 3 a 3.0 antes de ser sumado, el resultado final es que f vale 8.0
c=d/b; // b se convierte a 5.0, el resultado es 0.6 pero al asignarse a un integer, c vale 0
2.3.6. Otros operadores
El operador ? es el nico operador temario de C. Su resultado = condicion ? valor si cierto : valor si falso;
sintaxis es:
Se evala la condicin y en caso de verificarse, el resultado final es el primer valor y si no, el segundo.
Ejemplos:
int a=3, b=8,c;
c= a<b ? 6 : 7; // como 3<8, se asigna un 6 a c c= a==b? a: a*b; // a b, luego se asigna 24 a c
El operador sizeof permite conocer el tamao que
sizeof (tipo) sizeof (variable)
ocupan en memoria los diferentes tipos de datos.
Su uso consiste en poner entre parntesis el tipo del elemento o identificador, del que se desea conocer su
tamao y se devuelve el tamao en bytes. Ejemplo:
int tam; float a; char b;
tam = sizeof (char); //tam valdra 1 tam = sizeof (short); //tam valdra 2

Programacin en C 11
tam = sizeof (int); //tam valdra 4 tam = sizeof (long); //tam valdra 4
La conversin forzada de tipo o cast no es
[variable tipo_1] = (tipo) [variable tipo_1]
propiamente un operador. Sera una operacin.
La operacin de forzar el cambio del tipo de dato al realizar asignaciones entre variables de distinto tipo.
Seran conversiones implcitas de tipo en las que el compilador proporciona warning (aviso) de que pueden
perderse datos (precisin). Por tanto, esta circunstancia debe ser tenida en cuenta. Ejemplo
int a=3; float b=3.5;
a=(int) b; //se fuerza la conversion de 'b' a entero (3); 'a' valdra 3, y el compilador no da avisos
Direccin e indireccin. Los operadores recuadrados se relacionan con punteros. El & direccin
operador & (direccin), aplicado a una variable devuelve su posicin de memoria. El
* indireccion
operador * (indireccin), aplicado a una direccin de memoria, devuelve su contenido.
Por fin, los operadores sealados se relacionan con vectores o estructuras de datos.
acceso a componente de una -> acceso a componente de una
|| acceso a componente del vector estructura estructura mediante puntero
2.3.7. Precedencia y asociatividad
Cuando se combinan operadores, el orden en el que se evalan depende de su prioridad y asociatividad. En
caso de operadores aritmticos, la precedencia coincide con la notacin matemtica: multiplicacin y
divisin con prioridad sobre suma y resta. Para modificar la precedencia o sintaxis se usan parntesis. Su
uso en exceso, no afecta de forma negativa a la operacin. Su omisin puede generar errores. Ejemplo:
int a=3, b=5, c=8, d;
d=a*b+3; // primero a*b, luego +3; 'd' vale 18 d=a+c/2; // primero c/2, luego +a, 'd' vale 7
d=c/a+b; // primero c/a, 2 (div. entera) +b, d=7 d=c/(a+b); // primero (a+b), 8, luego d=1
En la siguiente tabla se ordenan de mayor a menor prioridad los operadores.
Operadores Asociatividad Operadores Asociatividad
- ~ ++ -- (cast) *(indireccion) &(direccin) sizeof dcha a izq *, /, %, +, - (aritm.) izq a dcha
=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^= dcha a izq () [] . -> izq a dcha
<<, >>, <, <=, >, >=, ==, != izq a dcha ^ izq a dcha
& (de bits) izq a dcha |, &&, ||, ?: izq a dcha
La asociatividad establece como se agrupan operadores de la misma precedencia. La mayora de los
operadores se asocian de izquierda a derecha. Algunos, principalmente las asignaciones, tienen
asociatividad en sentido contrario. Ejemplo: Establecer el orden de evaluacin de la siguiente expresin.
r= x < 3.0f && 2 * x != 3 + y || n++;
La primera operacin sera n++, luego 2*x, 3+y, x<3.0f. El resultado de 2*x se compara con 3+y. El
resultado se evala (operacin &&) con x<3.0f. Se acaba comparando (||) ese resultado con n++.
2.3.8. Funciones matemticas
En los operadores de C no existe por ejemplo operaciones #include <math.h>
como la potenciacin o raz cuadrada. Existe una biblioteca
void main ()
matemtica con funciones que permiten estas
operaciones. El ejemplo muestra una implementacin de { float x=3.0f,r;
su uso. Otras funciones de esta biblioteca son las
r=sqrt(x); //r sera igual a x, o sea 3
trigonomtricas, exponenciales, logaritmos, valores
absolutos, redondeos, etc. }
2.4. E/S por consola
Aunque la E/S por consola tiene varias funciones, #include <stdio.h> //este include es necesario
se resume el manejo bsico de dos funciones, printf
void main ()
y scanf, para desarrollar ejemplos sencillos con
interaccin con el usuario. { int x=3;
Para usar estas funciones se incluye el fichero printf("El valor de x es %d \n",x);

Programacin en C 12
<stdio. h>. La funcin printf muestra texto por
} // El valor de x es 3
pantalla, pero tambin el contenido numrico de las
variables, como muestra el ejemplo siguiente:
En este caso, la funcin recibe 2 parmetros separados por coma. El primero la cadena de texto a mostrar
por pantalla, limitada con comillas. En esa cadena, el smbolo % seguido de una letra, indica el tipo de dato.
As %d sirve para enteros, %f para reales y %c para caracteres (muestra en pantalla el caracter ASCII
correspondiente al parmetro numrico). El segundo parmetro es la variable a mostrar por pantalla (x). El
mismo printf () permite mostrar varias variables, como por ejemplo el cdigo de la izquierda.
Ntese que en el primer parmetro, la cadena de texto, contiene tantos %d como variables a mostrar, cada
una en su sitio. A continuacin, se ponen los nombres de las variables separados por comas. El valor de la
primera variable va al primer %d, el de la segunda al segundo %d, etc. Tambin se pueden indicar
expresiones matemticas, como en el ejemplo de la derecha.
#include <stdio.h> #include <stdio.h>
void main () void main ()
{ int a=3,b=S,c; {
c=a+b; float a=3.1f,b=S.lf;
printf("La suma de %d y %d es %d \n",a,b,c); printf("La suma de %f y %fes %f \n",a,b,a+b);
} // La suma de 3 y 5 es 8 } // La suma de 3.100000 y 5.100000 es 8.200000
Para capturar las pulsaciones de teclado y asignarlas a una variable se puede hacer con la funcin scanf ().
La sintaxis es similar a la de printf, excepto #include <stdio.h>
en los parmetros de las variables, que
void main ()
deben ir precedidos del smbolo &. Se
puede solicitar ms de un dato a la vez. { float peso, altura,imc;
El ejemplo de la derecha sirve para printf("Introduzca su peso (kg) y su altura (m): ");
calcular el ndice de masa corporal (IMC)
scanf("%f %f",&peso, &altura); // se piden los 2 datos
de una persona, cuando se indica su peso
y altura. imc=peso/(altura*altura);
2.5. Estilo printf("Su IMC es: %f\n",imc);
Existen distintas convenciones en cuanto }
al estilo del cdigo, la forma de usar
tabulaciones, espacios, etc. con el objetivo #include <stdio.h>
de hacer el cdigo ms legible. // descripcion del programa
Se recuerda la importancia de los void main () { // bloque de sentencias: 1 tabulacion
comentarios.
// comentarios necesarios
Un estilo correcto puede ser el mostrado a
continuacin. unsigned char c=3; int d=123;

Destacan 3 aspectos: las llaves que abren // lnea en blanco despus de las declaraciones
y cierran un bloque de sentencias if(d<200) {
aparecen solas, cada una en una lnea; el
cdigo en un bloque est identaificado con float e=1.234f; // 2 tabulaciones
una tabulacin respecto a las llaves; y se int f=2;
deja una lnea en blanco para separar
partes de cdigo en cada bloque de e=e* f+2.1f;}
sentencias. }

2.6. Ejemplos
Ejemplo. Escribir la salida por pantalla de los siguientes programas:
#include <stdio.h> #include <stdio.h>
void main () void main ()
{ int a=3,b=4,c; { int a=3,b=4,c;
c=a*4+b-2; c=a>b;

Programacin en C 13
a=c*a; b=c*a+a;
b=c-a?a:c; a=a && b;
printf("%d %d %d\n",a,b,c); printf("%d %d %d\n",a,b,c);
} // SOLUCION: 424214 } // SOLUCION: 130
#include <stdio.h> #include <stdio.h>
void main () void main ()
{ float a=3.0f,b=4.0f; int c,d; { float a=3.0f,b=4.0f; int c,d;
c=a/b; e= (a>b 11 a>1);
a=a/b+c; e &= 15;
d=a+b; d=c++;
b=a*d; a=b*c*d;
printf("%f %f %d %d\n",a,b,c,d); printf("%f %f %d %d",a,b,c,d);
// SOLUCION: 0.750000 3.000000 0 4 } // SOLUCION: 8.000000 4.000000 2 1
Ejemplo. Desarrollar un programa para calcular la fuerza de atraccin entre dos cuerpos, siendo sus masas
y su distancia tecleados por el usuario.
Las masas sern introducidas en #include <stdio.h>
toneladas, la distancia en cm., y el
void main () {
resultado se dar en Newtons.
2 const float G=6.67e-11f; // se usa una constante
La frmula a aplicar es: F = G M1M2 / d ,
2 2
siendo G = 6,67 10-11 Nm /kg . La float masa1,masa2,distancia,fuerza;
salida por pantalla debe ser similar a la
siguiente. printf("Introduzca masa cuerpo 1 (Ton):"); scanf("%f",&masa1);
printf("Introduzca masa cuerpo 2 (Ton):"); scanf("%f",&masa2);
printf("Introduzca distancia entre ellos (cm):");
Introduzca masa cuerpo 1 (Ton): 1
scanf("%f",&distancia);
Introduzca masa cuerpo 2 (Ton): 2
masa1*=1000; masa2*=1000; // en kg
Lntroduzca la distancia entre ellos (cm): 2
distancia/=100; // en metros
Fuerza atraccin = 0.333500 N
fuerza=G*masa1*masa2/(distancia*distancia);
printf("Fuerza atraccion=%f N\n",fuerza);
}
Ejemplo. Calcular el permetro y rea de un circulo cuyo radio se introduce por teclado.
#include <stdio.h>
void main ()
{ const float pi=3.141592f;
float radio,perimetro,area;
printf("Introduzca radio:"); scanf("%f",&radio);
perimetro=2*pi*radio;
area=pi*radio*radio;
printf("Perimetro:%f Area:%f\n",perimetro,area);
}

Programacin en C 14
3. SENTENCIAS DE CONTROL
Las tareas repetitivas y decisiones requieren sentencias de control. Se implementan con palabras
reservadas que permiten alterar el flujo secuencial del programa. Las sentencias de control se pueden
agrupar en dos tipos: condicionales y bucles.
Las sentencias condicionales if-else y switch-case permiten tomar decisiones en funcin de condiciones.
Los bucles son sentencias de control para realizar tareas repetitivas. Se implementan con for, while y do-
while. Saltos o rupturas del flujo de ejecucin se realizan con break, continue y goto
3.1. Condicionales
La toma de decisiones condicionales se realiza generalmente con la sentencia if, aunque en ciertos casos
puede ser conveniente utilizar una estructura switch-case.
3.1.1. If-else
Es una sentencia usada para tomar decisiones. Permite ejecutar un bloque u otro en funcin de una o varias
condiciones escritas entre parntesis. La sentencia if funciona como muestra la figura. Se ejecutar el
bloque de sentencias A si se cumple la condicin ( 0). El bloque de sentencias B se cumple en todo caso,
ya que queda fuera de la condicin. Como en el ejemplo de la derecha.
#include <stdio.h>
if (condicin) void main ()
{ { int edad;
sentencias A, printf("Introduzca edad: "); scanf("%d",&edad);
} if(edad>=18) printf("Es usted mayor de edad\n");
sentencias B; printf("Final del programa\n");
}

Para ejecutar un bloque de sentencias en caso que se cumpla la condicin, o ejecutar otro, en caso
negativo, existe la instruccin else (sino) para fonnar la estructura if-else que funciona como sigue:
#include <stdio.h>
if (condicin){ void main ()
sentencias A; { int edad;
} printf("Edad: "); scanf("%d",&edad);
else { if (edad>=18) printf("Mayor de edad\n");
sentencias B; else { // es decir edad<18
} printf("Faltan %d aos para ser adulto\n",18-
edad); }
sentencias C;
}
Cuando la condicin se evala y no es cero, se ejecuta el primer bloque de sentencias. Esa evaluacin
puede ser aplicada directamente a una variable. Por ejemplo, la sentencia if (impar) realizar un proceso si
la variable impar no es mltiplo de 2, es decir impar.
Cuando el bloque de sentencias consta de una nica sentencia las llaves son opcionales. Si adems los
separadores no se tienen en cuenta, podra escribirse el cdigo anterior de las siguientes formas, todas
correctas y equivalentes. La diferencia est en el estilo.
if (impar) { if (impar)
printf("%d es impar\n",numero); printf("%d es impar\n", numero);
} else
else { printf("%d es par\n",numero);
printf("%d es par\n",numero); if (impar) printf("%d es impar\n", numero);
} else printf("%d es par\n",numero);

Programacin en C 15
Los if-else pueden encadenarse o anidarse, sin embargo, en muchos casos se pueden evitar anidaciones
usando expresiones booleanas compuestas. Ejemplo:
Cuando se concatenan if-else hay que #include <stdio.h>
tener en cuenta el orden. Si se usan
void main () {
llaves, el encadenamiento queda definido
explcitamente. Si no se usan llaves int edad;
cada else se empareja con el if ms
cercano. printf("Introduzca edad: "); scanf("%d",&edad);

Es importante tener presente que la if(edad>=18 && edad<=65) printf("Es edad laboral\n");
tabulacin a la hora de escribir un else
programa facilita la comprensin del
mismo, pero no afecta a la interpretacin printf("No est en edad laboral\n");
del compilador. }
Es un error comn poner slo = en lugar de == a la hora de comparar. Entonces, en vez de comparar, se
asigna un valor. Si el valor asignado no es 0 se da por cierta la comparacin, en caso contrario no. Ejemplo:
Otra forma de construir la estructura if para elegir entre opciones es con if (condicin)
else if.
instruccionesA
Es la forma de controlar el flujo del proceso ante una decisin de varias
else if (condicin2)
alternativas. Las expresiones se evalan en orden y en el momento que se
cumpla una, se termina toda la cadena de decisiones en su totalidad. Es instruccionesB
decir, se deja de evaluar el if. Su estructura es la mostrada.
else if(condicin3)
El ejemplo anterior de determinar si un ao es bisiesto, podra haberse
instruccionesC
codificado con una estructura tipo if-else if Un uso tpico es la distincin de
intervalos, como en este ejemplo de la derecha, en que se pretende dada else
una nota numrica entera, escribir la calificacin cualitativa correspondiente.
instruccionesD
Ntese la forma de resolverlo mediante if independientes y mediante la estructura i f-e 1 se i f. Este ltimo
caso suele ser mucho ms fcil y robusto frente a errores u omisiones en el cdigo.
iinclude <stdio.h> #include <stdio.h>
main () { main() {
int a; float nota;
printf ("Escriba el afio: "); printf ("Nota: "); scanf ("%f",&nota);
scanf ("%d",&a); if (nota<5) printf ("Suspenso\n");
if (a%4 !=0) printf ("Ao %d no bisiesto",a); elseif (nota<7) printf("Aprobado\n");
else (if (a%400==0) printf ("Ao %d bisiesto",a); else if (nota<9) printf("Notable\n");
else (if (a%100==0) printf ("Ao %d no bisiesto",a); elseif (nota<10) printf("Sobresaliente\n");
else printf ("Ao %d bisiesto", a) ; else printf ("Matricula \n") ;
} }
Otra fonna de escribir un if-else es con el operador condicional ?:, que logra expresiones compactas, tal
como muestra el cdigo siguiente:
//con if //con el operador condicional
#include<stdio.h> #include<stdio.h>
main () { main () {
int mayor,a,b; int mayor,a,b;
printf("Introduzca a y b: "); scanf("%d %d",&a,&b); printf("Introduzca a y b: "); scanf("%d %d",&a,&b);
if (a>b) mayor=a; a>b ? mayor=a:mayor=b;
else mayor=b; // tambien mayor=(a>b ? a:b);
printf("Mayor: %d\n",mayor); printf("Mayor: %d\n",mayor);
} }

Programacin en C 16
3.1.2. Switch-case
La estructura switch-case permite la evaluacin de una condicin mltiple (bifurcacin multidireccional). Es
similar a varios if encadenados, pero ms legible.
switch (expresin) {
case valor 1: sentenciasA
break;
case valor2: sentenciasB
break;

default: sentenciasF
}
La expresin debe ser (o tener como resultado) una variable entera (char, int, etc.), cuya igualdad ser
comprobada en las distintas condiciones ( valor1, valor2), cuyo tipo tiene que ser tambin entero. No
pueden existir dos case con el mismo valor. Cuando la expresin coincida con un valor, se ejecutarn las
instrucciones de su bloque correspondiente.
Cada grupo queda limitado por la palabra clave #include <stdio.h>
case y, normalmente, por la palabra clave break, no
main () {
precisando llaves, aunque estas pueden usarse si
se cree conveniente. int opcion;
Con break se sale del switch, como en los casos del printf ("Introduzca opcion:\n");
do-while, for, etc. Si no hay break se sigue
ejecutando secuencialmente el switch hasta printf("1. Imprimir\n"); printf("2. Copiar\n");
detectar otro break o finalizar el switch. printf("3. Guardar\n"); scanf ("%d",&opcion);
A veces es dificil saber si se tienen controlados switch (opcion){
todos los posibles valores que puede tomar la
expresin de control. En este caso se deja una case 1: printf ("Imprimiendo "); break;
salida por defecto para valores no controlados con case"2: printf ("Copiando... "); break;
la palabra reservada default.
case 3: printf ("Guardando "); break;
Puede haber switch anidados, es decir, dentro de un
case puede haber otro switch. Una utilidad tpica es default: printf ("Opcion incorrecta\n");
la construccin de mens, como se muestra en el }
ejemplo de la derecha.
}
3.2. Bucles
Un bucle es una o varias sentencias repetidas un nmero de veces que depende de condiciones, para
realizar tareas repetitivas de forma compacta. El bucle termina cuando se incumple la condicin de entrada
o se indica expresamente, con la sentencia break, y el programa contina la ejecucin con la sentencia
siguiente al bucle.
3.2.1. For
La estructura del bucle for es la mostrada en el cuadro. Si el bloque est formado slo por una sentencia,
las llaves son opcionales. El bucle for lleva asociada una variable entera, real o de caracteres, que controla
el bucle.
#include <stdio.h>
void main () {

for (inicio; condicin; progresion) { int i;

bloque de sentencias for {i=0; i<10; i++) {

} printf("%d ",i);
}
} // imprime 0,1,2,3,,9

Programacin en C 17
La variable debe indicar 3 aspectos: inicio, condicin y progresin, escritos entre puntos y coma y
encerrados entre parntesis. El aspecto de inicio es es el valor inicial de la variable. La condicin es la que
marca la ejecucin del bucle, mientras se cumpla. El incremento de la variable es el que sufre en cada
iteracin. En el ejemplo se imprimen los nmeros del 0 al 9, usando un bucle for. Como norma de estilo, las
variables de los bucles se suelen nombrar como los subndices, es decir, letras como i, j o k. El inicio,
condicin y progresin puede expresarse con una expresin, en principio, de cualquier tipo. Por ejemplo,
usando caracteres, en la forma for (i='a';i<='z';i++).
3.2.2. While y Do-while
Este tipo de bucles ejecutan un bloque de sentencias mientras se cumpla una condicin, escrita entre
parntesis. Las sentencias se indican entre llaves a menos que sea nica, caso en que pueden omitirse. La
estructura de ambos bucles es la mostrada en la figura. Hay que notar la diferencia sutil: en el bucle while,
la condicin se evala al principio, con lo cual el bloque de sentencias puede no realizarse. En el bucle Do-
while, la condicin se evala despus, con lo que el bloque de sentencias se hace al menos una vez.

while (condicin) { Do {
sentencias sentencias
} }while (condicin);

En la siguiente tabla se muestra el mismo ejemplo, usando las dos estructuras. El programa escribe por
pantalla el cuadrado de los 20 primeros nmeros naturales.
#include<stdio.h> #include<stdio.h>
main () { main () {
int x=1,c; int x=1,c;
while (x<=20) { Do {
c=x*x; c=x*x;
printf("%d al cuadrado es %d\n",x,c); printf ("%d al cuadrado= %d\n",x,c);
x++; x=x++;
} } while (x<=20);
} }
Cualquier tarea repetitiva a implementar con un bucle puede utilizar cualquier estructura, for, while o do-
while. Pueden reescribirse de forma equivalente. Sin embargo, indicaciones de diseo son:
1. Si el nmero de veces a repetir el bucle se conoce a priori, en principio se optar por for.
2. Si el bucle se repetir un nmero de veces desconocido a priori, se optar por while o do-while, adems
2.1. Si el bucle se realiza al menos 1 vez, entonces se opta por do-while
2.2. Si el bucle no tiene por qu realizarse, se optar por while
Los bucles se pueden anidar para describir tareas mltiplemente repetitivas. El ejemplo muestra 2 bucles for
anidados para mostrar por pantalla las tablas de multiplicar del 1 al 9.
#include<stdio.h> Tabla del 1
main () { 1 x 1=1
int i,j; 1 x 2=2
for (i=1; i<10; i++) {
printf("Tabla del %d\n",i); Tabla del9
for (j=1; j<10; j++) printf("%d x %d = %d\n",i,j,i*j);
} 9 x 9=81
}

Programacin en C 18
La sentencia for o while no llevan punto y coma final. Si se escribe por error, se interpretar como una
sentencia vaca y el funcionamiento del programa no ser el esperado.
3.3. Alteraciones del flujo
Las sentencias break, continue y goto permiten alterar el flujo normal de ejecucin de una estructura de
control. Las dos primeras son usadas y validas. Goto NO debe usarse. Switchcase no permite el uso del
continue. Al aplicar break y continue a un bucle, permiten abandonarlo en cualquier momento o saltar a la
siguiente iteracin sin terminar el bloque de sentencias completo. Suelen ligarse a condiciones (if).
3.3.1. Break y Continue
La sentencia break interrumpe un bucle al ejecutarse, continuando la ejecucin del programa en la primera
instruccin fuera del bucle. En la figura, slo se ejecutaran las sentencias A. Si se encuentra en un bucle
anidado, se aplica al bucle en que se ejecuta, no a los externos. Por su parte, la sentencia continue se usa
para que no se ejecuten las sentencias que restan del bucle, sin abandonarlo. Su uso es ms delicado, ya
que puede generar ms errores semnticos.
while (condicin) { while (condicin) {
sentenciasA; sentenciasA,
break; continue,
sentenciasB; sentenciasB:
} }
3.3.2. Goto
En programacin estructurada no debe usarse Goto. Es una bifurcacin Goto etiqueta;
incondicional que dirige la ejecucin del programa desde el punto de deteccin

hasta una etiqueta asociada sin necesidad de condicin. Se entiende por
etiqueta una referencia alfanumrica seguida de dos puntos. etiqueta:
3.4. Ejemplos y ejercicios
Ejemplo 1. Escribir la salida por pantalla de los siguientes programas:
#include<stdio.h> #include<stdio.h>
main () { main () {
int i,j; int i=1,s=0;
for (i=0,j=10;i<=5;i++,j--) printf("%d ",i*j); while (i) { i++;
} if (i%2) s+=3; else s+=5;
// 0 9 16 21 24 25 if (s>20) break;
printf("%d ",s);
} // 5 8 13 16
#include<stdio.h> #include<stdio.h>
main () { main () {
int i=10, j=0; int i=10,j=0;
do { i--; j++; for(i=0;i<4;i++) {
printf("%d%d",i,j); switch (i) {
if(i*j>10) { case 0: printf("a");
printf("Break"); case 1: printf("b");
break; case 2: printf("c");
} default: printf("E");
}while(i>0 && j<10); }
} }
// 9182Break } // abcEbcEcEE

Programacin en C 19
En negrita se muestran las salidas.
Ejemplo 2. Calcular el mximo, mnimo y media de un Ejemplo 3. Sumar 50 nmeros dados por teclado.
conjunto de "n" reales introducidos por teclado. Si se detecta uno negativo, indicarlo y abandonar
el bucle indicando los valores ledos.
#include<stdio.h> #include<stdio.h>
main () { rnain () {
int i,num_datos; int i,surna=0,nurn;
float media,min,max,dato;; for(i=1;i<=50;i++) {
printf("Numero de datos: "); scanf("%d",&num_datos); printf("Dato %d:",i); scanf("%d",&nurn);
media=0.0f; if (nurn<0) {
for (i = 1; i <= num_datos; i++) { printf("Nmero negativo\n");
printf("Dato %d:",i); scanf("%f",&dato); break;
media+=dato; }
if (max<dato || i==1) max=dato; surna+=nurn;
if (min>dato || i==1) min=dato; }
media=media/num_datos; printf("Valores leidos: %d\n,i)
printf("Min=%f Mx=%f ",min,max); printf (Total de la suma: "%d\n,surna);
printf("Media=%f\n",media); }
}
Otros ejemplos.
Ejemplo 4. Calculadora. Aplicacin que Ejemplo 5. Adivinar un nmero. Sea un juego en que la
funcione como calculadora capaz de realizar 4 mquina elige un nmero entero de 0 a 99 y el usuario
operaciones sobre reales: multiplicacin, suma, intenta adivinarlo. La mquina informa si la estimacin
resta y divisin. El usuario introduce por orden es menor, mayor o si ha acertado. Si acierta, le informa
el primer operando, el signo de la operacin y el del nmero de intentos. Usar srand() para inicializar la
segundo operando en una sola linea, sin semilla aleatoria y rand() para generar los nmeros
espacios. El programa se ejecuta hasta que se aleatorios. La semilla de generacin de nmeros
pulsa Ctrl+C. Los operandos tendrn precisin aleatorios se inicializa una vez. Se pasa como
normal y se switch case. La salida por pantalla parmetro el tiempo actual, para que cada vez que se
ser similar a: 12+13.5 =25.500000. juega sea una semilla diferente.
#include <stdio.h> #include <stdlib.h> // para srand() y rand()
void main(void) { #include <time.h> // para time()
float a,b,resultado; // operandos #include <stdio.h>
unsigned char operador; // operacion void main(void) {
while (1) { int numero,n;
scanf("%f%c%f",&a,&operador,&b); int intentos=0;
switch(operador) { srand(time(NULL)); // inicializa la semilla aleatoria
case +: resultado=a+b; break; numero=rand()%100; // genera numero aleatorio
case -: resultado=a-b; break; printf("Ya se dispone del nmero a adivinar [0-99]\n");
case *: resultado=a*b; break; do {
case /: resultado=a/b; break; printf("Adivine numero: "); scanf("%d",&n);
default: printf(" No operador\n"); intentos++;
break; if(n<nurnero) printf("Se ha quedado corto\n");
} else printf("Se ha pasado\n");
printf("=%f\n",resultado); } while(nurnero!=n);

Programacin en C 20
} printf ("Se adivin en %d intentos!\n",intentos);
} }
Ejercicio 1. Realizar un programa que pida un nmero entero 'n' al usuario, mostrndo las tablas de sumar y
multiplicar de dicho nmero, as como su cuadrado y cubo. Una vez mostrado esto, volver a pedir otro
nmero al usuario, y as indefinidamente hasta que se pulse Ctrl+C o se cierre la aplicacin.
Ejercicio 2. Realizar un programa que pida un nmero entero 'n' al usuario y le informe si dicho nmero es
primo o no es primo. La forma ms sencilla es dividir dicho nmero por todos los nmeros menores que el
(excepto el 1 ). Si no es divisible por ninguno, entonces es primo.
Ejercicio 3. Realizar un programa que cuente desde el nmero A hasta el numero B de N en N, siendo A,B y
N introducidos por el usuario. Se tiene que permitir la cuenta hacia atrs, si N es negativo. En cualquier
caso, el programa tiene que comprobar que A y B son compatibles con un salto dado, es decir, si el salto es
positivo, A debe de ser mayor que B.
Ejercicio 4. Realizar un juego en el que el jugador piensa un nmero del 1 al 100 y el ordenador trata de
adivinarlo. El jugador debe informar al ordenador si ha acertado, se ha quedado corto o se ha pasado.

Programacin en C 21
4. TIPOS AVANZADOS DE DATOS
Cuando se ha de tratar con varias variables simultneamente, la lgica invita a usar estructuras de datos
que los agrupen. El lenguaje C ofrece la posibilidad de agrupar variables del mismo tipo bajo un nico
nombre genrico, simplificando su gestin. La identificacin de cada dato se usan ndices. Este tipo de
estructura se denomina vector, arreglo o array.
4.1. Vectores. Inicializacin y acceso a datos
La declaracin de un array sigue la sintaxis del resto de
variables. Debe indicarse su tipo y especificar entre corchetes tipo nombre [cantidad de elementos];
la dimensin del vector. Su sintaxis es:
Sean 3 sensores trmicos que proporcionan un valor de temperatura en grados con decimales. Se podran
declarar 3 variables, pero lo lgico puede ser usar un vector de 3 elementos como sigue.
Esta sentencia indica que se ha definido un vector de valores #include <stdio.h>
float llamado "temp", con 3 elementos, numerados del 0 al 2.
void main()
La numeracin, al comenzar en 0, termina en el tamao del
{ float temp [ 3];
vector menos una unidad. Es importante resaltar que la
cantidad de elementos no puede ser una variable, tiene que //sentencias del programa
ser siempre constante, no se puede especificar el tamao con
}
una variable.
S se pueden usar directivas del preprocesador (realmente son constantes). Estas directivas son una
equivalencia entre la etiqueta y el valor. No son variables. En el siguiente ejemplo NUM_SENSORES,
realmente es la constante 3.
#define NUM_SENSORES 3 void main ()
void main () {
{ float temp[3]={23.4, 24.9, 27.2};
float temp[NUM_SENSORES]; // correcto //sentencias del programa
//sentencias del programa }
}

Al igual que en los tipos bsicos de datos, al declarar un vector, tambin se puede dar un valor inicial a cada
componente, que no es comn salvo que el vector contenga pocos elementos. Para inicializar valores de un
vector, puede hacerse como en el cdigo central.
En la inicializacin cada elemento se separa mediante una coma y se colocan entre llaves. Los valores
dados a los elementos del vector se almacenan en memoria tal como se muestra en la figura. Otra opcin
de inicializacin consiste en dejar que el compilador cuente por s slo la cantidad de elementos que
contiene el vector (float temp[]={23.4, 24.9, 27.2};). No se indica el nmero de elementos entre los
corchetes, pero se inicializan. Este cdigo es equivalente al anterior.
Al compilar el programa se cuenta el nmero de elementos entre llaves y se usa como si se hubiera
indicado entre corchetes. Cada elemento del vector se almacena en posiciones consecutivas de memoria tal
como refleja la figura. La posicin del vector la decidde el SO, igual que una variable normales, pero
ocupando posiciones consecutivas. Existe la opcin de inicializar solo el primer o primeros componentes de
un vector. En el ejemplo anterior, podra ser:
float temp[3]={23.4}; //asigna la primera, resto a 0
float temp[3]={23.4, 24.9}; //inicializa la 1 y 2, la tercera a 0
Aqu si es importante indicar la dimensin del vector, si se desea usar un vector de 3 componentes. En caso
contrario, el vector tendra slo 1 componente.
Si se desea acceder individualmente a un dato basta con indicar el nombre del vector y la posicin que
ocupa el dato entre corchetes. Si en el ejemplo anterior se desea asignar valores a cada componente por
separado, para posteriormente hallar el valor de su media, podra hacerse como en el cdigo propuesto a la
izquierda.
El compilador no controla el tamao de los vectores. Si el programador intenta acceder a un componente
que no es del vector, el compilador no produce errores, pero si lo har en la ejecucin del programa. Es el

Programacin en C 22
caso del cdigo central. Recordar que los elementos de un vector de dimensin N, se numeran de 0 a N-1.
La figura derecha muestra cmo se dara el error de ejecucin por desbordamiento de vector.
#include <stdio.h>
void main () void rnain ()
{ float temp[3); {
float media; float temp[3]={23.4, 24. 9, 27.2};
// se escriben los componentes temp[3]=12.2;
temp[0]=13.5; // error ejecucion
temp[1]=24.5; temp[-1]=23.0;
temp[2]=31.2; // error ejecucion
media=(temp[0]+temp[1]+temp[2])/3.0f; }
printf("Temp media: %f\n",media);
}
La inicializacin puede hacerse directamente, por ejemplo, pidiendo los datos al usuario. Adems, el manejo
de componentes presentado no es la forma habitual. Lo habitual es hacerlo con una variable que sirva de
ndice para el accesoa a cada dato y controlarlo con una estructura, como por ejemplo un for. A
continuacin se muestran varios ejemplos de manejo de vectores.
Ejemplo: Introducir los das que tiene cada mes del ao y mostrarlo en pantalla.
#include<stdio.h>
void main ()
{ int mes[12], i;
for(i=0;i<12;i++){ printf("Introduzca los das del mes %d: ",i+1); scanf("%d",&mes[i]); }
for(i=0;i<12;i++) printf("El mes %d tiene %d dias\n",i+1,mes[i]);
}
Ejemplo: Introducir 10 datos enteros por teclado y mostrar la lista de dichos datos en orden inverso.
#include <stdio.h>
void main()
{ int i, vect[10];
printf("Escriba 10 datos:\n");
for(i=0;i<10;i++){ printf("Dato %d: ",i); scanf("%d",&vect[i]); }
printf("Datos en orden inverso:");
for(i=9;i>=0;i--) printf("%d ",vect[i]);
}
4.1.1. Operaciones con vectores
Algunos ejemplos de operaciones tpicas con vectores como el modulo, la media de sus elementos, el valor
absoluto de un vector, producto escalar, etc. Los ejemplos manejan vectores inicializados en cdigo y
omiten la muestra por pantalla, por simplicidad.
Ejemplo. Dado un vector de 5 nmeros enteros que Ejemplo. Calcular la media de los elementos de un
teclea el usuario, convertirlo a su valor absoluto vector de 10 elementos tecleados por el usuario.
#include <stdio.h> #include <stdio.h>
void main () { int datos[S]; #define N 10
int i; void main () { int i;
// se piden datos float vect [N];
for(i=0;i<5;i++){ float med, suma;

Programacin en C 23
printf("Dato %d: ",i); // pedir datos
scanf("%d",&datos[i]); } for(i=0;i<N;i++) {
// valor absoluto de cada componente printf("Elemento %d: ",i);
for(i=0;i<5;i++) { scanf("%f",&vect[i]); }
if(datos[i]<0) datos[i]=-datos[i]; } // calcular media
// se muestra el vector por pantalla suma=0;
printf("Vector: "); for(i=0;i<N;i++) suma+=vect[i];
for(i=0;i<5;i++) printf("%d ",datos[i)); med=suma/N;
printf("\n"); printf("La media es: %f\n", med);
} }
En este ejemplo se usa #define para indicar el valor 10. Esto ofrece la ventaja que si alguna vez se tuviera
que cambiar el programa, slo cambiando el valor 10 por el que correspondiera, se evitaran errores.
Ejemplo. Realizar el producto escalar entre dos Ejemplo. Realizar la suma de 2 vectores v1 y v2
vectores v1 y v2 sobre v3 (v3=v1+v2)
iinclude <stdio.h> #include <stdio.h>
void main () void main () { float v1[5]={1,34,32,45,34};
{ float v1[5]={1,34,32,45,34}; float v2[5]={12,-3,34,15,-5};
float v2[5]=(12,-3,34,15,-5}; float v3 [5]; int i;
float prod=0; for(i=0;i<5;i++) v3[i]=v1(i]+v2[i);
int i; printf("Vector3: );
for(i=0;i<5;i++) prod+=v1[i]*v2[i); for(i=0;i<5;i++) printf("%f ",v3[i));
printf("El producto escalar es: %f\n",prod); printf(\n");
} }
4.1.2. Ordenacin de un vector
Ordenar un vector es una tarea que se #include <stdio.h>
puede hacer de diversas formas, con
void main () { int datos[5];
distintos algoritmos. Algunos ms
eficientes que otros, ms rpidos. Se int i,j;
muestra a continuacin una forma
didctica de fcil comprensin. // se piden datos

4.2. Matrices. Inicializacin y for(i=0;i<5;i++) { printf("Dato %d: ",i); scanf("%d",&datos(i]);}


acceso a datos // se ordenan
Una matriz es una estructura de datos que for(i=0;i<4;i++) {
puede entenderse como la extensin de un
vector a dos dimensiones. for(j=i+1;j<5;j++) {

Por tanto una matriz es un conjunto de if(datos[i]>datos[j]) {


datos de un tamao definido y int aux=datos[i]; datos[i]=datos[j]; datos[j]=aux;}
consecutivos en memoria, que permite el
acceso a un elemento indicando su }
posicin en la matriz. } // se muestra el vector por pantalla
Las matrices resultan tiles para trabajar printf("Vector: ");
con problemas matemticos que usan
estas estructuras o para mantener tablas for(i=0;i<5;i++) printf("%d ",datos[i]); printf("\n");
de datos a los que se accede con }
frecuencia.
Para declarar una matriz bidimensional o multidimensional, hay que indicar el tamao o nmero de
componentes de cada dimensin. La declaracin de una matriz en lenguaje C sigue la siguiente sintaxis.

Programacin en C 24
Si la matriz es bidimensional, tamao 1 y tipo nombre _matriz[tamao1] [tamao2] [tamao3] ... ;
tamao2 hacen referencia a las filas y columnas
tipo nombre_ matriz [filas] [columnas];
de dicha matriz.
Al igual que con los vectores, entre corchetes solo puede existir una constante. Si se pone una variable
sera un error de sintaxis. Como ejemplo de declaracin se mencionan las siguientes:
Matriz de nmeros reales de 10x10 float matriz[10] [10];
Matriz tridimensional de nmeros enteros 20x20x10 int Tridimensional [20] [20] [10];
Las matrices se pueden inicializar en el momento de su declaracin, empleando llaves para separar filas y
comas para separar sus elementos. Ejemplo:
Matriz de 2 filas por 3 columnas de enteros int matriz [2] [3] = { {1, 2, 3 },{4, 5, 6 }} ;
Si se especifican los elementos consecutivos, #include <stdio.h>
se asignan los valores primero por filas y
void main ()
luego por columnas. Por lo tanto la
inicializacin anterior es equivalente a: int { int i,j;
matriz [ 2] [ 3] = {1 , 2, 3, 4 , 5, 6};
int matriz[2] [3]= { {1,2,3}, {4,5,6} };
El acceso a los datos de una matriz es similar
for(i=0;i<2;i++) // para cada fila
al de un vector, pero referenciando la fila y
columna del dato. for(j=0;j<3;j++) // para cada columna
Para mostrar el contenido de la matriz anterior printf("%d\t",matriz[i] [j]);
por pantalla, se necesitaran dos bucles for
anidados, uno que itera sobre filas y otro printf("\n");
sobre columnas. }
As se imprimen las filas consecutivamente.
Ejemplo: Suma de dos matrices m1 y m2 Ejemplo: Algoritmo para multiplicar matrices de 3x3
sobre m3 (m3=m1+m2)
#include <stdio.h> #include <stdio.h>
void main () void main(void)
{ { int p[3] [3] ={ {1, 3, -4}, {1, 1, -2}, {-1, -2, 5} };
int i,j; int q[3] [3] ={ {8, 3, 0}, {3, 10, 2}, {0, 2, 6} };
int m1 [ 2] [ 3] = { 1, 2, 3, 4 , 5, 6 } ; int r[3] [3];
int m2[2] [3]= { 4, 5, 12, 23, -5, 6 }; int i, j, k; // Variables ndice
int m3 [2] [3]; int sum;
for(i=0;i<2;i++) for(i=0; i<3; i++) { // Multiplica las matrices p y q
for (j=0; j<3; j++) { for(j=0; j<3; j++) {
m3 [i][j] =m1 [i][j] +m2[i][j] ; sum=0;
for(i=0;i<2;i++) for(k=0; k<3; k++) sum+=p[i] [k]*q[k] [j];
for (j=0;j<3;j++) { r[i][j] =sum; }
printf("%d\t",m3[i] [j]); } // Imprime el resultado
printf("\n"); for (i=0; i<3; i++) {
} for (j=0; j<3; j++)
} printf("%d\t", r[i] [j]); printf("\n"); }
} }
4.3. Cadenas de caracteres. Inicializacin, E/S por consola
Si el tipo de un vector es char, se trata de una cadena de caracteres. En una cadena cada caracter ocupa
una posicin de memoria (1 B) y debe terminar siempre con el caracter nulo (\0), que el programador
obligatoriamente. Como en vectores, el nombre de la cadena es un puntero al primer elemento.

Programacin en C 25
Cada caracter se almacena como su cdigo ASCII y el caracter nulo como 0. Una de las utilidades del
mismo, es indicar a las funciones que manejan caracteres el fin de la cadena. Estas funciones se
encuentran en la biblioteca <string.h>. Si por error no se incluye el \0 se producirn errores. La declaracin
de una cadena de caracteres difiere de la de un vector numrico.
La formalizacin de la declaracin debe seguir el
char nombre_cadena [cantidad de elementos];
formato mostrado.
Sea una cadena de caracteres de un mximo de 20 elementos (19 tiles y /0). Se puede proceder as:
#include <stdio.h> Para inicializar una cadena de caracteres puede seguirse cualquiera de las
siguientes opciones:
void main ()
char texto[6]={76,73,66,82,79,0}; // valores ASCII
{
char texto[6)={'L', 'I', 'B', 'R','O', '\0'}; // Caracteres ASCII
char cad[20];
char texto[6]="LIBRO"; char texto []="LIBRO";
}
En la primera sentencia se inicializa la cadena con la palabra deseada definida por los cdigos de la tabla
ASCII. En el segundo caso es igual, pero con cada letra, apstrofos y separados por coma. Como con
vectores, debe colocarse el valor inicial entre llaves y no olvidar el caracter \0.
En la segunda y tercera se inicializa la cadena con la palabra LIBRO. Es el compilador quien agrega el \0 y
cuenta los caracteres de la palabra. Si se desea capturar una cadena desde el teclado se puede realizar,
por ejemplo, usando gets () o scanf () especificando %s para cadenas. La forma ms sencilla de pedir una
cadena de caracteres (incluyendo espacios) y mostrarla en pantalla podra ser como se muestra a la izqda.
#include <stdio.h> #include <stdio.h>
void main () void main ()
{ { char nombre[10], apellido[20];
char frase[30]; printf("Cual es su nombre?");
printf("Teclee una frase: \n"); scanf("%s",nombre); // no lleva &
gets(frase); printf("Cual es su apellido?"); scanf("%s",apellido);
printf("La frase es\n"); printf("Nombre completo: %s %s\n", nombre,
apellido);
puts(frase); // equivale a printf("%s\n",frase);
}
}
Para leer palabras, se puede usar la funcin scanf () con el especificador %s. Scanf () deja de leer si se
introduce un espacio, tabulacin o retomo de carro. Por ejemplo, el programa de la derecha pregunta
nombre y apellido y luego lo muestra. Ntese que scanf() aplicado a cadenas, no lleva &.
4.3.1. Operaciones con cadenas
Mientras que los vectores se definen por su nmero de componentes, las cadenas se limitan con '\0'. Para
trabajar con una cadena, habitualmente se recorre uno a uno sus caracteres con un bucle while ().
Ejemplo. Contar la cantidad de caracteres que Ejemplo. Realizar un programa que convierta una
hay en la frase: "Don Quijote de La Mancha." palabra de minsculas a maysculas.
#include <stdio.h> finclude<stdio.h>
void main () void main ()
{ char cad[]="Don Quijote de La Mancha"; { char nombre[75]; int i=0; int desp='A'-'a';
int i=0; printf ("Palabra: "); scanf("%s",nombre);
while(cad[i] != '\0') { while(nombre[i]!='\0') {
i++; if(nombre[i]>='a' && nombre[i]<='z') // si
no es minuscula, nada
printf("La frase: %s tiene %d caracteres",cad,i);
nombre[i]=nombre[i]+desp; i++; } ;
// La frase Don Quijote de La Mancha tiene 24
caracteres printf("En Mayusculas: %s\n",nombre);
}

Programacin en C 26
Otros ejemplos.
Ejemplo. Realizar un programa que cuente el Ejemplo: Concatenar dos cadenas
nmero de veces que aparece una letra en una
palabra introducidas por teclado.
iinclude<stdio.h> iinclude<stdio.h>
void main() void main ()
{ char palabra[75]; char letra; int i=0,cont=0; { char frase1(100],frase2[100); int i=0,j=0;
printf("Letra:"); printf ("Escriba una frase: "); gets(frase1);
scanf("%c",&letra); printf ("Escriba otra frase: "); gets(frase2);
printf ("Escriba una palabra: "); while(frase1[i]!='\0') { i++; j=0;
scanf("%s",palabra); while(frase2[j] !='\0') {
while(palabra(i]!='\0') { frase1[i]=frase2[j];
if(palabra(i]==letra) i++; j++;
cont++; i++; };
}; frase1[i]='\0';
printf("Nnumero de veces: %d\n",cont); printf("Concatenadas: %s\n",frase1);
} }
4.3.2. Biblioteca string.h
Muchas funciones de manejo de cadenas de caracteres estn implementarlas en una biblioteca, cuyo
fichero de cabecera es <string. h>, que se incluye con #include <string.h>
#include <stdio.h>
#include <string.h>
La funcin srtlen () void main ()
devuelve el nmero de
{ char nombre[100];
caracteres de una
Longitud de cadena sin contar el '\0'. int longitud;
una cadena
En el ejemplo se cuenta printf("Introduzca su nombre: ");
la cantidad de
caracteres del nombre scanf("%s",nombre);
tecleado por el usuario. longitud= strlen(nombre);
printf("Nombre de %d caracteres\n", longitud);
}
#include <stdio.h>
#include <string.h>
void main ()
Pasar a La funcin _strupr () { char nombre[lOO);
maysculas convierte a maysculas
printf("Introduzca su nombre: ");
una cadena de
caracteres. scanf("%s",nombre);
_strupr(nombre);
printf("En mayusculas %s\n", nombre);
}
La funcin strcpy () #include <stdio.h>
copia una cadena en
#include <string.h>
otra. Se debe observar
Copiar una que la cadena destino void main ()
cadena tenga espacio
{ char cadena_a_copiar[] = "Aprendo el Lenguaje C.";

Programacin en C 27
suficiente.
char destino[50];
Si la cadena origen es
strcpy( destino, cadena_a_copiar);
ms larga que la de
destino, los caracteres printf( "Valor final: %s\n", destino);
sobrantes se eliminan.
}
#include <stdio.h>
#include <string.h>
void main ()
La funcin strcat () copia { char nombre_completo[5O];
una cadena a
Concatenar char nombre [ 1 ="Juana";
continuacin de otra. En
cadenas
el siguiente ejemplo se char apellido[ ]="de Arco";
copia la cadena "de
strcpy( nombre_completo, nombre);
Arco" a la cadena
"Juana". strcat(nombre_completo, " "); //Copia espacio blanco
strcat( nombre_completo, apellido );
printf( "Nombre completo: %s.\n", nombre_completo);
} // Nombre completo: Juana de Arco.
#include <stdio.h>
#include <string.h>
void main ()
{ char frase1[100],frase2[100];
La funcin strcmp () int orden;
devuelve 0 si las dos
Comparar printf ("Escriba una frase: "); gets(frase1);
frases son iguales, 1 si
cadenas
estn ordenadas printf ("Escriba otra frase: "); gets(frase2);
alfabticamente y -1 si
orden=strcmp(frase1,frase2);
no lo estn.
if(orden==0) printf("Iguales\n");
if (orden==1) printf("Frase1>frase2 ordenadas\n");
if (orden==-1) printf("Frase1<frase2 no ordenadas \n");
}
4.4. Estructuras. Inicializacin y acceso a datos
Las estructuras son un conjunto de datos de diferentes tipos agrupados struct nombre_ estructura
bajo un mismo identificador. Cada elemento es un campo. Una estructura
{
en C responde a la idea de registro. A diferencia de los vectores en los que
cada elemento es del mismo tipo, en una estructura cada miembro puede tipo1 miembro1;
ser de tipo diferente y se almacenan en posiciones contiguas de memoria.
tipo2 miembro2;
Antes de declarar una variable tipo estructura, se debe definir el modelo de
;
la misma. Aunque no es obligatorio, es buena prctica definirla antes de la
funcin main. Su sintaxis es la mostrada. }
Sea el problema del almacenamiento de los datos de un contacto para una agenda de telfonos. Para ello
se proceder a definir, por ejemplo, una estructura de datos llamada contacto con los siguientes campos:
Como se observa, la estructura contiene diferentes variables (campos) que struct contacto
permiten almacenar los datos de un contacto. Una estructura de datos se
{ char nombre[30];
comporta como un tipo nuevo de datos, lo que implica que struct contacto
se manejara como un tipo nuevo de datos por lo que el paso siguiente es int telefono;
declarar una variable tipo contacto, por ejemplo, amigo.
char edad;
La forma de declarar una variable de tipo de una estructura es struct
[nombre_ estructura] [nombre de la variable]. };

Programacin en C 28
Si hubiera que declarar varias variables, podra hacerse en varias lneas, o incluso en una sola, tal y como
se hace para variables de tipos de datos bsicos: struct contacto amigo1, amigo2;
Cuando se maneja un conjunto de datos de un determinado tipo, lo normal es usar vectores de estructuras.
Si el programador asigna valores iniciales al declarar una variable de tipo estructura, puede hacerlo de
forma similar a los vectores. En la lnea de declaracin se aade la asignacin y entre llaves se indican los
valores iniciales. Estos valores tienen que ir en el orden en que se han definido los campos. Por ejemplo, la
variable "amigo" se podra inicializar como: struct contacto amigo={"Pepe", 666222333, 23};
Se puede asignar, igual que con vectores, solo los primeros miembros. El resto quedara asignado al valor
0, por defecto. El acceso a un campo de una estructura se realiza a travs del operador punto, siguiendo la
sintaxis: nombre variable.miembro. En el ejemplo, sera amigo.edad=25;
A continuacin se muestra un programa que pide los datos de un contacto y los muestra en pantalla y su
homlogo sin usar el registro. Aunque produce el mismo resultado, para programas complejos se
recomienda el uso de estructuras.
#include <stdio.h> #include <stdio.h>
struct contacto { void main () {
char nombre[30); char nombre[30);
int telefono; int telefono;
char edad; char edad;
}; printf("Nombre: ");
void main () { scanf("%s", nombre);
struct contacto amigo; printf("Telefono: ");
printf("Nombre: " ); scanf("%s", amigo.nombre ); scanf("%d", &telefono);
printf("Telefono: " ); scanf("%d", &amigo.telefono ); printf("Edad: ");
printf("Edad: " ); scanf( "%d", &amigo.edad ); scanf( "%d", &edad);
printf("**Contacto**\n"); printf("**Contacto**\n");
printf("%s\n",amigo.nombre); printf("%s\n",nombre);
printf("Tlf: %d\n",amigo.telefono); printf("Tlf %d\n",telefono);
printf("Edad: %d\n",amigo.edad); printf("Edad: %d\n",edad);
} }
4.4.1. Copia de estructuras
Las estructuras tienen una propiedad interesante que no struct contacto amigo= {"Pepe",1234567,23};
tienen los vectores: la posibilidad de realizar una copia o
struct contacto amigo2;
asignacin directa de una variable de tipo estructura a otra
del mismo tipo. Cuando se realiza la copia se hace una amigo2=amigo; // copia
asignacin directa miembro a miembro de la estructura. Se
printf("%s\n",amigo2.nombre);
muestra en el ejemplo.
printf("Tlf: %d\n",amigo2.telefono);
Para copiar vectores hay que hacerlo componente a
componente (o usar funciones como memcpy ()), lo que printf("Edad: %d\n",amigo2.edad);
resulta ms engorroso.
// Imprime Pepe, Tlf: 1234567, Edad: 23
4.4.2. Vector de estructuras
Si se necesita almacenar varios datos con el mismo formato de estructura se puede recurrir a la definicin
de un vector de estructuras. Esta es una gran utilidad de las estructuras. En el ejemplo anterior, para
gestionar una agenda de contactos, tpicamente con un nmero elevado, se podr definir un nico vector,
cuyo ndice permita acceder a cada contacto particular directamente. La declaracin de un vector de
estructuras sigue el esquema tpico.
Una vez definida la estructa, se declara un
struct nombre_ estructura nombre_ variable[ cant e/em];
vector incluyendo la palabra clave struct.

Programacin en C 29
En el ejemplo anterior, podra gestionarse una agenda de por ejemplo, 100 contactos. La declaracin
quedara: struct contacto agenda[100]. La variable "agenda" es un vector de 100 componentes, cada uno de
ellos una estructura "contacto" que contiene nombre, telfono y edad. La inicializacin de un vector de
estructuras se hace mediante como combinacin de la inicializacin de un vector y estructura, de forma
similar a las matrices. Los datos de cada contacto se indican entre llaves. Cada componente del vector, se
separa por comas y el vector a su vez tambin se limita por llaves. Ejemplo:
struct contacto agenda[3]={ {"Pepe",1234567,23}, {"Maria",2345678,17}, {"Paco",9123456,34} } ;
Accediendo a travs del ndice del vector, se recupera cada contacto. En el caso anterior, para mostrar el
contenido de la agenda por pantalla, podra usarse el programa de la izquierda. Si en vez de mostrar la
agenda, se codifica una comparacin de algn campo, se dispondr de una agenda con funcin de
bsqueda. Por tanto, habra que modificar el programa anterior para que permita hacer una bsqueda
inversa: a partir de un telfono se muestren los contactos que tienen asignado ese nmero. Es el programa
de la derecha.
#include <stdio.h> #include <stdio.h>
struct contacto { struct contacto {
char nombre[30]; char nombre[30];
int telefono; int telefono;
char edad; } ; char edad; } ;
void main () { void main()
int i; { int i, telefono;
struct contacto agenda[3]={ struct contacto agenda[3]={ {"Pepe",l234567,23},
{"Pepe",1234567,23}, {"Maria",2345678,17}, {"Maria",1234567,17},{"Paco",9123456,34} };
{"Paco",9123456,34}};
while (1) {
for(i=0;i<3;i++) {
printf("Telefono a buscar: "); scanf("%d",&telefono);
printf("**Contacto**\n");
for(i=0;i<3;i++) if(telefono==agenda[i].telefono) {
printf("%s\n",agenda[i] .nombre);
printf("**Contacto**\n");
printf("Tlf: %d\n",agenda[i].telefono);
printf("%s\n",agenda[i].nombre);
printf("Edad: %d\n",agenda[i] .edad);
printf("Tlf: %d\n",agenda[i].telefono);
};
printf("Edad: %d\n",agenda[i].edad);
}
}
En el cdigo, si el telfono no se encuentra, no se muestra nada en pantalla. Modificar este programa para
mostrar un mensaje en ese caso, conseguir ms funcionalidades como obtener un telfono a partir de un
nombre u ordenar la agenda alfabticamente requieren modificaciones sencillas.
4.4.3. Estructuras de estructuras y vectores
Cada miembro de una estructura se precede de su tipo, que no tiene que ser un tipo bsico. Puede ser otra
estructura o un vector. En el ejemplo anterior podra almacenarse la fecha de nacimiento de un contacto,
aadiendo tres campos (da, mes y ao) o crear una estructura "fecha" y usarla como campo. Como en el
ejemplo de la izquierda. De la misma forma, uno de los miembros puede ser un vector de datos. Es el
ejemplo de la derecha.
#include <stdio.h> #include <stdio.h>
struct fecha { struct alumno {
char dia; char nombre[30);
char mes; int matricula;
int agno; float notas[5];
}; };
struct contacto { void main () {
char nombre[30];

Programacin en C 30
int telefono; int i;
struct fecha nacimiento; struct alumno alum=("Juan",93321,(10,5,3,4,8}};
}; printf("Notas de %s-
%d:\n",alum.nombre,alum.matricula);
void main ()
for(i=0;i<5;i++)
{
printf("Nota %d: %f\n",i,alum.notas[i]);
struct contacto amigo={"Pepe",1234567,{1,5,1970}};
}
struct fecha hoy={1,5,2007};
printf("%s nacio:\n",amigo.nombre);
printf("Dia %d\n",a.migo.nacimiento.dia);
printf("Mes %d\n",amigo.nacimiento.mes);
printf("Ao %d\n",amigo.nacimiento.agno);
if(hoy.dia==amigo.nacimiento.dia &&
hoy.mes==amigo.nacimiento.mes)
printf("Hoy es el cumpleai\os de
%s!\n",amigo.nombre);
4.5. Uniones
Las uniones de datos tienen un tratamiento similar a las estructuras. La union nombre {
diferencia es que en memoria se reserva espacio para la variable que ms
tipo1 nombre1;
espacio requiera. Por eficiencia, el uso de uniones es ms espordico que el
de estructuras y su inters limitado. Las uniones pueden contener en tipo2 nombre2;
diferentes momentos diferentes tipos de valores de diferentes tamaos. La
};
sintaxis de una unin es como la de una estructura.
El valor que se recupere de una unin debe ser del mismo tipo que el del ltimo valor almacenado. Es por
tanto responsabilidad del programador controlarlo. Si se define una unin nmero capaz de almacenar dos
tipos diferentes: entero y decimal se pondra:
La variable nmero ser lo suficientemente grande para contener el mayor de union numero
los valores tipo que la integran. Se puede asignar a nmero cualquiera de los
{ int entero;
dos tipos y usarlo en expresiones.
float decimal;
Los miembros de una unin comienzan su almacenamiento en la misma
direccin de memoria; si hay espacio sobrante queda vaco. Para declarar una };
variable de tipo nmero se hara: unin numero n;.
Como en las estructuras, se podr seleccionar uno de los campos (slo uno en un instante dado) con el
punto, por elemplo n.entero=2;. Si se accede leyendo un miembro distinto al ltimo asignado, su valor es
indeterminado (se lee basura). As, en el ejemplo, si se hiciera n.entero=2 y luego n.decimal=3.0f, al hacer
printf("%d %f\n",n.entero,n.decimal), podra obtenerse 1077938128 3.000000.
4.6. Enumeraciones
Con la construccin enum se pueden definir tipos de datos enteros que tengan un rango limitado de valores,
y darle un nombre significativo a cada posible valor.
La forma de definirla es similar a las estructuras,
enum nombre_enum { lista_de_enumeracin};
usando la palabra clave enum. Su formato es:
Por ejemplo, para gestionar y almacenar la informacin del da de la semana, podra usarse una variable
entera y los nmeros 1 para el lunes, 2 martes, etc. En vez de eso, se puede usar una enumeracin:
En el ejemplo los idenficadores se asignan como lunes=0, martes= 1, etc. #include <stdio.h>
Si se desea que la cuenta comience en 1, se indica:
enum dia {
enum dia
lunes, martes, miercoles,
{ lunes=1, martes, miercoles, jueves, viernes, sabado, domingo jueves, viernes, sabado,
domingo
};
};
En este caso los valores van del 1 al 7. Tambin se pueden dar valores

Programacin en C 31
individuales:
void main () {
enum codigo_telefonico
enum dia hoy;
{
hoy = sabado;
Andalucia=95, Madrid=91, Barcelona=93
printf("%d\n",hoy);
};
} // Por pantalla saldra '5'
4.7. Tipos definidos por el usuario
Con typedef se pueden crear nuevos tipos #include <stdio.h>
de datos. Por ejemplo si se desea que la
typedef struct fecha {
palabra ENTERO sea equivalente a int, se
pondra: int dia;
typedef int ENTERO; int mes;
Se podra usar la palabra ENTERO en int agno;
lugar de int. Usar maysculas conviene
} FECHA;
para resaltar el significado de la palabra
definida. As se podra hacer: void main ()
ENTERO a=3; // igual que int a=3; { FECHA hoy; // Ya no es necesario "struct"
La ventaja de typedef cuando se manejan hoy.agno=2007;
estructuras es que se evita tener que poner
la palabra "struct" al declarar variables. hoy.dia=27;

As, podra declararse el tipo definido por el hoy.mes=5;


usuario "fecha" con la estructura vista printf("Hoy es: %d-%d-%d\n",hoy.dia,hoy.mes,hoy.agno );
anteriormente, como a la derecha.
}
4.8. Ejemplos
Ejemplo 1. Hacer un programa que sustituya el Ejemplo 2. Programa que solicita N enteros
campo de valor mnimo de un vector por el valor 0, y menores a 500. Cambiar los datos de valor impar
muestre el vector por pantalla. por el valor par inmediatamente inferior. Muestra la
lista final, en que todos los nmeros son pares.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
int datos[5]={1,3,-2,4,5}; int i; int datos[500]; int i,n;
int mnimo; int indice_minimo; printf("Cuantos datos:"); scanf("%d",&n);
// se piden los datos
minimo=datos[0]; for(i=0;i<n;i++) {
indice_minimo=0; printf("Dato %d: ",i);
for(i=1;i<5;i++) { scanf("%d",&datos[i));
if(minimo>datos[i]) { }
minimo=datos[i]; indice_minimo=i; } for(i=0;i<n;i++)
printf("Min %d y est en if(datos[i]%2==1) datos[i]-=1;
%d\n",minimo,indice_minimo);
// se muestra el vector por pantalla
datos[indice_minimo]=0;
printf("Vector: ");
// se muestra el vector por pantalla
for(i=0;i<n;i++) printf("%d ",datos[i]);
printf("Vector: ");
printf("\n");
for(i=0;i<5;i++) printf("%d ",datos[i));
}
printf("\n");
}

Programacin en C 32
Ejemplo 3. Pedir letra y palabra. Se mostrar la Ejemplo 4. Programa que permita buscar en una
palabra en que las ocurrencias de la letra se agenda de contactos por su nombre.
sustituyan por el caracter '*'. Finalmente se
Los campos de la estructura sern nombre, telfono
eliminarn de la cadena dichas ocurrencias. Se
y edad. Los datos de la agenda sern inicializados
muestra un ejemplo: una salida por pantalla:
en cdigo con 3 contactos de ejemplo.
Letra: a
El programa informa si no se encuentran contactos.
Escriba una palabra: Palabra Mostrar los contactos que coincidan con el nombre
y su nmero.
palabra: P*l*br*
#include<stdio.h> #include <stdio.h>
void main () { #include <string.h>
char palabra[75]; char letra; int i,j; struct contacto {
printf("Letra:"); scanf("%c",&letra); char nombre[30];
printf ("Escriba palabra: "); int telefono;
scanf("%s",palabra); char edad;
i=0; };
while(palabra[i]!='\0') // sustituir por '*' { void main () {
if(palabra[i]==letra) palabra[i]='*'; int i, encontrado, iguales;
i++; char nombre[30];
} struct contacto agenda[3]={ {"Pepe",l234567,23},
printf("palabra: %s\n",palabra); {"Maria",1234567,17},{"Paco",9123456,34}};
i=0; j=0; while (1) {
while(palabra[i]!='\0) { // eliminar los '*' printf("Buscar a:: "); scanf("%s",nombre);
if(palabra[i]!='*') palabra[i]=palabra[j]; encontrado=0; // alarma
else palabra[i]=`; for(i=0;i<3;i++) {
i++; iguales=strcmp(nombre,agenda[i] .nombre);
j++; if(iguales==0) { // si son iguales
} encontrado++;
printf("palabra: %s\n",palabra); printf("**Contacto**\n");
} printf("%s\n",agenda[i) .nombre);
printf("Tlf: %d\n",agenda[i].telefono);
printf("Edad: %d\n",agenda[i].edad);
};
if(encontrado==0)
printf("Contacto no encontrado\n");
else
printf("Se han encontrado %d
contactos\n",encontrado);
}

Programacin en C 33
5. PUNTEROS
64
La memoria del ordenador se suele representar en general, con 2
direcciones (el exponente es el tamao de la palabra en bits o el
nmero de lneas del bus de direcciones), cada una de un byte, como
64
una matriz de N=2 filas de anchura 8 bits, como en la figura.
Los bytes se organizan en grupos, por ejemplo, un dato tipo int ocupa
cuatro posiciones de memoria.
Si en un programa se hace la declaracin e inicializacin int x=27; la representacin grfica se hara como
en la siguiente figura.
En general, cuando se hace referencia a la posicin de memoria
ocupada por una variable se entiende la direccin de memoria del
primer byte de la variable. Por ejemplo, en el caso de x, su direccin de
memoria sera la 1245048.
Por agilidad, se representar cada variable, independientemente de su
tamao, como si estuviese almacenada en una nica posicin de
memoria.
Los vectores y cadenas de caracteres estn formados por un conjunto de variables en posiciones
consecutivas de memoria. Por ejemplo, la declaracin:
int vector[3]={10,20,30};
char cadena[]="Hola";
El vector y la cadena quedaran almacenados de forma parecida a la mostrada en la
figura. En general, los elementos de una matriz se guardan en posiciones
consecutivas de memoria. Por ejemplo, la matriz int tabla[2] [2)={{40,50},{60,70}}, se
imagina con la estructura matemtica habitual, la primera fila con los elementos 40 y
50 y la segunda fila con 60 y 70. Pero en memoria se guardarn 40, 50, 60 y 70, en
posiciones consecutivas.
Es decir, cada fila de la matriz se almacena como un vector que se puede manejar como tal. En el ejemplo,
los dos vectores a los que da lugar la declaracin de la matriz son tabla[0], con los elementos tabla[0][0] y
tabla[0][1] y tabla [1], con los elementos tabla[1][0] y tabla[1][1]).
Siguiendo este esquema, un puntero es una variable que contiene la direccin de memoria de un objeto, por
ejemplo, otra variable. El espacio de memoria ocupado por un puntero es el nmero de bytes necesarios
para especificar una direccin de 32 bits, por tanto, un puntero ocupar 4 bytes u 8 si son 64 bits.
En la figura se representa un esquema conceptual de memoria en la que
se almacena una variable llamada x, de tipo entero, cuyo valor es 27 y
otra variable llamada p, que es un puntero de valor la direccin de
memoria ocupada por la variable x.
En la jerga se dice entonces que el puntero p apunta a x, lo que suele
representarse grficamente con una flecha como en la figura.
5.1. Declaracin e inicializacin
Un puntero se declara indicando el tipo de dato apuntado seguido de un
tipo *nombre _puntero;
asterisco y un identificador.
Por ejemplo, para declarar un puntero que apunte a datos de tipo char llamado pchar sera char *pchar.
Tambin se pueden declarar punteros genricos que pueden a apuntar a algn tipo de dato escribiendo la
palabra reservada void en lugar del tipo, por ejemplo void *pgen. Al declarar un puntero, no apuntara a
nada hasta que se inicie o asigne una direccin de memoria.
Con los punteros se usan dos operadores: & y *. El operador &, se conoce como "direccin de" y
proporciona la direccin de memoria en la que se almacena su operando. As, si x es una variable, &x es la
posicin de memoria donde se almacena dicha variable. Esa posicin de memoria se asigna en el momento
de la declaracin y no cambia durante la ejecucin del programa.
El operador *, conocido como "indireccin", proporciona el contenido de la direccin de memoria de su
puntero. Si x es una variable y p es un puntero que apunta a x (es decir, p=&x), *p es el contenido de la
posicin de memoria donde se almacena el valor de x. La variable x puede sustituirse por *p en cualquier
lugar del programa.

Programacin en C 34
Un puntero puede iniciarse en el momento de su declaracin como cualquier variable. Por ejemplo: int x,
*p=&x; El asterisco de la declaracin del puntero no debe confundirse con el operador indireccin de". Esta
declaracin es equivalente a: int x, *p; p=&x; Un puntero tambin se puede iniciar con el valor NULL
(puntero nulo), por ejemplo: char *pchar=NULL.
NULL es una constante simblica usada en lugar de cero como mnemotcnico para indicar que es un valor
especial para un puntero. No tiene sentido intentar escribir en la posicin cero de memoria a la que apunta
un puntero NULL, ya que es una posicin reservada para el SO. NULL suele usarse, adems para realizar
operaciones de comprobacin, como verificar si el puntero que devuelve una funcin es NULL, que suele
ser indicativo de errores.
En general, a un puntero se le asigna la direccin de memoria de un objeto declarado previamente. El
programador no necesita conocer las posiciones de memoria ocupadas por un objeto, simplemente las
maneja, asignando al puntero el valor devuelto por el operador &. No tiene sentido, en principio, que el
programador asigne directamente direcciones de memoria al puntero, como por ejemplo p=1245052, ya que
es el SO el que gestiona la memoria.
El sentido que puede tener, es saltar la ejecucin de programas para realizar otras tareas. Es la filosofa
englobada en ataques de seguridad informtica tipo Buffer Overflow.
5.2. Operaciones con punteros
5.2.1. Asignacin
A un puntero se le puede asignar otro #include <stdio.h>
puntero del mismo tipo con el resultado
void main () {
que ambos apuntarn al mismo objeto,
como en el el programa del ejemplo. int x=7, *p1, *p2;
A un puntero tambin se le puede asignar p1=&x; p2=p1;
un puntero genrico (void *), en cuyo caso
se produce una conversin implcita del printf ("*p1 vale %d\n", *pl); printf ("*p2 vale %d\n", *p2);
puntero genrico al tipo de datos del } // salida: *p1 vale7 *p2 vale 7
puntero asignado.
5.2.2. Aritmtica y comparacin
Entre punteros que apuntan por ejemplo, a elementos de una misma matriz, vector o cadena de caracteres,
se pueden realizar las operaciones aritmticas o de comparacin tpicas. A un puntero se le puede sumar o
restar un nmero entero.
Por ejemplo, si p es un puntero que apunta a un elemento de un vector y n un nmero entero, la operacin
de asignacin: p+=n; hace que el puntero apunte al elemento situado n posiciones ms adelante. Por
ejemplo, si n= 1 el puntero apunta al siguiente elemento del vector. Se muestra un ejemplo a la izquierda.
#include <stdio.h> void main () {
void main () { int vector[10], x, *p;
int vector[5]={7, 1, -3, 4, 2}; vector[5]=17;
int *p; p=&vector[5]; // p apunta a vector[5]
p=vector; // equivale a p=&vector[0] x=*p+3; // x=vector[5]+3=20
printf("%d\n", *p); // imprime 7 x=*(p+1); // x=vector[6], valor indeterminado
p+=2; // p=&vector[2] (*p) ++; // vector[5]=18
printf("%d\n", *p); // Imprime -3 p++; // p apunta a vector[6]
} }
Los operadores de incremento(++) y decremento(--) tambin se pueden usar para hacer que el puntero
apunte, respectivamente, al siguiente elemento o al anterior, al que apuntaba. Tienen la misma prioridad
que * y &. El ejemplo de la derecha muestra varias operaciones con un puntero y un vector.
Dos punteros que apuntan a los elementos de #include <stdio.h>
un mismo vector se pueden comparar, en el
void main () {
sentido de comparar sus direcciones de
memoria. As, si p apunta al primer elemento int vector[10]; int *p, *q;
de un vector y q al sexto, el resultado de la
comparacin p<q es verdadero. p=vector; // equivalente a p=&vector[0]

Programacin en C 35
Tambin se pueden restar punteros, dando q=&vector[5]; // q apunta a vector[5] (6 elemento)
como resultado el nmero de elementos que
printf("%d\n", q-p); //Resta de punteros (imprime 5)
existen entre ellos. El programa muestra un
ejemplo. if (p<q) { p+=3; // p apunta a vector[3]
En resumen, las operaciones con punteros *p=27; // vector[3]=27}
son la asignacin de punteros del mismo tipo,
printf("%d\n", vector[3]); // imprime 27
sumar o restar enteros al puntero, y restar o
comparar punteros que apuntan a elementos }
de la misma matriz.
5.3. Punteros a vectores
En C, para acceder a los elementos de un vector puede hacerse con subndices o punteros como se ha
visto. El uso de una notacin u otra depende del programador. El acceso con punteros es ms rpido y
suele ser preferida por programadores expertos.
Sea la declaracin: int v[10], *p=&v[0];. El puntero p apunta al primer elemento del vector. El nombre de un
vector es un puntero que apunta al primer elemento. Luego v y &v[0] tienen el mismo valor. Por tanto, se
podra haber escrito la declaracin anterior como int v[10], *p=v;.
Sin embargo, el nombre de un vector es un int v[10],
puntero constante, cuya direccin no se puede
*p=v; v++; // Operacin ilegal (p++ si seria vlido)
modificar, y no est permitido realizar algunas
operaciones, como en el ejemplo. v=&v[3]; // Operacin ilegal (p=&v[3] si sera vlido)
Siguiendo con el ejemplo, si p apunta a v[0], por definicin (p+1) apunta a v[1], (p+2) a v[2], etc. En general,
se cumple que (p+i) es la direccin de v[i] y que *(p+i) es el contenido de v[i].
De esta forma, asignando al puntero p la primera #include <stdio.h>
direccin del vector v, donde aparezca v[i] se podr
void main () {
escribir *(p+i) y el programa funcionar igual.
int i, vector[3]={10,20,30}, *p=vector;
El programa del ejemplo imprime los elementos de
un vector accediendo a ellos con notacin de // Notacin con subndices
subndices y de vectores. Se puede resaltar que si
for (i=0; i<3; i++) printf("%d\n", vector[i]);
se tiene:
tipo vector[TAM]; // Notacin con punteros

tipo* p=vector, // o tipo * p=&vector[0], es lo mismo for (i=0; i<3; i++) printf("%d\n", *(vector+i));
// Notacin con punteros
Entonces es equivalente, para un ndice "i":
for (i=0; i<3; i++) printf("%d\n", *(p+i));
//Notacin con subndices
vector[i] *(p+i) p[i] *(vector+i)
for (i=0; i<3; i++) printf("%d\n", p[i]);}

5.4. Punteros a cadenas de caracteres


Lo explicado es de aplicacin a cadenas de caracteres teniendo en cuenta que los elementos de la cadena
son caracteres individuales y que el ltimo elemento es el caracter nulo ('\0'). Los siguientes programas
ilustran el manejo de cadenas de caracteres usando notacin de punteros. El primero imprime una cadena
caracter a caracter, manejando el puntero de dos formas distintas y el de la derecha cuenta e imprime en la
pantalla el nmero de caracteres de una palabra introducida desde el teclado.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
char mensaje[]="Hola Mundo"; char palabra[51], *p=palabra;
char *p=mensaje; // p apunta a mensaje[0] int contador=0;
int i; printf("Introduzca una palabra\n"); scanf("%s", palabra);
// Manteniendo el puntero apuntado a mensaje[0] /* incrementa el nmero de caracteres ledos y apunta
el puntero al siguiente carcter */
i=0;
while (*p!='\0') { contador++; p++;}
while(*(p+i) !='\0') { printf("%c", *(p+i)); i++; }

Programacin en C 36
printf("\n"); printf("%s tiene %d caracteres\n", palabra, contador);
// Moviendo el puntero por la cadena }
while (*p!=' \0') { printf("%c", *p); p++;}
printf("\n");
}
5.5. Punteros a estructuras
Un puntero a una estructura se declara con la palabra struct, el nombre de la estructura y el nombre del
puntero precedido por asterisco.
Antes tendr que estar declarada la estructura. La
struct nombre_ estructura *nombre puntero;
sintaxis es:
Lo habitual es declarar un tipo de dato definido como se muestra en el ejemplo de la derecha.
struct fecha { typedef struct {
int dd; int dd;
int nun; int nun;
int aaaa; int aaaa;
}; } fecha;
void main () { void main () {
struct fecha *p; fecha *p;
// Otras sentencias // Otras sentencias
En ambos casos, se est declarando una estructura de 3 campos y un puntero, llamado p, que apunta a
variables de ese tipo de estructura.
El programa del cuadro declara e inicializa el #include <stdio.h>
puntero que apunta a una variable de tipo
typedef struct {
estructura, con sobrenombre fecha, llamada
f, declarada previamente. int dd; int nun; int aaaa;
Para acceder a un campo de una estructura } fecha;
con un puntero, hay que escribir el nombre
void main () {
del puntero seguido del operador -> y el
campo de la estructura, es decir: fecha f, *p=&f;
nombre _puntero-> campo p->dd=29; p->nun=7; p->aaaa=2007;
En el programa se muestra la asignacin de printf("%d-%d-%d\n", f.dd, f.nun, f.aaaa);
valores a los campos de la estructura a
travs de su puntero para, a continuacin, }
mostrar en pantalla los valores asignados.
5.6. Punteros a punteros
Un puntero puede apuntar a otro puntero. La #include <stdio.h>
declaracin sigue el esquema general, salvo que en
void main () {
vez de asterisco, se utilizan dos (**).
int x=1, *p, **pp;
Es un smbolo que significa "doble indireccin". En el
programa del ejemplo, se declaran tres variables de p=&x;
tipo int: x, un puntero p, y un puntero a puntero
pp=&p;
llamado pp.
printf("El valor de x es %d\n", **pp);
A la variable x se le asigna el valor 1, a p la direccin
de x (p apunta a x) y a pp la direccin de p (pp apunta }
a p ).
Finalmente, la ltima lnea del programa imprime el
valor de x aplicando el operador de doble indireccin
al puntero pp.

Programacin en C 37
5.7. Asignacin dinmica de memoria
Al declarar un objeto (una variable, vector, estructura, etc.) se le asigna un espacio fijo de memoria para ese
objeto durante la ejecucin del programa. Esta forma de asignacin se denomina esttica porque el tamao
se fija antes de ejecutar el programa. Si no se conoce a priori el tamao de datos a manejar, se puede
gestionar de forma ms eficiente la memoria con una asignacin dinmica, al ejecutar el programa y
liberarla cuando no sea necesaria. La asignacin dinmica, en combinacin con punteros, permite crear
estructuras de datos potentes como las listas enlazadas o rboles binarios. En C, la asignacin dinmica se
realiza con funciones especiales de la biblioteca <stdlib.h>, cuyas dos bsicas son malloc y free.
La funcin malloc permite asignar, en ejecucin, un bloque de
void *malloc (unsigned int n_bytes);
memoria de n bytes consecutivos. Su prototipo es:
El parmetro formal de la funcin de la funcin, n_bytes, es un entero sin signo que indica el nmero de
bytes a asignar. La funcin devuelve un puntero genrico (void *) que apunta a la primera posicin de
memoria del bloque asignado o un puntero nulo (NULL) si no hay memoria suficiente.
Para usar la funcin, hay que declarar un puntero que apunte al tipo de int *p;
objetos que se almacenarn en el bloque de memoria. Por ejemplo, para
p=malloc(10*sizeof(int));
reservar memoria para 10 nmeros enteros se hara:
Cuando a p, puntero que puede apuntar a datos enteros, se le asigna el puntero genrico (void *) de la
funcin malloc se produce una conversin implcita de tipo (a int *) antes de la asignacin. Se puede realizar
una conversin explcita, por ejemplo, si se desea compatibiblidad con C++, en que la conversin explcita
de punteros es obligatoria, haciendo en el ejemplo: p=(int *)malloc(10*sizeof(int));
Aunque se conozca el tamao de los tipos de datos, int *p;
conviene para evitar errores y favorecer la
p=malloc(10*sizeof(int));
portabilidad, usar la funcin sizeof que proporciona
directamente el nmero de bytes que mide un tipo de if (p==NULL) printf("No hay memoria\n");
datos. Tras asignar la memoria suele comprobarse
else printf( "Memoria asignada\n");
que el proceso ha sido correcto.
Esto es comprobar si el puntero devuelto por malloc es NULL (lo que indicara error) y asegurar que no se
realizan operaciones de lectura o escritura con el puntero. Es el caso del ejemplo. Si el puntero devuelto es
NULL, adems de escribir un mensaje de aviso, suele detenerse la ejecucin del programa. Para ello puede
usarse, por ejemplo, la funcin exit de la biblioteca <stdlib.h> cuyo prototipo es void exit(int estado). Para
indicar que el programa se ha detenido normalmente, se escribe exit (0) y en caso contrario exit (1).
La funcin free permite liberar un bloque de memoria asignado. Su prototipo es: void free (void *q);
A la funcin hay que darle de argumento el puntero que apunta al bloque de memoria asignado por malloc.
As, para liberar los 40 bytes de memoria anteriores reservados, se sentencia free (p). El programa de la
izquierda rescribe el ejemplo de la estructura para imprimir una fecha usando asignacin dinmica de
memoria. El de la derecha imprime al revs una palabra introducida desde el teclado.
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
typedef struct { #include <string.h>
int dd, mm, aaaa; void main() {
} fecha; char *p;
void main () { int i, longitud;
fecha *p; p=malloc (51);
p=malloc(sizeof(fecha)); if(p==NULL) { printf("No hay memoria\n"); exit (1);}
if (p==NULL) { printf("Introduzca una palabra\n"); scanf("%s", p);
printf("No hay memoria"); exit(1); } longitud=strlen(p);
p->dd=29; p->mm=7; p->aaaa=2007; for(i=longitud-l;i>=0;i--) printf("%c", *(p+i));
printf("%d-%d-%d\n", p->dd, p->mm, p->aaaa); printf("\n");
free(p); free(p);
} }

Programacin en C 38
6. ESTRUCTURA DE UN PROGRAMA
Estructurar un programa es importante para que sea fcilmente interpretado por otra persona. Para ello se
usan funciones, subprogramas que divididen el problema en partes independientes que evitan errores y la
reutilizacin de cdigo. El lenguaje C permite la definicin de funciones por parte del programador.
6.1. Directivas del preprocesador
Las directivas del preprocesador no forman parte del lenguaje C, ni son entendidas por el compilador, sino
por un programa previo denominado preprocesador. Las directivas se distinguen de las lneas de cdigo por
comenzar con el smbolo #. Las directivas del preprocesador ms usadas son:
#include. Permite incluir en el cdigo generado otros archivos de #include <fichero.h>
cabecera. Esta directiva puede usarse dos formas distintas, como se
muestra en el cuadro. #include "miFichero.h"

La diferencia entre usar < > o " " est en la ubicacin del fichero de cabecera. Al usar < > se indica al
preprocesador que busque en los directorios por defecto del entorno de desarrollo y en los que suelen estar
ficheros de libreras estndar como <stdio.h>. Cuando se usa , se indica que primero busque en la
carpeta de trabajo, en la que se suelen estar los archivos fuente. Si no lo encuentra, buscar en las dems
carpetas. Las comillas se usan por tanto para ficheros de cabecera del usuario. La directiva #include es lo
primero que se escribe. El lenguaje C contiene una amplia variedad de archivos cabecera (.h) de utilidad.
#define y #undef. La directiva #define permite definir smbolos, nombres #define nombre valor
alternativos a una constante y #undef elimina smbolos previamente
definidos. La declaracin de las directivas es la mostrada. #undef nombre_smbolo

Las directivas no llevan punto y coma (;) al final. Aunque no obligatorio, los smbolos definidos empleando la
directiva suelen escribirse en maysculas para facilitar la lectura. El preprocesador recorre el archivo
sustituyendo las ocurrencias de la constante por su valor numrico.
Macros. La directiva #define tambin permite definir #define nombre_macro (param1, ... , paramN)
macros, operaciones sobre datos o variables. La
sintaxis de una macro es la mostrada. cdigo

En la sintaxis, cdigo son sentencias en C y los parmetros (param1,,paramN) son smbolos en cdigo.
El preprocesador sustituye cada llamada a la macro por su cdigo y los parmetros por los valores que
tengan cuando se llama a la macro. Como en el ejemplo.
En el cdigo se define la macro CUBO, que tiene un #include <stdio.h> #include <stdio.h>
nico parmetro llamado "x".
#define CUBO(x) x*x*x void main ()
Cuando el preprocesador encuentra la macro, la
void main () { {
sustituye directamente por su cdigo sustituyendo la
"x" por el parmetro entre parntesis, en el cdigo, la float a=3,b; float a=3,b;
variable a. Es el cdigo de la derecha. El uso de
b=CUBO (a); b=a*a*a;
macros se desaconseja a menos que sea necesario.
En caso de usarse se recomienda que el cdigo sea printf("b= %f\n",b); printf("b= %f\n",b);
lo ms sencillo posible.
} }
6.2. Funciones
En general, un problema se divide en partes ms sencillas que #include<stdio.h>
permitan un desarrollo ms gil, mantenible y reutilizable. Estas
// prototipos (interfaz) de funciones
partes, subprogramas son las funciones. Ya se han usado
funciones como printf () o scanf (). El inters, ahora est en la void Saluda();
definicin de funciones por parte del programador. El cuadro
void main () {
muestra un ejemplo.
El cuerpo de la funcin Saluda () est fuera de main (), que Saluda(); // llamada a la funcion
contiene una llamada o invocacin a la funcin. El flujo de }
ejecucin del programa comienza en la primera linea del main,
que llama a la funcin Saluda. En ese momento se salta al // cuerpo de funciones
cdigo de la funcin, que comienza a ejecutarse. Cuando se void Saluda() {
completa la ejecucin de la funcin se contina en la siguiente
lnea de main, que como en este caso no hay, termina. La printf("Hola mundo\n");
funcin main () siempre debe existir en un programa. Slo un }
bloque de cdigo puede llamarse main.

Programacin en C 39
6.2.1. Declaracin y definicin
Al igual que para usar una variable hay que declararla previamente, para programar una funcin, es
necesario establecer su prototipo o declaracin. El prototipo de una funcin consta de 3 partes principales:
1. Nombre de la funcin. Es su identificador que debe seguir las normas sintcticas de identificadores (sin
espacios, no comenzar por nmero, etc.) y de estilo (nombre significativo).
2. Parmetros de funcin. Debe especificarse su tipo (tipos bsicos, vectores, estructuras) y puede darse el
caso de no tenerlos.
3. Tipo de la funcin o tipo de su valor de retomo. Indica el valor que se devuelve, si es el caso.
La sintaxis del prototipo de una funcin es la tipo nombre_funcin (tipo1 param1, ... , tipoN paramN)
mostrada. Se puede analizar alguna funcin de la
{
biblioteca <math.h> como ejemplo.
// cuerpo de la funcin;
As, el prototipo de la funcin sqrt () para calcular
la raz cuadrada es: double sqrt(double);. }
El prototipo especifica que la funcin tiene un parmetro de entrada, especificado entre parntesis de tipo
double. Este parmetro es el nmero del que hallar la raz cuadrada.
El tipo de la funcin es double, es decir, el valor que devuelva ser la raz cuadrada del parmetro indicado
representado como un real de precisin doble. En el caso anterior no se da nombre al parmetro, por ser
obvio. Los nombres de los parmetros no son necesarios en los prototipos pero si convenientes.
Si el usuario quisiera programar una funcin que calcule el rea de un circulo de radio conocido, el prototipo
de la funcin podra ser: float AreaCirculo(float radio), equivalente a float AreaCirculo(float). El usuario
especifica el radio, pasado como parmetro a AreaCirculo, que devuelve un dato float, el rea. Aunque el
nombre "radio" es opcional, en los prototipos, es muy informativo, ya que, por ejemplo, elimina la duda de si
se est pasando un radio o un dimetro.
Si no se especifica valor de retomo de la funcin, se asume que se devuelve un valor de tipo int. Si no se
desea que una funcin devuelva valor, se puede indicar con el tipo void (vaco). Esto es valido para los
parmetros, como en la funcin Saluda(), que podra haberse definido como void Saluda(void).
En la sintaxis del prototipo, se especifica cuerpo de la funcin. Lgicamente es el conjunto de sentencias
que implementan la tarea propia de la funcin. Se recomienda usar idntica nomenclatura para declaracin
y defiicin. Por fin, el cuerpo de la funcin no est en la funcin main (), sino a nivel global. Una funcin se
define tpicamente al formalizar la llave de cierre de la funcin main () y la ltima lnea de su cdigo suele
ser la que permite devolver el resultado: la palabra clave return.
Cuando el compilador se encuentra una llamada a una funcin, debe conocer su prototipo. La declaracin
puede hacerse con el interfaz o con el cdigo, justo despus de las directivas del preprocesador. Se
recomienda declarar slo el interfaz, es decir, el prototipo de las funciones y despus del cuerpo de cdigo
principal definir el cdigo de cada funcin.
6.2.2. Llamada
La llamada a una funcin se realiza con el nombre de la void main ()
misma y entre parntesis los parmetros que requiere su
{ float r,a;
interfaz para operar.
printf("Radio: "); scanf("%f",&r);
El nmero y tipo de parmetros empleados en la llamada
a la funcin debe coincidir con los definidos en el a=AreaCirculo(r);
prototipo. La llamada a la funcin se puede realizar en
/* llama a la funcion, pasando "r" como radio y
cualquier lnea de cdigo en que se precise.
asignando el area a "a" */
Si la funcin devuelve algn valor (no es tipo void) la
llamada a la funcin debe estar incluida en una expresin printf("Area: %f\n",a);
que recoja el valor devuelto. }
Los datos concretos empleados en la llamada a una funcin se denominan parmetros reales (en el ejemplo
el radio "r"), ya que se refieren a la informacin que se transfiere a la funcin para su proceso. Los nombres
de los parmetros formales no tienen por qu coincidir con los nombres de las variables usadas como
parmetros reales en el momento de la llamada.
6.3. mbito de una variable
Las variables de un programa pueden clasificarse en funcin del mbito en el cual se conocen y por tanto
son accesibles. Se distingue entre variables locales y globales.

Programacin en C 40
La idea es sta. Sea un pas donde su presidente se llama Alfredo. Si en una reunin de amigos alguno se
llama Alfredo, en el mbito de la reunin Alfredo es una variable local. Si alguien invoca el nombre de
Alfredo, todos entendern que se est refiriendo a ese amigo. Si en la reunin no hubiera ningn Alfredo, el
mbito siguiente, por ejemplo el pas, sera ahora el de la variable Alfredo, es decir, se referira al
presidente, con la consiguiente carga de improperios que se diran sobre ese tal Alfredo.
De esta forma, una variable global se halla declarada fuera #include <stdio.h>
de toda funcin, al inicio del programa principal. Su mbito
int a=3; //variable global
se extiende para todas las funciones del fichero de cdigo
fuente. El ''tiempo de vida" es el que dura la ejecucin del void funcion();
programa, por lo que se crean al comienzo de la ejecucin
void main() { //main tiene acceso a "a"
del programa y se destruyen al finalizar la ejecucin.
printf("A vale: %d\n",a);
En las funciones no hay que declarar las variables
globales. Se pueden usar sin ms, como insultar al a=7;
presidente. Eso si, los cambios efectuados sobre una
variable global afectan a los que la usen posteriormente. funcion(); // /tambien tiene acceso a "a"

En el siguiente ejemplo existe una variable global "a", printf("A vale: %d\n",a);
accesible a todas las funciones. Al principio, vale 3. Al }
comenzar la ejecucin, el programa mostrar ese valor.
Luego, se cambia su valor a 7 y se llama a funcin(). void funcion () {
Cuando se ejecuta, lo primero es mostrar a, luego se printf("A vale en la funcion: %d\n",a);
mostrar en pantalla el valor 7. Al seguir la ejecucin de
funcin(), se asigna el valor 10 a a y termina. Se sigue a= 10;
ejecutando en main de forma que se vuelve a mostrar el }
contenido de a. Ahora ser 10.
El uso de variables globales debe evitarse a ser posible. Cuando el cdigo fuente queda repartido en
ficheros diferentes y se quiere usar una variable global para todos, se usa la palabra clave extern.
En cuanto a variables locales, son las que se #include <stdio.h>
declaran en el mbito particular de una funcin. Slo
void funcion();
se conocen en ese mbito. Su tiempo de vida es el
de ejecucin de la funcin. Se crean al invocar la void main () {
funcin y se destruyen al finalizar su ejecucin.
int a=3; // a es local a main
Como en el ejemplo de la derecha. No se puede
acceder a las variables fuera de su mbito. Resulta b=28; // Error de sintaxis. b no se conoce
evidente ya que adems los nombres son distintos. }
Podra plantearse el caso en que ambas variables,
aunque locales, se llamasen igual. Es el ejemplo del void funcion () {
presidente Alfredo. Cada funcin tiene por su int b=1; // b es local a la funcin
espacio de memoria, luego varias funciones pueden
definir variables locales con el mismo nombre sin a=18; // Error de sintaxis. a no se conoce
que su trato interfiera con otras funciones. }
Cada funcin tiene sus variables propio dato, y las modificaciones que haga sobre el no tienen ningn
efecto sobre los otros, tal y como se muestra en el ejemplo siguiente:
6.4. Parmetros de una funcin. Paso por valor y referencia
Cuando se realiza la llamada a una funcin con parmetros, su valor concreto en una invocacin se llama
argumentos. Si el tipo de dato del argumento no coincide con el del parmetro, se produce una conversin
implcita de tipo en caso de ser posible. Por ejemplo, si se pasa un argumento entero, estando definido un
parmetro tipo float, el entero se convierte implcitamente a float. El compilador mostrar un warning
avisando de ello. El caso contrario, parmetro entero y argumento float, lo convertira implcitamente a
entero, truncando decimales. El compilador tambin avisara de esta accin.
Al proceder de esta forma se le denomina paso por valor (o copia) porque a la funcin Imprime no se le
pasa el argumento, la variable, sino una copia de su valor, que se obtiene del parmetro. Si se usa el
argumento como tal, se dice que se hace un paso por referencia. La idea implcita es similar a la de
variable global y local. La idea de variable global se asociara al paso por referencia y la de variable local al
paso por valor o copia.
As, el paso por valor copia el valor del argumento #include <stdio.h>
en el parmetro correspondiente. La funcin no
void ImprimeDoble(float x);
modifica el valor de los parmetros.

Programacin en C 41
En el ejemplo propuesto, el cdigo de la funcin void main () {
modifica el valor de su parmetro x. El programa
float a= 1.5f;
principal, el main() lo utiliza con la variable a.
ImprimeDoble(a);
Por tanto, funcionalmente, main usa x para
imprimir a, pero ImprimeDoble no tiene acceso a printf("a= %f\n",a);
a, slo usa su valor. De ah el nombre de paso
}
por valor.
Cuando ImprimeDoble multiplica por 2 x, modifica void ImprimeDoble(float x) { // copia el valor de a en x
la copia local del dato, es decir x, pero la variable x*=2; // modifica x, no a
a sigue sin modificarse, por lo que despus de la
llamada a ImprimeDoble, al imprimir a se mostrar printf("Doble = %f\n",x);
su valor en main, esto es a=1.500000. }
Un caso para pensar, es que ocurrira si a, en vez de llamarse a, se llamase x. Es lo explicado antes. El
resultado no cambia, ya que la x del mbito de ImprimeDoble es una copia de la x del mbito main.
Es decir una persona se puede llamar Antonio en dos mbitos, Segovia y #include <stdio.h>
Madrid, y an llamndose igual son dos personas diferentes. En el paso
void Duplica(float* p);
por referencia, se copia la direccin de memoria del argumento en el
parmetro formal. Para recibir una direccin de memoria, el parmetro void rnain () {
formal de la funcin debe ser un puntero.
float a=1.5f;
Como se est dando la referencia, la direccin de memoria del
Duplica(&a);
argumento, la funcin podr modificar su valor. En el ejemplo, la funcin
Duplica, recibe como argumento un puntero. Cuando se pasa como printf("a = %f\n",a);
parmetro &a, se da en el puntero "p" la direccin de memoria de "a", por
ejemplo, 1234. Cuando se hace float d=*p; es equivalente a hacer }
d=*(1234)=a=1.5. Con lo cual, la siguiente instruccin, *p = 2*d; dejar el void Duplica(float* p) {
valor a= 3.0.
float d=*p;
Lo interesante es apreciar que la funcin Duplica modifica el valor de "a"
sin conocer variable con ese nombre. Lo que conoce es su direccin. *p = 2*d;
Entra en casa y roba. Cambia su valor. Esto es lo peligroso. Y se debe a }
que cualquier operacin sobre *p es como hacerla con "a".
Para intercambiar 2 valores mediante una funcin NO es posible hacerlo sin el uso del paso por referencia.
Una funcin puede devolver slo un valor, el que se especifica con return.
Si se desea que una funcin devuelva varios tinclude <stdio.h>
valores, puede hacerse con variables pasadas
void operaciones (float x, float y, float *s, float *p, float *d);
por referencia, como en el ejemplo propuesto.
void main () {
La funcin operaciones no devuelve valor,
aunque poda haberse aprovechado para float a=1.0f, b=2.0f, suma, producto, division;
alguna operacin. De los 5 parmetros de la
funcin, los 2 primeros se pasan por valor y operaciones(a, b, &suma, &producto, &division);
los 3 ltimos por referencia (las operaciones a printf("La suma vale %f\n", suma);
devolver).
printf("El producto vale %f\n", producto);
El paso por referencia lo usa, por ejemplo,
scanf. Si se hace scanf("%f",&x); para printf("La division vale %f\n", division);
modificar la variable ''x" y escribir en ella el }
valor tecleado, se pasa "x" por referencia. Sin
embargo printf () reciba los parmetros por void operaciones (float x, float y, float *s, float *p, float *d) {
valor ya que no necesita cambiar su *s=x+y; *p=x*y; *d=x/y;
contenido, slo una copia.
}
6.4.1. Retorno
6.4.1.1. Retorno vaco (void) y de resultados
Al finalizar la ejecucin de una funcin, se devuelve el control #include <stdio.h>
a la parte del cdigo desde donde se realiz la llamada.
void ContarHasta(int n);
Cuando una funcin es de tipo void, no devuelve valor. Su
ejecucin termina en la ltima sentencia, pero se puede void main () {

Programacin en C 42
terminar antes, usando la sentencia return.
ContarHasta(-3); ContarHasta(5);
Return finaliza la funcin en el momento en que se ejecuta. Se
}
devuelve el control a la lnea siguiente de donde se realiz la
llamada. En el ejemplo, se implementa una funcin que void ContarHasta(int n) {
cuenta de 0 hasta el nmero entero que se pasa como
int i;
parmetro.
if (n<=0){ printf("Parametro negativo!\n");
Si se introduce como parmetro un nmero negativo se
informa al usuario y se termina la funcin, ya que no se quiere return;
seguir ejecutando nada. Si el parmetro es positivo, la funcin
termina cuando se llega a la llave de cierre. }

Se podra haber puesto return antes de esta llave, pero si el for(i=0;i<=n;i++) printf("%d ",i);
tipo de la funcin es void, es opcional. } // aqu return es opcional
El uso de funciones con un tipo de retomo diferente a void es ms comn, ya que la devolucin de
resultados por una funcin ayuda a la creacin de sw reutilizable.
El caso comn es la funcin que a partir de parmetros de #include <stdio.h>
entrada, efecta una operacin con ellos y devuelve un valor
float Doble(float x);
como resultado.
void main () {
En el ejemplo, la funcin calcula el doble de un nmero pasado
como parmetro, que no es modificado. La funcin devuelve el float=1.5f, b;
valor doble en el tipo de retomo. La funcin Doble() calcula en
b=Doble(a); printf (b=%f/n,b);
una variable local llamada "d" el doble del parmetro "x". Cuando
la funcin realiza return d, asigna el valor de "d" a la variable del }
lado izquierdo de la sentencia b=Doble (a).
float Doble(float x) {
La llamada a "Doble( a)" se usa con el valor de su retomo, en
este caso "d". El mismo resultado se podra haber obtenido con float d;
una funcin Doble () ms compacta, haciendo directamente d=x*2; return d;
return x*2. }
As, la funcin se usa como una variable ms. Si se hiciera printf("Doble de %f es = %f\n", a, Doble(a));, en
el ltimo %f, se refiere al valor devuelto por Doble, directamente. Otra opcin es usar recurrencia, por
ejemplo, con una sentencia del estilo: printf("Cuadruple de %fes= %f\n", a, Doble(Doble(a))), donde primero
se calculara el doble de a y el resultado servira de parmetro, otra vez de Doble(), es decir el cudruple.
Cuando una funcin tiene tipo de retomo, el uso de return es siempre obligatorio y debe devolver un valor
del tipo adecuado. Si el valor devuelto no es del tipo adecuado, se realiza una conversin implcita, caso de
ser posible. Si por ejemplo, la funcin es de tipo float, pero se devuelve un entero, el entero promociona a
float.
Si la funcin es de tipo entero y se intenta devolver un float, el compilador avisar con un warning de posible
perdida de datos, al truncar los decimales del nmero que se devuelve. La utilidad de las funciones que no
devuelven datos es limitada.
Por ejemplo, se puede hacer una funcin que calcule el mximo de dos nmeros. A la izquierda se muestra
un ejemplo en que se devuelve valor mientras a la derecha no.
#include <stdio.h> #include <stdio.h>
int maximo (int a, int b); void maximo(int, int);
void main () { void main () {
int a, b, max; int a, b;
printf("Introduce n: "); scanf("%d %d",&a, &b); printf("Introduce n: "); scanf("%d %d",&a, &b);
max=maximo(a,b); maximo(a,b);
printf("El maximo es %d",max); }
} void maximo(int a, int b) {
int maximo(int a, int b) { if(a>b) printf("El maximo es %d",a);
if (a>b) return a; else return b; else printf("El maximo es %d",b);
} }

Programacin en C 43
6.4.1.2. Retorno booleano
Es comn el uso de funciones que devuelven un #include <stdio.h>
nmero entero, de slo dos valores, 0 (falso) o 1
int EsImpar(int n);
(verdadero). En este caso se pueden usar estas
funciones directamente como proposiciones lgicas. void main () {
En el ejemplo la funcin EsImpar admite un int num;
parmetro de tipo entero y devuelve 0 si el nmero
es par y 1 si es impar. Se usa la funcin de forma printf("Teclee un n: ); scanf("%d",&num);
compacta. Se lee intuitivamente como "Si el nmero if (Esimpar (num)) printf("Su numero es impar\n");
num es impar".
else printf("Su numero es par\n");
Otra tarea tpica de los programas es contar con una
funcin tipo men que pregunte al usuario si desea }
continuar la ejecucin del programa, mostrar un int EsImpar(int n) {
men, repetir la pregunta si la respuesta del usuario
no es adecuada, etc. e integrarlo en el main con if (n%2==1) return 1; else return 0;
alguna sentencia del estilo while(menu()). }
6.4.2. Funciones que llaman a otras funciones
Sea el siguiente ejemplo en que se realiza un programa para calcular el !
( )=
nmero combinatorio "n sobre k": ( )! !
Para calcular ese valor se puede usar el programa de la izquierd. Se observa que utiliza 3 bucles for para
calcular el factorial de 3 nmeros: repite el cdigo 3 veces, lo que indica que se puede usar una funcin
para aligerar el cdigo, como en el ejemplo de la derecha.
#include <stdio.h> #include <stdio.h>
int factorial(int a); int factorial(int a);
void main() { void main () {
int n, k, i; int n, k, NsobreK;
int factN=1,factNK=1,factK=1; printf("Introduzca n y k: "); scanf("%d %d", &n,&k);
int n_sobre_k; NsobreK = factorial(n)/(factorial(n-k)*factorial(k));
printf("Introduzca n y k: "); scanf("%d %d", &n,&k); printf("%d sobre %d = %d\n", n, k, NsobreK);
for(i=1; i<=n; i++) factN*= i; }
for(i=1; i<=(n-k); i++) factNK*= i; int factorial(int a) {
for(i=1; i<=k; i++) factK*= i; int i, res=1;
n_sobre_k = factN/(factNK*factK); for(i=1; i<=a; i++) res*=i;
printf("%d sobre %d = %d\n", n, k, n_sobre_k); return res;
} }
Ahora, es la funcin factorial la que es llamada 3 veces desde el programa principal. Si se quisiera usar
dicho nmero combinatorio para otro clculo, sera aconsejable englobarlo en una nica funcin.
As, la instruccin NsobreK = factorial(n) / (factorial(n-k) #include <stdio.h>
*factorial(k)); puede definirse en otra funcin,
int factorial(int a);
combinaciones (), como en el cuadro. La funcin
combinaciones hace uso de la funcin factorial para int combinaciones(int n,int k) {
realizar sus clculos. Este ejemplo resulta ms legible y
int result=factorial(n)/(factorial(n-k)*factorial(k));
escalable. Si se quisiera realizar un nuevo programa que
imprimiese el triangulo de Pascal (hasta cierto nmero de return result;
filas, pasadas como parmetro), podra hacerse
utilizando la funcin combinaciones, facilitando la labor. }

En este supuesto, habra que tener en cuenta que el tipo void main () {
int se vera desbordado para nmeros mayores a 12. Se []
podria ampliar el rango con el tipo doble y el riesgo de
aproximaciones, ya que aunque el rango es mayor, el NsobreK = combinaciones(n,k);
nmero de sus cifras significativas tambin est limitado. }

Programacin en C 44
6.4.3. Funciones, vectores y matrices
En C, cualquier matriz (de dimensin 1 (vector) o n), siempre se pasa a una funcin por referencia. Un
argumento de una funcin que indique una matriz ser la direccin de memoria de su primer elemento. La
funcin puede modificar el contenido de los elementos del vector ya que conoce la direccin de memoria
donde se almacenan. El nombre de un vector es un puntero que apunta a su primer elemento. Por ejemplo,
la declaracin int vector[3]={10,20,30}, verifica que vector y &vector[0] tienen el mismo valor.
Para declarar una funcin que reciba un vector como argumento puede declararse el parmetro formal de 3
formas: como vector sin dimensin, como vector con dimensin o un puntero. As, los 3 prototipos
siguientes seran equivalentes:
void funcion (int vector[]); void funcion (int vector[3]); void funcion (int *p);
El segundo prototipo no suele usarse ya que, lo que se indica es una direccin de memoria. Si se desea que
la funcin conozca la dimensin del vector hay que aadir un parmetro a la funcin con ese dato. Por
ejemplo void funcion (int vector[], int dirnension). En los siguientes programas ejemplo, se imprime los
elementos de un vector usando la misma funcin escrita con subndices y punteros.
#include <stdio.h> #include <stdio.h>
void irnprime(int v[], int n); // Con subindices void irnprirne(int *p, int n); // Con punteros
void rnain () { void rnain () {
int v1[3]={10,20,30}; int v1[3]={10,20,30};
irnprirne(v1,3); irnprirne(v1,3);
} }
void imprime(int v[], int n) { void irnprirne(int *p, int n) {
int i; int i;
for(i=0;i<n;i++) printf("%d\n",v[i]); for(i=0;i<n;i++) printf("%d\n",*(p+i));
} }
El programa siguiente usa 2 funciones, una que calcula el producto escalar de 2 vectores y otra que calcula
el mnimo.
#include <stdio.h> float ProductoEscalar(float v1[], float v2[], int n) {
float ProductoEscalar(float v1[], float v2(], int n); int i;
float Minimo(float *p, int n); // igual a (float p[],int n) float prod=0;
void main () { for(i=0;i<n;i++) prod+=vl[i]*v2(i];
float v1[5]={1,2,3,-4,5}; return prod;
float v2[5]={-6,-7,8,9,10}; }
float producto_escalar, min1, min2; float Minimo(float *p, int n) {
producto escalar=ProductoEscalar(v1,v2,5); float min=p[0];
printf("Producto escalar = %f\n", producto_escalar); int i;
min1=Minimo(v1,5); for(i=0;i<n;i++) {
min2=Minimo(v2,5); if(p(i]<min) min=p[i];
printf("El mnimo de v1 vale %f\n", min1); }
printf("El mnimo de v2 vale %f\n", min2); return min;
} }
En caso de matrices, como con vectores, su nombre es un puntero a su primer elemento. En la declaracin
int tabla[2] [2]={{1, 2}, {3,4}}; los valores de tabla y &tabla[0][0] son iguales. Si se quiere declarar una funcin
que pueda recibir una matriz bidimensional como argumento puede declararse el argumento como una
matriz en la que omitir la primera dimensin (filas) pero no la segunda (columnas). As, los prototipos
siguientes son equivalentes void funcion (int matriz[] [3]), void funcion (int matriz[3] [3]). En este caso, puede
seguirse el criterio de poner todas las dimensiones de la matriz sabiendo que la primera es innecesaria.
#include <stdio.h>

Programacin en C 45
6.4.4. Funciones y cadenas
En C, una cadena de caracteres siempre se pasa por referencia. Lo expuesto para vectores es vlido para
cadenas, teniendo en cuenta que estn formadas por caracteres individuales en lugar de nmeros y que
terminan con el caracter nulo ('\0'). Cuando una cadena se pasa como parmetro a una funcin se pasa la
direccin de memoria de su primer elemento, que suele especificarse con el nombre de la cadena.
En la declaracin char saludo[]="Hola"; el nombre de la cadena, saludo, es un puntero al primer elemento de
la cadena (coincide con &saludo[0]). Para declarar una funcin que reciba una cadena de caracteres como
argumento puede declararse el parmetro formal de 3 formas: como cadena con dimensin, sin dimensin o
como puntero. Lo habitual es usar las 2 ltimas opciones porque el compilador ignora el nmero entre
corchetes y prepara la funcin para recibir una direccin de memoria, no una cadena de caracteres con
dimensin. Los prototipos void funcion (char cadena[]) y void funcion (char *p) son equivalentes.
En caso de cadenas no es necesario pasarle a la funcin la dimensin de la cadena, ya que el final lo marca
el caracter nulo. Los siguientes programas imprimen los elementos de una cadena, caracter a caracter,
usando la misma funcin escrita con subndices y punteros.
#include <stdio.h> #include <stdio.h>
void imprime(char cadena[]); void imprime(char *p);
void main () { void main () {
char saludo[]="Hola"; char saludo[]="Hola";
imprime(saludo); imprime(saludo);
} }
void imprime(char cadena[]) { void imprime(char *p) {
int i=0; int i=0;
while(cadena(i]!='\0') { while(*(p+i) !='\0') {
printf("%c", cadena[i]); printf("%c", *(p+i));
i++; } i++; }
printf("\n"); printf("\n");
} }
Como con vectores, pueden mezclarse notaciones. Por ejemplo, prototipo y cabecera pueden escribirse con
punteros y en el cuerpo de la funcin usar subndices, o viceversa.
6.4.5. Funciones y estructuras
Al definir una estructura de datos se puede utilizar de forma anloga a los tipos bsicos de datos. Este
comportamiento tambin se da con funciones: las estructuras pueden pasarse como parmetros (por valor o
referencia) y pueden ser devueltas como retomo de la funcin.
6.4.5.1. Estructuras como parmetros de una funcin
Para ilustrar la idea, se presenta el siguiente #include <stdio.h>
ejemplo, en que se usa una estructura para
struct complejo {
almacenar la parte real e imaginaria de un nmero
complejo y se pasa a una funcin que imprime dicho float real;
nmero. El paso se realiza por valor, es decir, se
imprime una copia del nmero complejo. float imaginaria;
};
En este dominio, empieza a evidenciarse la utilidad
de usar tipos predefinidos por el usuario ( typedef), void Imprimir(struct complejo c);
de forma que el cdigo se pueda escribir de forma
ms compacta y legible. En el ejemplo, podra void main () {
definirse: struct complejo comp={1, -3}; // inicializacion
typedef struct complejo { Imprimir (comp);
float real; void Imprimir(struct complejo e) {
float imaginaria; printf("%f %+f i\n",c.real,c.imaginaria);
} complejo; } // imprime 1.000000 + 3. 000000i

Programacin en C 46
En el caso de paso por referencia, sera similar. Sea una funcin que solicite por pantalla los datos de un
nmero complejo.
Si a dicha funcin se pasa por valor #include <stdio.h>
(copia) una estructura, cuando el
#include <math.h>
usuario teclee los datos, estos no se
almacenaran correctamente. typedef struct complejo {
Para ello, como en el cdigo de float real;
ejemplo, se pasa el parmetro por
referencia para que la funcin pueda float imaginaria;
escribir en l. } complejo;
La clave del programa est en la void Pedir(complejo* c);
sentencia Pedir(&comp); en que se
ofrece a la funcin Pedir la posicin void main() {
de memoria del registro, la complejo comp;
estructura complejo.
Pedir(&comp);
De esta forma, ya se tiene la
referencia y por tanto el control total }
de la estructura, lo que permite void Pedir(complejo* c) {
modificarla al gusto.
float re,im;
En el cuerpo de la funcin Pedir, se
almacena directamente en la printf("Introduzca parte real e imag: "); scanf("%f %f",&re,&im);
estructura, los valores introducidos c->real=re;
por el usuario, en los campos real
e imaginario. Como se indica, esto c->imaginaria=im;
se puede hacer directamente. } // tambin valdra scanf("%f %f",&c->real,&c->imaginaria);
6.4.5.2. Estructuras como retorno de una funcin
Las estructuras admiten realizar asignaciones de forma simple, ganando agilidad con respecto a vectores y
cadenas. Las funciones admiten como tipo de retomo una estructura, lo que tambin agiliza el proceso
frente a vectores y cadenas.
El cdigo de la izquierda muestra el de una funcin que permite calcular el nmero complejo resultante de la
multiplicacin de otros dos complejos, pasados como parmetros. En el programa principal se asocia
directamente a una estructura, c3, el valor de retorno de la funcin con la sentencia, c3=Multiplica(c1,c2).
El ejemplo a la derecha muestra una funcin que hace uso de otra funcin y maneja estructuras; la funcin
Unitario, que devuelve un complejo de modulo unidad paralelo a un complejo pasado como parmetro.
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
typedef struct complejo { typedef struct complejo {
float real; float real;
float imaginaria; float imaginaria;
} complejo; } complejo;
void Imprimir(complejo c); void Imprimir(complejo c);
void Pedir(complejo* c); void Pedir(complejo* c);
float Modulo(complejo c); float Modulo(complejo c);
complejo Multiplica(complejo a,complejo b); complejo Multiplica(complejo a,complejo b);
void main () { complejo Unitario(complejo c);
complejo c1,c2,c3; void main () {
Pedir(&c1); complejo c1,c2;
Pedir(&c2); Pedir(&c1);
c3=Multiplica(c1,c2); c2=Unitario(c1);

Programacin en C 47
Imprimir(c3); Imprimir(c2);
} }
complejo Multiplica(complejo a,complejo b) { complejo Unitario(complejo c) {
complejo res; complejo res;
res.real=a.real*b.real-a.imaginaria*b.imaginaria; float m=Modulo(c);
res.imaginaria=a.real*b.imaginariata.imaginaria*b.real; res.real=c.real/m;
return res; res.imaginaria=c.imaginaria/m;
} return res;
}
6.5. Estructuracin de funciones en ficheros
Los anteriores ejemplos desarrollan funciones relacionadas con nmeros complejos. Lo lgico es
empaquetarlas para reutilizarlas, dado el caso, en otros programas. Para esto, lo suyo es estructurar y
separar el cdigo en distintos archivos, tcnica comn en en el desarrollo de programas reales.
La forma de hacerlo puede ser la
siguiente. Se introducen 2 nuevos
ficheros, un fichero de cabecera,
"complejos.h", slo con los prototipos y
otro fichero de cdigo fuente llamado
"complejos.c" que contendr el cuerpo de
las funciones. Ambos ficheros
constituyen la pseudo-biblioteca de
complejos que es portable a otra
aplicacin. El fichero del programa
principal (main) la referencia con un
include al fichero de cabecera (#include
complejos.h) y al compilar se debe incluir
en el proyecto (makefile) el fichero
"complejos.c". Es la estructura que
muestra la figura.
6.5. Recursividad
La recursividad es el proceso por el que una funcin se llama a s misma hasta que satisface una condicin.
Este proceso se emplea usualmente para #include <stdio.h>
clculos repetitivos en los que el resultado de
int factorial(int num);
cada iteracin se determina a partir del
resultado de iteraciones anteriores.De existir void main () {
una solucin no recursiva suele preferirse a la
int i;
recursiva. La solucin no recursiva puede
derivar en un programa muy extenso por lo que for (i = 0;i <= 10;i++)
habr que evaluar la conveniencia adecuada de
su aplicacin. printf("%d = %d\n",i,factorial(i));

Un ejemplo comn de recursividad es el clculo int factorial(int num) {


del factorial, como el ejemplo. Efectivamente, if (num <= 1) return 1;
el calculo de n! es similar al de n*(n-1)!.
Aplicando este razonamiento hasta llegar al else return num*factorial(num-1); // recursividad
factorial de 1 (1!=1), podra quedar el cdigo }
propuesto.
6.6. Parmetros de la funcin main
En los casos presentados, la funcin main no recibe parmetros. Es posible pasarle parmetros que el
usuario introduzca desde lnea de comandos del SO. Los parmetros de la funcin main son 2 y se les
conoce con el nombre de argc y argv.
El parmetro argc es de tipo entero y contiene el nmero de parmetros que el usuario introducir. El
nombre del programa se considera el primer parmetro, por lo que al menos argc vale uno. El parmetro
argv es un vector de punteros a cadenas (char *argv []) . Cada elemento de la cadena contiene los

Programacin en C 48
parmetros que el programa necesita para su ejecucin y deben introducirse uno a uno separados con
espacio o tabulador. El ejemplo muestra el formato de la funcin main con parmetros. Si se compilase en
un ejecutable denominado parametros.exe, la ejecucin en lnea de comandos sera la mostrada a la dcha.
C:\>parametros.exe Hola 10 Adios
#include <stdio.h>
Existen 4 argumentos
void main( int argc, char *argv[]) {
Argumento 0: parmetros.exe
int i;
Argumento 1: Hola
for(i=0;i<argc;i++) printf("Argumento %d: %s\n",i,argv[i]);
Argumento 2: 10
}
Argumento 3: Adis
Los parmetros introducidos son tratados siempre como cadenas de texto, incluso si son nmeros. Si es el
usuario el que trata un parmetro como nmero, debe convertirlo usando funciones tipo sscanf, atoi o atof.
6.7. Ejemplos
Ejemplo 1. Escribir la salida por pantalla de los siguientes programas:
#include<stdio.h> #include<stdio.h>
void f1 (void) ; typedef struct persona {
void f2(void); int edad;
void main() { float peso;
f1 (); f2 (); } persona;
} void f(persona* p) {
void f1 (void) { p->edad=p->edad+1; p->peso=p->peso+3;
printf("1 "); f2(); }
} void main () {
void f2(void) { persona per={18,55.5f}; f(&per);
printf("2 "); printf("%d %f\n",per.edad,per.peso);
} // 1 2 2 } // 19 58.30000
#include<stdio.h> #include<stdio.h>
int f(int* p); void f1(int dato);
void main () { void main () {
int v[3]={1,12,123}; int a; int dato=3;
a=f(v); printf("%d\n",a+1); f1 (dato); printf("Dato= %d\n",dato);
} }
int f(int* p) { return *(p+1); } // 13 void f1(int dato) { dato=dato*2; } // Dato=3
Otros dos ejemplos.
Ejemplo 2. Programa que calcule la funcin Ejemplo 3. Ecuacin cuadrtica. Programa que
exponencial de un nmero mediante la serie de resuelva una ecuacin de segundo grado de tipo
x 2 3 n 2
Taylor: e = 1 + x/1! + x /2! + x /3! ++x /n!. ax +bx+c=0. Se implementar una funcin que
admita los parmetros a, b, c del usuario y devuelva
Para ello se preguntar al usuario el nmero ''x"
(por referencia) las races de la ecuacin.
con el que realizar el clculo de la exponencial,
que se realizar mediante la suma de los 10 Comprobar que la ecuacin es cuadrtica y gestionar
primeros trminos de la serie, codificando las las soluciones. Los operandos tendrn precisin
funciones exponencial (), factorial () y potencia (). normal. Usar la sentencia switch.
#include <stdio.h> #include <stdio.h>
#define PRECISION 10 #include <math.h> // para funcion raiz cuadrada
double exponencial(double num); int raices(float a,float b,float c,float* r1,float* r2);

Programacin en C 49
double factorial(int num); void main(void) {
double potencia(double base,int expo); float a,b,c; //coeficientes
void main(void) { float r1,r2; //raices, soluciones
double x,e_x; int ret;
int i; printf("Introduzca los coeficientes a,b y c\n");
2
printf("Numero: "); scanf("%lf",&x); printf("de ax +bx+c=0, separados por espacios\n");
e_x=exponencial(x); scanf("%f %f %f",&a,&b,&c);
printf("la exp.de %lf es %lf\n",x,e_x); printf("Ecuacion %fx2+%fx+%f=0\n",a,b,c);
} ret=raices(a,b,c,&r1,&r2);
double exponencial(double num) { switch (ret) {
double ex=1; case 0:printf("Sol. reales %f %f\n",rl,r2);
int i; break;
for(i=1;i<PRECISION;i++) { case 1:printf("Raiz doble %f\n",rl); break;
ex+=potencia(num,i)/factorial(i); case 2:printf("Raices complejas%f+-
i%f\n",rl,r2);
return ex;
break;
}
case 3:printf("Ecuacion lineal, raiz: %f\n",rl);
double factorial(int num) {
break;
double fact=1;
case 4: printf("Error parametros\n"); break;
int i;
}
for(i=1;i<=num;i++)
}
fact*=i;
int raices(float a,float b,float c,float* rl,float* r2) {
return fact;
float disc; //discriminante
}
if(a!=0.0f) { //ecuacion cuadrada
double potencia(double base,int expo) {
disc=b*b-4*a*c;
double res=1;
if(disc>0) { // 2 soluciones reales
int i;
*r1=(-b+(float)sqrt(disc))/2*a;
for(i=0;i<expo;i++)
*r2=(-b-(float)sqrt(disc))/2*a;
res*=base;
return 0; }
return res;
else if(disc==0) { //raiz doble
}
*r1=*r2=-b/2*a;
return 1; }
else { //soluciones complejas
*r1=-b/2*a;
*r2=(float)sqrt(-disc)/2*a;
return 2; }
}
else { // ecuacion no cuadrada
if(b!=0.0f) *r1=-c/b; return 3; else return 4;}
}

Programacin en C 50
7. ENTRADA / SALIDA
En C, la transferencia de datos entre dispositivos se realiza con funciones de E/S de la biblioteca <stdio. h>.
Por tanto, cualquier programa que utilice una de estas funciones tendr que contener #include <stdio.h>.
La biblioteca stdio.h proporciona los prototipos de las funciones de E/S y define, entre otros, dos tipos de
datos nuevos: size_t y FILE. El primero es un sinnimo de unsigned int y el segundo una estructura de
datos para la lectura y escritura de ficheros. Esta biblioteca tambin contiene dos constantes simblicas de
uso comn al operar con ficheros: NULL (puntero nulo), cuyo valor es 0, y EOF (End Of File, Fin de
Fichero), cuyo valor es -1.
La E/S estndar son, respectivamente, teclado y pantalla. De las funciones que contiene stdio.h para
mostrar datos en pantalla o leerlos desde teclado slo se vern para salida (pantalla) printf, putchar y puts y
para entrada (teclado) scanf, getchar, gets y fflush.
Las funciones printf y scanf son de propsito general. Permiten leer y escribir caracteres individuales,
cadenas de caracteres, nmeros, etc. con un determinado formato. Las funciones ms simples de E/S son
putchar, que escribe un caracter en la pantalla, y getchar, que lee un carcter de teclado. Las funciones
puts y gets escriben y leen cadenas de caracteres, respectivamente.
La funcin fflush tiene el cometido especial de limpiar un buffer de datos, por ejemplo, el de entrada de
datos de teclado. Las funciones anteriores no son las nicas. Existen otras, como getc o getche, que se han
omitido porque son muy similares.
7.1. Salida estndar
7.1.1. printf
La funcin printf () convierte valores del programa en caracteres con un formato especificado para mostrar
en pantalla.
El prototipo de la funcin printf es: int printf( const char *formato[argumento1, argumento2,...]);
El primer parmetro de la funcin (const char *formato) es obligatorio y es una cadena de control entre
comillas dobles, formada por caracteres ordinarios, secuencias de escape y especificadores de formato. El
resto de parmetros (argumento1, argumento2 ... ) suelen ser variables, nmeros, etc. y pueden no existir.
La funcin devuelve un nmero entero de tipo int igual al nmero de caracteres escritos por la funcin o un
nmero negativo si ocurre error. Cuando se escribe en la pantalla es habitual asumir que no va a haber
errores por lo que el valor devuelto por la funcin no suele comprobarse. En general, una secuencia de
escape est formada por la barra invertida \ seguida de una serie de caracteres. En C, las secuencias de
escape comunes son:
Escape Definicin Escape Definicin Escape Definicin

\n Nueva lnea (retomo de \r Retomo de carro sin \b Retroceso (backspace)


carro y salto de lnea) salto de lnea
\\ Barra invertida \ooo Carcter ASCII (o es un \xhh Carcter ASCII (h es un
(backslash) dgito octal) dgito hexadecimal)
\t Tabulador horizontal \' Comilla simple \" Comilla doble
Una particularidad de las secuencias de escape es que pueden ser usadas directamente como caracteres
individuales, como en el programa del cuadro.
El programa declara una variable de tipo char de nombre car y le #include <stdio.h>
asigna un valor con la instruccin car='\x41'. El valor 41 hexadecimal
void main () {
se corresponde con 65 en decimal, que es el valor ASCII de la letra
A. La instruccin podra haberse escrito directamente car='A'. char car;
La ltima lnea del programa imprime en pantalla el carcter car='\x41';
asignado a la variable car. Para ello, en la cadena de control ha
printf("El valor de car es %c\n", car);
aparece un elemento nuevo: %c, un especificador de formato que se
usa para imprimir caracteres individuales. } // salida: El valor de car es A
Cuando la funcin printf encuentra un especificador de formato en la cadena de control, busca el valor a
imprimir en la lista de argumentos que viene a continuacin y lo imprime con el fonnato especificado.
Si en la cadena de control aparece ms de un especificador de formato la bsqueda se har de forma
correlativa, es decir, el primer especificador de formato ser sustituido por el primer elemento de la lista, el
segundo por el segundo, etc. En los programas siguientes se muestran dos ejemplos de esta interpretacin.

Programacin en C 51
En el primer programa, al encontrar la funcin printf el especificador de formato %c lo sustituye por el primer
valor, el de la variable car y lo imprime como caracter. Con el segundo especificador de formato, %d, para
nmeros enteros, tambin imprime la variable car, pero esta vez su valor como nmero.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
char car=' A'; float x=2, y=S, z; z=x/y;
printf("El valor ASCII de %c es %d\n", car, car); printf("La division de %d y %d vale %d\n", x, y, z);
} // salida: El valor ASCII de A es 65 } // salida: La divisin de 2 y 107374 vale 0
Los tipos de datos de C tienen que ser impresos con formatos compatibles. En caso contrario pueden
obtenerse resultados impredecibles o incorrectos. Es el caso del segundo programa, en que el resultado es
errneo porque se imprimen nmero reales de tipo float con formato de nmero entero. Para obtener un
resultado correcto habra que sustituir %d por especificadores como %f o %g obteniendo, respectivamente,
las salidas La divisin de 2.000000 y 5.000000 vale 0.400000 y La divisin 2 y 5 vale 0.4.
Con %f se imprimen los nmeros reales con cifras decimales y %g las trunca. La sintaxis mnima para
expresar un especificador de formato consiste en escribir el smbolo de porcentaje seguido de una letra que
indica el tipo de datos que se van a imprimir, [%tipo], donde tipo es una letra que indica si el argumento
asociado tiene que interpretarse como un caracter individual, una cadena de caracteres o un nmero. La
tabla siguiente muestra el significado de las letras usadas para especificar tipo y significado:
Tipo Tipo de Salida Tipo Tipo de Salida
datos datos
d int Enteros con signo en decimal i int Enteros con signo en decimal
u int Enteros sin signo en decimal o int Enteros sin signo en octal
x int Enteros sin signo hex. (abc...) X int Enteros sin signo hex. (ABC..)
f double Valor con signo; [-]m.dddddd e double Valor con signo: [-]m.dddddde[
]ddd
E double Valor con signo de la forma [- g double Valor con signo compacto en
]m.ddddddE[ ]ddd formato f o e
G double Valor con signo compacto en c int Carcter individual de un byte
formato f o E
s cadena Escribe la cadena hasta el primer '\0'
El especificador de formato tambin puede incluir, entre el smbolo % y el tipo, campos opcionales que
permiten, por ejemplo, controlar la alineacin del texto de salida, la impresin de signos o la precisin (los
corchetes indican que el campo escrito entre ellos es opcional):
El campo [flags] controla la alineacin de salida e impresin de signos, % [flags] [ancho] [.precisin] tipo
blancos, puntos decimales y prefijos octales y hexadecimales.
La tabla siguiente indica el significado de los flags ms comunes:
flags Significado Por defecto
- Alineacin a la izquierda dentro del campo ancho especificado Se alinea a la derecha
+ Antepone al nmero su signo ( + o -) Se antepone el signo -
0 Rellena salida con 0s no significativos hasta el ancho mnimo Sin ceros de relleno
blanco (' ') Antepone un espacio en blanco si el nmero es positivo con signo Sin espacio en blanco
# Depende del campo obligatorio tipo: Sin prefijo
tipo: o, x o X. Antepone 0, x0 o X0
tipo: e, E, f, g, y G. La salida tiene siempre punto decimal Slo punto decimal si
dgitos despus
tipo: c, d, i, u, s
Se truncan los ceros
tipo: g y G. No se truncan los ceros arrastrados
arrastrados
El campo # se ignora

Programacin en C 52
El campo [ancho] indica el nmero mnimo de caracteres de salida. Si el nmero de caracteres de salida es
menor que ancho se aaden espacios en blanco. Si es mayor que ancho, no truncan; se imprimen todos los
caracteres. El significado del campo [.precisin] depende del campo obligatorio tipo segn se indica en la
tabla siguiente:
tipo Significado del campo precisin Por defecto
Nmero mnimo de dgitos que se escriben. Si el nmero de 1 dgito
d, i, u, o, x, X
dgitos de salida es menor que precisin se aaden ceros a la
izquierda
6 dgitos
e, E, f Nmero de dgitos despus del punto decimal. El valor se
redondea
g, G 6 dgitos
Mximo nmero de dgitos significativos

Nmero mximo de caracteres que se escriben


s Se imprimen todos los
caracteres hasta el
primer carcter nulo
Ninguno
c

7.1.2. putchar y puts


La funcin putchar escribe un caracter en la pantalla. Su prototipo es: int putchar (int caracter)
A la funcin hay que pasarle como argumento el caracter a escribir y devuelve el caracter escrito o EOF si
ocurre un error.
Por ejemplo, el programa de la izquierda #include <stdio.h> #include <stdio.h>
imprime la letra A y luego salta una lnea. Si
void main () { void main () {
se desea usar la funcin printf en lugar de
putchar, el programa podra escribirse como char car= A; char car= A;
se muestra a la derecha.
putchar (car) ; putchar (`\n); printf("%c\n", car);
Por su parte, la funcin puts escribe una
cadena de caracteres en pantalla. } }

A la funcin hay que pasarle como argumento la cadena de caracteres a imprimir y devuelve un valor no
negativo si la operacin es correcta o EOF si ha habido un error. Su prototipo es:
La funcin puts () sustituye el caracter nulo ('\0), con el que termina int puts(const char *cadena);
cualquier cadena de caracteres, por el caracter de nueva lnea ('\n).
As salta automticamente una lnea tras imprimir la cadena.
7.2. Entrada estndar
7.2.1. scanf
La funcin scanf () lee datos desde el teclado, con el formato especificado en la cadena control, y los
almacena en su lista de argumentos.
El prototipo de la funcin scanf es: int scanf( const char *formato[argumento1, argumento2,...]);
La funcin devuelve un nmero entero de tipo int igual al nmero de argumentos correctamente ledos y
asignados o EOF si ha habido un error. Por ejemplo, en el programa siguiente, se comprueba ese valor.
El primer parmetro de la funcin #include <stdio.h>
(consl char *formato) es una cadena
void main () {
de control escrita entre comillas
dobles formada por especificadores int f=0; float dato=0.0f;
de formato separados por un espacio
en blanco. Pueden usarse caracteres printf("Introduzca el dato: "); f=scanf("%f", &dato);
adicionales si se desea que los datos if (f==EOF) printf("Error\n") else printf("El dato es %f\n", dato);
se introduzcan con un formato
concreto. }

Los especificadores de formato ms comunes se recogen en la siguiente tabla:

Programacin en C 53
Especificador Dato de entrada Especificador Dato de entrada
de formato de formato
%d Entero decimal %s Cadena de caracteres

%f Nmero real %[caracteres] Lee caracteres hasta encontrar uno que no est
en caracteres

%c Carcter individual %[/\caracteres] Lee caracteres hasta encontrar uno que est en
caracteres

Un error frecuente es escribir mensajes dentro de la cadena de control. Si es necesario escribir un mensaje
en la pantalla del estilo "Introduzca datos", se usan las funciones printf o puts antes que scanf.
El resto de parmetros de la funcin (argumento1, argumento2...) son las direcciones de memoria de las
variables en que se almacenan los datos ledos. Es decir, los argumentos se pasan por referencia. Cuando
se lee una cadena de caracteres, el nombre de la cadena no va precedido del smbolo & porque es un
puntero con la direccin de memoria que apunta a su primer elemento. Sin embargo, para el resto de
variables (nmero enteros o reales, caracteres, etc.) el argumento est formado por el nombre de la variable
precedido obligatoriamente del operador & ("direccin de"). Si se olvida el operador & al llamar a scanf el
compilador no lo detecta y puede tener consecuencias.
Cuando en la cadena de control aparecen caracteres ordinarios distintos de espacios en blanco para
separar los especificadores de formato, los datos introducidos desde el teclado tienen que contener esos
caracteres en el mismo orden en que aparecen.
Por ejemplo, en el siguiente programa la fecha tiene que #include <stdio.h>
ser introducida con el formato "%d-%d-%d", es decir, scanf
void main () {
tiene que leer un entero, a continuacin un signo menos
que es descartado, luego otro entero y asi sucesivamente. int d, m, a;
Si scanf encuentra un caracter distinto del signo menos
printf("Introduzca la fecha (dd-mm-aaaa): ");
despus de introducir un entero, incluido el retomo de
carro, la funcin termina en ese momento. La nica scanf("%d-%d-%d", &d, &m ,&a);
manera de introducir los datos correctamente es escribir,
por ejemplo, 14-12-2007 y luego pulsar retomo de carro. }

Cuando en la cadena de control se deja un espacio en blanco despus de cada especificador de formato,
scanf lee, pero ignora espacios en blanco introducidos de teclado hasta encontrar un caracter que no sea
espacio en blanco, normalmente retomo de carro.
Cuando se leen caracteres individuales, los espacios en blanco son #include <stdio.h>
ledos como otro caracter, lo quel puede dar lugar a errores. Por
void main () {
ejemplo, si al ejecutar el siguiente programa, se teclea x y z,
aparecera en pantalla x y z, a vale x, b vale , c vale z. A la variable char a, b, c;
b se le asigna el espacio en blanco dejado entre x e y. Para evitarlo
puede dejarse un espacio en blanco entre los especificadores de scanf("%c%c%c", &a, &b, &c);
formato (por ejemplo, "%c %c %c"). printf("a vale %e\n",a);
La funcin scanf slo lee caracteres hasta encontrar el primer espacio printf("b vale %e\n",b);
en blanco. Para leer cadenas con espacios puede usarse % [^\n], la
especificacin de formato personalizada, que lee caracteres hasta que printf("e vale %e\n",e);
se encuentra retomo de carro. En este caso puede ser ms cmodo }
usar la funcin gets que lee la cadena hasta que se pulsa enter.
7.2.2. getchar, gets y fflush
La funcin getchar lee un caracter individual desde el teclado. Su prototipo es: int getchar(void);
No tiene argumentos y devuelve el caracter ledo o EOF si encuentra la marca o se da error. Para guardar el
caracter ledo desde el teclado se asigna a una variable de tipo char el valor devuelto por la funcin getchar.
La funcin gets lee una cadena de caracteres desde teclado hasta que se
char *gets(char *cadena);
pulsa enter. Su prototipo es:
El argumento de la funcin es el nombre de la cadena donde se almacenar la cadena leda y devuelve un
puntero a la misma o un puntero nulo (NULL) si se produce error.

Programacin en C 54
Por ejemplo, el siguiente programa usa gets para leer la #include <stdio.h>
cadena de caracteres de teclado. A diferencia de scanf, puede
main () {
lee directamente cadenas de caracteres que contengan
espacios en blanco. char asignatura[50]; gets(asignatura);
Para hacer lo mismo usando scanf hay que utilizar la printf("%s\n", asignatura);
especificacin personalizada de formato % [^\n], como se
}
indic.
La funcin fflush limpia un buffer de datos. Su prototipo es el presentado. La funcin tiene como argumento
un puntero de tipo FILE que apunta al buffer de datos a limpiar y devuelve un 0 si el buffer se ha limpiado
correctamente o EOF si se ha producido un error.
En el caso habitual de limpieza del buffer de entrada del
int fflush(FILE *flujo);
teclado, simplemente se escribira fflush(stdin);
La necesidad de usar fflush se muestra en el programa de la izquierda, para leer 2 caracteres de teclado.
Con ese cdigo, si se pulsa A, B, slo se mostrar por pantalla Los caracteres son A y . Es decir, B no se
lee, porque no se ha limpiado el buffer de lectura. Al leer A y pulsar enter, el buffer contiene en su primera
posicin A y en la segunda /n. A se asigna a car1 y al pulsar B, pasa a la segunda posicin porque ahora
est /n en la primera. Con lo cual se asigna /n a car2.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
char car1, car2; char car1, car2;
printf("Primer caracter\n"); scanf ("%c", &car1); printf("Primer caracter\n"); scanf("%c" 1 &car1);
printf("Segundo caracter\n"); scanf("%c", &car2); printf("Segundo caracter\n"); fflush(stdin);
printf("Los caracteres son %c y %c\n", car1, car2); scanf("%c", &car2);
} printf("Los caracteres son %c y %c\n" 1 car1, car2);
}
Para evitar este problema, se usa la funcin fflush, como en el programa de la derecha. Bastara limpiar el
buffer antes de ejecutar la segunda funcin scanf.
7.3 Gestin de ficheros
En cualquier programa al terminar su ejecucin se pierden los datos. Para gestionar un almacenamiento
permanente en memoria secundaria se usan ficheros.
Las funciones usadas para leer o escribir ficheros son similares a las presentadas para leer de teclado o
escribir en pantalla. Por ejemplo, fprintf y fscanf se usan para escribir y leer datos con formato de un fichero.
Al tratar con un fichero la secuencia de operaciones es abrir, operar (R/W) y cerrar. En cada operacin
pueden darse errores que hay que controlar para una ejecucin correcta.
7.3.1. Abrir y cerrar ficheros. fopen y fclose
La funcin fopen abre un fichero para lectura
FILE *fopen (const char *nombre, const char *modo);
o escritura. Su prototipo es:
El primer parmetro de la funcin es el nombre del fichero, que tiene que respetar las normas del SO en
que se ejecute el programa. En los ejemplos se ha usado el formato DOS, vlido para la mayora de los SO,
como WS o UNIX. En DOS, el nombre del archivo puede tener de 1 a 8 caracteres y la extensin de 0 a 3.
En los ejemplos slo se usa la extensin .txt, de manera que los archivos generados por los programas
pueden abrirse con cualquier editor de texto. El segundo parmetro de fopen indica el modo en que se
abrir el fichero: lectura, escritura o adicin ("r", "w" o "a", respectivamente). Se describe en la tabla.
Modo Descripcin
"r" Abre el fichero para leer (read). El fichero tiene que existir. Si no existe, se produce un error
''w" Abre el fichero para escribir (write). Si el fichero no existe, se crea. Si existe, se borra su contenido
"a" Abre un fichero para aadir informacin (add). Los datos nuevos se aaden al final del fichero. Si el
fichero no existe, se crea.

Programacin en C 55
Existen otros modos de abrir un fichero, por ejemplo, aadiendo un signo ms a los anteriores se obtienen
tres nuevos modos ("r+", "w+" y "a+") que permiten abrir el fichero para leer y escribir. Tambin pueden
abrirse en modo texto o binario, aadiendo la letra t o b al modo. Por ejemplo, "rb", "wb" o "ab". Por omisin,
el fichero se abre en modo texto.
Al crear un fichero el sistema aade automticamente al final la marca fin de fichero (EOF). La funcin fopen
devuelve un puntero a una estructura FILE o puntero nulo (NULL) si se produce error (si el fichero a abrir no
existe, est protegido contra escritura, etc.). Normalmente se comprueba si se ha producido error y, si es el
caso, se avisa al usuario de la circunstancia. Si no se produce error al abrir el fichero, el puntero que
devueve fopen se asigna a un puntero de tipo FILE que debe haberse declarado previamente. Por ejemplo:
La primera instruccin declara un puntero de tipo FILE llamado "pf" y la FILE *pf;
segunda le asigna el puntero devuelto por la funcin fopen, que en este
pf=fopen("datos.txt", "r");
ejemplo intenta abrir un fichero llamado datos.txt para leer su contenido.
Una vez realizada la asignacin, para referirse al fichero (datos.txt) se usar el puntero asociado al mismo
(pf ). Por ejemplo, el siguiente programa abre en modo lectura el fichero llamado "datos.txt":
Al finalizar el programa, cualquier fichero abierto se #include <stdio.h>
cierra automticamente. Sin embargo, es buena
void main () {
prctica cerrar un fichero cuando se ha acabado de
trabajar con l, por ejemplo, para permitir que otros FILE *pf;
programas puedan acceder al mismo.
pf=fopen("datos.txt", "r");
De forma simtrica, la funcin fclose cierra un
if (pf==NULL) { printf("Error al abrir el fichero\n");
fichero abierto previamente con fopen.
Hay que pasar a la funcin un argumento puntero return; } // Termina el programa
de tipo FILE que apunte al fichero. La funcin else printf("Fichero abierto\n");
devuelve 0 si el fichero se cierra correctamente o
EOF si se produce error. }

Su prototipo es el mostrado en el cuadro.


Un ejemplo sencillo de apertura de ficheros es el que muestra el siguiente
int fclose (FILE *pf);
bloque de cdigo.
Al ejecutar el programa, puede comprobarse que en el directorio de trabajo aparece un fichero de nombre
"datos.txt" que puede editarse y comprobar que est vaco, ya que no se han realizado escrituras. Por fin, a
la derecha se muestra un esquema de estructura de programa para lectura o escritura de un fichero.
#include <stdio.h>
#include <stdio.h> void main () {
void main () { FILE *pf;
FILE *pf;
pf=fopen("datos.txt", "w"); // Se abre el fichero para escribir pf=fopen("nombre ", "modo'');
fclose(pf); // Se cierra // Operaciones de lectura o escritura
} fclose(pf);

}
7.3.2. Gestin de errores
Al realizar operaciones de R/W con ficheros debe comprobarse si se dan errores en la operacin o si se ha
alcanzado la marca EOF. Para ello C ofrece funciones de la biblioteca <stdio. h>, como son ferror, perror,
cleaerr y feof. Sus prototipos se muestran a continuacin.
La funcin ferror detecta si se ha producido un error despus de int ferror (FILE *pf);
operar con el fichero. El argumento es el puntero de tipo FILE
void perror (const char *mensaje);
asociado al fichero. Devuelve un valor distinto de 0 si se produce
error y 0 en caso contrario. void clearerr (FILE *pf);
La funcin perror escribe un mensaje de error en pantalla. int feof (FILE *pf);

Programacin en C 56
El argumento es un mensaje que se mostrar en pantalla. La funcin escribe el mensaje terminado en dos
puntos y aade el mensaje de error dado por el sistema. La funcin clearerr borra los errores que se hayan
producido. El argumento es el puntero tipo FILE que apunta al fichero.
El siguiente programa muestra un ejemplo de uso de #include <stdio.h>
las tres funciones anteriores. Si no existera el fichero
void main () {
datos.txt, su salida por pantalla sera del estilo:
FILE *pf;
datos.txt:_ No such file or directory
pf=fopen("datos.txt", "r");
Por fin, la funcin feof detecta el final del fichero. El
argumento es el puntero asociado al fichero. La funcin if (pf==NULL) { perror("datos.txt"); return; }
devuelve un valor no nulo tras la primera operacin que
// operaciones de lectura
intente leer despus de la marca EOF. Por ejemplo,
para leer un fichero que no se conoce su fin, puede if (ferror (pf)) { // Equivale a if(ferror(pf)!=0)
usarse el bucle: while (feof(pf)==0) { // operaciones R/W
};. A veces, la expresin condicional del bucle se perror("Error de lectura"); clearerr(pf);
escribe de forma ms compacta: while(!feof(pf)) {// } else printf("Lectura correcta");
operaciones R/W };. El uso de funciones de gestin de
errores es siempre recomendable para detectar errores fclose(pf);
y hacer los programas ms robustos. }
7.3.3. Operaciones de lectura/escritura
Las funciones de la biblioteca <stdio. h> ms usadas para leer y escribir ficheros se muestran en la tabla.
Escritura Lectura Operaciones de R/W Escritura Lectura Operaciones de R/W
fprintf () fscanf () Con formato fputs () fgets () Cadenas de caracteres
fputc () fgetc () Caracteres individuales fwrite () fread () Bloques de bytes
Las ms usadas pueden ser fprintf y fscanf por su potencialidad y facilidad de uso. Las siguientes, fputc,
fgetc, fputs y fgets, pueden ser de utilidad en alguna aplicacin. Su funcionalidad est incluida en fprintf y
fscanf, que podran sustituir a sus homlogas printf y scanf. Las funciones fwrite y fread son ms especficas
de ficheros. Permiten leer o escribir en bloques de bytes, til, por ejemplo, cuando hay que trabajar con
tipos de datos avanzados como estructuras.
7.3.3.1. fprint y fscanf
Ambas permiten escribir y leer datos con formato. Son idnticas a printf y scanf salvo por un parmetro
adicional antes de la cadena de control, que recibe el puntero asociado al fichero con el que trabajar. Por
ejemplo, para mostrar el mensaje "Hola" en pantalla puede escribirse printf("%s\n", "Hola"); Lo mismo, en un
fichero sera fprintf(pf, "%s\n", "Hola"), siendo pf el puntero asociado al fichero donde escribir.
Con fprintf tambin puede imprimirse en pantalla, al estilo de printf, poniendo en lugar del puntero el nombre
del flujo de datos para salida estndar (stdout): fprintf(stdout, "%s\n, "Hola"). De forma anloga, para el
teclado una cadena de caracteres (sin espacios en blanco) se leera scanf("%s", cadena), siendo cadena
declarada previamente. Para realizar la misma operacin leyendo los datos de un fichero sera: fscanf(pf,
"%s", cadena), donde pf es el puntero asociado al fichero. Lo mismo si se usa fscanf para leer de teclado:
fscanf(stdin, "%s", cadena), donde stdin es el flujo de datos asociado con la entrada estndar.
El programa de la izquierda abre un archivo, "datos.txt", escribe el mensaje "Hola" y un salto de lnea y lo
cierra. A continuacin lo vuelve abrir para leerlo con fscanf y muestra su contenido en la pantalla.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
FILE *pf; char cadena[11]; int v1[5]={-1, 3, 5, 0, 4}; int v2[5]={4, 9, -8, 2, 3};
pf=fopen("mensaje.txt", "w"); int i; FILE *pf;
fprintf(pf, "%s\n", "Hola"); fclose(pf); pf=fopen("datos.txt", "w");
pf=fopen("mensaje.txt", "r"); fprintf(pf, "%d\n", 5); // Se guarda la dimensin
fscanf(pf, "%s", cadena); fclose(pf); for(i=0; i<5; i++)
printf("%s\n", cadena); fprintf(pf, "%d\t%d\n", v1[i], v2[i]); fclose(pf);
} }

Programacin en C 57
A la derecha, en el ejemplo se guardan dos vectores en un fichero, cada uno en una columna. La dimensin
escrita en la primera lnea del fichero es un recurso muy usado que facilita la lectura posterior del fichero, ya
que se conoce el nmero de operaciones de lectura a hacer.
Si no se conoce el nmero de datos que contiene el #include <stdio.h>
fichero hay que leer los datos hasta la marca EOF.
void main () {
Una alternativa es usar la funcin feof. Sin embargo,
a veces se recorre el fichero secuencialmente con la FILE *pf; int x;
propia funcin fscanf. Se implementa en el cdigo
del cuadro. Cuando se escribe la condicin booleana pf=fopen("datosl.txt", "r");
fscanf( ... )!=EOF dentro del bucle no slo while(fscanf(pf, "%d", &x) !=EOF) printf("%d\n", x);
comprueba si el valor devuelto por la funcin es
distinto de EOF; tambin se ejecuta la operacin de fclose(pf);
lectura correspondiente. }
7.3.3.2. fputc, fputs, fgetc y fgets
Estas funciones permiten escribir o leer ficheros carcter a carcter. int fputc (int car, FILE *pf);
Sus prototipos son los mostrados. int fgetc (FILE *pf);
La funcin fputc escribe un caracter en el fichero. El parmetro, car, es el caracter a escribir y "pf", el
puntero asociado al fichero. La funcin devuelve el caracter escrito o EOF si se produce error.
La funcin fgetc lee un caracter del fichero. El argumento es el puntero asociado al fichero. La funcin
devuelve el caracter ledo (que habr que asignar a una variable de tipo char declarada previamente) o EOF
si se produce error o se lee fin de fichero. Puede que la funcin devuelva EOF sin llegar al final del fichero.
Para distinguir casos habra que usar feof o ferror.
Las funciones fputs y fgets permiten escribir o leer int fputs (const char *cadena, FILE *pf);
cadenas de caracteres en ficheros. Sus prototipos son los
mostrados en la tabla. char *fgets (char *cadena, int n, FILE *pf);

La funcin fputs escribe una cadena de caracteres en un fichero sin el caracter nulo ('\0'). A la funcin hay
que pasarle como primer argumento la cadena a escribir y de segundo, el puntero asociado al fichero.
Devuelve un valor negativo si funciona correctamente o EOF si se produce error. Para recuperar la cadena
de caracteres escrita en el fichero es aconsejable aadir el caracter de nueva lnea ('\n') al final de la
cadena, como en el ejemplo de la izquierda.
#include <stdio.h> #include <stdio.h>
void main () { void main () {
FILE *pf; FILE *pf; char cadena[101];
pf=fopen("datos.txt", "w"); pf=fopen("datos.txt", "r");
fputs("Hola Mundo", pf); if( fgets(cadena, 101, pf) ==NULL) printf("Error o EOF\n");
fputc ( \n, pf) ; else printf( "%s", cadena);
fclose(pf); fclose(pf);
} }
La funcin fgets lee una cadena de caracteres de un fichero. El primer parmetro es el nombre de la cadena
de caracteres donde se almacena la cadena leda, terminada automticamente con ' \0 '. El segundo
parmetro, n, est relacionado con el tamao de la cadena a leer. El tercer argumento, es el puntero
asociado al fichero.
La funcin devuelve un puntero al principio de la cadena leda o un NULL si hay error o se llega a EOF. Otra
vez, para distinguir casos habra que usar feof o ferror. La cadena de caracteres leda por la funcin fgets va
hasta el primer caracter '\n' inclusive, hasta EOF o hasta que se leen n-1 caracteres.
El programa a la derecha abre un archivo para lectura, llama a la funcin fgets y comprueba si ha habido
error o se llega a EOF. Si el puntero no es nulo, se lee la cadena hasta un mximo de 100 caracteres.
7.3.3.4. fwrite y fread
Los programas presentados manejan archivos de texto en los que los datos se almacenan como caracteres
ASCII que ocupa un byte cada uno. Una forma de almacenar datos que requiere menos espacio es el modo

Programacin en C 58
binario, en que los datos se almacn igual que en memoria. As, en modo binario un nmero int o float
ocupara cuatro bytes.
Las funciones fwrite y fread permiten escribir y size_t fwrite (const void *p, size_t n, size_t e, FILE *pf);
leer datos en bloques de bytes, de longitud fija y
en binario. Sus prototipos los mostrados. size_t fread (void *p, size_t n, size_t e, FILE *pf);

La funcin fwrite escribe un bloque de bytes en el fichero asociado con "pf ", e elementos de longitud "n"
bytes almacenados a partir de la posicin de memoria indicada por el puntero "p". La funcin devuelve el n
de elementos escritos. Si el nmero devuelto es menor que "e", se produce un error. El programa siguiente
guarda dos nmeros enteros en un archivo.
La funcin fread lee un bloque de bytes. La funcin lee, del fichero #include <stdio.h>
asociado a "pf", "e" elementos de longitud n bytes y los almacena a
void main () {
partir de la direccin de memoria indicada por el puntero "p". La
funcin devuelve el n de elementos ledos. int a=1, b=2;
Si el nmero devuelto es menor que e, se ha producido un error o se pf=fopen("datos.txt", "wb");
ha llegado a EOF. Para distinguir casos habra que usar feof o ferror.
Para leer un fichero con fread hasta el final se puede usar el bucle fwrite(&a, sizeof(int), 1, pf);
while(fread(, , e, ...)==e) {}. fwrite(&b, sizeof(int), 1, pf);
Es decir, se realiza la operacin de lectura con fread y se compara el fclose (pf);
valor devuelto con el nmero de elementos a leer (e). Una de las
aplicaciones ms tiles es al leer o escribir tipos de datos avanzados }
como las estructuras.
El ejemplo de la izquierda muestra un programa para rellenar fichas de alumnos almacenadas en un fichero.
El de la derecha abre el archivo anterior y muestra las fichas en la pantalla.
#include <stdio.h>
typedef struct {
char nombre[51];
char apellidos[51];
#include <stdio.h>
int matricula;
typedef struct {
} ficha;
char nombre[Sl];
void main () {
char apellidos[Sl];
ficha alumno;
int matricula;
char opcion;
} ficha;
FILE *pf;
void main () {
pf=fopen("alumnos.txt", "wb");
ficha alumno;
do {
FILE *pf;
fflush(stdin);
pf=fopen("alumnos.txt", "rb");
printf("\nNombre: "); gets(alumno.nombre);
while(fread(&alumno, sizeof(ficha), 1, pf)==1) {
printf("Apellidos: "); gets(alumno.apellidos);
printf("Nombre: %s\n", alumno.nombre);
printf("n matricula:"); scanf("%d", &alumno.matricula);
printf("Apellidos: %s\n", alumno.apellidos);
// Se guarda la ficha
printf("n matricula:
fwrite (&alumno, sizeof (ficha), 1, pf);
%d\n\n",alumno.matricula);}
fflush (stdin);
fclose(pf);
printf("\nMas alumnos (s/n)?\n"); }
scanf("%c", &opcion);
} while (opcion=='s');
fclose (pf);
}

Programacin en C 59
7.4. Ejemplos
Ejemplo 1. Ordenar datos de un fichero. Ordenar Ejemplo 2. Gestion de pedidos. En un fichero de texto,
un fichero de datos llamado "Vector.txt", que "lnventario.txt" se almacena informacion del inventario
contiene un maximo de 100 datos de tipo real. El de un almacen con los campos elemento, capacidad,
primer dato contenido en el fichero es un nmero stock y precio unitario
entero que define el nmero de datos que viene a
Elemento es una cadena de texto sin espacios que
continuacion en el fichero.
identifica el tipo de objeto; "capacidad" un entero que
indica el n maximo de elementos que pueden
almacenarse; "stock" el n de elementos en stock y
"precio_unitario" el precio de cada elemento.
El programa lee el fichero de texto y genera otro
llamado "Pedidos.txt" en que se escribe una lista de los
objetos de los que no hay stock (stock=0) y la cantidad
que se deben pedir (capacidad maxima de ese
elemento). El programa mostrar ademas el valor total
del almacen actual, asi como el coste del pedido a
realizar.
#include <stdio.h> #include <stdio.h>
void main(void) { void main(void) {
FILE* f; FILE* f1; FILE* f2;
int num_datos, i,j; float coste_total=0.0f; float valor_actual=0.0f;
float vector[100]; char item[30];
f=fopen("Vector.txt","r"); int capacidad; int cantidad; int leidos;
fscanf(f,"%d",&num_datos); float precio;
for(i=0;i<num_datos;i++) fscanf(f,"%f",&vector[i)); f1=fopen("Inventario.txt","r");
fclose(f); f2=fopen("Pedidos.txt","w");
for(i=0;i<num_datos-1;i++) { while (1) {
for(j=i+1;j<num datos;j++) { leidos=fscanf(fl,"%s %d %d
%f",item,&capacidad,&cantidad,&precio);
if(vector[i]>vector[j]) {
if (leidos! =4) break;
float aux=vector[i];
if(cantidad==0) {
vector[i]=vector[j];
fprintf(f2,"%s %d\n",item,capacidad);
vector[j]=aux;
coste_total+=capacidad*precio;
}
}
}
valor_actual+=cantidad*precio;
}
printf("El importe total del pedido sera:
f=fopen("Vector.txt","w");
%f\n",coste_total);
fprintf(f,"%d\n",num_datos);
printf("El valor del almacenes:
for(i=0;i<num_datos;i++) fprintf(f,"%f\n",vector[i]); %f\n",valor_actual);
fclose(f); fclose (f1); fclose(f2);
} }
Ejemplo 3. Codificador y decodificador de ficheros de texto. Programa que lee un fichero de texto del disco
y lo codifica / decodifica en un nuevo fichero de texto. El fichero de texto de entrada se generar con
NotePad con extensin .txt. Los nombres de ambos ficheros, de entrada y salida se le pedirn al usuario.
Si el programa no encuentra el archivo de entrada, informar del error y termina su ejecucin. Se pedir al
usuario si quiere codificar (1) o decodificar (0) el archivo en cuestin. La codificacin consistir en cambiar
cada caracter por el siguiente a>b, b>c y sucesivamente, incluidos los caracteres de control, espacios,
retornos de carro, etc. La descodificacin consistir en cambiar cada caracter por el anterior b> a, c>b, etc.

Programacin en C 60
#include <stdio.h>
void main(void) {
FILE* f_ent; FILE* f_sal;
int codifica, n;
char nombre_ent[200],nombre_sal[200], car;
printf("Nombre fichero entrada: "); scanf("%s",nombre_ent);
printf("Codificar (1) o descodificar (0): "); scanf("%d",&codifica);
printf("Nombre fichero salida: "); scanf("%s",nombre_sal);
f_ent=fopen(nombre_ent,"r"); // fichero de entrada
if(f_ent==NULL) {
printf("No se encuentra fichero %s\n",nombre_ent);
return; // se termina
}
f_sal=fopen(nombre_sal,"w");
while (1) {
n=fscanf(f_ent, "%e", &car);
if (n !=1) break;
if(codifica) car+=1; // cdigo Csar, mdulo 1
else car-=1;
fprintf(f_sal,"%c",car);
printf("Fin de la codificacion-descodificacion\n");
fclose(f_ent); fclose(f_sal);
}

Programacin en C 61
8. PROBLEMAS RESUELTOS
8.1. Mximo comn divisor
Programa que calcule el mximo comn #include <stdio.h>
divisor (MCD) de dos nmeros enteros
void main () {
positivos, m y n, usando el algoritmo
propuesto por Euclides en el ao 300 aC: int m, n, temporal;
1. Comprobar que m es mayor o igual que n. do { // Peticin de m y n positivos
Si m es menor, hay que intercambiar los
valores de m y n. printf("Introduzca m y n\n");
scanf("%d %d", &m, &n);
2. Se divide m entre n y se calcula el resto.
} while (m<l 11 n<l);
3. Si el resto es cero, n es el MCD (fin).
// Intercambio de m y n si m<n
4. Si el resto es distinto de cero, se
reemplaza m por n y n por resto. if (m<nl {
5. Se vuelve al punto 2. temporal=m; m=n; n=temporal;
Los nmeros m y n se introducirn por // Algoritmo de Euclides
teclado. Si el usuario introduce un nmero
menor que 1 se pide que los introduzca de while ((m%n)!=0) temporal=m;m=n; n=temporal%n;
nuevo. // Imprime el MCD
printf("El MCD vale %d\n", n);
}
8.2. Adivinar un nmero
#include <stdio.h>
void main () {
int min=0, max=1000; // Intervalo del juego
int prediccion, intentos=0, respuesta;
Programa que adivina un nmero entre 0 y printf("Piense un numero del %d al %d\n\n", min, max);
1000 que ofrece el usuario.
do {
El programa debe ir proponiendo nmeros al
prediccion=(maximo+minimo)/2;
usuario y ste debe contestar si son
mayores o menores que el nmero elegido. printf("\nEs su numero %d?\n", prediccion);
Si la mquina lo acierta, se imprimie el printf("1. Si \n");
nmero de intentos.
printf("2. No, es menor \n");
printf("3. No, es mayor \n\n");
scanf("%d", &respuesta);
intentos++;
switch(respuesta) {
case 2:max=prediccion;
break;
case 3:minimo=prediccion;
}
} while(respuesta!=1);
printf("\nHan sido necesarios %d intentos\n", intentos);
}

Programacin en C 62
8.3. Lotera
Programa que obtenga una combinacin de lotera compuesta por 6 nmeros distintos en el rango 1-49,
ambos incluidos. Para ello se usar la funcin NumeroAleatorio() que devuelve un entero aleatorio del rango
definido [min,max].
La funcin NumeroAleatorio no comprueba que los nmeros sean distintos. Esto puede dar combinaciones
que no sean vlidas para la lotera. El programa del usuario debe solucionar este problema. Se recomienda
usar vectores para almacenar la informacin. Se presentan 2 alternativas.
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
int NumeroAleatorio(int min,int max); int NumeroAleatorio(int min,int max);
int main () { int main () {
int i,numero,repetido; int numeros[6]; int i,j;
int extraido[49]={0}; // bolas en el bombo int numeros[6);
for(i=0;i<6;i++) { // 6 numeros distintos for(i=0;i<6;i++) { //6 numeros distintos
do { do {
int j; // sacar un n aleatorio numero=NumeroAleatorio(1,49);
numero=NumeroAleatorio(1,49); repetido=0; // no repetido
repetido=0; // no repetido for(j=0;j<i;j++) { // se comprueba
if(extraido[numero-1]==1) repetido=1; if(numeros[j]==numero) {
else { repetido=1;
extraido[numero-1)=1; break;
repetido=0; }
} while(repetido); }
numeros[i]=numero; // se sabe que es distinto } while(repetido);
// se guarda y al siguiente numeros[i]=numero; // es distinto
} // mostrar numeros // se guarda y al siguiente
for(i=0;i<6;i++) printf("%d ",numeros[i)); // mostrar numeros
printf("\n"); for(i=0;i<6;i++) printf("%d ",numeros[i));
} int NumeroAleatorio(int min,int wax) {
int NumeroAleatorio(int min,int max) { int n=min+rand()%(max-min+l);
int n=min+rand()%{max-min+l); return n;
return n; }
}
8.4. Integral
#include <stdio.h>
Programa que calcule la integral numrica de una void main () {
2
funcin (usar como ejemplo y=x , aunque se puede
double a,b,x;
usar otra) en el intervalo [a,b], siendo a y b tecleados
por el usuario. double incremento=1e-7,suma=0,integral;
La integral corresponde al rea encerrada por la printf("Introduzca a y b: "); scanf("%lf %lf",&a,&b);
funcin con el eje X en ese intervalo y se puede
for(x=a;x<=b;x+=incremento) suma+=exp(x);
aproximar cuando x es muy pequeo por:
integral=suma*incremento;
I(x) = f(x) x
Usar un valor de x=6. printf("la integral es: %lf\n",integral);
}

Programacin en C 63
8.5. Nmeros primos
Programa que muestre por pantalla los #include <stdio.h>
nmeros primos menores que uno dado
#include <math.h>
por el usuario.
#include <stdlib.h>
Para ello se programar una funcin
denominada EsPrimo, que tiene como int EsPrimo(int n);
nico parmetro el nmero entero que
se quiere comprobar si es primo o no. int PrimoAleatorio(int min, int max);

La funcin devuelve un 1 si el nmero void main () {


es primo y 0 en caso contrario. int i,num;
Una vez realizado, se solicita una printf("Numero:"); scanf("%d",&num);
funcin que genere un nmero primo
aleatorio en el rango [min,max] siendo for(i=1;i<=num;i++) {
estos valores parmetros. if(EsPrimo(i)) printf("%d ",i);
El nmero primo aleatorio ser devuelto printf("\n20 Numeros primos aleatorios:\n");
mediante el valor de retomo de la
funcin. for(i=0;i<20;i++) printf("%d ",PrimoAleatorio(0,100));

Usar esta funcin para imprimir 20 }


nmeros aleatorios por pantalla en el int EsPrimo(int n) {
rango 0-100.
int divisor;
Se usa la funcin rand () de <stdlib. h>
que devuelve un entero aleatorio en el for(divisor=2;divisor<=sqrt(n);divisor++) {
rango 0-65535. if (n%divisor==0) return 0; // no primo
return 1; // primo
}
int PrimoAleatorio(int min, int max) {
int num;
do {
num=min+rand()%(max-min+l);
} while(!EsPrimo(num));
return num;
}
8.6. Fibonacci
Programa que genera los n primeros #include <stdio.h>
trminos de la serie de Fibonacci.
void main () {
El valor de n (entero y positivo) se lee
int n, i, penultimo=0, ultimo=1, siguiente;
por teclado. En esta serie los dos
primeros nmeros son 1, y el resto se do {
obtiene sumando los dos anteriores:
printf("Introduzca el valor de n\n"); scanf("%d", &n);
1, 1, 2, 3, 5, 8, 13, 21.
} while (n<=2) ;
Garantizar mediante un bucle que el
printf("\n%d\n%d\n", penultimo, ultimo);
nmero introducido por el usuario es
mayor o igual que 2, para que al menos for(i=1 ; i<=(n-2) ; i++) { // calcula e imprime el siguiente
la sucesin tenga siempre 2 trminos.
siguiente=penultimo+ultimo; printf("%d\n", siguiente);
Se pide al usuario un nmero y caso de
que no sea mayor de dos, se le vuelve // actualiza los ltimos trminos
a pedir. penultimo=ultimo;
ultimo=siguiente;
}
}

Programacin en C 64
8.7. Media y rango de un vector
Programa, que muestra por pantalla la media y void RellenaVector (float v[],int num);
rango de un vector de nmeros aleatorios.
float CalcularMedia (float v[], int num);
El rango de un vector es la diferencia entre el valor
float CalculaRango (float v[], int num);
mximo y el mnimo de dicho vector.
//cuerpo de las funciones
La estructura del programa a completar es:
void RellenaVector (float v[],int num) {
#include <stdio.h>
int i;
#include <stdlib.h>
for(i=0; i<num; i++) v[i] = NumeroAleatorio();
#define NUM_ELEM 55
}
void test ();
float CalcularMedia (float v[], int num) {
void main() {
int i;
test ();
float media,suma=0;
}
for(i=0; i<num; i++) suma+= v[i];
void test () {
media = suma/num;
float m, r;
return (media);
float vector[NUM_ELEM];
}
RellenaVector(vector,NUM_ELEM);
float CalculaRango (float v[], int num) {
m=CalcularMedia(vector,NUM ELEM);
int i;
r=CalculaRango(vector,NUM-ELEM);
float rango, min, max;
printf("Media= %f Rango= %f\n", m,r);
min = max= v[0];
}
for(i=l; i<num; i++) {
La funcin RellenaVector inicializar los contenidos
del vector. if(v[i]>max)
Para generar los nmeros aleatorios se supone que max=v[i];
se dispone de una funcin float NumeroAleatorio ()
que devuelve un nmero aleatorio distinto cada vez if (v[i]<min) min=v[i];
que es llamada. }
rango = max - min;
return (rango);
}

Programacin en C 65
9. APLICACIN PRCTICA: AGENDA
Se presenta una aplicacin completa para la gestin de una agenda telefnica. Se pretende crear un
programa que muestre lo explicado en los apartados anteriores en conjunto.
El programa se simplifica bastante con respecto a lo que podra ser una implementacin real, en cuanto a
comprobaciones, por ejemplo. La memoria se gestiona estticamente, admitiendo un nmero mximo de
contactos. Una implementacin ms correcta usara memoria dinmica.
Los requisitos de la agenda se resumen en que el programa debe cargar automticamente los datos al
comienzo desde un fichero de texto denominado "Agenda.txt", que contendr tantas lneas como contactos.
Cada lnea tendr el nombre, apellido y nmero de telfono de cada contacto.
El programa debe permitir mostrar la agenda, aadir un nuevo contacto y eliminarlo. Al terminar, el
programa guardar automticamente los datos actualizados en el mismo fichero del que los ley. Asimismo
permitir buscar contactos por nombre, apellido o telfono, mostrando los contactos que coincidan. Tambin
permitir ordenar la agenda en orden alfabtico creciente segn nombre, apellido o telfono.
9.1. Funciones
Directivas Estructura de datos del contacto
#include <stdio.h> typedef struct {
#include <string.h> char nombre[51];
#include <stdlib.h> char apellido[51];
#include <conio.h> int telefono;
#define MAX_NUM_CONTACTOS 200 } contacto;
// constantes del campo para buscar u ordenar
#define NOMBRE 1
#define APELLIDO 2
#define TELEFONO 3
Funciones que manejan 1 slo contacto para E/S Funciones con 2 contactos
contacto PedirContacto(); int Compara(contacto c1,contacto c2,int campo);
void MostrarContacto(contacto c); // Devuelve -1 si c1<c2, 0 si c1=c2 o 1 si c1>c2
// para E/S por fichero de texto, pasando un fichero
abierto como parametro
void Intercambia(contacto* c1,contacto* c2);
int LeerContacto(FILE* f,contacto* c);
// Para ordenamiento de la agenda
int GuardarContacto(FILE* f,contacto c);

Funciones que manejan toda la agenda


int LeerAgenda(char fichero[],contacto agenda[],int* void OrdenarAgenda(contacto agenda[],int num,int
num); campo);
int GuardarAgenda(char fichero[],contacto /* ordena la agenda en funcion del "campo" */
agenda[],int num);
/* leer y guardar los datos en el fichero */
void NuevoContacto(contacto agenda[],int*
num,contacto c);
void MostrarAgenda(contacto agenda[],int num); /* Aade el contacto "c" a la agenda */
/* Muestra la agenda por pantalla */
void EliminarContacto(contacto agenda[],int* num,int
i);
void BuscarContacto(contacto agenda[],int
num,contacto c,int campo); /* Elimina el contacto numero "i" de la agenda */
/* Busca los contactos cuyo "campo" coincida con el
contacto "c" pasado como parmetro */

Programacin en C 66
Funciones de E/S basica y menus del programa
int Menu(); // menu principal void ImprimePie(); // linea de guiones; formato
int MenuCampo(); // seleccionar un campo void Buscar(contacto agenda[],int num);
void ImprimeCabecera(); // significado de campos // funcion auxiliar para datos de contacto a buscar
Funcin principal
void main () {
contacto agenda [MAX NUM CONTACTOS];
int num_contactos=0, opcion=0, n;
contacto c; // la estructura (registro)
// se leen los datos del fichero
LeerAgenda("Agenda.txt",agenda,&num_contactos);
do {
opcion=Menu();
switch(opcion) {
case 0: printf("Saliendo del programa\n");
break;
case 1: MostrarAgenda(agenda,num_contactos);
break;
case 2: c=PedirContacto();
NuevoContacto(agenda,&num_contactos,c);
break;
case 3: Buscar(agenda,num_contactos);
break;
case 4: n=MenuCampo();
OrdenarAgenda(agenda,num_contactos,n);
MostrarAgenda(agenda,num_contactos);
break;
case 5: MostrarAgenda(agenda,num_contactos);
printf("Numero de contacto a eliminar: "); scanf("%d",&n);
EliminarContacto(agenda,&num_contactos,n);
MostrarAgenda(agenda,num_contactos);
break;
}
// detalles para una salida mas limpia
printf("Pulse una tecla para continuar ... \n");
getch(); // espera a que se pulse una tecla
system("cls");
} while(opcion!=0);
// al salir, el programa guarda automaticamente el fichero
GuardarAgenda("Agenda.txt",agenda,num_contactos);
}

Programacin en C 67
9.2.1. Gestin de 1 contacto
Estas funciones permiten mostrar o solicitar al usuario que teclee los datos de un contacto y guardarlos o
leerlos de un fichero previamente abierto. Las funciones de E/S de fichero, necesitan como parmetro el
puntero a FILE, abierto previamente con fopen (). Si el fichero no ha sido abierto, o la lectura o escritura
falla, la funcin correspondiente (fprintf o fscanf) devolver algo distinto al nmero de campos correctamente
gestionados, en este caso 3.
PedirContacto MostrarContacto
contacto PedirContacto() { void MostrarContacto(contacto c) {
contacto c; printf("%-20s %-20s %-
10d\n",c.nombre,c.apellido,c.telefono);
fflush (stdin);
}
printf("Nombre: "); gets(c.nombre);
printf("Apellido: "); gets(c.apellido);
printf("Telefono: "); scanf("%d", &c.telefono);
return c;
}
LeerContacto GuardarContacto
int LeerContacto(FILE* f,contacto* c) { int GuardarContacto(FILE* f,contacto c) {
if(f==NULL) return 0; // si f no est abierto if (f==NULL) return 0;
if(3==fscanf(f,"%s %s %d",c->nombre,c- if(3==fprintf(f,"%s %s %d\n" ,c.nombre, c.apellido,
>apellido,&c->telefono)) return 1; // se lee bien c.telefono)) return 1;
return 0; return 0;
} }
9.2.2. Gestin de 2 contactos
Estas funciones operan sobre 2 contactos pasados como parmetros. La primera, Intercambia, pasa los
parmetros por referencia para modificar sus valores. Es necesaria para ordenar la agenda.
La segunda, Compara es similar a strcmp de <string. h>, devolviendo -1, 0 1 en funcin del orden lgico
de los contactos pasados como parmetros. En funcin del campo se analiza uno u otro miembro de la
estructura. Comparar contactos es mejor que comparar telfonos o nombres, ya que permitira con algunas
modificaciones realizar bsquedas con condiciones compuestas del tipo "Nombre=X y Apellido=Y".
Intercambia Compara
void Intercambia(contacto* c1,contacto* c2) { int Compara(contacto c1,contacto c2,int campo) {
contacto aux=*c1; if(campo==NOMBRE)
*c1=*c2; return strcmp(c1.nombre,c2.nombre);
*c2=aux; if(campo==APELLIDO)
} return strcmp(c1.apellido,c2.apellido);
if(campo==TELEFONO) {
if(c1.telefono<c2.telefono) return -1;
else if(c1.telefono>c2.telefono) return 1;
else return 0;
}
return -1;
}
9.2.3. Gestin de la agenda
Las siguientes funciones realizan operaciones que afectan a la agenda completa pasada como parmetro a
cada una de las funciones..

Programacin en C 68
Mostrar agenda Buscar Contacto (coincidencia con c)
void MostrarAgenda(contacto agenda[],int num) { void BuscarContacto(contacto agenda[],int
num,contacto e, int campo) {
int i;
int i;
ImprimeCabecera();
ImprimeCabecera();
for(i=0;i<num;i++) {
for(i=0;i<num;i++) {
printf("%d: ",i); //n de contacto
if(0==Compara(agenda[i],c,campo)) // se ha
Mostrarcontacto(agenda[i));
encontrado {
ImprimePie();
printf("%d: ",i); //mostramos el numero de
} contacto
MostrarContacto(agenda[i]);//y luego el
contacto
}
}
ImprimePie();
}
Ordenar Agenda (en funcin del campo) Aadir nuevo contacto
void OrdenarAgenda(contacto agenda[],int num,int void NuevoContacto(contacto agenda[),int*
campo) { num,contacto c) {
int i,j; int n=*num;
for(i=0;i<num-1;i++) { if(n<MAX_NUM_CONTACTOS) { // no supera max
for(j=i+1;j<num;j++) agenda[n]=c; // se aade
if(0<Compara(agenda[i),agenda[j],c n++; // incrementa el numero de contactos
ampo)) Intercambia(&agenda[i],&agenda[j]);
*num=n; // actualiza parametro por
} // si desorden referencia
} }
Eliminar Contacto Guardar en la Agenda
void EliminarContacto(contacto agenda[],int* num,int int GuardarAgenda(char fichero[],contacto
n) { agenda[),int num) {
int i; int i;
int numero=*num; FILE* f=fopen(fichero,"w");
if(n<0 || n>= numero) return; if ( f==NULL) return 0;
// n de contacto debe existir, si no, no se hace nada for(i=0;i<num;i++) GuardarContacto(f,agenda[i));
for(i=n;i<numero-1;i++) { // mueve una posicion fclose(f);
agenda[i]=agenda[i+1]; return 1;
*num=numero-1; // n contactos disminuye 1 }
}
}
Leer Agenda
int LeerAgenda(char fichero[),contacto agenda[],int* num) {
contacto contact; // var temporal para leer del fichero
int n=0; // n de contactos leidos
FILE* f=fopen(fichero,"r");

Programacin en C 69
if(f==NULL) return 0;
while(LeerContacto(f,&contact)) { agenda[n]=contact; n++; }
*num=n; // actualiza n contactos
fclose(f);
return 1;
}
9.2.4. Gestin de E/S
Se definen funciones auxiliares: la presentacin del men de opciones, seleccin del usuario y la
comprobacin, as como un par de funciones de formato de cabecera y pie. Tambin se incluye una funcin
auxiliar para la bsqueda de contactos, que se encarga de pedir al usuario lo que hay que buscar, para
luego llamar a BuscarContacto, que efectivamente realiza la bsqueda.
Men Men Campo
int Menu() { int MenuCampo() { // selecciona un campo
int opcion=-1; int opcion=-1;
printf (" Menu Principal\n") ; printf("Seleccione campo \n");
printf("0. Salir\n"); printf("%d-Nombre\n",NOMBRE);
printf("1. Mostrar agenda\n"); printf ("%d-Apellido\n" ,APELLIDO);
printf("2. Nuevo contacto\n"); printf("%d-Telefono\n",TELEFONO);
printf("3. Buscar contacto\n"); do {
printf("4. 0rdenar agenda\n"); printf("Elija una opcion: "); fflush (stdin);
printf("5. Eliminar contacto\n"); scanf("%d",&opcion);
do { } while(opcion<NOMBRE || opcion>TELEFONO);
printf("Elija una opcion: "); fflush(stdin); return opcion;
scanf("%d",&opcion); }
} while(opcion<0 || opcion>6);
return opcion;
}
Buscar Contacto Imprimir Cabecera
void Buscar(contacto agenda[],int num) { void ImprimeCabecera(){
contacto c; printf("---------------------------------------------------\n");
int campo=MenuCampo(); printf("i: %-20s %-20s %-
10s\n","Nombre","Apellido","Telefono");
switch(campo) {
printf("---------------------------------------------------\n");
case NOMBRE: printf("Nombre a buscar: ");
}
scanf("%s",c.nombre); break;
case APELLIDO:printf("Apellido a buscar: "); Imprimir Pie
scanf("%s",c.apellido);break; void ImprimePie()
case TELEFONO:printf("Tfno. a buscar: "); { printf("------------------------------------------------\n");}
scanf("%d",&c.telefono);
break;
BuscarContacto(agenda,num,c,campo);
}

Programacin en C 70
9.3. Resultado
El programa no usa variables globales, ya que su uso no es recomendable. Las variables que contienen los
datos de la agenda, son locales a la funcin rnain y se pasan como parmetro a las funciones que las
precisan. Esto permite, con modificaciones, por ejemplo, que el programa gestione 2 agendas de 2
personas diferentes. Se muestran salidas de ejemplo de ejecucin del programa.
Men principal Mostrar Agenda
------------------------------------------------
Menu Principal #:Nombre Apellido Telefono
0. Salir ------------------------------------------------
1. Mostraragenda 0: Diego Alonso 92123098
2. Nuevo contacto 1: Jaime Gomez 92478494
3. Buscar contacto 2: Manolo Benltez 933453453
4. Ordenar:agenda 3: Arturo Femandez 913456789
5. Eliminar contacto 4: Maria Gonzalez 91234567
Elija una opclon: 1 5: Lucia Femandez 911234567
6: Isabel Rodrtguez 91234567
------------------------------------------------
Pulse una tecla para continuar...
Buscar Contacto Ordenar Agenda
......... Seleccione campo...........
1. Nombre
......... Seleccione campo........... 2. Apellldo
1. Nombre 3. Telefono
2. Apellldo Elija una opclon: 1
3. Telefono Apellido a buscar: Femandez
Elija una opclon: 2 ------------------------------------------------
Apellido a buscar: Femandez #:Nombre Apellido Telefono
------------------------------------------------ ------------------------------------------------
#:Nombre Apellido Telefono 0: Arturo Femandez 913456789
------------------------------------------------ 1: Diego Alonso 92123098
3: Arturo Femandez 913456769 2: Isabel Rodriguez 91234567
5: Lucia Femandez 911234567 3: Jaime Gomez 92476494
------------------------------------------------ 4: Lucia Femandez 911234567
Pulse una tecla para continuar 5: Manolo Benitez 933453453
6: Maria Gonzalez 91234567
------------------------------------------------
Pulse una tecla para continuar

Programacin en C 71
BIBLIOGRAFIA
Curso de programacin C/C++, Ceballos, Francisco Javier, Ra-ma, 1995
El lenguaje de programacin C, Kemighan, Brian W. y Ritchie, Dennis M., Prentice Hall Hispanoam., 1991
A Book on C. Programming in C, Fouth Edition. Al Kelley, Ira Pohl. Addison-Wesley. 1997
Programacin estructurada en CAntonakos J.L. , Mansield Jr., KC. Prentice Hall, 1997
Programacin en Microsoft C, Robert Lafore - Grupo Waite, Anaya, 1990
Programacin en C/C++, Pappas, Ch.H y Murray, V.H. Anaya, 1966
Visual C++ 6.0 Manual del programador. Beck Zaratian. McGraw Hill. 1999
Aprendiendo Borland C++ 5, Amush, C. Prentice Hall, 1997

Recursos web:
http://www.tayuda.com/avudainf/index.htm
http://www.lab.dit.upm.es/~lprg/material/apuntes/index.html
http://w\vw.clai.upm.es/
https://www.fdi.ucm.es/profesor/fernan/DBD/

Programacin en C 72

También podría gustarte