Anda di halaman 1dari 165

AAPPU

UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

1. Estructura de un programa en C.
Ejemplo 1: rea de un crculo (a)
/*programa para calcular el area de un circulo*/
#include <stdio.h> /* incluye el archivo de la biblioteca stdio.h donde estan definidas las funciones que
luego usa el programa */
main()
/* es la funcion principal del programa. Todos los programas en C incluyen esta
linea*/
{
float radio, area;
/* declaracion de variables*/
printf("Radio = ?");
/* instruccion de salida */
scanf("%f", &radio);
/* instruccion de entrada */
area = 3.14159 * radio * radio;
/* instruccion de asignacion*/
printf("Area = %f", area);
/* instruccion de salida*/
}
Comentarios:
1.1 Forma de comentar lneas en C
1.2 La instruccin #include <stdio.h>: Sirve para incluir las definiciones de las funciones de la biblioteca
que luego utilizaremos.
1.3 Main(): Existe en todos los programas en C. Main(), void main() [no devuelve nada], int main()
[espera que devuelva 0, si todo ha ido bien]. Al final, return 0.
1.4 Mayscula/minsculas.
1.5 printf; scanf
Ejemplo 2: El mismo programa, utilizando la funcin procesar que se encarga de llevar a cabo los
clculos. Incluye la constante simblica PI.
/* programa para calcular el area de un circulo*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en coma
flotante y devuelve un argumento tambien en coma flotante*/
main()
{
float radio, area; /* declaracion de variables*/
printf("Radio = ? ");
scanf("%f", &radio);
area = procesar(radio);
printf("Area = %f", area);
-1-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
float procesar(float r)

/* definicion de funcion*/

{
float a;
a = PI * r * r;
return(a);

/* declaracion de variable local*/

}
Ejemplo 3: Lo mismo, pero en este caso se ha incluido una sencilla rutina que comprueba que el radio es
positivo; en caso contrario le asigna al rea el valor 0.
{
float radio, area;
printf("Radio = ? ");
scanf("%f", &radio);
if (radio < 0)
area = 0;
else
area = procesar(radio);
printf("Area = %f", area);

/* declaracion de variables*/

/* esta condicion evita el calculo con radios negativos */

}
float procesar(float r)

/* definicion de funcion*/

{
float a;
a = PI * r * r;
return(a);

/* declaracion de variable local*/

}
Ejemplo 4: Este programa ampla los anteriores permitiendo el clculo del rea de un nmero de crculos
que especificamos al principio mediante la variable n.
/* programa para calcular el area de un circulo*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en coma
flotante y devuelve un argumento tambien en coma flotante*/
main()
{
float radio, area;
/* declaracion de variables*/
int cont, n;
/* cont es la variable contadora y n es el n de circu.*/

-2-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("numero de circulos? ");


scanf("%d", &n);
for (cont = 1; cont <= n; ++cont){ /* repite tantas veces como circulos */
printf("\nCirculo numero %d: Radio = ? ", cont);
scanf("%f", &radio);
if (radio < 0)
/* esta condicion evita el calculo con radios negativos */
area = 0;
else
area = procesar(radio);
printf("Area = %f\n", area);
}
}
Ejemplo 5: En el programa anterior podemos hacer que calcule el rea de un nmero indeterminado de
crculos, introduciendo la posibilidad de parar el proceso cuando demos al radio el valor 0.
/* programa para calcular el area de un circulo*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en
coma flotante y devuelve un argumento tambien en coma flotante*/
main()
{
float radio, area;
/* declaracion de variables*/
int cont;
/* cont es la variable contadora */
printf("Para PARAR, introduzca 0 como valor del radio\n");
printf("\nRadio = ? ");
scanf("%f", &radio);
for (cont = 1; radio != 0; ++cont){

/* repite mientras el radio sea distinto de cero. Esto evita


tener que contar*/

if (radio < 0) /* esta condicion evita el calculo con radios negativos */


area = 0;
else
area = procesar(radio);
printf("Area = %f\n", area);
scanf("%f", &radio);
}
}

-3-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

float procesar(float r) /* definicion de funcion*/


{
float a; /* declaracion de variable local*/
a = PI * r * r;
return(a);
}
Ejemplo 6: Una variante del programa anterior, esta vez utilizando la instruccin while en lugar de for.
/* programa para calcular el area de un circulo utilizando un bucle while*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en coma
flotante y devuelve un argumento tambien en coma flotante*/
main()
{
float radio, area; /* declaracion de variables*/
int cont;
/* cont es la variable contadora */
printf("Para PARAR, introduzca 0 como valor del radio\n");
printf("\nRadio = ? ");
scanf("%f", &radio);
while (radio != 0){ /* repite mientras radio sea distinto de cero*/
if (radio < 0)
area = 0;
else
area = procesar(radio);
printf("Area = %f\n", area);
scanf("%f", &radio);
}
}
float procesar(float r) /* definicion de funcion*/
{
float a; /* declaracion de variable local*/
a = PI * r * r;
return(a);
}

-4-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

En general, la instruccin while se continuar ejecutando mientras la expresin contenida en los


parntesis se considere cierta. Esto quiere decir que el programa funcionara igual si sustituysemos la
lnea
while (radio != 0)
simplemente por
while (radio)
ya que cualquier valor de radio no nulo se interpretar como una condicin cierta.
Ejemplo 7: Algunos problemas requieren que se almacene en el ordenador los resultados. Esto se puede
llevar a cabo de diferentes modos. En particular, aqu vamos a hacer uso de lo que se conocen como
formaciones. El programa hace uso de dos formaciones, llamadas radio y area, para almacenar el radio y
el rea de hasta 100 crculos. Cada formacin se puede imaginar como una lista de nmeros. Los
elementos de una formacin estarn numerados, empezando por el elemento 0. As el radio del primer
crculo se almacenar dentro del elemento de la formacin radio[0].
/* programa para calcular el area de de varios circulos. Los resultados se
almacenan en una formacion*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en coma
flotante y devuelve un argumento tambien en coma flotante*/
main()
{
int n, i = 0;
float radio[100],
area[100];

/* declaracion de variables*/
/* declaracion de formaciones*/

printf("Para PARAR, introduzca 0 como valor del radio\n\n");


printf("Radio = ? ");
scanf("%f", &radio[i]);
while (radio[i]) {
/* repite mientras radio sea distinto de cero*/
if (radio[i] < 0)
area[i] = 0;
else
area[i] = procesar(radio[i]);
printf("Radio = ? ");
scanf("%f", &radio[++i]);

-5-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
n = --i;

/*el mayor valor de i*/


/*presentamos los elementos de la formacion*/

printf("\nRelacion de resultados\n\n");
for (i = 0; i <= n; ++i)
printf("Radio = %f
Area = %f\n", radio[i], area[i]);
}
float procesar(float r) /* definicion de funcion*/
{
float a; /* declaracion de variable local*/
a = PI * r * r;
return(a);
}

Ejemplo 8: En este programa se le asigna un nombre a cada crculo. Los caracteres del nombre se
almacenan en la formacin texto. El nombre, el radio y el rea de cada crculo se definen como
componentes de una estructura. Definiremos estonces crculo como una formacin de estructuras. As,
circulo[0]. nombre hace referencia al nombre del primer crculo, circulo[0].radio al radio de dicho
crculo y crculo[0].area a su rea.
/* programa para calcular el area de de varios circulos. Los resultados se almacenan en una formacion. Se
introduce una cadena de caracteres para nombrar cada conjunto de datos*/
#include <stdio.h>
#define PI 3.14159
float procesar(float radio); /* Declaracion de variable. Indica que procesar acepta un argumento en coma
flotante y devuelve un argumento tambien en coma flotante*/
main()
{
int n, i = 0;
struct {

/* declaracion de variables*/

char texto[20];
float radio;
float area;
} circulo [10]; /*declaracion de variable tipo estructura*/

printf("Para PARAR, introduzca FIN como valor del texto\n");


printf("\nNombre: ");
scanf("%s", circulo[i].texto);
while (circulo[i].texto[0] !='F'){

-6-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("Radio = ? ");
scanf("%f", &circulo[i].radio);
if (circulo[i].radio < 0)
circulo[i].area = 0;
else
circulo[i].area = procesar(circulo[i].radio);
++i;
printf("\nNombre: "); /*Siguiente conjunto de datos*/
scanf("%s", circulo[i].texto);
}
n = --i;
/*presentar los elementos de la formacion*/
printf("\nRelacion de resultados\n\n");
for (i = 0; i <= n; ++i)
printf("%s
Radio = %f
Area = %f\n", circulo[i].texto,
circulo[i].radio,
circulo[i].area);
}
float procesar(float r)

/* definicion de funcion*/

{
float a; /* declaracion de variable local*/
a = PI * r * r;
return(a);
}

2. Conceptos bsicos de C.
P=1 [gott]
Caracteres permitidos :
+
*
/
=

<
>
(
)
[
:
;
.
,
_@
Identificadores : a) letras y dgitos (el primero siempre letra).
b) es costumbre utilizar minsculas
c) caracteres ilegales: (), (-), espacios en blanco
d) algunos compliadores solo admiten 8 (la mayora, 31)
Ejemplo 9:
identificadores vlidos

-7-

%
]
$

&
\
{

#
|
}

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

X
nombres

y12
area

suma_1
porc_imp

_temperatura
TABLA

orden-no

indicador error

identificadores incorrectos
4num

Ejemplo 10: palabras reservadas


Do
if
float
else
continue
son algunos ejemplos de palabras reservadas. Slo se pueden utilizar para su propsito ya establecido.
Ejemplo 11: Tipos de datos
Tipo de datos

int
char
float

descripcin

Requisito tpico de memoria


(determina el rango de valores
permisible)
2 bytes
1 byte
4 bytes

entero
carcter
Nmero en coma flotante (un
nmero que incluye punto decimal
y/o exponente )
Nmero en coma flotante de doble 8 bytes
precisin (ms cifras significativas
y mayor valor posible en el
exponente)

double

Algunos tipos de datos bsicos se pueden ampliar mediante el uso de calificadores:


tipo
short int x
long int x
unsigned int x

significado
Entero corto
Entero largo
Entero positivo

La declaracin unsigned int ocupa la misma memoria que int. La diferencia estriba en que en el segundo
caso el primer bit est reservado para el signo mientras que en el primero todos los bits se utilizan para
representar el valor numrico (esto permite almacenar nmeros casi el doble de grandes).
Se puede omitir la palabra int.
Ejemplo 12: Mediante el uso del operador sizeof(tipo) permite detectar el tamao de los tipos bsicos de
tu ordenador:
#include<stdio.h>
main()
{
int tipo;
printf("El tamao de tipo es %i", sizeof(tipo));

-8-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
Constantes: C tiene cuatro tipos de constantes tpicos:
a) Constantes enteras
b) Constantes en coma flotante.
c) Constantes de carcter.
d) Constantes de cadenas de caracteres.
Los tipos (a) y (b) representan nmeros (constantes de tipo numrico) y cumplen las siguientes reglas:
1. No pueden incluir comas ni espacios en blanco.
2. Pueden ir precedidas de un signo menos. (Realmente el signo es un operador que
cambia el signo de la constante que siempre es positiva).
3. El valor de una constante no puede exceder unos determinados lmites que dependen
del tipo de constante y pueden variar de un compilador a otro.
Ejemplo 13: Las siguientes constantes enteras decimales estn escritas incorrectamente:
12,245
36.0
10 20 30
123-45-6789
0900

carcter ilegal (,)


carcter ilegal (.)
carcter ilegal (espacio en blanco)
carcter ilegal (-)
el primer dgito no puede ser cero.

Siempre es posible especificar una constante entera larga aadiendo la letra L (mayscula o minscula)
al final de la constante. Siempre es posible identificar una constante sin signo aadindole la letra U
(mayscula o minscula) al final de la constante.
Ejemplo 14: Se muestran a continuacin varias constantes enteras largas y sin signo (siempre UL, nunca
LU):

50000U
123456789L
123456789UL

decimal (sin signo)


decimal (larga)
decimal (larga y sin signo)

Una constante en coma flotante es un nmero en base 10 (a diferencia de las constantes enteras, que
pueden ser octales o hexadecimales) que contiene un punto decimal o un exponente.

Ejemplo 15: Se muestran a continuacin varias constantes en coma flotante.


0.
50000.
2E-8

1.
0.000743
0.006e-3

0.2
12.3
1.6667E+8

827.602
315.0066
.12121212e12

A continuacin se especifican varias constantes que NO son en coma flotante


1
1,000.0
2.E+10.2

debe tener punto decimal o exponente


carcter ilegal (,)
el exponente debe ser una cantidad entera

-9-

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

3E 10

carcter ilegal (espacio en blanco) en el exponente

La interpretacin de una constante en coma flotante con exponente es justamente la misma que en
notacin cientfica, excepto que se sustituye la base 10 por la letra E (o e):
Ejemplo 16:

La cantidad 3
flotante:


 

1.2 10-3 = 1.2E-3


-0.1 105 = -0.1E5
105 se puede representar mediante cualquiera de las siguientes constantes en coma

300000
.3e6

3e5
0.3E6

3e+5
30E4

3E5
30.E+4

3.0e+5
300e4

Una constante de carcter es un solo carcter, encerrado con comillas simples.


A

Las constantes son almacenadas como el correspondiente cdigo (normalmente el cdigo ASCII) que las
representa.
Secuencias de escape: Representan ciertos caracteres no imprimibles, as como otros cuyo uso
especial en lenguaje C obliga a utilizar esta notacin: Siempre comienzan por una barra inclinada
hacia atrs (\):

carcter
Sonido (alerta)
retroceso
Tabulador horizontal
Tabulador vertical
Nueva lnea (avance de lnea)
Avance de pgina
Retorno de carro
Comillas ()
Comilla simple ()
Signo de interrogacin
Barra inclinada hacia atrs (\)
nulo

Secuencia de escape
\a
\b
\t
\v
\n
\f
\r
\
\
\?
\\
\0

Valor ASCII
007
008
009
011
010
012
013
034
039
063
092
000

La secuencia de escape \0 se utiliza para indicar el final de una cadena de caracteres. Hay que sealar que
la constante de carcter nulo \0 no es equivalente a la constante 0.
Constantes de cadena de caracteres: Constan de un cierto nmero de caracteres consecutivos,
encerrados entre comillas dobles.

Ejemplo 16: Se muestran a continuacin varias constantes de cadena de caracteres:


verde

Washinton, D. C. 20005

- 10 -

270-32-3456

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

$19.95
LA RESPUESTA ES
2*(I+3)/J

Lnea 1\nLnea 2\nLnea 3

Ntese que varios de los caracteres de la penltima constante son secuencias de escape que daran lugar al
siguiente resultado por pantalla:
Lnea 1
Lnea 2
Lnea 3
Ejemplo 17: La siguiente constante de cadena de caracteres incluye tres caracteres especiales:
\tPara continuar, pulsar la tecla \RETURN\\n
Los caracteres especiales son \t (tabulador), \ (comillas ) y \n (nueva lnea).
El compilador inserta automticamente un carcter nulo (\0) al final de toda constante de cadena de
caracteres, como el ltimo carcter de sta. Este carcter no aparece cuando se presenta la cadena por
pantalla, o cuando se imprime, pero est.
Ejemplo 18: El siguiente programa incluye una funcin que recibe como parmetro de entrada una cadena
de caracteres y devuelve el nmero de caracteres. Para ello utiliza una estructura while que se ejecuta
hasta identificar el carcter final de la cadena (\0).
/* programa para calcular la longitud de una cadena de
#include <stdio.h>
#define PI 3.14159
int largo(char cadena[]); /* Declaracion de variable. Indica que largo
acepta como argumento cadenas de
caracteres y devuelve un entero*/
main()
{
char cadena[50];

/* declaracion de variables*/

int respuesta;

/* cont es la variable contadora */

printf("\nIntroduzca su texto = ? ");


scanf("%s", cadena);
respuesta = largo (cadena);
printf("El texto posee %d caracteres", respuesta);
}
int largo(char cadena[])

/* definicion de funcion*/

- 11 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

{
int i = 0;
while (cadena[i++] != '\0');
return(i);

/* declaracion de variable local*/

Debemos tener en cuenta que una constante de carcter (por ejemplo A) y su correspondiente constante
de cadena (A) no son equivalentes. La primera tiene un solo carcter con un nico valor (su cdigo
ASCII, 65) mientras que la segunda contiene dos caracteres con dos cdigos (65 y 000, este ltimo de la
secuencia de escape \0 que siempre aade el compilador al final de cualquier constante de cadena de
caracteres).
Variables: Una variable es un identificador que se utiliza para representar cierto tipo de informacin
dentro de una determinada parte del programa. En su forma ms sencilla, las variables se utilizan para
representar un nico dato, es decir, una cantidad numrica o una constante de carcter. En alguna parte
del programa se le asigna un valor que, posteriormente, puede ser recuperado simplemente haciendo
referencia al nombre de la variable. Es evidente que una misma variable puede tener asignados diferentes
valores en distintas partes del programa; sin embargo, la naturaleza de los datos asignados no puede
cambiar (es decir, si una variable representa enteros, luego no puede representar constantes de caracteres
en el mismo programa).
Ejemplo 19: El siguiente programa realiza varios procesos con variables. Trata de explicar en qu
consisten:
#include <stdio.h>
main()
{
int introducciones = 0;
int numero, sumapar=0, sumaimpar=0; /*declara las variables y las pone a cero */
printf("Introduzca un numero: \n");
scanf("%d", &numero);
while(numero !=0)
{
if (numero % 2) /* el operado '%' sirve para calcular el resto del cociente de dos
numeros enteros. Si el resto es distinto de cero (esto ocurre cuando
el numero es impar), numero % 2 devolvera true y se ejecuta la
condicion*/
sumaimpar = sumaimpar + numero;
else
sumapar = sumapar + numero;
introducciones++;
printf("Introduzca un numero: \n");
scanf("%d", &numero);
}
printf("Se han introducido %d numeros", introducciones);
printf("\nLos pares suman: %d", sumapar);

- 12 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\nLos impares suman: %d", sumaimpar);


}
Ejemplo 20: Intenta realizar un programa que calcule el factorial de un nmero entero pedido por
pantalla.
/* el siguiente programa devuelve el factorial de un nmero entero*/
#include<stdio.h>
main()
{
int numero;
int cont = 0, respuesta = 0;
printf ("Introduzca un numero mayor que 1: \n");
scanf("%d", &numero);
cont = numero -1;
respuesta = numero;
while (cont != 1)
{
respuesta = respuesta*cont;
cont = cont - 1;
};
printf("Su factorial vale: %d", respuesta);
}
Formaciones: Son otra clase de variables. Una formacin hace referencia a una lista o coleccin de datos,
todos del mismo tipo. Cada uno de esos datos se distingue de los otros por la posicin que ocupa (su
ndice). Si la fomacin es unidimensional y contiene n elementos, el ndice ser un entero cuyo valor
podr variar entre 0 y n-1.
Ejemplo 21: Supongamos que deseamos almacenar la cadena Zaragoza en una formacin de caracteres
unidimensional llamada letras. Como Zaragoza contiene 8 caracteres, letras ser una formacin de 9
elementos (el ltimo representa el carcter nulo que indica el final de la cadena):
Z
0

a
1

r
2

a
3

g
4

o
5

z
6

a
7

\0
8

Discutiremos las formaciones con ms detalle en secciones sucesivas.


Declaraciones: Una declaracin asocia un tipo de datos especificado a un grupo de variables. En C es
necesario declarar todas las variables antes de que aparezcan en el programa.
Ejemplo 22: Interpreta las siguientes declaraciones:
int a, b;
float raiz1, raiz2;
char indicador, texto[80];

- 13 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

short int c;
short d;
long int f;
long g;
unsigned x, y;
Obsrvese que las variables sin signo x e y pueden representar valores aproximadamente dos veces
mayores que a y b. Sin embargo, x e y no pueden representar cantidades negativas.
Se le pueden asignar valores iniciales a las variables dentro de la declaracin de tipo, tal y como ocurre
con las variables contador y estrella:
int contador = 0;
char estrella = *;
Una formacin de tipo carcter tambin puede ser inicializada mediante una declaracin:
char letras[] = Zaragoza
El tamao puede especificarse o no, dentro del corchete:
char letras[9] = Zaragoza
Expresiones: Una expresin puede ser o bien una entidad simple (una constante, una variable, un
elemento de una formacin)
letras[4]
o bien una combinacin de entidades simples relacionadas por uno o ms operadores.
Ejemplo 23 : Se muestran a continuacin algunas expresiones sencillas:
a+b
x=y
c=a+b
x <= y
x == y
++i

/*representa la suma de los valores asignados a a y b*/


/*asigna a y el valor de x*/
/*asigna a c el valor de la expresin (a+b)*/
/*vale 1 (cierto) si el valor de la x es menor o igual al de y. Vale 0 (falso) en caso
contrario*/
/* vale 1 (cierto) si el valor de x coincide con el de y y 0 (falso) en caso contrario.
Comprese esta expresin con la segunda*/
/*hace que el valor de la i se incremente en 1. Es equivalente a la siguiente
expresin*/

i = i-1
Instrucciones: Una instruccin hace que el ordenador efecte alguna accin. En C existen tres tipos de
instrucciones:
tipo
de expresin

sintaxis
expresin1 ;

ejemplo
a =3;
c = a + b;

- 14 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

la ejecucin hace que se evale la ++i;


printf(Area = %f, area);
expresin.
;
{
{
instruccin1;
pi = 3.141593;
instruccin2;
circunferencia = 2 * pi * radio;
instruccin3;
area = pi * radio * radio
...
}
}
intruccin () {
while ( cont <= n) {
subinstruccin1;
printf(x= );
subinstruccin2;
scanf(%f, &x);
...
suma = suma + x
}
++cont;
}

compuestas

de control

Constantes simblicas: Son nombres que se definen una vez, al principio del programa, y sustituyen una
secuencia de caracteres. Se escriben en maysculas para distinguirlos del resto de los identificadores en
C. Obsrvese que las definiciones no acaban en punto y coma.
Ejemplo 24 :
#define INTERESES 0.23
#define PI 3.141593
#define TRUE 1
#define AMIGA Susana
La sustitucin de texto por una constante simblica ser llevada a cabo en cualquier parte del programa
que se encuentre despus de #define, excepto dentro de una cadena de caracteres.
Ejemplo 25 : Obsrvese la diferencia entre las dos apariciones de CONSTANTE:
#define CONSTANTE 6.023E23
printf(CONSTANTE = %f, CONSTANTE);
En el primer caso, va entre comillas y no es sustituido, no as en el segundo.

3. Operadores y expresiones.
Tipo
aritmticos

monarios

Operador
+
*
/
%
-

Propsito
suma de dos nmeros
resta de dos nmeros
producto de dos nmeros
cociente de dos nmeros
resto de dos nmeros enteros
un signo menos delante de una
constante positiva (en C todas lo son)

- 15 -

Ejemplo/ int a = 10, b = 3;


a+b
a-b
a*b
a/b
a%b
-a

Valor
13
7
30
3
1
-10

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

++

<

hace que su operando incremente su


valor en uno
hace que su operando decremente su
valor en uno
devuelve el nmero de bytes asignado a
un dato
menor que

<=
>
>=
==

menor o igual que


mayor que
mayor o igual que
igual que

if (b == a)

!=

distinto que

if (b != a)

&&

y (lgico)

if ((b != a) && (4 *b < a ))

||

o (lgico)

(a > b) || (b > a)

negacin de lo que viene despus

!(a > b)

se asigna el valor de una expresin a un


identificador
expr1 += expr2
(equivale a expr1 = expr1 + expr2)
expr1 -= expr2
(equivale a expr1 = expr1 - expr2)
expr1 *= expr2
(equivale a expr1 = expr1 * expr2)
expr1 /= expr2
(equivale a expr1 = expr1 / expr2)
expr1 -= expr2
(equivale a expr1 = expr1 % expr2)
expr1 ? expr2 : expr3

suma = a * b

-sizeof
lgicos

asignacin

+=
-=
*=
/=
%=
condicional

?:

++a
es equivalente a a = a +1
--a
es equivalente a a = a -1
printf(Caracter: %d, sizeof b)
printf(Caracter: %d, sizeof (char))
if (b < a)
.....
else
.....;

a += b
(equivale a a = a + b)
a -= b
(equivale a a = a - b)
a *= b
(equivale a a = a * b)
a /= b
(equivale a a = a / b)
a %= b
(equivale a a = a %b)
(a < b) ? 0 : 100

Conversin de tipos en operaciones aritmticas: Cuando se realiza una operacin aritmtica entre
operandos de diferente tipo, estos pueden sufrir una conversin en el proceso. El resultando final, en
general, se expresar con la mayor presicin posible, de forma consistente con los tipos de los operandos,
tal y como se puede apreciar en el siguiente ejemplo:
Ejemplo 26 : Dado el siguiente encabezado de programa
#include <stdio.h>
int i = 7;
float f = 5.5;

- 16 -

11
10
2
1
(cierto)

0
(falso)
1
(cierto)
0
(falso)
1
(falso)
0
(falso)
30
13
7
30
3.33333
1
100

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

char c = w
main()
{
}
intenta deducir de qu tipo acabarn siendo las siguientes expresiones cuando sean evaluadas:
Expresin
i+f
i+c
i + c c
(i + c) (2 * f / 5)

Valor
12.5
126
78
123.8

Tipo
doble precisin
entero
entero
doble precisin

Obsrvese que la w se codifica como 119 y el 0 como 48 (ASCII). SI se desea se puede convertir el valor
resultante de una expresin a un tipo de datos diferente:
((int) (i+f)) % 4
y dara 3. Sin la conversin a entero la expresin no sera vlida.
Ejemplo 27 : Un programa en C incluye una variable entera i, cuyo valor inicial es 1. Supongamos que el
programa incluye tres instrucciones printf:
Opcin a

Opcin b

#include<stdio.h>
main()
{
int i = 1;
printf("i = %d\n", i);
printf("i = %d\n", ++i);
printf("i = %d\n", i);
}
Por pantalla aparece el siguiente resultado:

#include<stdio.h>
main()
{
int i = 1;
printf("i = %d\n", i);
printf("i = %d\n", i++);
printf("i = %d\n", i);
}

i=1
i=2
i=2

i=1
i=1
i=2

Qu diferencia aprecias?
Otro operador monario interesante es sizeof. Sirve para calcular el nmero de bytes asignados al operando
sobre el que acta (vase el ejemplo 12).
Ejemplo 28 : Evala las siguientes expresiones:
variables
i = 1; j = 2 ; k = 3

Expresin
i<j
(i + j) >= k

- 17 -

Interpretacin
cierto
cierto

valor
1
1

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

i = 7; f = 5.5 ; c = w (119 ASCII)

(j + k) > (i + 5)
j == 2
f>5
(i + f) <= 10
c == 119
c != p
c >= 10 * (i + f)
(i >= 6) && (c == w)
(i >= 6) || (c == 119)
(f < 11) && (i > 100)
(c != p ) || ((i + f) <= 10)
i <= 3
!(i <= 3)
!(i > (f+1))
!(f > 5)

falso
cierto
cierto
falso
cierto
cierto
falso
cierto
cierto
falso
cierto
falso
cierto
falso
falso

0
1
1
0
1
1
0
1
1
0
1
0
1
0
0

Conversin de tipos en operaciones de asignacin: Cuando se realiza una operacin de asignacin entre
un operando y una variable de diferente tipo se puede producir una conversin automtica de tipos. En
particular:
a) Un valor en coma flotante puede ser truncado si es asignado a un identificador entero.
b) Un valor de doble precisin puede ser redondeado si se asigna a un identificador en
coma flotante (de simple precisin).
c) Una cantidad entera puede ser alterada si es asignada a un identificador ms corto o a un
identificador de carcter (se pueden perder algunos bits significativos).
Ejemplo 29: En las siguientes expresiones de asignacin, supongamos que i y j son variables de tipo
entero y que j = 5.
Expresin
Valor
i = 3.3
3
i = 3.9
3
i = -3.9
-3
i=j
5
i = j/2
2
i=2*j/2
5
i = 2 * (j / 2)
3
Ejemplo 30: En las siguientes expresiones de asignacin, supongamos que i y j son variables de tipo
entero tales que i = 5, j = 7. f y g son variables en coma flotante cuyos valores son 5.5 y 3.25,
respectivamente.
Valor
Expresin
Expresin equivalente
i += 5
i=i+5
10
f -= g
f=fg
8.75
j *= (i - 3)
j = j * (i - 3)
14
f /= 3
f=f/3
1.8333333
i % = (j 2)
i = i % (j - 2)
0
Ejemplo 31: Supongamos que x, y y z son variables enteras que tienen asignados los valores 2, 3 y 4,
respectivamente. La expresin

- 18 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

x *= -2 * (y + z) / 3
es equivalente a la expresin
x = x * (-2 * (y + z) / 3 ).
Ambas expresiones harn que a la variable x se le asigne el valor 8.
Ejemplo 32: Discute el resultado de las siguientes expresiones
a)
b)
c)
d)
e)
f)

(i < 0) ? 0 : 100;
(f < g) ? f : g;
(f < g) ? i : f;
indicador = (i < 0) ? 0 : 100;
min = (f < g) ? f : g;
c ++= (a > 0 && a <= 10) ? ++a : a /b;

4. Funciones de biblioteca.
El lenguaje C a veces hace uso de ciertas funciones que no forman parte del lenguaje en s, pero que
debido a su uso frecuente lo parecen. Son las llamadas funciones de biblioteca. Suelen agruparse por
temas, en archivos separados (archivos de biblioteca), que son proporcionados junto con el compilador.
En la siguiente tabla se recogen algunas de las ms frecuentes. La columna tipo indica el tipo de datos que
devuelve la funcin. Cuando aparece void quiere decir que la funcin no devuelve nada.
funcin

tipo

abs (i)
acos (d)
ceil(d)

int
double
double

cos(d)
cosh(d)
exp(d)
fabs(d)
floor(d)

double
double
double
double
double

fmod(d1,d2)
getchar()
isalnum(c)

double
int
int

isalpha(c)

int

isascii(c)

int

isdigit(c)

int

propsito

archivo
(include)
devuelve el valor absoluto de i
stdlib.h
devuelve el arco coseno de d
math.h
redondea por exceso al entero ms prximo (el entero ms pequeo math.h
que sea mayor o igual a d)
devuelve el coseno de d
math.h
devuelve el coseno hiperblico de d.
math.h
eleva e a la potencia d-sima.
math.h
devuelve el valor absoluto de d.
math.h
redondea por defecto al entero ms prximo (el entero ms grande que math.h
no sea mayor que d).
devuelve el resto de la divisin d1/d2, con el mismo signo que d1.
math.h
introduce un carcter desde el dispositivo de entrada estndar.
stdio.h
determina si el argumento es alfanumrico. Devuelve un valor distinto ctype.h
de cero si es cierto; en otro caso devuelve 0.
determina si el argumento es alfabtico. Devuelve un valor distinto de ctype.h
cero si es cierto; en otro caso devuelve 0.
determina si el argumento es un caracter ASCII. Devuelve un valor ctype.h
distinto de 0 si es cierto; en otro caso devuelve 0.
determina si el argumento es un dgito decimal. Devuelve un valor ctype.h
distinto de cero si es cierto; en otro caso devuelve 0.
- 19 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

islower(c)

int

isupper(c)

int

log(d)
pow(d1,d2)
printf(...)
putchar(c)
rand()
scanf(...)
sin(d)
sqrt(d)
srand(u)
strlen(s)
tan(d)
toascii(c)
tolower(c)

double
double
int
int
int
int
double
double
void
int
double
int
int

determina si el argumento es una minscula. Devuelve un valor


distinto de cero si es cierto; en otro caso devuelve 0.
determina si el argumento es un caracter ASCII imprimible (hex 0x20
0x7e; octal 040-176). Devuelve un valor distinto de cero si es cierto;
en otro caso devuelve 0.
determina si el argumento es una mayscula. Devuelve un valor
distinto de cero si es cierto; en otro caso devuelve 0.
devuelve el logaritmo en base 10 de d.
devuelve d1 elevado a la potencia d2-sima.
enva datos al dispositivo de salida estandar.
manda un carcter al dispositivo de salida estndar.
devuelve un entero positivo aleatorio.
introduce datos desde el dispositivo de entrada estndar.
devuelve el seno de d.
devuelve la raiz cuadrada de d.
inicializa el generador de nmeros aleatorios.
devuelve el nmero de caracteres de una cadena.
devuelve la tangente de d.
convierte el valor del argumento a ASCII.
convierte una letra a minscula.

toupper(c)

int

convierte una letra a mayscula.

isprint(c)

ctype.h
ctype.h

ctype.h
math.h
math.h
stdio.h
stdio.h
stdlib.h
stdio.h
math.h
math.h
stdlib.h
string.h
math.h
ctype.h
ctype.h
stdlib.h
ctype.h
stdlib.h

Ejemplo 33: El siguiente programa hace uso de la funcin de biblioteca sqrt() para hallar las raices de una
ecuacin de 2 grado:
#include<stdio.h>
#include<math.h>
main()
{
float a, b, c, raiz, x1, x2;
printf ("La ecuacion es de la forma a*x^2 + b*x +c");
printf ("\nIntroduzca el coeficiente a: \n");
scanf("%f", &a);
printf ("Introduzca el coeficiente b: \n");
scanf("%f", &b);
printf ("Introduzca el coeficiente c: \n");
scanf("%f", &c);
raiz = sqrt(b * b -4 * a * c);
x1 = (-b + raiz)/ (2* a);
x2 = (-b - raiz)/ (2* a);
printf("Las raices de la ecuacion son %f, %f", raiz, x2);
}

6. Entrada y salida de datos.

- 20 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 35: El siguiente esquema ilustra el uso de varias rutinas de entrada/salida de datos de la
biblioteca estandar de C.

#include <stdio.h>
main()
{
char c, d;
float x, y;
int i, j, k;
c = getchar();
scanf(%f, &x);
scanf(%d %d, &i, &j);
...
putchar(d);
printf(%3d %7.4f, k, y);

/*entrada de un carcter*/
/*entrada de un nmero en coma flotante*/
/*entrada de enteros*/
/*instrucciones de accin*/
/*salida de un carcter*/
/*salida de nmeros*/

}
getchar(): Mediante esta funcin de biblioteca se introducen caracteres uno a uno. En principio no
necesita argumentos, aunque siempre debe ir seguida de un par de parntesis vacos. En general se escribe
as:
x = getchar();
donde x es una variable tipo carcter, previamente declarada. Si se encuentra una condicin de fin de
archivo (end of file) cuando se est leyendo un carcter con esta funcin, sta devolver de forma
automtica el valor de la constante simblica EOF. (Este valor se define dentro del archivo stdio.h.
Normalmente EOF tendr asignado el valor 1, aunque puede variar de un compilador a otro.) Ms
adelante estudiaremos cmo se puede combinar este hecho con el uso de la instruccin if-else para
detectar el final de un archivo.
putchar(): Mediante esta funcin de biblioteca se visualizan caracteres uno a uno (es complementaria a
la anterior). En general se escribe as:
putchar(x);
donde x es una variable tipo carcter, previamente declarada, que queremos visualizar.
Ejemplo 34: El siguiente programa hace uso de las funciones getchar(), toupper() y putchar() para leer
una minscula y escribir una mayscula:
#include <stdio.h>
#include <ctype.h>
main()
{
int minusc, mayusc;
minusc = getchar();

- 21 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

mayusc = toupper(minusc);
putchar(mayusc);
}
Tambin se puede utilizar putchar para visualizar una constante de cadena de caracteres almacenada
dentro de una formacin unidimensional de tipo carcter. Se pueden escribir entonces los caracteres uno a
uno (mediante una instruccin for, por ejemplo):
Ejemplo 36: El siguiente programa lee una lnea de texto en minsculas, la almacena en una formacin de
tipo carcter unidimensional y despus la escribe en maysculas:
#include <stdio.h>
#include <ctype.h>
main()
{
char letras[80];
int cont, auxiliar;
/*lectura de la linea*/
for (cont = 0; (letras[cont]= getchar()) != '\n'; ++cont); /* el bucle comienza asignando a cont el valor
0. Lee un carcter del teclado y se lo asigna
a letras[0]. Repite para el siguiente siempre
y cuando no se introduzca el carcter de
nueva lnea (\n)*/
/*asignamos a auxiliar el ultimo valor del contador*/
auxiliar = cont;
/*escribimos la linea en mayusculas*/
for (cont = 0; cont < auxiliar; ++cont)
putchar (toupper(letras[cont])) ;
}
scanf(): Mediante esta funcin de biblioteca se pueden introducir en el ordenador cualquier combinacin
de valores numricos, caracteres sueltos y cadenas de caracteres. En general se escribe as:
scanf( cadena de control, arg1, arg2, ..., argn);
donde cadena de control es a una cadena de caracteres que hace referencia al formato de los datos leidos y
arg1, arg2, ..., argn son argumentos que representan los datos. (En realidad, los argumentos representan
punteros que indican las direcciones de memoria en las que se almacenan los datos. Lo estudiaremos ms
adelante). En la cadena de control debe aparecer un grupo de caracteres por cada dato de entrada.
Normalmente cada uno de estos grupos comienza por el signo % seguido de un caracter que indica el tipo
de dato que va a ser leido (caracter de conversin). Los grupos pueden ir seguidos o separados por
espacios en blanco.
Ejemplo 37: El siguiente esquema ilustra una aplicacin tpica de la funcin scanf:
#include <stdio.h>

- 22 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

main()
{
char concepto[20];
int no_partida;
float coste;
scanf("%s %d %f", concepto, &no_partida, &coste);
printf("%s %d %f", concepto, no_partida, coste);
}
La cadena de control es, en este caso %s %d %f . Continen tres grupos de caracteres. El primero, %s,
indica que concepto es una cadena de caracteres. El segundo, %d, que no_partida es un entero decimal y
el tercero, %f, que coste es un valor en coma flotante. Los caracteres de conversin ms frecuentes son
los que aparecen descritos en la siguiente tabla:
carcter de
conversin
c
d
e
f
g
h
i
o
s
u
x
[...]

significado
el dato es un carcter
el dato es un entero decimal
el dato es un valor en coma flotante
el dato es un valor en coma flotante
el dato es un valor en coma flotante
el dato es un entero corto
el dato es un entero decimal, octal o hexadecimal
el dato es un entero octal
el dato es una cadena de caracteres seguida de un carcter de espaciado (se aade
automticamente el carcter nulo \0 al final)
el dato es un entero decimal sin signo
el dato es un entero hexadecimal
el dato es una cadena de caracteres que puede incluir caracteres de espaciado (se
explicar mas adelante)

Obsrvese que el nombre de los argumentos debe ir precedido siempre del smbolo &, excepto si se trata
del nombre de una formacin. En el ejemplo 37, el resultado es el mismo si no hubiramos intercalado
espacios en blanco entre los grupos de caracteres:
scanf("%s%d%f", concepto, &no_partida, &coste);
Si la cadena de control comienza por la lectura de un dato tipo carcter, suele ser conveniente que el
primer carcter de conversin aparezca precedido de un espacio en blanco.
scanf(" %s %d %f", concepto, &no_partida, &coste);
Esto evita la asignacin a concepto de caracteres extraos que se pueden haber introducido con
anterioridad.

- 23 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 38: La conversin de caracteres tipo se aplica a aquellas cadenas que acaban en un carcter de
espaciado. Por lo tanto, si deseo asignar a la formacin concepto una cadena que incluya espacios en
blanco tendr que utilizar un procedimiento distinto. Una forma sera utilizar la funcin getchar() dentro
de un bucle, como ya se estudi en el ejemplo 36. Otro mtodo consiste en utilizar la funcin scanf,
sustituyendo la cadena de control por una secuencia de caracteres encerrados entre corchetes.:
#include <stdio.h>
main()
{
char linea[80]; /*La cadena sera de una longitud inferior a 80 caracteres, incluyendo el caracter nulo
que se aade al final*/
scanf("%[ ABCDEFGHIJKLMNOPQRSTUVWXYZ]", linea);
printf("%s", linea);
}
Si introducimos la cadena CIUDAD DE ALMERIA por el teclado, la cadena ntegra es asignada a la
formacin lnea, ya que est compuesta slo por letras maysculas y espacios en blanco. Sin embargo, si
escribo Ciudad de Almera, el programa devuelve C, ya que la primera letra minscula (en este caso la i)
se interpretara como el primer carcter a continuacin de la cadena.
Ejemplo 39: Otra forma de hacer lo mismo es mediante el uso, dentro de los corchetes, del acento
circunflejo ^:
#include <stdio.h>
main()
{
char linea[80];
scanf(" %[^\n]", linea); /*Se leen los caracteres siempre y cuando no coincidan con \n (nueva linea).
Observese el espacio en blanco entre y % con el fin de ignorar los
caracteres no deseados introducidos con anterioridad*/
printf("%s", linea);
}
No habr restricciones, salvo que la cadena ocupe ms de una lnea.
Ejemplo 40: Los caracteres consecutivos que no sean de espaciado y que componen un dato definen en
conjunto lo que llamaremos un campo. Podemos limitar la longitud mxima de los campos introduciendo
en la cadena de control un nmero sin signo entre % y el carcter de conversin:
#include <stdio.h>
main()
{
int a, b, c;;

- 24 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

scanf("%3d %2d %1d", &a, &b, &c);


printf("%d %d %d", a, b, c);
}
Si introducimos 123456 el programa devuelve 123 45 6. Si introducimos 1234 56 el programa devuelve
123 4 5 (haz tus propias pruebas hasta entender perfectamente lo que ocurre).
printf(): Esta funcin sirve para escribir cualquier combinacin de valores numricos, caracteres sueltos
y cadenas de caracteres. Es anloga a la funcin de entrada scanf. Su estructura es
printf(cadena de control, arg1, arg2, ..., argn)
donde cadena de control es una cadena de caracteres que contiene informacin sobre el formato de salida
y arg1, arg2, ..., argn son argumentos que representan los datos de salida.
Ejemplo 41: En el siguiente programa los dos primeros argumentos dentro de la funcin printf son
variables simples, el tercero es una expresin aritmtica y el ltimo es una funcin que tienen una
expresin numrica como argumento:
#include <stdio.h>
#include<math.h>
main()
{
float i = 2.0, j = 3.0;
printf("%f %f %f %f", i, j, i+j, sqrt(i+j));
}
Los espacios en blanco entre los datos son generados por los espacios en blanco que aparecen en la
cadena de control. Si escribimos la intruccin printf del siguiente modo
printf("%f%f%f%f", i, j, i+j, sqrt(i+j));
el resultado sera
2.0000003.0000005.0000002.236068
Obsrvese que, a diferencia de lo que ocurra con la funcin scanf, los argumentos no estn precedidos
del smbolo &. Esto es debido a que, para la funcin printf, los argumentos no representan direcciones de
memoria.
Ejemplo 42: Veamos la diferencia entre el carcter de conversin %f y %e:
#include <stdio.h>
main()
{

- 25 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

float x = 5000.0, y = 0.0025;


printf("%f %f %f %f\n\n", x, y, x*y, x/y);
printf("%e %e %e %e", x, y, x*y, x/y);
}
Ejemplo 43: Podemos modificar el nmero de cifras decimales que aparecen (precisin), situando un
nmero entre el smbolo % y el carcter de conversin. La precisin es un entero sin signo que siempre es
precedido de un punto decimal. Si, adems, se especifica la longitud mnima del campo, entonces el orden
es el que aparece a continuacin:
%(longitud mnima del campo).(precisin)(carcter de conversin)
Por ejemplo,
main()
{
float x = 123.456;
printf("%7f %7.3f %7.1f\n\n", x, x, x);
printf("%12e %12.5e %12.3e", x, x, x);
}
Ntese que, en la primera lnea, el tercer nmero se ha redondeado debido a que la precisin es de una
cifra decimal. Obsrvese, as mismo, que se aaden ceros cuando es necesario hasta alcanzar la longitud
de campo mnima especificada (siete caracteres). La diferencia con la funcin scanf est en que en ella el
indicador de longitud fija una longitud mxima (y no mnima como ocurre en printf).
Ejemplo 44: La funcin printf interpreta la conversin tipo s de forma distinta a como lo hace la funcin
scanf. En la funcin printf se utiliza la conversin tipo s para visualizar una cadena de caracteres que
acaba en el carcter nulo ( \0 ). Se pueden incluir espacios intercalados en la cadena. Vase la diferencia
en el siguiente ejemplo:
/*Este programa ilustra las diferencias sintacticas entre lectura y escritura*/
#include <stdio.h>
main()
{
char linea[80];
scanf(" %[^\n]", linea);
printf("%s", linea);
}
Ejemplo 45: Otro ejemplo sobre el indicador de longitud de campo:
#include <stdio.h>

- 26 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

main()
{
int i = 12345;
float x = 345.678;
printf("%3d %5d %8d\n\n", i, i, i);
printf("%3f %10f %13f\n\n", x, x, x);
printf("%3e %13e %16e", x, x, x);
}
En la primera lnea de salida aparece el mismo entero decimal tres veces seguidas con tres longitudes de
campo mnimas dsitintas (3, 5 y 8 caracteres). El valor entero completo se visualiza dentro de cada
campo, aunque la longitud sea demasiado pequea (como ocurre en el primero). El segundo valor de
salida de la primera lnea es precedido de un espacio en blanco, debido al espacio existente entre los dos
primeros grupos de la cadena de control. El tercer valor es precedido por cuatro espacios en blanco, uno
debido al existente entre los grupos correspondientes de la cadena de control y los otros tres que aparecen
para rellenar la longitud de campo mnima, mayor que el nmero de caracteres del valor de salida.
Obsrvese las situaciones anlogas en las dos lneas siguientes.
Ejemplo 46: El siguiente programa ilustra el uso de las especificaciones de longitud de campo mnimo y
precisin en la visualizacin de una cadena de caracteres. En este caso la precisin determina el mximo
nmero de caracteres que pueden ser visualizados. Si la precisin especificada es menor que el nmero
total de caracteres de la cadena, no se mostrarn los caracteres sobrantes de la parte derecha.
#include <stdio.h>
main()
{
char linea[12];
printf("Escribe una cadena de caracteres:\n");
scanf("%s", linea);;
printf("%10s %15s %15.5s %.5s", linea, linea, linea, linea);
}
Si introducimos la palabra hexadecimal el programa visualiza:
hexadecimal

hexadecimal

hexad hexad

La primera cadena se visualiza completa, aunque conste de 11 caracteres y la longitud mnima sea slo de
10. A la segunda cadena se le aaden cuatro espacios en blanco a la izquierda para satisfacer el requesito
su requisito de campo mnimo (15); de esta forma se dice que la segunda cadena est ajustada a la
derecha de su campo. La tercera cadena aparece con slo cinco caracteres, debido a la precisin (5). Sin
embargo, se han aadido 10 espacios en blanco para satisfacer la longitud mnima (15). La ltima cadena
tambin consta de 5 caracteres, pero en este caso no se han aadido espacios en blanco a la izquierda, ya
que no se ha especificado la longitud mnima.

- 27 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 47: Adems de la longitud mnima, la precisin y el caracter de conversin, cada grupo de
caracteres dentro de la cadena de control de una instruccin printf puede incluir un indicador. El
indicador, que siempre debe aparecer inmediatamente a continuacin del signo %, permite controlar
algunos detalles de la salida de un determinado dato. En la siguiente tabla aparecen los indicadores de uso
comn:
indicador
-

+
0

significado
Ajusta el dato a la izquierda dentro del campo (si se requieren espacios en blanco
para conseguir la longitud de campo mnima, se aaden despus del dato en lugar
de antes.)
Cada dato numrico es precedido por un signo (+ o -). Sin este indicador slo los
datos negativos son precedidos por el signo -.
Hace que se presenten ceros en lugar de espacios en blanco. Se aplica slo a
datos que estn ajustados a la derecha dentro de campos de longitud mayor que
la del dato.
(espacio en blanco)
Cada dato numrico positivo es precedido por un espacio en blanco. Este
indicador se ignora si se encuentra presente tambin el indicador +.
(con las conversiones de tipo o y x)
Hace que los datos octales y decimales sean precedidos por 0 y 0x,
respectivamente.
(con las conversiones de tipo e, f y g)
Hace que se presenten todos los nmeros en coma flotante con un punto, aunque
tengan un valor entero. Impide el truncamiento de los ceros de la derecha
realizada por la conversin tipo g.

El siguiente programa ilustra el uso de indicadores con cantidades enteras y en coma flotante:
#include <stdio.h>
main()
{
int i = 123;
float x = 12.0, y = -3.3;
/*Los dos puntos sirven para indicar el principio del primer campo y el final
del ultimo campo de cada linea. La primera linea ilustra como aparecen los
numeros enteros y en coma flotante sin ningun indicador. Cada numero es
ajustado a la derecha del campo.*/
printf(":%6d %7.0f %10.1e:\n\n", i, x, y);
/*La segunda linea muestra los mismos numeros, con las mismas conversiones
pero esta vez con el indicador -. Los numeros se ajustan en este caso a la
izquierda y los espacios en blanco hasta completar la longitud del campo se
aaden despues.*/
printf(":%-6d %-7.0f %-10.1e:\n\n", i, x, y);
/*La tercera linea muestra el efecto de aadir el indicador +. Los numeros se
ajustan a la derecha, como en la primera linea, pero cada numero (tanto
positivos como negativos), va precedido de su correspondiente signo.*/

- 28 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf(":%+6d %+7.0f %+10.1e:\n\n", i, x, y);


/*En la cuarta linea vemos el efecto de combinar un indicador - con un
indicador +.*/
printf(":%-+6d %-+7.0f %-+10.1e:\n\n", i, x, y);
/*La ultima linea muestra dos numeros en coma flotante, cada uno mostrado en
primer lugar sin el indicador #, y despues con el. Observese que el efecto
del indicador es incluir un punto decimal en el numero 12. (que se escribe
con la conversion de tipo f) e incluir la ristra de ceros en el numero
-3.30000 (escrito con la conversion de tipo g).*/
printf(":%7.0f %#7.0f %7g %#7g:", x, x, y, y);
}
Ejemplo 48: Los indicadores tambin pueden ser utilizados con las cadenas de caracteres, tal y como
muestra el siguiente programa:
#include <stdio.h>
main()
{
char linea[13];
printf("Escribe una cadena de caracteres:\n");
scanf("%s", linea);;
printf(":%15s %15.5s %.5s:\n\n", linea, linea, linea);
printf(":%-15s %-15.5s %-.5s:\n\n", linea, linea, linea);
}
Prueba introduciendo cadenas de distintas longitudes, siempre inferiores a doce caracteres (por ejemplo,
menor-caso, ornitorringo; qu ocurrira si introduces ornitorringos?).

7. Preparacin y ejecucin de un programa en C


Ejemplo 49: El siguiente ejemplo muestra las etapas que deben llevarse a cabo para realizar
correctamente un programa en C. En este caso el programa que queremos realizar servir para calcular la
cantidad de dinero acumulada en un banco al cabo de t aos cuando el depsito se acumula anualmente a
un cierto tanto por ciento de inters compuesto r. La respuesta a esta pregunta se puede hallar mediante el
uso de la ecuacin
Cf=C0(1+i)n

donde Cf representa el capital al cabo de los n aos, C0 el capital inicial e i es el tanto por uno de inters,
es decir
i=r/100.
El programa debera incluir las siguientes partes:
main()
- 29 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

{
/*declaracin de variables*/
/*lectura de los valores de C0, r y n (los facilita el usuario)*/
/*calculo de i a partir de r*/
/*calculo de Cf a partir de C0, n e i*/
/*Escritura de valor calculado de Cf*/
}
(Realiza el DFD del programa).
En principio todos estos pasos parecen muy sencillos. Sin embargo, alguno de ellos requieren ser
detallados un poco ms antes de la realizacin del programa. Una vez terminado el esquema, traducimos
cada uno de los apartados a su correspondientes sentencias en C:
#include <stdio.h>
#include <math.h>
main()
{
/*declaracin de variables*/
float c0, r, n, i, cf;
/*pide el valor de C0 y lee su valor*/
/*lectura de los valores de C0, r y n (los facilita el usuario)*/
/*pide el valor de r y lee su valor*/
printf("Por favor, introduce el capital inicial: ");
/*pide el valor de n y lee su valor*/
scanf("%f", &c0);
printf("Por favor, introduce el numero de aos: ");
scanf("%f", &n);
printf("Por favor, introduce el interes: ");
scanf("%f", &r);
/*calculo de i a partir de r*/
i = r / 100 ;
/*calculo de Cf a partir de C0, n e i. Utiliza la funcion de biblioteca pow*/
cf = c0 * pow((1+i), n) ;
/*Escritura de valor calculado de Cf*/
printf("\nEl capital al cabo de los %.0f aos es: %.2f\n", n, cf);
}
Ejemplo 50: Repite el proceso (incluido el diagrama DFD) para el problema de hallar las raices de una
ecuacin de segundo grado. Intenta resolver la existencia de raices negativas mediante la utilizacin de
mensajes de error:
#include <stdio.h>
#include <math.h>
main()
{
/*declaracin de variables*/
float a, b, c, x1, x2, argraiz;
/*lectura de los coeficientes del polinomio (los facilita el usuario)*/
printf("El programa sirve para resolver una ecuacion de 2 grado: a*x^2+b*x+c=0");
printf("\n\nPor favor, introduce el coeficiente a: a = ");

- 30 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

scanf("%f", &a);
printf("Por favor, introduce el coeficiente b: b = ");
scanf("%f", &b);
printf("Por favor, introduce el termino independiente c: c = ");
scanf("%f", &c);
/*calculo del argumento de la raiz*/
argraiz = b*b - 4 *a * c ;
/*calculo de las raices. Diferencia entre las raices reales y las complejas*/
if (argraiz < 0)
printf("La ecuacion tiene raices complejas. No soy capaz de calcularlas");
else
x1 = (-b+sqrt(argraiz))/(2*a);
x2 = (-b-sqrt(argraiz))/(2*a);
/*Escritura de valor calculado de Cf*/
printf("\nLas soluciones de la ecuacion propuesta son: \nx1 = %.2e, \nx2 = %.2e", x1, x2);
}

8. Instrucciones de control.
Antes de considerar las intrucciones de control ms comunes (ejecuciones condicionales, selecciones,
bucles, etc; son similares a las estudiadas en otros lenguajes de programacin), convendra repasar el uso
de los operadores <, <=, >, >=, == y !=.
Ejemplo 51: Interpreta el significado de las siguientes expresiones lgicas:
Expresin
cont <= 100
sqrt(a+b+c) > 0.005

respuesta == 0
balance >= maximo

car1 < T

letra != x
(cont <= 100) && (car1 != *)
(balance < 1000.0) || (estado == R)
(respuesta < 0) || ((respuesta > 5.0) && (respuesta <= s))
!((pagos >= 1000.0)&& (estado == s))

- 31 -

Significado
cierto si la variable numrica cont tiene
asignado un valor menor o igual que 0.
cierto si la raiz cuadrada de la suma de los
valores numricos de a, b y c es superior a
0.005.
cierto si la variable numrica respuesta
vale 0.
cierto si la variable numrica balance
tiene asignado un valor mayor igual que
el de la variable numrica maximo.
cierto si el carcter representado por car1
(variable tipo carcter) es anterior al
carcter T; esto es, si el cdigo ASCII
del carcter rpresentado por car1 es
inferior a 84.
cierta si el carcter representado por letra
no es x.
(para que lo completen los alumnos)

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

cont <= 100 && car1 != *


balance < 1000.0 || estado == R
respuesta < 0 || respuesta > 5.0 && respuesta <= 10.0
!(pagos >= 1000.0 && estado == s)
estado = (balance == 0) ? C : 0
Ejecucin condicional: if-else.
Esta instruccin bifurca la ejecucin del programa en dos posibles caminos. La eleccin de uno u otro
depende de la evaluacin de una expresin lgica.
if(expresin lgica)
instruccin 1;
[else
instruccin 2;]
Ejemplo 51: Los siguientes ejemplos muestran el uso de la instruccin if-else:
a) if (x <0) printf (%f, x);
b) if (debito > 0) credito = 0;
c) if (x <= 3.0)
{
y = 3 * pow(x, 2);
printf(%f\n, y);
}
d) if ((balance < 1000.) || (estado == R)) printf (%f, balance);
e) if((a >= 0) && (b <= 5)) {
xmid = (a + b) / 2;
ymid = sqrt(xmid);
}
f) if (estado == S)
tasa = 0.20 * pago;
else
tasa = 0.14 * pago;
g) if (debito > 0){
printf(cuenta n %d esta en numeros rojos, no_cuenta);
credito = 0;
}
else
credito = 1000.0;
h) if (x <= 3)
y = 3 * pow(x, 2);
else
y = 2 * pow((x-3), 2);
printf(%f\n, balance);
i) if (circulo ){
scanf(%f, &radio);
area = 3.14159 * radio * radio;

- 32 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf(Area del rectangulo = %f, area);


}
En el caso (a) el valor de tasa se determina de dos formas posibles, dependiendo del carcter que se le
haya asignado a la variable estado. Una forma ms concisa de hacer lo mismo es la siguiente:
tasa = (estado == S) ? (0.20 * pago) : (0.14 *pago);

Ejemplo 52: A continuacin un ejemplo de tres instrucciones if else anidadas:


#include <stdio.h>
main()
{
/*declaracin de variables*/
int hora;
printf("que hora es (introduce un valor entero)?: ");
scanf("%d", &hora);
if ((hora >= 0) && (hora < 12))
printf("Buenos dias");
else
if ((hora >= 12) && (hora < 21))
printf("Buenas tardes");
else
if ((hora >= 21) && (hora <24))printf("Buenas noches");
else
printf("Hora no valida;");
}
Bucles: while.
La instruccin while se utiliza para generar bucles, en los cuales un grupo de instrucciones se ejecuta de
forma reiterada hasta que se satisface alguna condicin.
while(expresin lgica) instruccin;
La instruccin se ejecutar mientras el valor de la expresin sea cierto (valor no nulo).
Ejemplo 53: El siguiente programa muestra por pantalla los nmeros del 1 al que el usuario indique:
#include <stdio.h>
main()
{
/*declaracin de variables*/
int n, digito = 1;
printf("Hasta que numero desea contar?: ");
scanf("%d", &n);
while (digito <= n){

- 33 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("%d\n", digito);
++digito;
}
}
Ejemplo 54: Podemos hacer los mismo mediante esta versin ms concisa del mismo programa:
#include <stdio.h>
main()
{
/*declaracin de variables*/
int n, digito = 1;
printf("Hasta que numero desea contar?: ");
scanf("%d", &n);
while (digito <= n)
printf("%d\n", digito++);
}
Ejemplo 55: En este ejemplo se lee una lnea de texto en minsculas carcter a carcter y se almacena en
una formacin llamada letras. El programa contina leyendo caracteres hasta que se lea un carcter de fin
de lnea (EOL). A continuacin se transforman los caracteres a maysculas, utilizando la funcin de
biblioteca toupper.
#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux, cont = 0;
/*leer el texto en minusculas*/
letras[cont] = getchar();
while (letras[cont] != EOL) {
cont = cont + 1;
letras[cont] = getchar();
}
/*escribir el texto en mayusculas*/
cont = 0;
while (cont < aux){
putchar (toupper(letras[cont]));
++cont;
}
}

- 34 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ntese que cont tiene asignado inicialmente el valor cero. Su valor se incrementa en 1 cada vez que el
primer bucle se ejecuta. El valor final de cont, al concluir el primer bucle, pasa a aux. El valor de aux
determina el nmero de veces que se ejecuta el segundo bloque.
Ejemplo 56: Observa las diferencias entre el programa anterior y el que viene a continuacin:
#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux, cont = 0;
/*leer el texto en minusculas*/
letras[cont] = getchar();
while (letras[cont] != EOL) {
cont = cont + 1;
letras[cont] = getchar();
}
aux =cont;
/*escribir el texto en mayusculas*/
cont = 0;
while (cont < aux){
putchar (toupper(letras[cont]));
++cont;
}
}
Hace lo mismo que el anterior, aunque escrito de esta manera resulta ms simple para aquellos alumnos
failiarizados con otros lenguajes de programacin de alto nivel (Pascal, BASIC).
Ejemplo 57: A continuacin vamos a intentar disear un programa que utilice la instruccin while para
calcular la media de una lista de n nmeros. El programa debera tener la siguiente estructura (entre todos
en la pizarra mediante un DFD):
/*Definicion e inicializacin de variables*/
/*Lectura de la longitud de la lista*/
/*Lectura de los n numeros (while)*/
/*Clculo de la media*/
/*Escritura de la respuesta*/
Comenzamos desarrollando el ncleo del programa, el apartado en el que se van leyendo los nmeros:

- 35 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/*Lectura de los n numeros (while)*/


/*Realiza los siguientes pasos de forma repetida mientras que la variable cont no sea mayor que n*/
/*Lee un numero de la lista . Cada numero se almacena en la variable de coma flotante x*/
/*Aade el valor de x al actual de suma*/
/*Incrementa en 1 el valor de cont*/
A partir de aqu ya hemos determinado las variables necesarias y el momento en el que deben ser
inicializadas. Podemos escribir el apartado de definicin de variables:
/*Definicion e inicializacin de variables*/
int n, cont = 1;
float x, media, suma = 0;
El resto de los apartados son muy simples y ya los hemos detallado en ejemplos anteriores. El programa
completo quedara del siguiente modo
#include <stdio.h>
main()
{
/*Definicion e inicializacin de variables*/
int n, cont = 1;
float x, media, suma = 0;
/*Lectura de la longitud de la lista*/
printf("Cuantos numeros vas a introducir?: ");
scanf("%d", &n);
/*Lectura de los n numeros (while)*/
/*Realiza los siguientes pasos de forma repetida mientras que la variable
cont no sea mayor que n*/
while (cont <= n){
printf("Numero %d: x =", cont);
/*Lee un numero de la lista . Cada numero se almacena en la
variable de coma flotante x*/
scanf("%f", &x);
/*Aade el valor de x al actual de suma*/
suma += x;
/*Incrementa en 1 el valor de cont*/
++cont;
}
/*Clculo de la media*/
media = suma / n;
/*Escritura de la respuesta*/
printf("\nLa media es %f\n", media);
}

- 36 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Bucles: do - while.

Cuando se construye un bucle utilizando la instruccin while, la condicin se verifica al comienzo de


cada iteracin. Sin embargo, en algunos casos conviene disponer de una instruccin que realice la
comprobacin una vez ejecutado el bucle. Esto se consigue mediante la instruccin do- while.
do instruccin while(expresin lgica);
La instruccin se ejecutar reiteradamente, en tanto en cuanto la expresin lgica sea cierta. Ntese que el
bucle se ejecutar al menos una vez ya que la condicin se evala al final. La instruccin deber incluir
algn elemento que altere el valor de la expresin para que el bucle pueda finalizar.
Ejemplo 58: Vamos a modificar el programa del ejemplo 53 con el fin de incluir una instruccin dowhile. Dicho programa mostraba por pantalla los nmeros enteros del 1 al que nosotros indicsemos:
#include <stdio.h>
main()
{
/*declaracin de variables*/
int n, digito = 1;
printf("Hasta que numero desea contar?: ");
scanf("%d", &n);
do {
printf("%d\n", digito);
digito++;
}
while(digito <= n);
}
Ejemplo 59: Reescribamos el ejemplo 55, que permita pasar un texto en minsculas a maysculas, pero
esta vez sustituyendo los dos bucles while por otros dos conla instruccin do-while:

#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux, cont = -1;
/*leer el texto en minusculas*/
do
++cont;

- 37 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

while((letras[cont]= getchar()) != EOL);


aux = cont;
/*escribir el texto en mayusculas*/
cont = 0;
do {
putchar(toupper(letras[cont]));
++cont;
}
while (cont < aux);
}
Las instrucciones
putchar(toupper(letras[cont]));
++cont;
podran haberse escrito de forma ms concisa como
putchar(toupper(letras[cont++]));
A continuacin intentaremos reescribir el bucle del ejemplo 57 mediante una instruccin do-while
(ejercicio propuesto).

Bucles: for.

La instruccin for es probalemente la mas utilizada de las tres que se usan para llevar a cabo bucles.
for (expresin1; expresin2; expresin3) instruccin;
donde expresin1 se utiliza para inicializar un parmetro (se le suele llamar ndice), expresin2 es la
condicin que debe ser satisfecha para que contine la ejecucin del bucle y expresin3 modifica el valor
del parmetro inicializado en la expresin1. Por lo tanto, cualquier expresin for es equivalente a la
siguiente expresin while:
expresin1;
while (expresin2) {
instruccin;
expresin3;
}
El uso de la expresin for suele estar indicado en aquellos casos en los que se conoce a priori el nmero
de iteraciones que debe realizar el bucle.
A continuacin vamos a reescribir algunos de los programas anteriores mediante el uso de la instruccin
for.

- 38 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 60: Volvamos a plantearnos el problema propuesto en los ejemplos 53 y 58, consistente en hacer
una lista con los nmeros que van del 1 al n, siendo fijado n por el usuario del programa. Ahora vamos a
utilizar una instruccin for para llevar a cabo las iteraciones:
#include <stdio.h>
main()
{
/*declaracin de variables*/
int n, digito = 0;
printf("Hasta que numero desea contar?: ");
scanf("%d", &n);
for (digito = 0; digito <= n; ++digito)
printf("%d\n", digito);
++digito;
}
Desde el punto de vista sintctico no es necesario que aparezcan las tres expresiones en la instruccin for,
aunque deben aparecer los puntos y comas. No obstante es preciso entender claramente lo que ocurre
cuando se omiten. Es posible omitir la primera y la tercera expresin si se inicializa y/o se altera el ndice
de alguna otra forma. Sin embargo, si se omite la segunda expresin, se est asumiendo que sta es
siempre cierta por lo que el bucle se ejecutar indefinidamente a menos que se haya previsto algn otro
mecanismo de interrupcin (break o return).
Ejemplo 61: Modifiquemos en este caso el programa que transformaba una lnea de texto en minsculas a
maysculas (ejemplos 55 y 59) con el fin de entender el uso de la instruccin for.
#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux, cont;
/*leer el texto en minusculas*/
for (cont = 0; (letras[cont]= getchar()) != EOL; ++cont);
aux = cont;
/*escribir el texto en mayusculas*/
for(cont = 0; cont < aux; ++cont)
putchar (toupper(letras[cont]));
}

Se puede apreciar que el uso de la instruccin for permite escribir los bucles de una forma ms concisa.

- 39 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 62: Revisemos el programa propuesto en el ejemplo 57, cuya utilidad era el clculo de la media
de una lista de n nmeros. En este caso la instruccin for es ms complicada que en los ejmplos anteriores
(es una instruccin compuesta).
#include <stdio.h>
main()
{
/*Definicion e inicializacin de variables*/
int n, cont = 1;
float x, media, suma = 0;
/*Lectura de la longitud de la lista*/
printf("Cuantos numeros vas a introducir?: ");
scanf("%d", &n);
/*Lectura de los n numeros (for)*/
/*Realiza los siguientes pasos de forma repetida mientras que la variable
cont no sea mayor que n*/
for (cont = 1; cont <= n; ++cont){
printf("Numero %d: x =", cont);
/*Lee un numero de la lista . Cada numero se
almacena en la variable de coma flotante x*/
scanf("%f", &x);
/*Aade el valor de x al actual de suma*/
suma += x;
}
/*Clculo de la media*/
media = suma / n;
/*Escritura de la respuesta*/
printf("\nLa media es %f\n", media);
}
Ejemplo 63: Los bucles, al igual que las intrucciones if-else, se pueden anidar uno dentro de otro. Si se
opta por este tipo de estructuras, debemos asegurarnos de que cada bucle est complentamente incluido
en el de nivel superior, y est controlado por un ndice distinto al de los otros bucles. Es ms, las
estructuras de control pueden involucrar tanto bucles como intrucciones if-else.
En el siguiente programa es un ejemplo de lo dicho. En l se calculan las medias de varias listas de
nmeros. Si conocemos de antemano el nmero de listas, podemos utilizar una instruccin for para
controlar el nmero de veces que se ejecuta el clculode la media. El clculo de la media se puede realizar
por cualquiera de los mtodos expuestos anteriormente (vanse los ejemplos 47 y 52).
#include <stdio.h>
main()
{
/*Definicion de variables*/
int n, cont, nlistas, contlista;
float x, media, suma;

- 40 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/*Lectura del numero de listas*/


printf("De cuantas listas vas a calcular la media?: ");
scanf("%d", &nlistas);
/*Bucle exterior (procesa cada lista de numeros)*/
for (contlista = 1; contlista <= nlistas; ++contlista){
/*inicializar y leer la longitud de la lista*/
suma = 0;
printf("\nLista numero %d\nCuantos numeros vas a introducir?: ", contlista);
scanf("%d", &n);
/*Lectura de los n numeros*/
for (cont = 1; cont <= n; ++cont){
printf("Numero %d: x =", cont);
scanf("%f", &x);
suma += x;
}
/*fin del bucle interno*/
/*Calcula la media y escribe el resultado*/
media = suma /n;
printf("\nLa media es %.1f\n", media );
}
/*Fin del bucle externo*/
}
Ejemplo 64: Este ejemplo ejemplifica la utilizacin de dos tipos distintos de bucles, uno anidado dentro
de otro. El programa lee una lnea de texto en minsculas, la convierte a maysculas y a continuacin se
procesa la lnea siguiente. El proceso dura hasa que se detecte una lnea cuyo primer carcter sea un
asterisco.
#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux=0, cont=0;
while ((letras[0] = getchar()) != '*') {
/*leer linea de texto*/
for(cont = 0; (letras[cont] = getchar()) != EOL; ++cont);
aux = cont;
/*escribir linea de texto*/
for (cont = 0; cont < aux; ++cont)
putchar(toupper(letras[cont]));
printf("\n\n");
} /*fin del bucle externo*/

- 41 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("Hasta luego, Lucas");


}

Este programa contiene varios elementos interesantes:






Primero, contiene dos instrucciones for, una dentro de otra. Cada instruccin for incluye una
instruccin compuesta, que consta de varias instrucciones individuales encerradas entre llaves. Se
utiliza un ndice diferente en cada instruccin for (los ndice son contlista y cont, respectivamente).
Ntese que suma se debe inicializar ahora dentro del bucle exterior y no en la declaracin. Esto
permite que suma sea puesta a cero cada vez que se introduce un nuevo conjunto de datos (es decir,
al comienzo de cada pasada a travs del bucle exterior).
Las operaciones de entrada se encuentran todas acompaadas de la presentacin de rtulos y
mensajes, indicando al usuario qu datos son requeridos. De esta forma, vemos pares de funciones
printf y scanf en varios lugares a lo largo del programa.
Dos de las funciones printf contienen varios caracteres de nueva lnea, para controlar el espaciamiento
de las lneas de salida. Esto hace que la salida asociada a cada conjunto de datos (cada pasada por el
bucle externo) se identifique con facilidad.
Finalmente, ntese que el programa est organizado en segmentos separados fcilmente identificables,
con cada segmento precedido por una lnea en blanco y un comentario.

Ejemplo 65: Conversin de varias lneas de texto a maysculas. Vamos a ampliar los programas de
conversin de minsculas a maysculas presentados anteriormente para que varias lneas de texto en
minsculas se conviertan a maysculas, realizndose la conversin lnea a lnea. El programa leer una
lnea de texto en minsculas, la mostrar por pantalla en maysculas y a continuacin procesar otra
lnea, hasta que detecte una lnea cuyo primer carcter sea un asterisco.
Este ejemplo ilustra la utilizacin de dos tipos distintos de bucles, uno anidado dentro del otro.
El bucle externo se utiliza para procesar las lneas de texto. Incluye dos bucles internos separados, el
primero de ellos para leer una lnea de texto y el segundo para visualizarla una vez convertidos los
caracteres a maysculas. Debe tenerse en cuenta que estos dos bucles internos no estn anidados entre s.
/* convertir varias lneas de texto de minsculas
a maysculas. Continuar la conversin hasta que
primer carcter de una lnea sea un asterisco (*) */
#include <stdio.h>
#include <ctype.h>
#define EOL '\n'
main()
{
char letras[80];
int aux, cont;
while ((letras[0]
= getchar())
!= '*') {
/* leer lnea de texto */
for(cont = 0; (letras[cont] = getchar()) != EOL; ++cont);
aux = cont;
/* escribir la lnea de texto */
for (cont = 0; cont < aux; ++cont)
putchar(toupper(letras[cont])) ;

- 42 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\n\n") ;
} /* fin del bucle externo */
printf("Hasta luego");
}
Ejemplo 66: Codificacin de una cadena de caracteres. Muchos programas involucran tanto repeticin
como ejecucin condicional. Escribamos un programa sencillo en C que lea una secuencia de caracteres
ASCII y escriba en su lugar una secuencia de caracteres codificados. Si un carcter es una letra o dgito,
lo reemplazaremos por el siguiente carcter en el conjunto de caracteres, excepto Z que ser reemplazado
por A, z por a y 9 por 0. Por tanto, 1 se transforma en 2, C en D, p en q, etc. Cualquier carcter que no sea
letra o dgito se reemplazar por un punto (.).
Proceso:
Comenzar por la lectura de los caracteres. Se utilizar la funcin scanf para este fin. Se
introducirn y almacenarn todos los caracteres hasta el carcter de nueva lnea (\n), pero
sin incluir ste, en una formacin de 80 elementos de tipo carcter llamada 1inea.
A continuacin se codificarn y visualizarn los caracteres individualmente dentro de un
bucle for. El
bucle procesar cada uno de los caracteres de 1inea hasta encontrar el carcter de escape \0,
que indicael final de la secuencia de caracteres. (Recurdese que la secuencia de escape \0
se aade automticamente al final de cada cadena de caracteres.) Dentro del bucle se
incluyen varias instrucciones if e1se anidadas para realizar la codificacin adecuada.
Se visualizar cada carcter codificado utilizando la funcin putchar.





Se muestra a continuacin el programa en C completo.


/* lee una cadena de caracteres, reemplaza a continuacin cada carcter por su carcter codificado
correspondiente */
#include <stdio.h>
main()
{
char linea[80];
int cont;
/* leer la cadena completa */
printf("Introducir debajo una lnea de texto:\n");
scanf("%[^\n]", linea);
/* codifica cada carcter y lo escribe */
for (cont = 0; linea[cont] != '\0'; ++cont)
{
if (( (linea[cont] >= '0') && (linea[cont] < '9')) ||
((linea[cont] >= 'A') && (linea[cont] < '2')) ||
((linea[cont] >= 'a') && (linea[cont] < 'z')))
putchar(linea[cont] + 1);
else if (linea[cont] == '9') putchar('0');
else if (linea[cont] == 'Z') putchar('A');
else if (linea[cont] == 'z') putchar('a');
else putchar('.');
}
}

- 43 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 67: Clculos repetidos del inters compuesto con deteccin de error. El siguiente programa
calcula valor final (F) en el que se transforma una cierta cantidad de dinero P cuando se deposita durante
un cierto nmero de aos n en una cuenta bancaria a un inters compuesto r. El programa permite la
ejecucin repetida (varios clculos sucesivos, utilizando diferentes conjuntos de datos de entrada para
cada uno de ellos), y es capaz de detectar errores en la introduccin de los datos de entrada (un valor
negativo no tiene sentido y se interpretar como un error). Si se detecta un error, se presentar un mensaje
pidiendo al usuario que introduzca de nuevo el dato.
#include <stdio.h>
#include <math.h>
main( )
{
float p, r, n, i, f;
/* lee el valor de la suma inicial */
printf("Por favor, introduce la suma inicial (P) ");
printf("\n(Para finalizar el programa, introducir 0 como valor):");
scanf("%f", &p);
if (p < 0) {
printf("\nERROR -intntelo de nuevo, por favor: ");
scanf("%f", &p);
}
while (p > 0)
{
/* bucle principal */
/* leer los restantes datos de entrada */
printf("\nPor favor, introduce el inters (r): ");
scanf("%f", &r);
if (r < 0) {
printf("\nERROR -Intntelo de nuevo, por favor: ") ;
scanf("%f", &r) ;
}
printf("\nPor favor, introduce el nmero de aos (n): ") ;
scanf("%f", &n) ;
if (n < 0) {
printf("\nERROR -Intntelo de nuevo, por favor: ") ;
scanf("%f", &n) ;
}
/* calcular i Y f */
i = r/100;
f = p * pow( (1 + i), n) ;
/* escribir salida */
printf("\nEl valor final (F) es: %.2f\n", f) ;
/* leer la suma inicial para la siguientes pasada */
printf("\n\nPor favor, introduce la suma inicial (P) ") ;
printf("\n(Para finalizar el programa, introduce 0 como valor): ");
scanf("%f", &p) ;
if (p < 0) {
printf("\nERROR -intntelo de nuevo, por favor: ") ;
scanf("%f" , &p) ;
}

- 44 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

} /* fin del bucle principal */


}
Ejemplo 68: Solucin de una ecuacin algebraica. Este ejemplo ilustra cmo se pueden utilizar los
ordenadores para resolver ecuaciones algebraicas, incluidas aquellas que no se pueden resolver utilizando
mtodos ms directos. Consideremos, por ejemplo, la ecuacin

x5

 

3 x 2 10

Esta ecuacin no se puede transformar de forma que obtengamos una solucin exacta para x. Sin
embargo, se puede determinar la solucin mediante un procedimiento repetido de prueba y error
(procedimiento iterativo) que refina sucesivamente un valor inicial supuesto.
Comenzaremos por reescribir la ecuacin de la forma siguiente:

  

10 3 x 2

Nuestro procedimiento empezar por suponer un valor de x, sustituir este valor en la parte derecha de la
ltima ecuacin y a continuacin calcular un nuevo valor de x. Si este valor nuevo es igual (o muy prximo) al valor anterior, es que hemos obtenido la solucin de la ecuacin. De otra forma se sustituir este
nuevo valor en la parte derecha de la ecuacin y se volver a obtener otro valor de x, y as sucesivamente.
Continuar este procedimiento hasta que los valores sucesivos de x sean lo suficientemente prximos
(esto es, hasta que el cmputo converja) o hasta que se exceda un nmero especificado de iteraciones. La
ltima condicin se ocupa de impedir que continen los clculos indefinidamente en el caso de que los
resultados obtenidos no converjan.
Para ver cmo funciona el mtodo, supongamos un valor inicial de x = 1.0. Sustituyendo este valor
en la parte derecha de la ecuacin anterior obtenemos:

 
  
   
!!## "" $$
x

10 3 1.0 2

1.47577

A continuacin, sustituimos este nuevo valor en la ecuacin y obtenemos

10 3 1.475772

1.28225

1.38344

Continuando con este prodedimiento, se obtiene:

x
x

10 3 1.282252

10 3 1.38344 2

1.33613

Y as sucesivamente. Ntese que los valores consecutivos de x parecen converger a la respuesta final.
El xito del mtodo depende del valor inicial tomado para x. Si este valor es demasiado grande en valor
absoluto, la cantidad entre corchetes ser negativa, y no se puede elevar una cantidad negativa a un
exponente fraccionario. Por tanto, debemos comprobar si el valor de 10-3x2 es negativo antes de sustituir
el valor de x por el de la parte derecha de la ecuacin.
Con vistas a la redaccin del programa, definamos los siguientes smbolos:
cont
= un contador de iteraciones (cont se incrementar en 1 en cada iteracin)
valor
= el valor de x sustituido en la parte derecha de la ecuacin

- 45 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

raiz
test
error
indicador

= el nuevo valor calculado de x


= la cantidad (10 -3x2)
= el valor absoluto de la diferencia entre raiz y valor
= una variable entera que indica la continuacin o no de la iteracin

Continuaremos los clculos hasta que se satisfaga una de las siguientes condiciones:
1. El valor de error es menor que 0.00001, en cuyo caso habremos obtenido una solucin
satisfactoria.
2. Se han realizado cincuenta iteraciones (cont = 50) .
3. La variable test tiene valor negativo, en cuyo caso no pueden continuar los clculos.

%%

%% %
%%
%%
%

Ahora podemos redactar el siguiente esquema del programa.


Por conveniencia, definir las constantes simblicas CIERTO y FALSO.
Declarar todas las variables e inicializar las variables enteras indicador y cont (asignar CIERTO
a indicador y 0 a cont).
Leer el valor inicial de valor.
Llevar a cabo el siguiente procedimiento iterativo mientras indicador sea CIERTO.
Incrementar el valor de cont en l.
Asignar FALSO a indicador si el nuevo valor de cont es igual a 50. Esto indica la ltima
pasada por el bucle.
Examinar el valor de test. Si su valor es positivo, proceder como sigue.
Calcular un nuevo valor para raiz; escribir el valor actual de cont, seguido del valor
actual de raiz.
Evaluar error, que es el valor absoluto de la diferencia entre raiz y valor. Si este
valor es mayor que 0.00001, asignar el valor actual de raiz a valor y continuar con la
siguiente iteracin. De otra forma, escribir los valores actuales de raiz y cont y poner
indicador a FALSO. El valor actual de rai z se considerar la solucin deseada.
Si el valor actual de test no es positivo, no se puede seguir con los clculos. Por lo tanto,
se muestra un mensaje de error apropiado (por ejemplo Nmeros fuera de rango) y se
cambia indicador a FALSO.
Despus de completar el paso 4, escribe un mensaje de error adecuado (por ejemplo No es
posible obtener convergencia) si cont tiene el valor de 50 y el valor de error es mayor que
0.00001.
He aqu el programa en C completo.

/* determinar las races de una ecuacin algebraica utilizando un procedimiento iterativo */


#include <stdio.h>
#include <math.h>
#define CIERTO 1
#define FALSO O
main()
{
int indicador=CIERTO, cont=0;
float valor, raiz, test, error;
/* leer parmetros de entrada */
printf("Valor inicial: ");
scanf("%f", &valor);

- 46 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

while (indicador)
{
/* comienza bucle principal */
++cont;
if (cont == 50) indicador = FALSO;
test = 10. -3. * valor * valor;
if (test> 0){
/* otra iteracin */
raiz = pow(test, 0.2);
printf("\nIteracin nmero: %2d", cont);
printf("
x= %7.5f", raiz);
error = fabs(raiz - valor);
if (error> 0.00001) valor = raiz;/* repetir el clculo */
else {
/* visualizar la respuesta final */
indicador = FALSO;
printf("\n\nRaz= %7.5f", raiz);
printf(" N de iteraciones = %2d", cont);
}
}
else
{
/* mensaje de error */
indicador = FALSO; ");
printf("\nNmeros fuera de rango -");
printf ("intenta con otro valor inicial");
}
}
if ((cont == 50) && (error > 0.00001)) /*otro mensaje de error */
printf("\n\nConvergencia no obtenida tras 50 iteraciones");
}
La instruccin switch.
Esta instruccin permite que se ejecute una determinada sentencia o grupo de sentencias dependiendo del
valor de una expresin:
switch (expresin) instruccin{
case expresin1:
instruccion1;
break;
case expresin2:
instruccion2;
break;
case expresin3:
instruccion3;
break;

default:
instruccin;
}
donde expresin devuelve un entero (tambin puede ser tipo char, ya que los caracteres individuales
tienen valores enteros). Cada expresin que sigue a case puede ser simple o compuesta. Cuando se ejecuta

- 47 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

la instruccin switch, la expresin se evala y se transfiere el control directamente al grupo de


instrucciones cuya etiqueta case tenga el mismo valor que el de expresin. Si ninguno de los valores de
las etiquetas case coincide con el valor de expresin, entonces no se seleccionar ninguno de los grupos
de la instruccin switch. En este caso se transfiere el control directamente a la instruccin default (si
existe) o a la instruccin que se encuentre a continuacin.
Ejemplo 69:En el siguiente ejemplo se le asigna a la variable y un cierto valor dependiendo de cunto
vale la variable indicador:
switch (indicador) {
case -1:
y = fabs(x);
break;
case 0:
y = sqrt(x);
break;
case 1:
y = x;
break;
case 2:
case 3:
y=2*(x-1);
break;
default:
y = 0;
}
Ejemplo 70: Un ejemplo tpico del uso de esta intruccin para mens en los que el usuario debe elegir
entre varias opciones:
switch (eleccion = getchar()) {
case 'r':
case' R' :
printf("ROJO");
break;
case 'b':
case 'B':
printf("BLANCO");
break;
case 'a':
case 'A':
printf("AZUL") ;
}
En algunos casos el uso de default permite generar mensajes de error que advierten al usuario de que no
ha elegido correctamente:
switch (eleccion = toupper(getchar())) {

- 48 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

case 'R':
printf("ROJO") ;
break;
case 'B':
printf("BLANCO") ;
break;
case 'A':
printf("AZUL") ;
break;
default:
printf("ERROR");
}
Qu misin cumple aqu la funcin de biblioteca toupper()? Se te ocurre otro modo de conseguir lo
mismo?
Ejemplo 71: Clculo de la depreciacin. Consideremos cmo calcular la depreciacin anual para
algunos objetos susceptibles a ello, tales como un edificio, una mquina, etc. Hay tres mtodos
comnmente usados para el clculo de la depreciacin:

&

&

&

a) Mtodo lineal. En este mtodo el valor original del objeto se divide por su vida (nmero total de
aos). El cociente resultante ser la cantidad en que el objeto se deprecia anualmente. Por ejemplo,
si un objeto de 6000 se deprecia en diez aos, entonces la depreciacin anual ser 6000/10 = 600
.. Por tanto, el valor del objeto habr disminuido en 600 cada ao. La depreciacin anual es la
misma cada ao cuando se utiliza este mtodo.
b) Mtodo de balance doblemente declinante. El valor del objeto disminuye cada ao en un
porcentaje constante. Para obtener el factor de depreciacin, dividimos dos por la vida del objeto.
Este factor se multiplica por el valor del objeto al comienzo de cada ao (y no el valor original del
objeto) para obtener la depreciacin anual. Por tanto, la verdadera cantidad depreciada variar de un
ao al siguiente. Supongamos, por ejemplo, que deseamos depreciar un objeto de 6000 en diez
aos, utilizando el mtodo del balance doblemente declinante. El factor de depreciacin ser 2 / 10 =
0.2. La depreciacin ser:

&

'''

=
1200
Primer ao:
0.20 6000
Segundo ao:
0.20 (6000-1200)
=
960
Tercer ao:
0.20 (6000-1200-960) =
768

c) Mtodo de la suma de los dgitos de los aos. El valor del objeto ir disminuyendo en un
porcentaje que es diferente cada ao. El factor de depreciacin ser una fraccin cuyo denominador
es la suma de los dgitos de 1 a n, en donde n representa la vida del objeto. Si, por ejemplo,
consideramos un tiempo de vida de diez aos, el denominador ser 1 + 2 + 3 + + 10 = 55. La
depreciacin anual se obtiene multiplicando el factor de depreciacin por el valor original del
objeto. As, con el mismo ejemplo que en los apartados anteriores:

AO

FACTOR DE
DEPRECIACIN

DEPRECIACIN
( )

- 49 -

Primer ao:
Segundo ao:
Tercer ao:

(10/55)
(9/55)
(8/55)

)) )

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

(10/55) 6000 = 1090


(9/55) 6000 = 981.82
(8/55) 6000 = 872.72

Deseamos escribir un programa en C que nos permita seleccionar algunos de estos mtodos para cada
conjunto de clculos.
/* calcula la depreciacin mediante tres mtodos diferentes */
#include <stdio.h>
main()
{
int n, anual, eleccion = O;
float val, aux, deprec;
while (eleccion = 4) {
/* leer datos de entrada */
printf("\nMtodo: (l-LR 2-BDD 3-SDA 4-Fin) ");
scanf("%d", &eleccion);
if (eleccion >= 1 && eleccion <=3) {
printf("Valor original: ");
scanf("%f", &val);
printf("Nmero de aos: ");
scanf("%d", &n);
}
switch (eleccion) {
case 1
/* mtodo lineal */
printf("\nMtodo de la lnea recta\n\n");
deprec = val/n;
for (anual = 1; anual <= n; ++anual) {
val -= deprec;
printf("Fin de ao %2d", anual);
printf(" Depreciacin: %7.2f", deprec);
printf(" Valor actual: %8.2f\n", val);
}
break;
case 2:
/* mtodo de balance doblemente declinante */
printf("\nMtodo balance doblemente decl.\n\n");
for (anual = 1; anual <= n; ++anual) {
deprec = 2*val/n;
val -= deprec;
printf("Fin de ao %2d", anual);
printf(" Depreciacin: %7.2f", deprec);
printf(" Valor actual: %8.2f\n", val);
}
break;
case 3:
/* mtodo de la suma dgitos de los aos */
printf("\nMtodo de la suma de los dgitos");
printf ("de los aos\n\n");

- 50 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

aux = val;
for (anual = 1; anual <= n; ++anual) {
deprec = (n-anual+1)*aux / (n*(n+1)/2);
val -= deprec;
printf("Fin de ao %2d", anual);
printf(" Depreciacin: %7.2f", deprec);
printf(" Valor actual: %8.2f\n", val);
}
break;
case 4:
/*
fin de los clculos */
printf("\nHasta luego tronc\n");
break;
default:
/* generar mensaje de error */
printf("\nEntrada de datos incorrecta");
printf("-repite por favor\n");
}
/* fin de switch */
/*fin de while */

}
La forma de realizar los clculos correspondientes al mtodo de la suma de los dgitos de los aos puede
resultar algo oscura. En particular, el trmino (n-anual+1) en el numerador requiere una cierta aclaracin.
Esta cantidad se utiliza para contar decrecientemente (de n a 1) mientras anual avanza crecientemente (de
1 a n). Estos valores declinantes son requeridos por el mtodo de la suma de los dgitos de los aos. Por
supuesto, podramos haber utilizado en lugar de esto un bucle con un contador que decreciese, esto es,
for (anual = n; anual >= 1; --anual)
pero entonces habramos necesitado el correspondiente bucle con contador creciente que escribiese los
resultados calculados anualmente. Tambin el trmino (n*(n+1)/2) que aparece en el denominador esuna
frmula para la suma de los n primeros dgitos, esto es, 1 + 2 + ...+ n.

Las instrucciones break y continue.


La instruccin break se utiliza para terminar la ejecucin de bucles o salir de una instruccin switch. Se
puede utilizar dentro de una instruccin while, do-while, for o switch. Se ocupa de transferir el control
fuera de la instruccin en la que se haya, a la primera instruccin que se encuentre a continuacin
suya..Puede escribirse sencillamente de la siguiente forma:
break;
sin contener ninguna otra expresin o instruccin.
La instruccin continue se utiliza para saltarse el resto de la pasada actual a travs de un bucle. El bucle
no termina cuando se encuentra una instruccin continue. Sencillamente no se ejecutan las instrucciones
que se encuentran a continuacin en l y se salta directamente a la siguiente pasada a travs del bucle.
(Ntese esta diferencia importante entre continue y break.)

- 51 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

La instruccin con t in u e se puede incluir dentro de una instruccin while, do-while, for. Simplemente se
escribe as:
continue;
sin contener ninguna otra expresin o instruccin.
Ejemplo 72: He aqu algunas muestras de bucles que contienen instrucciones break. En cada situacin, el
bucle continuar su ejecucin mientras el valor actual de la variable en coma flotante x no sea mayor que
100. Sin embargo, se saldr del bucle si se detecta un valor de x negativo.
Primero consideremos un bucle while.
scanf("%f", &x);
while (x <= 100) {
if
(x < O) {
printf("ERROR -VALOR NEGATIVO DE X");
break;
}
/* procesar el valor no negativo de x */

scanf ("%f ", &x);


}
Consideremos un bucle do-while que hace lo mismo:
do {
scanf("%f", &x);
if (x <

0) {
printf("ERROR -VALOR NEGATIVO DE X");
break;

}
/* procesar el valor no negativo de x */
} while (x <= 100);
Finalmente, he aqu un bucle for semejante.
for

(cont = 1; x <= 100; ++cont) {


scanf("%f", &x);
if
(x < 0)
{
printf("ERROR -VALOR NEGATIVO DE X");
break;
}
/* procesar el valor no negativo de x */
...

}
Ejemplo 73: En el caso de varias instrucciones while, do -while, for o switch anidadas, una instruccin
break causar la transferencia de control fuera de la instruccin ms interna en la que se encuentre, pero
no fuera de las instrucciones externas.

- 52 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for (cont = O; cont <= n; ++cont) {

while (c = getchar() = '\n') {


if (c = '*') break;

}
}
Si la variable de carcter c tiene asignado un asterisco (*), entonces el bucle while terminar. Sin
embargo, continuar la ejecucin del bucle for. Por tanto, si el valor de cont es menor que n cuando se
salga del bucle while, el ordenador incrementar cont y har otra pasada a travs del bucle for.
Ejemplo 74 : Veamos a continuacin algunos ejemplos sobre el uso de la instruccin continue.
do {
scanf("%f", &x)
if (x < 0) {
printf("ERROR -VALOR NEGATIVO DE X");
continue;
};
/* procesar el valor no negativo de x */

} while (x <= 100);


He aqu un bucle for semejante.
for (cont = 1; x <= 100; ++cont) {
scanf("%f", &x);
if (x < 0) {
printf("ERROR -VALOR NEGATIVO DE X");
continue;
}
/* procesar el valor no negativo de x */

}
En cada caso no se ejecutar la parte en la que se procesa el valor actual de x si ste es negativo. Se
continuar en este caso con la siguiente pasada del bucle.
Es interesante comparar estas instrucciones de control con las mostradas en el Ejemplo 73, que hacen uso
de la instruccin break en lugar de la continue. (Por qu no se incluye en este ejemplo una modificacin
del bucle while que aparece en dicho ejemplo?)
El operador ,.
Introducimos ahora el operador coma (,), que se utiliza principalmente en la instruccin for. Este operador
permite que aparezcan dos expresiones en situaciones en donde slo se utilizara
una expresin. Por ejemplo, es posible escribir:

- 53 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for (expresinla, expresinlb; expresin 2; expresin 3) instruccin


en donde expresinl y expresinlb b son dos expresiones, separadas por el operador coma, que se ocupan
de inicializar dos ndices utilizados simultneamente dentro del bucle for. Anlogamente se puede utilizar
en una instruccin for el operador coma de la siguiente forma:
for (expresinl; expresin 2; expresin3a, expresin3b) instruccin

Aqu expresin3a y expresin3b, separadas por el operador coma, aparecen en lugar de la expresin nica
habitual con el fin de alterar los dos ndices que se estn utilizando simultneamente dentro del bucle (por
ejemplo, un ndice podra irse incrementando mientras el otro decrece).
Ejemplo 75 : Bsqueda de palndromos. Un palndromo es una palabra o una frase que se lee de la
misma forma hacia delante que hacia atrs. Por ejemplo, las palabras ala y rapar son palndromos.
Tambin lo es la frase dbale arroz a la zorra el abad, si no tenemos presentes espacios en blanco, signos
de puntuacin y el acento de dbale.
Escribamos un programa en C que lea una lnea de texto que contenga una palabra o una frase y
determine si es o no un palndromo. Para hacer esto, compararemos el primer carcter con el ltimo, el
segundo carcter con el penltimo, y as sucesivamente, hasta que hayamos alcanzado el punto medio del
texto. Las comparaciones en nuestro programa incluirn los signos de puntuacin y los espacios en
blanco.
Con el fin de esbozar el esquema del programa, definamos las siguientes variables:
letras = una formacin de caracteres de 80 elementos. Estos elementos sern los caracteres de la
lnea de texto.
aux
= una variable entera que indicar el nmero de caracteres asignados a letras, sin incluir
el carcter de escape \0 del final.
cont
= una variable entera que se utilizar como ndice al movemos hacia delante en letras.
contr
= una variable entera que se utiliza como ndice al movemos hacia atrs en letras
indicador = una variable entera que se utilizar para indicar una condicin de cierto/falso. La
condicin cierto indicar que se ha encontrado un palndromo.
Bucle
= una variable entera cuyo valor es siempre igual al, apareciendo, por tanto, siempre
como cierta. La intencin de esto es continuar la ejecucin del bucle principal hasta
que una determinada condicin indique su fin.
Podemos escribir ahora el siguiente esquema:
l. Definir las constantes simblicas EOL <end ofline o fin de lnea), CIERTO y FALSO.
2. Declarar todas las variables e inicial izar bucle (asignar CIERTO a bucle).
3. Comenzar el bucle principal.
a) Asignar CIERTO a indicador, anticipando el hallazgo de un palndromo.
b) Leer la lnea de texto carcter a carcter y almacenarla en letras.
c) Comprobar si los tres primeros caracteres de la lnea en maysculas son F, I y N ,
respectivamente. Si es as, salir del bucle principal y finalizar la ejecucin del programa.
d) Asignar a aux el valor final de cont menos 1. Este valor indicar el nmero de caracteres
que tiene la lnea de texto, sin incluir el carcter de escape final \0.

- 54 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

e) Comparar cada carcter de la primera mitad de letras con el correspondiente en la


segundamitad. Si se encuentra alguna no coincidencia, asignar FALSO a indicador y salir
del bucle (ms interno) de comparacin.
f) Si indicador es CIERTO, escribir un mensaje que informe del hallazgo de un palndromo.
De otra forma, escribir un mensaje en el que se informe de que no se ha encontrado
ningn palndromo.
4. Repetir el paso 3 (hacer otra pasada por el bucle exterior), procesando otra lnea de texto.
El programa utiliza el operador coma en una instruccin for para comparar cada carcter de la primera
mitad de letras con el carcter correspondiente de la segunda mitad. Por tanto, mientras cont va tomando
valores de 0 a (aux-1)/2, contr lo hace de auxa (aux/2)+l. Ntese que se realiza la divisin entera
(obtenindose el cociente truncado) para establecer estos valores lmite. Obsrvese tambin que hay dos
operadores coma dentro de la instruccin for. Cada operador coma y sus operando s asociados se
encuentran entre parntesis. Esto no es necesario, pero recalca el hecho de que cada par de operandos
forman un argumento dentro de la instruccin for. Se muestra a continuacin el programa en C completo.
/* buscar un palndromo */
#include <stdio.h>
#include <ctype.h>
#define EOL
'\n'
#define CIERTO 1
#define FALSO O
main( )
{
char letras[80];
int aux, cont, contr, indicador, bucle = CIERTO;
/* bucle principal */
while (bucle) {
indicador = CIERTO;
/* leer el texto */
printf("Introduce una palabra o frase debajo:\n");
for (cont = 0; (letras[cont] = getchar()) 1= EOL; ++cont);
if ((toupper(letras[O]) == 'F') &&
(toupper(letras[l]) =='I') &&
(toupper(letras[2]) == 'N')) break;
aux = cont -1;
/* realizar la bsqueda */
for ((cont = O, contr = aux); cont <= aux/2;
(++cont, --contr)) {
if (letras[cont] != letras[contr]) {
indicador = FALSO;
break;
}
}
/* escribir mensaje */

- 55 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for (cont = 0; cont <= aux; ++cont)


putchar(letras[cont]) ;
if (indicador) printf(" ES un palndromo\n\n");
else printf(" NO ES un palndromo\n\n");
}
}

9. Funciones.
Una funcin es una parte de un programa que realiza determinadas tareas bien definidas agrupadas bajo
un nombre nico. Desde el punto de vista del programa principal, cada funcin se comporta como una
especie de caja negra que recibe cierta informacin del programa principal (argumentos) y devuelve una
cierta respuesta (mediante la instruccin return).
La definicin de una funcin tiene dos componentes principales: la primera lnea (incluyendo las
declaraciones de los argumentos) y el cuerpo de la funcin. La primera lnea de la definicin de una
funcin contiene la especificacin del tipo de valor devuelto por la funcin (tipo_de_dato), seguido del
nombre (nombre) de la funcin y [opcionalmente] un conjunto de argumentos (arg2, arg1), separados por
comas y encerrados entre parntesis. Cada argumento viene precedido por su declaracin de tipo (tipo1).
Deben seguir al nombre de la funcin un par de parntesis vacos si la definicin de la funcin no incluye
ningn argumento.

/*primera lnea*/
tipo_de_dato nombre(tipo1 arg1, tipo2 arg2, , tipon argn)
/*cuerpo de la funcin*/
{
tipo_de_dato variable; /*definicin de variables locales*/
instruccin1;
instruccin2;

return(variable)
}
El cuerpo puede incluir ms de una instruccin return con el fin de devolver valores al punto de llamada
de la funcin.
Si el cuerpo principal del programa aparece antes de la funcin main() el compilador requiere un aviso
previo de la existencia de las funciones que aparecern definidas con posterioridad. Con este fin se
utilizan los llamados prototipos de las funciones. La forma general de un prototipo de funcin es:
tipo_de_dato nombre(tipo1 arg1, tipo2 arg2, , tipon argn);
Obsrvese que el prototipo de funcin se parece a la primera lnea de definicin de dicha funcin, si bien
el prototipo finaliza con un punto y coma. Un programa con dos funciones definidas (adems de main())
tendra la siguiente estructura general:

- 56 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include<stdio.h>
/*prototipo de la primera funcin*/
tipo_de_dato funcion1(tipo1 arg1, tipo2 arg2, , tipon argn);
/*prototipo de la segunda funcin*/
tipo_de_dato funcion2(tipo1 arg1, tipo2 arg2, , tipom argm);
main()
{

/*llamada a funcion1*/

/*llamada a funcion2*/

}
/*primera lnea funcion1*/
tipo_de_dato funcion1(tipo1 arg1, tipo2 arg2, , tipon argn)
/*cuerpo de la funcin*/
{
tipo_de_dato variable; /*definicin de variables locales*/
instruccin1;
instruccin2;

return(variable)
}
/*primera lnea funcion2*/
tipo_de_dato funcion2(tipo1 arg1, tipo2 arg2, , tipom argm)
/*cuerpo de la funcin*/
{
tipo_de_dato variable; /*definicin de variables locales*/
instruccin1;
instruccin2;

return(variable)
}
Todo programa contiene al menos una funcin, la funcin main(). Si contiene varias, sus definiciones
pueden aparecer en cualquier orden, pero deben ser independientes unas de otras (esto es, una definicin
de una funcin no puede estar incluida en otra). Cuando se accede a una funcin desde alguna
determinada parte del programa (cuando se llama a la funcin), se ejecutan las instrucciones que aparecen
en el cuerpo de dicha funcin. Una vez que se ha completado la ejecucin de una funcin, se devuelve el
control al punto desde el que se accedi.

- 57 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Debemos tener en cuanta que es posible acceder a una misma funcin desde varios lugares distintos del
programa. Generalmente, una funcin procesar la informacin que le es pasada desde el punto del
programa en donde se accede a ella y devolver un solo valor. Sin embargo, existen casos especiales de
funciones que no requieren argumentos de entrada e incluso algunas no devuelven nada.
Ejemplo 76 : Conversin de un carcter de minscula a mayscula. Reconsideremos el programa
visto en el ejemplo 36 que lea un carcter y lo converta en mayscula utilizando la funcin de biblioteca
toupper.. Consideremos ahora un programa similar, aunque definiremos nuestra propia funcin para
realizar la conversin de minscula a mayscula.
/* convertir un carcter en minscula a mayscula utilizando una funcin definida por el programador */
#include <stdio.h>
char minusc_a_mayusc(char c1) /* definicin de la funcin */
{
char c2;
/* Se basa en el hecho de que la diferencia entre el cdigo ASCII de una letra minuscula
y su mayuscula correspondiente es siempre una constante (vease la tabla adjunta)*/
c2 = (c1 >= 'a' && c1 <= 'z') ? (-'A' + c1 + 'a') : c1;
return(c2) ;
}
main()
{
char minusc, mayusc;
printf("Por favor, introduce una letra minscula: ");
scanf("%c", &minusc);
mayusc = minusc_a_mayusc(minusc) ;
printf("\nLa mayscula equivalente es %c\n\n", mayusc);

**

}
Este programa consta de dos funciones:
la funcin main() requerida .
la funcin minusc_a_mayusc( ), definida por el programador, que convierte una minscula a
mayscula. Efecta la transformacin real del carcter. Esta funcin transforma nicamente las letras
en minsculas; el resto de los caracteres se devuelven intactos.
Para la comprensin de este programa es necesario observar que la diferencia entre el cdigo ASCII de
una letra minuscula y su mayuscula correspondiente es siempre una constante (vease la tabla adjunta):

Valor
ASCII
0

Carcter
NUL

Valor
ASCII
32

1
2
3

SOH
STX
ETX

33
34
35

Carcter
espacio en
blanco
!
"
#

Valor
ASCII
64

Carcter
@

Valor
ASCII
96

Carcter
'

65
66
67

A
B
C

97
98
99

a
b
c

- 58 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

EOT
ENQ
ACK
BEL
BS
HT
LF
VT
FF
CR
SO
SI
DLE
DC1
DC2
DC3
DC4
NAK
SYN
ETB
CAN
EM
SUB
ESC
FS
GS
RS
US

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

$
%
&

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

(
)
*
+
,
/
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?

D
E
F
G
H
1
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
y
Z
[
\
]
"
-

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

d
e
f
g
h
i
j
k
1
m
n
o
p
q
r
s
t
u
v
w
x
Y
z
{
I
}
~
DEL

A travs del argumento c1 se transfiere a la funcin una letra en minscula, y se devuelve la mayscula
correspondiente, c2, a la parte del programa que hizo la llamada (main), mediante la instruccin return.
Ntese que las variables minusc y mayusc en main se corresponden con las variables cl y c 2 dentro de
minusc_a_mayusc.
Una posible variacin sobre el mismo ejemplo podra ser la siguiente:
char minusc_a_mayusc(char cl)

/*funcin de conversin
definida por el programador */

{
if (c1 >= 'a' && c1 <= 'z')
return(-'A' + c1 -'a');
else
return(c1);
}
Esta funcin utiliza la instruccin if-else en lugar del operador condicional. Es menos compacta que la
versin anterior, pero puede resultar de ms fcil comprensin. Por otra parte, esta funcin no necesita la
variable local c2. Ntese que la funcin contiene dos instrucciones return. La primera devuelve una
expresin que representa la mayscula correspondiente a la minscula dada; la segunda devuelve el
carcter original, sin cambios.

- 59 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

La instruccin return puede faltar en la definicin de una funcin, aunque esto se considera generalmente
como una prctica de programacin pobre. Si una funcin alcanza el final del bloque sin encontrarse una
instruccin return, se devuelve el control al punto de llamada sin devolverse ninguna informacin. Se
recomienda en estos casos una instruccin return vaca (sin expresin), para hacer ms clara la lgica de
la funcin y hacer ms cmodas las modificaciones futuras de la funcin.
Ejemplo 77: Determinacin de la cantidad mayor de entre dos en una funcin. La siguiente funcin
acepta dos cantidades enteras y escribe la mayor. Lo importante en este ejemplo es darse cuenta que la
funcin no devuelve ninguna informacin al punto de llamada:
maximo(int x, int y); /*determina el mximo de dos cantidades
enteras*/
{
int z;
z = (x >= y)? X: y;
printf(\n\nValor maximo = %d, z);
return;
Ejemplo 78: Clculo del factorial de un nmero mediante una funcin. El clculo del factorial de un
nmero ya fue tratado en el ejemplo 20, En este caso definimos la funcin factorial para llevar a cabo
dicho clculo.
long int factorial(int n) /*calculo del factorial de n*/
{
int i;
long int prod = 1;
if (n > 1)
for (i=2; i<= n; ++i) prod *= i;
return(prod);
}
He aqu un programa completo en C que calcula el factorial mediante la funcin factorial. Ntese que la
definicin de la funcin precede a main.
/* calcular el factorial de una cantidad entera */
#include <stdio.h>
long int factorial(int n)
/* calcular el factorial de n */
{
int i;
long int prod = 1;
if (n > 1)
for (i = 2; i <= n; ++i)
prod *= i;
return(prod) ;
}
main()
{
int n;

- 60 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/* leer la cantidad entera */


printf("\nn = ");
scanf("%d", &n);
/* calcular Y visualizar el factorial */
printf("\nn! = %ld", factorial(n)); /* %ld es porque la variable
prod esta definida como long integer*/
}
La funcin definida por el programador (factorial) utiliza un argumento entero (n) y dos variables locales
-un entero ordinario (i) y un entero largo (prod)-. Puesto que la funcin devuelve un entero largo, la
declaracin de tipo long int aparece en la primera lnea de la definicin de la funcin. A continuacin se
presenta otra versin del programa, escrita descendentemente (es decir, main aparece delante de
factorial). Ntese la presencia del prototipo de la funcin al comienzo del programa. El prototipo de la
funcin indica que se definir, ms adelante en el programa, una funcin llamada factorial, que acepta una
cantidad entera y devuelve un entero largo.
/* calcular el factorial de una cantidad entera */
#include <stdio.h>
long int factorial(int n);

/* prototipo de funcin */

main( )
{
int n;
/* leer la cantidad entera */
printf("\nn = ");
scanf("%d", &n);
/* calcular y visualizar el factorial */
printf("\nn! = %ld", factorial(n));
}
long int factorial(int n)
/* calcular el factorial de n */
{
int i;
long int prod = 1;
if (n > 1)
for (i = 2; i <= n; ++i)
prod *= i;
return(prod) ;
}
Las llamadas a las funciones pueden abarcar varios niveles en un programa. Esto es, la funcin A
puede llamar a la funcin B, la cual puede llamar a la funcin C, etc. Tambin, la funcin A puede llamar
directamente a la funcin C, y as sucesivamente.
Ejemplo 79: En ocasiones se utiliza la palabra reservada void como especificador de tipo cuando se
define una funcin que no devuelve nada. En el siguiente ejemplo (vase tambin el ejemplo 77), la

- 61 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

funcin maximo calcula el mayor de dos nmeros y lo imprime, pero no devuelve ningn dato al punto de
llamada.
void maximo(int x, int y)
{
int z;
z = (x >= y) ? x: y;
printf(\n\nEl mayor es el %d, z);
return;
}
Se puede acceder (llamar) a una funcin especificando su nombre, seguido de una lista de argumentos
encerrados entre parntesis y separados por comas. Si la llamada a la funcin no requiere ningn
argumento, se debe escribir a continuacin del nombre de la funcin un par de parntesis vacos. La
llamada a la funcin puede formar parte de una expresin simple (como por ejemplo una instruccin de
asignacin) o puede ser uno de los operandos de una expresin ms compleja. Los argumentos que
aparecen en la llamada a la funcin se denominan argumentos reales, en contraste con los argumentos
formales que aparecen en la primera lnea de la definicin de la funcin.
Ejemplo 80: Si la funcin devuelve un valor, el acceso a la funcin se suele escribir a menudo como una
instruccin de asignacin, tal y como se puede apreciar en el siguiente ejemplo, una nueva versin del
programa que converta un carcter en minsculas a maysculas:
/* convertir un carcter en minscula a mayscula utilizando una funcin definida por el programador */
#include <stdio.h>
char minusc_a_mayusc(char cl)
/* definicin de la funcin */
{
char c2;
c2 = (cl >= 'a' && cl <= 'z') ? (-'A' + cl +'a') : cl;
return(c2);
}
void main(void)
{
char minusc, mayusc;
printf("por favor, introduce una letra minscula: ");
scanf("%c", &minusc);
mayusc = minusc_a_mayusc(minusc);
printf("\nLa mayscula equivalente es %c\n\n", mayusc);
}
En este programa, main contiene slo una llamada a la funcin definida por el programador
minusc_a_mayusc. La llamada es una parte de la expresin de asignacin mayusc =
minusc_a_mayusc(minusc). Cuando se accede a la funcin, se transfiere el valor de minusc a la funcin.
Este valor es representado por c1 dentro de la funcin. A continuacin se determina el valor de la
mayscula correspondiente, c2, y se devuelve al punto de llamada, en donde se asigna a la variable de
tipo carcter mayusc. Ntese que se pueden combinar las dos ltimas instrucciones de main de la forma
siguiente:

- 62 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\nLa mayscula equivalente es %c", minusc_a_mayusc(minusc));


Puede haber diversas llamadas a la misma funcin desde varios lugares de un programa. Pueden ser
distintos los argumentos reales de una llamada a otra. En todo caso, dentro de cada llamada a una funcin
los argumentos reales deben corresponderse con los argumentos formales de la definicin de la funcin;
es decir, el nmero de argumentos reales debe ser el mismo que el nmero de argumentos formales y
cada argumento real debe ser del mismo tipo de datos que el correspondiente argumento formal.
Ejemplo 81: Mayor de tres cantidades enteras. El siguiente programa determina la mayor de tres
cantidades enteras. Este programa utiliza una funcin que determina la mayor de dos cantidades enteras.
La funcin es semejante a la definida en el ejemplo 79, salvo que la funcin de este ejemplo devuelve el
valor mayor al punto de llamada en lugar de visualizarlo. Lo que se pretende es determinar la mayor de
las dos primeras cantidades y compararla a continuacin con la tercera. La cantidad mayor se escribe en
la parte principal del programa:
/* determinar la mayor de tres cantidades enteras */
#include <stdio.h>
int maxima(int x, int y) /* determinar la mayor de dos
cantidades enteras */
{
int z;
z = (x >= y) ? x : y;
return(z) ;
}
main()
{
int a, b, c, d;
/* leer las cantidades enteras */
printf("\na = ");
scanf("%d", &a);
printf("\nb = ");
scanf("%d", &b);
printf("\nc = ");
scanf("%d", &c);
/* calcular Y visualizar el valor mximo */
d = maximo(a, b);
printf("\n\nmaximo = %d", maximo(c, d));
}
Ntese que las dos instrucciones que acceden a maximo, es decir,
d = maximo(a,b);
printf("\n\nmaximo = %d", maximo(c, d));
se pueden reemplazar por la instruccin

- 63 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\n\nmaximo = %d", maximo(c, maximo(a, b)));


en la que una de las llamadas a maximo es un argumento para la otra llamada. Es decir, pueden incluirse
las llamadas, una dentro de otra, y no se necesita la variable intermedia d.
Ejemplo 82: Simulacin de la tirada de un dado. La simulacin de numerosos juegos de mesa requiere
un generador de nmeros aleatorios que produzca nmeros enteros distribuidos uniformemente entre 1 y
6. (Por uniformemente distribuidos entendemos que cada entero entre 1 y 6 tiene la misma probabilidad
de aparecer.) La mayoria de las versiones de C incluyen un generador de nmeros aleatorios entre sus
funciones de biblioteca. Estos generadores de nmeros aleatorios suelen devolver tpicamente un nmero
en coma flotante uniformemente distribuido entre 0 y 1, o una cantidad entera uniformemente distribuida
entre 0 y algn valor entero muy grande.
En nuestro caso utilizaremos una funcin llamada rand, de la biblioteca stdlib.h que devuelve un entero
uniformemente distribuido entre 0 y 215 -1 (esto es, entre 0 y 32767). Convertiremos entonces cada
cantidad entera aleatoria en un nmero en coma flotante x, que se encontrar entre 0 y 0.99999. Para
hacer esto escribimos:
x = rand()/32768.0
Ntese que el denominador se ha escrito como constante en coma flotante. Esto hace que el cociente, y
por tanto x, sean en coma flotante. La expresin
(int) (6 * x)
ser un entero truncado, con valor uniformemente distribuido entre O y 5. Por tanto, obtenemos el
resultado
deseado simplemente aadiendo 1; esto es,
n = 1 + (int) (6 * x)
Este valor representar el resultado del lanzamiento de un dado. La funcin srand, de la biblioteca stdlib.h
es necesaria para inicializar el generador de nmeros aleatorios. Esta funcin requiere de un entero
positivo, llamado semilla, que establezca la secuencia de nmeros aleatorios generados por rand. Se
gener una secuencia de nmeros aleatorios diferentes para cada semilla. Por comodidad, podemos incluir
un valor de la semilla como ocnstante simblica dentro del programa. Hemos de tener en cuenta que si el
programa se ejecuta repetidamente con la misma semilla, se gener cada vez la misma secuencia de
nmeros aleatorios. Con este razonamiento resulta sencillo escribir la funcin tirada:
#define semilla 12345 /*Al comienzo del programa principal*/
srand(SEM)
/*en el cuerpo del programa principal,
precediendo a la llamada a la funcion tirada*/

int tirada(int n) \*Simula el lanzamiento de un dado y devuelve el


entero entre 1 y 6 obtenido*\
{
float x;
int n;

- 64 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

x = rand() / 32768.0;
n = 1 + (int) (6*x);
return(n);

}
Esta funcin nos puede servir de base para llevar a cabo un programa que simule un juego de dados.
Proponemos al lector que realice los siguientes programas:
Un juego de tres jugadores en el que gane el jugador que obtenga la mayor puntuacin acumulada
tras tres jugadas. El juego se debe desarrollar interactivamente de forma que se simule una tirada
cuando, tras requerirle al jugador que pulse la tecla intro, este lo haga.
Modificar el juego para que el usario elija el nmero de jugadores.
Un juego que consista en no pasarse de 18 puntos, tras realizar el nmero de tiradas que cada jugador
estime oportuno (cada jugador puede decidir pasar). Gana el jugador que ms cerca se quede de 18,
pero sin pasarse.

++

Ejemplo 83: Paso de argumentos a una funcin. Cuando se le pasa un valor simple a una funcin
mediante un argumento real, se copia el valor del argumento real a la funcin. Por tanto, se puede
modificar el valor del argumento formal dentro de la funcin, pero el valor del argumento real en el
cuerpo principal desde el que se efecta la llamada no cambiar. Este procedimiento para pasar el valor
de un argumento a una funcin se denomina paso por valor. He aqu un sencillo programa en C que
contiene una funcin que modifica el valor de su argumento.
#include <stdio.h>
void modificar(int a);
/* prototipo de funcin */
main( )
{
int a = 2;
printf("\na = %d (desde main, antes de llamar a la funcin)", a);
modificar(a) ;
printf("\na = %d (desde main, despus de llamar a la funcin)", a);
}
void modificar(int a)
{
a *= 3;
printf("\n\na = %d (desde la funcin, modificando valor) ", a);
return;
}
Se visualiza el valor original de a (a = 2) cuando comienza la ejecucin de main. Este valor se pasa a la
funcin modificar, en donde se multiplica por 3 y se visualiza el nuevo valor. Ntese que es el valor
alterado del argumento formal el que se visualiza en la funcin. Finalmente, el valor de a en main (el
argumento real) se vuelve a visualizar, despus de haberse devuelto el control a main desde modificar.
Cuando se ejecuta el programa, se genera la siguiente salida:
a = 2 (desde main, antes de llamar a la funcin)
a = 6 (desde la funcin, modificando el valor)
a = 2 (desde main, despus de llamar a la funcin)

- 65 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Estos resultados muestran que a no se ha modificado dentro de main, aunque se haya modificado el valor
correspondiente de a en modificar.
Pasar un argumento por valor tiene sus ventajas e inconvenientes. Algo positivo es que permite que pueda
proporcionarse como argumento real una expresin en lugar de necesariamente una variable. Es ms, si el
argumento real es una simple variable, se protege su valor de posibles alteraciones por parte de la funcin.
Por otra parte, impide que se transfiera informacin desde la funcin hasta el punto de llamada mediante
los argumentos. Por tanto, el paso por valor implica que la transferencia de informacin slo pueda
realizarse en un sentido.
Ejemplo 84: Clculo de depreciacin. Vamos a retomar el ejemplo 71, reescribiendo el programa para
que utilice una funcin por separado para cada mtodo. Esto nos permite organizar el programa de una
forma mucho ms clara:
/* calcular la depreciacin mediante tres mtodos diferentes */
#include <stdio.h>
#include <ctype.h>
void lr(float val, int n);
/* prototipo de funcin */
void bdd(float val,
int n); /* prototipo de funcin */
void sda(float val,
int n); /* prototipo de funcin */
void escribir_salida(int anual, float depreciacion, float valor);
/* prototipo de funcin */
main( )
{
int n, eleccion = 0;
float val;
char resp1 = 'S', resp2 = 'S';
while (toupper(resp1) = 'N') {
/* leer datos de entrada */
if (toupper(resp2) != 'N') {
printf("\nValor original: ");
scanf("%f", &val);
printf("Numero de aos: ");
scanf("%d", &n);
}
printf("\nMtodo:
(l-LR 2-BDD 3-SDA) ");
scanf("%d", &eleccion);
switch (eleccion) {
case 1: /* mtodo de lnea recta */ '
printf("\nMtodo de lnea recta\n\n");
lr(val, n);
break;
case 2: /* mtodo de balance doblemente declinante */
printf("\nMtodo de balance dobl. decl. \n\n");
bdd(val, n);
break;
case 3: /* mtodo de la suma dgitos aos */

- 66 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\nMtodo de la suma dgitos aos\n\n");


sda(val, n);
}
printf("\n\nMs clculos? (S/N) ");
scanf("%1s", &resp1);
if (toupper(resp1) != 'N') {
printf("Introducir nuevos de datos? (S/N) ");
scanf("%1s", &resp2);
}
}
printf("\nHasta luego y que tenga un buen da\n");
}
void lr(float val, int n) /* mtodo de lnea recta */
{
float deprec;
int anual;
deprec = val/n;
for (anual = 1; anual <= n; ++anual) {
val -= deprec;
escribir_salida (anual, deprec, val);
}
return;
}
void bdd(float val, int n) /* mtodo de balance doble. declinante */
{
float deprec;
int anual;
for (anual = 1 anual <= n ++anual) {
deprec = 2*val/n;
val -= deprec;
escribir_salida (anual, deprec, val);
}
return;
}
void sda(float val, int n) /* mtodo de la suma de los dgitos de
los aos */
{
float aux, deprec;
int anual;
,
aux = val;
for (anual = 1 anual <= n ++anual) {
deprec = (n-anual+1)*aux / (n*(n+1)/2) ;
val -= deprec;
escribir_salida(anual, deprec, val);

- 67 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
return;
}
void escribir_salida(int anual, float depreciacion, float valor)
/* escribir los datos de salida */
{
printf("Fin de ao %2d" , anual);
printf(" Depreciacion: %7.2f", depreciacion);
printf(" Valor actual: %8.2f\n", valor);
return;
}
Ntese que an se emplea la instruccin switch, como en el Ejemplo 71, aunque ahora hay slo tres
opciones en lugar de cuatro. La opcin cuarta, que finalizaba la ejecucin en la versin anterior, se
maneja ahora mediante el dilogo interactivo al final de los clculos. Se proporciona ahora una funcin
por separado para cada tipo de clculos. Cada una de estas funciones incluye los argumentos formales val
y n, que representan el valor original del objeto y su tiempo de vida, respectivamente. Ntese que el valor
de val es alterado dentro de cada funcin, aunque el valor original permanece sin alteracin dentro de
main. Es esto lo que permite repetir el conjunto de clculos con el mismo valor de entrada.
La ltima funcin, escribir_salida, hace que los resultados de cada conjunto de clculos se
escriban ao a ao. Se accede las otras funcionesa. En cada llamada a escribir_salida, el valor modificado
de val se transfiere como argumento real, junto con el ao en curso (anual) y la depreciacin del ao en
curso (deprec). Ntese que estas cantidades son llamadas valor, anual y depreciacion, respectivamente,
dentro de escribir_salida.
Recursividad. Se llama recursividad a un proceso mediante el que una funcin se llama a s misma de
forma repetida, hasta que se satisface alguna determinada condicin. El proceso se utiliza para
computaciones repetitivas en las que cada accin se determina en funcin de un resultado anterior. Se
pueden escribir de este modo muchos problemas iterativos (repetitivos). Para que un problema se pueda
resolver recursivamente se deben cumplir al menos dos condiciones:

2.

El problema se debe poder escribir en forma recursiva. Por ejemplo, el factorial de un cierto nmero
n! = n (n - 1) (n - 2)tambin puede expresarse de forma recursiva como n! = n (n - 1)!.
La instruccin del problema debe incluir una condicin de fin. De lo contrario la funcin se llamar
a s misma de forma indefinida hasta desbordar la memoria del ordenador y provocar un error de
ejecucin.

Ejemplo 85: Llevemos a cabo el programa clsico de calcular el factorial de un cierto nmero (ejemplos
20 y 78 ) esta vez mediante una funcin recursiva:
/* calcular el factorial utilizando recursividad */
#include <stdio.h>
long int factorial (int n); /* prototipo de funcin */
main()
{
int n;
/* leer la cantidad entera */

- 68 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("n = ");
scanf("%d", &n);
/* calcular Y visualizar el factorial */
printf("n! = %ld\n", factorial(n));
}
long int factorial(int n) /*calcula el factorial*/
{
if(n <= 1)
return(1);
else
return(n*factorial(n-1));
}
La parte main del programa simplemente lee la cantidad entera n y llama a continuacin a la funcin
factorial. (Recurdese que utilizamos enteros largos para este clculo porque los factoriales son
cantidades enteras muy grandes, aun para valores pequeos de n.) La funcin factorial se llama a s
misma recursivamente, con un argumento real (n -1) que decrece en cada llamada sucesiva. Las llamadas
recursivas terminan cuando el valor del argumento real se hace igual a 1. Ntese que esta forma de la
funcin factorial es ms sencilla que la que aparece en el Ejemplo 78.
Cuando se ejecuta una procedimiento recursivo, las llamadas no se ejecutan inmediatamente, sino que se
colocan en una pila hasta que se alcanza la condicin de finalizacin. Entonces se ejecutan las llamadas
acumuladas a la funcin en orden inverso a como se generaron, como si se fuera vaciando la pila de
arriba abajo. Este proceso se ilustra en la figura que aparece a continuacin:
Las llamadas a factorial se generan en el siguiente orden
factorial(4)
factorial(3)
factorial(2)
Pero se ejecutan en orden inverso:
factorial(1)
PILA
factorial(1)
1
factorial(2)
factorial(2)=2* factorial(1)=2*1=2
factorial(3)
factorial(3)=3* factorial(2)=3*2=6
factorial(4)
factorial(4)=4* factorial(3)=4*6=24
Esta pila es lo que los angloparlantes denominan una estructura de tipo last-in, first out o, lo que es lo
mismo, el ltimo que entra es el primero que sale.

Ejemplo 86: Escritura inversa. El siguiente programa hace uso del orden inverso en la ejecucin de las
pilas de las funciones recursivas para leer, carcter a carcter, una lnea de texto y escribirla en orden
inverso:
/* leer una lnea de texto y escribirla en orden inverso utilizando
recursividad */
#include <stdio.h>
#define EOLN '\n'
void inverso (void);
/* prototipo de funcin */

- 69 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

main( )
{
printf("Introduce una lnea de texto debajo\n")
inverso();
}
void inverso (void)
/* lee una lnea de caracteres y la invierte */
{
char c;
if((c = getchar()) = EOLN) inverso();
putchar(c);
return;
}
La funcin main en este programa simplemente se ocupa de presentar un rtulo y llamar a la funcin
inverso, iniciando el proceso recursivo. La funcin recursiva inverso procede entonces a leer caracteres
hasta que se encuentre la condicin final de lnea (\n). Cada llamada a esta funcin hace que se introduzca
en la pila un nuevo carcter (un valor nuevo de c). Una vez que se encuentra el final de la lnea, se sacan
los caracteres de la pila y se visualizan ende forma que el ltimo en ser leido es el primero en ser escrito.
El resultado es que los caracteres se visualizan de forma inversa a como se introdujeron.
mbito de definicin de una variable. Existen dos criterios para clasificar las variables de un programa
en C: por sus tipo de datos y por su mbito. El tipo de datos se refiere al tipo de informacin
reperesentada por la variable (nmeros enteros, nmeros decimales, caracteres, etc.). El mbito hace
referencia a las partes del programa en las que dicha variable va a ser reconocida. Desde este segundo
punto de vista se distinguen dos tipos de variables:
1. Variables locales. Definidas dentro de una funcin.
1.1 Variables locales automticas. Su mbito est confinado a la funcin. Cualquier variable
declarada dentro de una funcin se interpreta como una variable automtica a menos que se
especifique lo contrario (todas las variables encontradas en los ejemplos anteriores han sido
variables automticas). As,
auto int a, b, c;
es equivalente a
int a, b, c;
y, por lo tanto, no es necesario especificar la palabra reservada auto al principio de cada
declaracin de variable. Las variables automticas definidas en funciones diferentes sern
independientes unas de otras, incluso si tienen el mismo nombre. En cierto modo, podemos
imaginar que las variables de este tipo se crean cuando se invoca la funcin que las contiene y se
destruyen al finalizar dicha funcin.
Si una variable automtica no es inicializada de alguna manera, su valor inicial ser impredecible
y, probablemente, incomprensible. Una variable automtica no mantiene su valor cuando se

- 70 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

transfiere el control fuera de la funcin en que est definida. Por tanto, cualquier valor asignado a
una variable automtica dentro de una funcin se perder una vez que se sale de la funcin.
1.2 Variables locales estticas. Tienen el mismo mbito que las anteriores, pero retienen sus
valores a lo largo de todo el programa. Como consecuencia, si se sale de la funcin y luego se
vuelve a entrar, las variable estticas retienen sus valores previos. Sin embargo, slo son accesibles
desde la funcin en la que estn definida, permaneciendo ocultas para el resto del programa. La
declaracin se realiza mediante la palabara reservada static:
static float a;
2. Variables globales o externas. Definidas fuera de todas las funciones. Puede accederse a ellas desde
cualquier funcin. Esto quiere decir que podemos asignarle un valor dentro de una funcin y usarlo
despus en otra funcin. Proporcionan, por lo tanto, un mecanismo adecuado de transferencia de
informacin entre funciones. La declaracin se debera realizar mediante la palabara reservada extern:
extern float a;
aunque debido a su posicin dentro del programa, resulta la mayora de las veces redundante, siendo
habitualmente omitida (una declaracin situada fuera de cualquier funcin se sobreentiende que es
externa. De hecho, algunos compiladores prohben la aparicin del especificador extern dentro de la
definicin de variable externa).Las variables externas siempre se inicializan por defecto cero a menos que
se les asigne inicialemente algn otro valor.
No es inusual definir variables automticas o estticas con el mismo nombre que las variables
externas. En tales situaciones las variables locales tienen precedencia sobre las variables externas, aunque
los valores de las variables externas no se vern afectados por la manipulacin de las variables locales.
Por tanto, las variables externas mantienen su independencia frente a las variables automticas o estticas.
Finalmente, debemos tener en cuenta que las formaciones pueden declararse tanto automticas
como externas, si bien las formaciones automticas no se pueden inicializar.
Ejemplo 87: Nmero medio de caracteres por lnea. Escribamos a continuacin un programa en C que
lea varias lneas de texto y determine el nmero medio de caracteres (incluyendo puntuacin y espacios
en blanco) en cada lnea. Estructuraremos el programa de tal manera que contine leyendo lneas hasta
encontrar una lnea cuyo primer carcter sea \n. Utilizaremos una funcin (contlinea) que lee una lnea de
texto y cuenta el nmero de caracteres, excluyendo el carcter de nueva lnea (\n) que marca el fin de la
lnea. La funcin principal mantiene una suma acumulativa, as como el nmero total de lneas que se han
ledo. La funcin funcin principal llama repetitivamente a contlinea (leyendo una nueva lnea cada vez)
hasta que se encuentre una lnea vaca. El programa divide entonces el nmero acumulado de caracteres
por el nmero total de lneas para obtener la media.
He aqu el programa completo:
/* leer varias lneas de texto y determinar el nmero medio de caracteres por lnea */
#include <stdio.h>
int contlinea(void);
main()

- 71 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

{
int n;
int cont = O;
int suma = O;
float media;

/* nmero de caracteres en una lnea */


/* nmero de lneas */
/* nmero total de caracteres */
/* nmero medio de caracteres por lnea */

printf("Introducir el texto debajo:\n");


/* leer una lnea de texto y actualizar los contadores */
while ((n = contlinea()) > O) {
suma += n;
++cont;
}
media = (float) suma / cont;
printf("\nNmero medio de caracteres por lnea: %5.2f", media);
}
int contlinea(void)
/* leer una lnea de texto y contar el nmero de caracteres */
{
char linea[80];
int cont = O;
while ((linea[cont] = getchar()) != '\n')
++cont;
return (cont);
}
Vemos que main contiene cuatro variables automticas: n, cont, suma y media, mientras que contlinea
contiene dos: linea y c o n t. (Ntese que linea es una formacin de caracteres de 80 elementos que
representa el contenido de una lnea de texto.) Tres de estas variables tienen asignados valor inicial cero.
Ntese tambin que cont tiene diferente significado dentro de cada funcin. Dentro de contlinea, cont
representa el nmero de caracteres en una lnea, mientras que en main, cont representa el nmero total de
lneas que se han ledo. Adems, cont se pone a cero cada vez que se accede a contlinea. Esto no afecta al
valor de cont dentro de main, ya que una variable es independiente de la otra. Esto estara claro si las
variables tuvieran nombres distintos, pero se ha usado el mismo nombre para las dos variables con el fin
de dejar de manifiesto la independencia de las variables automticas en funciones diferentes.
Podemos modificar el programa con el fin de utilizar variables externas:
/*leer varias lneas de texto y determinar el nmero medio de caracteres por lnea */
#include <stdio.h>
int suma = 0;
int lineas = 0;

/* nmero total de caracteres */


/* nmero total de lneas */

int contlinea(void);
main()
{
int n;

/* nmero de caracteres en una lnea */

- 72 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

float media;
/* nmero medio de caracteres por lnea */
printf("Introducir el texto debajo:\n");
/* leer una lnea de texto y actualizar los contadores */
while ((n = contlinea()) > 0) {
suma += n;
++lineas;
}
media = (float) suma / lineas;
printf("\nNmero medio de caracteres por lnea: %5.2f", media);
}
/* leer una lnea de texto y contar el nmero de caracteres */
int contlinea(void)
{
char linea[80];
int cont = 0;
while ((linea[cont] = getchar()) = '\n')
++cont;
return (cont);
}
Obsrvese que suma y lineas son variables externas que representan el nmero total (acumulado) de
caracteres ledos y el nmero total de lneas, respectivamente. A ambas variables se les ha asignado el
valor inicial cero. Estos valores se modifican sucesivamente dentro de main, segn se leen lneas
adicionales de texto. En la versin anterior del programa, se utilizaban dos variables automticas
diferentes llamadas cont en partes diferentes del programa. En la versin actual, sin embargo, las
variables que representan estas mismas cantidades tienen nombres distintos, pues una de las variables
(lineas) es ahora una variable externa. Debe destacarse que a suma y lineas no hay que asignarles
explcitamente el valor cero, puesto que las variables externas siempre se inicializan a cero a menos que
se designe algn otro valor. Se incluye el valor explcito de inicializacin cero para clarificar la lgica del
programa.

Ejemplo 88: Bsqueda de un mximo. Supngase que queremos encontrar el valor particular de x que
hace mxima la funcin y = x cos(x) en el intervalo limitado por x = 0 a la izquierda y x = a la derecha.
Una forma obvia de resolver este problema sera evaluar x = 0, x = 0.0001, x = 0.0002, ..., x = 3.1415 y x
= 3.1416 y determinar el mayor de stos por inspeccin visual. Este mtodo no sera muy eficiente y
requerira de intervencin humana para obtener el resultado final. En su lugar, utilizaremos el siguiente
esquema de eliminacin, que es un buen mtodo para todas las funciones que slo tiene un mximo (un
solo pico) dentro del intervalo de bsqueda:
1.Empezamos con dos puntos de bsqueda en el centro del intervalo de bsqueda, marcando una distancia
muy pequea entre ellos, como se muestra en la figura. Se emplea la siguiente notacin:

xi

xd

a = extremo izquierdo del intervalo

- 73 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

xi = punto interior de bsqueda a la izquierda


xd = punto interior de bsqueda a la derecha
b = extremo derecho del intervalo
sep = distancia entre xi y xd.
Si se conocen a, b y sep, entonces los puntos interiores pueden calcularse como
xi = a + .5 * (b -a -sep)
xd = a + .5 * (b -a + sep) = xi + sep
2. Evaluemos la funcin y = x cos(x) en xi y x xd. Llamemos a estos valores yi e yd, respectivamente.
yi = x*cos(xi)
yd = x*cos(xd)
Supngase que yi > yd. Entonces el mximo est en alguna parte entre a y xd. Por tanto, se retendr slo
esta parte del intervalo de bsqueda (vase de nuevo la figura).

xi

nuevo xi

xd

nuevo xd

xd

Por el contrario, si yi < yd

xi

xi

nuevo xi

xd

nuevo xd

Ahora nos referiremos al punto viejo xd como b, pues ste es el extremo derecho del nuevo intervalo de
bsqueda, y se generan otros dos nuevos puntos de bsqueda xi y xd. Estos puntos se localizarn en el
centro del nuevo intervalo de bsqueda, separados una distancia sep.
Por otro lado, ahora supngase que en nuestro intervalo original de bsqueda el valor de y d
resultamayor que yi. Esto indicaria que nuestro intervalo de bsqueda se halla entre xi y b. Por tanto,
renombramos el punto originalmente llamado xi como a y generamos dos nuevos puntos de bsqueda, xi

- 74 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

y xd, en el centro del nuevo intervalo de bsqueda. Continuamos generando un nuevo par de puntos de
bsqueda en el centro de cada nuevo intervalo, comparando los respectivos valores de y, y eliminando
una parte del intervalo de bsqueda hasta que el intervalo de bsqueda se hace menor que 3 * sep. Una
vez que esto sucede no se pueden distinguir los puntos interiores de los lmites. Por tanto finaliza la
bsqueda. Cada vez que hacemos una comparacin entre yi y yd, eliminamos la parte del intervalo de
bsqueda que contiene el valor ms pequeo de y. Si ocurre que ambos valores interiores son idnticos
(lo cual puede suceder pero es inusual), entonces el procedimiento de bsqueda se detiene y se supone
que el mximo tiene lugar en el centro de los dos ltimos puntos internos.
Una vez acabada la bsqueda, tanto porque el intervalo de bsqueda se ha hecho lo suficientemente
pequeo o porque los dos puntos interiores tienen valores idnticos de y, se puede calcular la localizacin
aproximada del mximo como
xmax = 0.5 * (xi + xd);
Consideremos la estructura general del programa para el caso general en que a y b son cantidades de
entrada pero sep tiene un valor fijo de 0.0001.
l. Asignar un valor sep = 0.0001.
2. Leer los valores de a y b.
3. Repetir lo siguiente hasta que yi se haga igual a yd (el mximo estar en el punto medio), o
elvalor ms reciente de (b -a) sea menor o igual que (3 * sep):
a) Generar los dos puntos interiores, xi y xd.
b) Calcular los correspondientes valores de yi e yd, y determinar cul es mayor.
c) Reducir el intervalo de bsqueda, eliminando la parte que no contenga el valor mayor de
y.
4. Evaluar xmax e ymax.
5. Escribir los valores de xmax e ymax, y parar.
Para traducir esta estructura en un verdadero programa es necesario escribir una funcin que evalue la
funcin matemtica y = x cos (x) .Llamemos a esta funcin curva. Esta funcin se puede escribir
fcilmente como se muestra a continuacin:
/* evaluar la funcin y = x * cos(x) */
double curva(double x)
{
return(x * cos(x));
}
Obsrvese que cos(x) es una llamada a una funcin de biblioteca de C.(math.h)
El tercer paso, en el que se realiza la reduccin del intervalo, puede llevarse a cabo mediante la funcin
reducir.
/* rutina de reduccin del intervalo */
void reducir(void)
{
xi = a + 0.5 * (b -a -CNST);
xd = xi + CNST;
yi = curva(xi);
yd = curva(xd;

- 75 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

if (yi > yd) {/* retener el intervalo izquierdo */


b = xd;
return;
}
if (yi < yd) {/* retener el intervalo derecho */
a =xi;
return;
}
Obsrvese, que los valores representados por las variables a, b, xi, xd, yi e yd, deben ser externas (de
lo contrario sus modificaciones no se podran trnsferir al cuerpo principal del programa desde reducir).
De hecho, toda la informacin transferida es a travs de las variables externas, ya que l instruccin return
no devuelve nada.
El programa completo quedara as:
/* encontrar el mximo de una funcin en un intervalo especificado */
#include <stdio.h>
#include <math.h>
#define CNST 0.0001
double a, b, xi, yi, xd, yd; /* variables globales */
void reducir(void) ;
/* prototipo de funcin */
double curva(double xi) ; /* prototipo de funcin */
main()
{
double xmax, ymax;
/* leer datos de entrada (puntos extremos del intervalo) */
printf("\na = ") ;
scanf("%lf", &a);
printf ("b = ") ;
scanf("%lf", &b) ;
/* bucle de reduccin del intervalo */
do
reducir() ;
while ((yi = yd) && ((b -a) > 3 * CNST)) ;
/* calcular xmax e ymax y escribir los resultados */
xmax = 0.5 * (xi + xd);
ymax = curva(xmax);
printf("\nxmax = %8.6lf ymax = %8.6lf", xmax, ymax);
}
/* rutina de reduccin del intervalo */
void reducir(void)
{
xi = a + 0.5 * (b -a -CNST);
xd = xi + CNST;
yi = curva(xi);
yd = curva(xd);

- 76 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

if (yi > yd) {


/* retener el intervalo izquierdo */
b = xd;
return;
}
if (yi < yd)
/* retener el intervalo derecho */
a = xi;
return;
}
/* evaluar la funcin y = x * cos(x) */
double curva(double x)
{
return(x * cos(x));
}

10. Formaciones (arrays).


Definicin y uso de las formaciones.
En C, una formacin no es ms que un conjunto de posiciones continuas de memoria. La direccin
ms baja corresponde al primer elemento (y se encuentra almacenada en el nombre de la formacin), y la
ms alta al ltimo.
POSICIN
VALOR

0
11

1
-2

2
4

3
1

En trminos generales, la defininicin de una formacin unidimensional es de la forma


tipo_de almacenamiento tipo_de_dato formacin[dimensin]
El tipo de almacenamiento es opcional; por defecto es automatic para las formaciones definidas dentro de
una funcin y extern para las formaciones definidas fuera.
Tamao de una formacin.
La dimensin no puede ser una variable en C, por lo que se requiere que a cada formacin tenga fijado de
antemano su longitud. Cuando se declara:
char cod_art[l0];
se est definiendo un formacin de caracteres que tiene 10 elementos. Los formaciones en C empiezan en
cero, por lo que los elementos de la formacin se numeran de 0 a 9. El compilador de C no hace
comprobacin de lmites en los formaciones, por lo que es posible sobreescribir fuera de un formacin, lo
que puede dar lugar a errores de ejecucin. Es responsabilidad del programador que se respeten los
mrgenes de validez de los ndices de la formacin.
Ejemplo 89: En algunas ocasiones es conveniente definir el tamao de una formacin a travs de una
constante simblica en vez de utilizar una cantidad entera fija. Esto puede simplificar la modificacin del
- 77 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

programa, ya que todas las referencias al tamao mximo de la formacin (por ejemplo, en bucles for)
pueden ser alteradas cambiando simplemente el valor de la constante simblica. En este ejemplo la
constante simblica TAMANO permite la modificacin del tamao mximo de la formacin letras sin
necesidad de modificar los bucles for:
/* leer una lnea un texto en minsculas y escribirla en maysculas */
#include <stdio.h>
#include <ctype.h>
#define TAMANO 80
main( )
{
char letras[TAMANO];
int cont;
/* leer la lnea */
for (cont = 0; cont < TAMANO; ++cont)
letras[cont] = getchar() ;
/* escribir la lnea en maysculas */
for (cont = 0; cont < TAMANO; ++cont)
putchar(toupper(letras[cont])) ;
}
Inicializacin de formaciones
Las formaciones automticas, a diferencia de las variables automticas, no pueden ser inicializadas. Sin
embargo, las definiciones de formaciones externas y estticas pueden incluir, si se desea, la asignacin de
valores iniciales. Los valores iniciales deben aparecer en el orden en que sern asignados a los elementos
individuales de la formacin, encerrados entre llaves y separados por comas. La forma general es
tipo_de almacenamiento tipo_de_dato formacin[dimensin]
= {valor1, valor2,...,
valorn};
donde valor1 se refiere al valor del primer elemento de la formacin, valor2 al valor del segundo
elemento, y as sucesivamente. La presencia de la expresin, que indica el nmero de elementos de la
formacin, es opcional cuando estn presentes los valores iniciales.
Ejemplo 90: A continuacin se muestran varias definiciones de formaciones que incluyen la asignacin
de valores iniciales:
int digitos[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
static f1oat x[6] = {0, 0.25, 0, -0.50, 0, 0};
char color[4] = {'R', 'O', 'J', 'O'};
El resultado de estas asignaciones iniciales, en trminos de los elementos individuales de la formacin, es
el siguiente:
digitos[0] = 1
digitos[l] = 2
digitos[2] = 3
digitos[3] = 4

x[0] =
x[l] =
x[2] =
x[3] =

0
0.25
0
-0.50

- 78 -

color[0] =
co1or[1] =
color[2] =
co1or[3] =

'R'
'O'
'J'
'O'

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

digitos[4] = 5
digitos[5] = 6
digitos[6] = 7
digitos[7] = 8
digitos[8] = 9
digitos[9] = 10

x[4] =
x[5] =

0
0

Todos los elementos de la formacin que no tienen asignados valores iniciales explcitos sern puestos
automticamente a cero. As, las siguientes definiciones de formaciones
int digitos[10] = {3, 3, 3};
static float x[] = {-0.3, 0, 0.25};
dan lugar a
digitos[O] = 3
digitos[l] = 3x[l] = 0
digitos[2] = 3x[2] = 0.25
digitos[3] = 0x[3] = 0
digitos[4] = 0x[4] = 0
digitos[5] = 0x[5] = 0
digitos[6] = 0
digitos[7] = 0
digitos[8] = 0
digitos[9] = 0

x[0] = -0.3

Obsrvese que el tamao de una formacin numrica no necesita se especificado explcitamente


cuando se incluyen los valores iniciales como una parte de la definicin de la formacin.
En las formaciones externas y estticas podemos inicializar facilmente sus valores a cero aadiendo
tras la definicin
for(i=0 ; i< TAMANO; i++) tabla[i] = 0;
Ejemplo 91: Las cadenas de caracteres (formaciones de caracteres) son manejadas de modo algo
diferente. En particular, cuando se le asigna una cadena de caracteres constante a una formacin esttica o
externa como parte de la definicin, la especificacin del tamao de la formacin normalmente se omite.
El tamao adecuado ser asignado automticamente. Esto incluir la provisin para el carcter nulo \0,
que se aade automticamente al final de cada cadena de caracteres. Para entenderlo mejor, consideremos
las siguientes definiciones de formaciones de caracteres:
char color[4] = "ROJO";
char color[] = "ROJO";
El resultado de estas asignaciones iniciales no es el mismo debido al carcter nulo, \0, que se aade
automticamente al final de la segunda cadena. De este modo, los elementos de la primera formacin son
color[0] = 'R'

- 79 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

color[l] = '0'
color[2] = 'J'
color[3] = '0'
mientras que los elementos de la segunda son
color[0] = 'R'
color[l] = 'O'
color[2] = 'J'
color[3] = 'O'
color[4] = '\0'
Por tanto, la primera forma es incorrecta, ya que el carcter nulo \0 no se incluy en la formacin. La
definicin de la formacin podra haberse escrito como
char color[5] = "ROJO";
Esta definicin es correcta, ya que ahora definimos una formacin de cinco elementos que incluye un
elemento para el carcter nulo. Sin embargo, muchos programadores prefieren la primera forma, que
omite el especificador de tamao.
Procesamiento de una formacin
En C no se permiten operaciones que involucren formaciones completas. As, si a y b son formaciones
similares (mismo tipo de datos, misma dimensionalidad y mismo tamao), las operaciones de asignacin,
de comparacin, etc., deben realizarse elemento por elemento. Esto se hace normalmente dentro de un
bucle, donde cada iteracin se usa para procesar un elemento de la formacin. El nmero de iteraciones
ser igual al nmero de elementos de la formacin procesada.
Ejemplo 92: El siguiente programa inserta las edades de 5 alumnos en las casillas de la formacin edades:
#include <stdio.h>
#include <conio.h>
void main()
{
int edades[5], ind;
/*insercin de los valores*/
for(ind = 0 ; ind <= 4; ind++){
printf("\nIntroduce la edad del %d: ", ind);
scanf("%d",&edades[ind]);
}
clrscr(); /*Borra la pantalla*/
/*lectura de los valores*/
printf( "\nVALORES INTRODUCIDOS");
printf("\n");
for(ind = 0; ind <= 4; ind++){
printf("\nedades[%d]=%d, ind, edades[ind]);
}

- 80 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Debemos observar que la instruccin scanf incluye un smbolo & delante de edades[ind] ya que se refiere
a un elemento simple de la formacin y no a la formacin entera.
Ejemplo 93: Te proponemos que completes el siguiente programa para que sea capaz de rellenar una
formacin con cien nmeros enteros generados al azar (entre 0 y 10) y contar luego cuantos de ellos son
mayores que 5 (vase el ejemplo 82)
#define TAMANO 100
void main()
{
int i, contador;
int tabla[TAMANO];
/*Introducimos en cada casilla un nmero aleatorio entre 0 y 10*/

/*Contamos los mayores que 5*/


contador = 0;
for(i = 0; i < TAMANO; i++)
if (tabla[i] > 5)
contdor++;
printf(\nHay %d mayores que 5, contador);
}
Ejemplo 94: Mediante el siguiente programa se podra hallar la media de la lista de nmeros almacenada
en la formacin tabla:
#define TAMANO 100
void main()
{
int i, suma;
float media;
int tabla[TAMANO];
/*Inicializamos la formacin con valores*/
/*Sumamos los valores*/
suma = 0;
for{i=0; i<TAMANO; i++)
suma = suma + tabla[i];
media =(float)/ (float)TAMANO;
printf("La media es %f/n", media);
}
Ejemplo 95: Desviaciones respecto a la media. Supongamos que queremos leer una lista de n cantidades
en coma flotante y luego calcular su media. Sin embargo, adems de calcular simplemente la suma, en
esta ocasin calcularemos tambin la desviacin de cada cantidad numrica respecto de la media,
mediante la frmula
d= xi- media

- 81 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

donde xi representa cada una de las cantidades dadas, i = 1,2, ..., n, y media la media calculada. Para
resolver este problema debemos almacenar las cantidades dadas en una formacin unidimensional de
elementos en coma flotante a la que llamaremos lista. Debe quedar claro que, a diferencia de lo que
ocurra en ejemplos anteriores en los que cada nmero era sustituido por su sucesor en la lista, en este
caso es preciso conservar los nmeros introducidos para calcular su correspondiente desviacin respecto
de la media;
/* calcular la media de n nmeros, despus computar la desviacin de cada nmero respecto a la media */
#include <stdio.h>
main( )
{
int n, cont;
float media, d, suma = 0;
float lista[100];
/* leer el valor de n */
printf("\nCuantos nmeros para calcular la media? ");
scanf("%d", &n);
printf("\n") ;
/* leer los nmeros y calcular su suma */
for (cont = 0; cont < n; ++cont) {
printf("i = %d
x = ", cont + 1);
scanf("%f", &lista[cont]);
suma += lista[cont];
}
/* calcular la media y escribir la respuesta */
media = suma / n;
printf("\nLa media es %5.2f\n\n", media);
/* calcular y escribir las desviaciones respecto de la media */
for (cont = 0; cont <n; ++cont) {
d = lista[cont] -media;
printf("i=%d x=%5.2f d=%5.2f\n", cont+1, lista[cont] , d);
}
}
En algunas aplicaciones puede ser conveniente asignar valores iniciales a los elementos de una
formacin. Esto requiere que la formacin sea definida o bien globalmente (tipo extern), o bien
localmente (dentro de una funcin) como una formacin static. El siguiente ejemplo ilustra el uso de una
definicin global de formacin.
/* calcular la media de n nmeros, despus computar la desviacin de cada nmero respecto a la media */
#include <stdio.h>
int n = 5;
float lista[] = {3, -2, 12, 4.4, 3.5};
main()
{
int cont;

- 82 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

float media, d, suma = 0;


/* calcular la media y escribir la respuesta */
for (cont = 0; cont < n; ++cont)
suma += lista[cont] ;
media = suma / n;
printf("\nLa media es %5.2f\n\n", media) ;
/* calcular y escribir las desviaciones respecto de la media */
for (cont = 0; cont <n; ++cont) {
d = lista[cont] -media;
printf("i=%d x=%5.2f d=%5.2f\n", cont+1, lista[cont], d);
}
}
Ejemplo 95: La insercin de un elemento en una formacin aade un nuevo elemento a los que ya
tenemos almacenados en memoria. El borrado es la operacin inversa, es decir, la que elimina una
posicin. El siguiente programa inserta un elemento elem en la posicin pos de la formacin tabla:
#define TAMANO 100
void main()
{
int pos, elem;
int tabla[TAMANO];
int numero_de_elementos;
/* Introducimos 50 valores en el array */
numero_de_elementos = 50;
for(i=0; i<numero_de_elementos; i++)
tabla[i]= i;
"
/* Pedimos al usuario que introduzca un nmero y una posicin */
printf("Introduzca un entero: ");
scanf("%d\n", elem);
printf("INDIQUE LA POSICION QUE DESEA: ");
scanf("%d\n", pos);
/* Introducimos el elemento nuevo */
for(i = (numero_de_elementos-1); i >= pos-1; i-)
tabla[i+1] = tabla[i]; /*reordenamos*/
tabla[pos-1] = elem;
/* Actualizamos el nmero de elementos */
numero_de_elementos++;
}
Obsrvese que la dificultad aparece en el desplazamiento de los registros con el fin de hacer hueco al
nuevo.

- 83 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 96: Anlogamente podemos crear un programa que borre el elemento que ocupa la posicin pos
y reordene el resto:
#define TAMANO 100
void main()
{
int pos, elem;
int tabla[TAMANO];
int numero_de_elementos;
/* Introducimos 50 valores en el array */
numero_de_elementos = 50;
for(i=0; i<numero_de_elementos; i++)
tabla[i]= i;
"
/*Pedimos al usuario que fije la posicin que desea borrar*/
printf("INDIQUE LA POSICION QUE DESEA: ");
scanf("%d\n", pos);
/*Borramos el elemento de la posicin*/
elem = tabla[pos-1]; /*Lo guardamos, por si acaso, en elem*/
for(i = pos-1; i < numero_de_elementos; i++)
tabla[i]=tabla[i+1];
/*Actualizamos el numero de elementos*/
numero_de_elementos-;
}
Ahora que sabemos como crear y modificar una formacin, insertando y borrando valores, podemos
describir las operaciones de bsqueda de un elemento, ordenacin y mezcla de formaciones.
Ejemplo 97: Bsquedas en formaciones. Una de las operaciones ms frecuentes con formaciones
consiste en localizar la posicin que ocupa un determinado elemento. Las instrucciones bsicas para esta
labor seran:
/*recorre la formacin mientras no encuentre el elemento elem*/
for(i=0; (i < TAMANO) && (tabla[i] = elem); i++);
/*si supera el tamao de la formacion sin hallarlo, da error*/
if (tabla[i-1] = elem) {
printf(\nEl elemento %d ocupa la posicion %d, elem, i-1);}
else {
if(i >= TAMANO)
printf(El elemento no se encuentra en la formacion);};
En el caso de que la formacin est ordenada, el algoritmo de bsqueda puede optimizarse. Estudia las
siguientes posibilidades e intenta sealar las diferencias con el anterior:

- 84 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for(i = 0; (i < TAMANO) && tabla[i] > elem; i++);


if((i >= TAMANO) || (elem = tabla[i]))
printf(El elemento no se encuentra en la formacion);
Otro ms (este recibe el nombre de bsqueda binaria).:
i = 0;
j = TAMANO 1;
do
{
medio = ((i + j)-((i+j)%2))/2; /*parte entera de (i+j)/2*/
if(elem > tabla[medio]) j = medio 1;
else i = medio + 1;
} while((tabla[medio] = elem) && (i < j));
Para entenderlo, observa el proceso detallado en la figura adjunta:
Buscamos el 3:
Primera iteracin:

i=0

j=8
1

medio = 4
Segunda iteracin:

i=0

j=3
1

medio = 1
Tercera iteracin:

i=2

j=3
1

medio = 1
nmero 3 encontrado!
Ordenaciones.
La ordenacin de un vector, es un proceso que permite organizar sus datos siguiendo una secuencia
especifica; lo habitual es ordenar los datos de menor a mayor o viceversa. Existen muchos mtodos para
ordenar vectores, siendo los ms conocidos el mtodo de la burbuja y el mtodo por seleccin.En casi
todos los casos se intenta que los algoritmos de ordenacin no consuman ms memoria que la propia
formacin. Por ello se suelen desestimar aquellos mtodos que requieren el trasvase de datos de una

- 85 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

formacin a otra frente a aquellos que slamente hacen uso de una nica formacin sobre la que realizan
todos los cambios.
Mtodo de la burbuja: Este mtodo est basado en el principio de comparacin: el programa va
comparando elementos adyacentes y los intercambia de posicin cuando stos no se encuentren en el
orden especificado (ascendente o descen dente) . Los pasos del mtodo de la burbuja son:
1. Se compara el primer elemento del vector con el segundo y, si estn en el orden adecuado, el
programa los mantendr en sus posiciones; en caso contrario, los intercambiar.
2. A continuacin, se comparan los elementos segundo y tercero, tercero y cuarto, y as hasta
comparar el penltimo con el ltimo; en cada una de estas comparaciones, el programa dejar
colocados cada par de datos en el orden adecuado.
3. Una vez hecha la ltima comparacin (penltimo y ltimo elemento), debe volverse al primer paso
y repetir todo el proceso tantas veces como elementos tenga el vector menos 1; de ese modo se
asegura que los elementos del vector estn ordenados en su totalidad.
A continuacin puede verse un ejemplo grfico del mtodo de la burbuja para ordenar un vector de modo
ascendente. El vector inicial es:
0
2

1
7

2
9

3
3

4
1

Se comparan el primer elemento con el 2, el 2 con el 3, el 3 con el 4 y el 4 con el 5, realizndose


los intercambios necesarios para dejar, en cada comparacin, el nmero menor a la izquierda. Al final de
dichas comparaciones el vector sera:
0
2

1
7

2
3

3
1

4
9

Como el vector no est ordenado, se debe hacer otra serie de comparaciones para intercambiar los datos
necesarios. El resultado de esta serie sera:
0
2

1
3

2
1

3
7

4
9

El vector sigue an sin estar ordenado, por lo que debe realizarse una tercera tanda de comparaciones e
intercambios, cuyo resultado sera:
0
2

1
1

2
3

3
7

4
9

Como an no est ordenado, se vuelve a realizar una nueva tanda de comparaciones con sus
correspondientes intercambios, dando como resultado el vector totalmente ordenado; el proceso se da por
finalizado.
0
1

1
2

2
3

3
7

4
9

Ejemplo 98: A continuacin incluimos el programa que contiene las sentencias e instrucciones necesarias
para utilizar el mtodo de la burbuja en la ordenacin ascendente de un vector.
- 86 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include<stdio.h>
#include<conio.h>
void main()
{
int i, j, vecto[10], aux;
for(i=0; i<=9;i++){
printf(\nIntroduce el elemento %d: , i+1);
scanf(%d, &vector[i]);
}
for(i = 0; i <= 9; i++){
for(j = 0; j <= 9; j++){
if(vector[j]>vector[i+j]){
aux = vector[j];
vector[j] = vector[j+1];
vector[j+1] = aux;
}
}
}
clrscr();
printf(\nVector ordenado ascendentemente);
for(i=0; i<= 9; i++){
printf(\n%d elemento = %d, i+1, vector[i]);
}
}
Ejemplo 99: Si se analiza detenidamente el mtodo de la burbuja, se deduce la existencia de dos
posibilidades que podran mejorar el algoritmo utilizado en los programas:
1. El proceso terminar cuando, al finalizar una tanda de comparaciones, el vector quede completamente
ordenado; esto se podra saber utilizando una variable que indicara si la comparacin, dos a dos, de
todos los elementos del vector ha producido entre ellos algn intercambio, ya que, de no ser as, el
vector estara completamente ordenado. Esta variable tomar inicialmente el valor n, para indicar
que no se ha producido ningn intercambio; cuando al comparar dos elementos stos no se
encuentren en el orden deseado, adems de intercambiarlos, el programa asignar el valor s a la
variable.
2. Cuando se finaliza la primera tanda de comparaciones, en la ltima posicin del vector estar situado
el elemento mayor o el menor dependiendo del tipo de ordenacin realizada (ascendente o
descendente). Este hecho puede aprovecharse para que, en la segunda tanda de comparaciones, no
haga falta comparar el penltimo elemento con el ltimo; por el mismo razonamiento, en la tercera
tanda no har falta comparar el antepenltimo dato con el penltimo, y as sucesivamente. El modo
de evitar estas comparaciones innecesarias consiste en utilizar un nuevo bucle, ms interno, con un
valor de salida que debe ir disminuyendo con el bucle externo. El siguiente programa permite
ordenar un vector mediante el mtodo de la burbuja mejorado.

#include<stdio.h>

- 87 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include<conio.h>
void main()
{
int i, j, vecto[10], aux;
char cambio = n;
for(i=0; i<=9;i++){
printf(\nIntroduce el elemento %d: , i+1);
scanf(%d, &vector[i]);
}
i =0;
do{
cambio = n;
for(j = 0; j <= 9-i; j++){
if(vector[j]>vector[i+j]){
aux = vector[j];
vector[j] = vector[j+1];
vector[j+1] = aux;
cambio = s;
}
}
i++;
}while((cambio = s) && (i < 9));
clrscr();
printf(\nVector ordenado ascendentemente);
for(i=0; i<= 9; i++){
printf(\n%d elemento = %d, i+1, vector[i]);
}
}
Ejemplo 100: Mtodo de ordenacin por seleccin: La ordenacin por seleccin consiste en mantener fijo
el primer elemento y comparlo con todos los dems; cada vez que se encuentre con un elemento menor
que l (ordenacin ascendente) ambos debern ser intercambiados. Una vez comparado el primer
elemento con todos los dems, el proceso se repite con el segundo, que se comparar a su vez con todos
los elementos excepto con el primero. A continuacin se comparar el tercer elemento con todos excepto
con el primero y el segundo, y as sucesivamente hasta acabar:
#include<stdio.h>
#include<conio.h>
void main()
{
int i, j, vecto[10], aux;
for(i=0; i<=9;i++){
printf(\nIntroduce el elemento %d: , i+1);
scanf(%d, &vector[i]);
}
for(i = 0; i <= 9; i++){
for(j = i+1; j <= 9; j++){

- 88 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

if(vector[i]>vector[j]){
aux = vector[i];
vector[i] = vector[j];
vector[j] = aux;
}
}
}
clrscr();
printf(\nVector ordenado ascendentemente);
for(i=0; i<= 9; i++){
printf(\n%d elemento = %d, i+1, vector[i]);
}
}
Se propone como ejercicio al alumno que disee un programa capaz de incluir el contenido de dos
formaciones unidimensionales distintas en una tercera y presentar sus elementos ordenados.
Paso de formaciones a funciones.
Una formacin completa se puede introducir como argumento en una funcin. Sin embargo, la
manera en la que la formacin se pasa difiere mucho de la de una variable ordinaria. Para pasar una
formacin a una funcin hemos de tener en cuenta que:
1. El nombre de la formacin debe aparecer solo, sin corchetes ni ndices, como un argumento real
en la llamada a la funcin.
2. El correspondiente argumento formal se escribe de la misma manera, pero debe ser declarado
como una formacin en la declaracin de los argumentos formales. Cuando se declara una
formacin unidimensional como un argumento formal, la formacin se escribe con un par de
corchetes vacos. El tamao de la formacin no se especifica en la declaracin de argumentos
formales.
3. Se debe tener cuidado al escribir prototipos de funciones que incluyan argumentos de formacin.
Una pareja vaca de corchetes debe seguir al nombre de cada argumento de formacin, indicando
de este modo que el argumento es una formacin. Si no se incluyen los nombres de los
argumentos en una declaracin de funcin, entonces una pareja vaca de corchetes debe seguir al
tipo de datos de argumento de formacin.
Ejemplo 101:El siguiente esquema ilustra el paso de una formacin desde la parte principal de un
programa a una funcin:
float media(int a, float x[]);
main()
{
int n;
float med;
float lista[100];

med = media(n, lista);

/* prototipo de funcin */

/* DECLARACION de variable */
/* DECLARACION de variable */
/* DEFINICION de formacin */

- 89 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

float media(int a, float x[])


{

/* DEFINICION de funcin */

Dentro de main vemos una llamada a la funcin media. Esta llamada a funcin contiene dos
argumentos reales, la variable entera n y la formacin unidimensional en coma flotante lista. Obsrvese
que lista aparece como una variable ordinaria en la llamada a la funcin; es decir, no se incluyen los
corchetes. La primera lnea de la definicin de la funcin incluye dos argumentos formales, a y x. La
declaracin de argumentos formales establece que a es una variable entera y x una formacin
unidimensional en coma flotante. Existe pues una correspondencia entre el argumento real n y el
argumento formal a. Anlogamente, existe una correspondencia entre el argumento real lista y el
argumento formal x. Debe tenerse en cuenta que el tamao de x no se especifica dentro de la declaracin
formal de argumentos. Ntese, as mismo, que el prototipo de funcin se podra haber escrito sin los
nombres de los argumentos, como
float media(int, float[]);

/* prototipo de funcin */

Ejemplo 102: El hecho de que una formacin pueda ser modificada globalmente dentro de una funcin
proporciona un mecanismo adecuado para mover mltiples datos a/o desde una funcin a la parte del
programa desde la que se hizo la llamada. Simplemente se pasa la formacin a la funcin y se alteran sus
elementos dentro de ella. O, si la formacin original debe ser preservada, se copia la formacin (elemento
por elemento) dentro de la parte del programa que hace la llamada, se pasa la copia a la funcin y se
realizan las alteraciones. El paso de una formacin desde la parte principal de un programa a una funcin
se usa en este ejemplo para reordenar una lista de nmeros de menor a mayor:
/*reordena una formacin de enteros, de menor a mayor */
#include <stdio.h>
#define TAM 100
void reordenar(int n, int x[]); /*prototipo de la funcion reordenar*/
main()
{
int i, n, x[TAM];
/* leer el valor de n */
printf("\nCuantos nmeros sern introducidos? ");
scanf("%d", &n);
printf("\n") ;
/* leer la lista de nmeros */
for (i = 0; i < n; ++i) {
printf("i = %d, x = ", i + 1);

- 90 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

scanf("%d", &x[i]);
}
/* reordenar todos los elementos de la formacin */
reordenar(n, x);
/* escribir la lista reordenada de nmeros */
printf("\n\nLista de nmeros reordenada:\n\n");
for (i = 0; i < n; ++i)
printf("i = %d, x = %d\n", i + 1, x[i]);
}
void reordenar(int n, int x[])
{
int i, elem, temp;

/* reordenar la lista de nmeros */

for (elem = 0; elem < n -1; ++elem)


/* encontrar el menor del resto de los elementos */
for (i = elem + 1; i < n; ++i)
if (x[i] < x[elem]) {
/* intercambiar los dos elementos */
temp = x[elem];
x[elem] = x[i];
x [i] = temp;
};
return;

..

Observaciones:
La funcin reordenar no devuelve nada.
Si un elemento de la formacin es alterado dentro de una funcin, esta alteracin ser reconocida en
la parte del programa desde la que se hizo la llamada. Eso es debido a que, cuando se pasa una
formacin a una funcin, no se pasan a la funcin una copia de valores de los elementos de la
formacin, sino que el nombre de la formacin se interpreta como la direccin del primer elemento
de la formacin (la direccin de la posicin de memoria que contiene el primer elemento de la
formacin). Esta direccin se asigna al correspondiente argumento formal cuando se llama a la
funcin. El argumento formal se convierte por tanto en un puntero al primer elemento de la
formacin (lo estudiaremos con detalle en las prximas secciones). Los argumentos pasados de esta
manera se dice que son pasados por referencia en contraposicin al paso de argumentos por valor.

Formaciones multidimensionales.
Se definen practicamente de la misma forma que las formaciones unidmensionales, salvo que requieren
una pareja de corchetes por cada una de sus dimensiones.
En trminos generales, la defininicin de una formacin multidimensional es de la forma
tipo_de almacenamiento tipo_de_dato formacin[dimensin1][dimensinn]

- 91 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 103: Se muestran a continuacin varias definiciones de formaciones multidimensionales:


float tabla[50] [50];
char pagina[24] [80];
static double registros[lOO] [66] [255];

static double registros [L] [M] [N] ;

La primera lnea define tabla como una formacin de elementos en coma flotante con 50 filas y 50
columnas (por tanto 50 50 = 2500 elementos), y la segunda lnea establece pagina como una formacin
de caracteres con 24 filas y 80 columnas (24 80 = 1920 elementos). La tercera formacin puede ser
vista como un conjunto de 100 tablas estticas en doble precisin, cada una con 66 lneas y 255 columnas
(por tanto 100 66 255 = 1 683000 elementos). La ltima definicin es anloga a la definicin
precedente excepto que las constantes simblicas L, M y N definen el tamao de la formacin.
En general, no es muy frecuente el uso de formaciones de ms de tres dimensiones. La reserva de
memoria para una formacin se realiza de forma permanente al principio del programa, antes de saber qu
parte de dicha reserva va realmente a ser utilizada. Esto hace que, en la mayora de las formaciones, el
nmero de posiciones de memoria ocupadas siempre sea inferior al nmero de posiciones de memoria
reservada (siempre desperdiciamos algo de memoria!). El tamao de la memoria desperdiciada
aumentar con la potencia de la dimensn de la formacin.

/ /

Ejemplo 104: Escribe un programa que sirva para visualizar en froma de tabla las siguientes formaciones
bidimensionales:
int valores[3] [4] = {1, 2, 3, 4,5,6,7,8,9,10,11, 12};
int valores[3] [4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int valores[3] [4] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}

};

Observaciones:
El orden natural en el que los valores iniciales son asignados se puede alterar formando grupos de
valores iniciales encerrados entre llaves ({ ...}).

- 92 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

11

Los valores dentro de cada par interno de llaves sern asignados a los elementos de la formacin
cuyo ltimo ndice vare ms rpidamente. Por ejemplo, en una formacin bidimensiona1, los valores
almacenados dentro del par interno de llaves sern asignados a los elementos de una fila, ya que el
segundo ndice (columna) se incrementa ms rpidamente.
Si hay pocos elementos dentro de cada par de llaves, al resto de los elementos de cada fila se le
asignarn ceros.
El nmero de valores dentro de cada par de llaves no puede exceder del tamao de fila definido.

11 2
1

Se propone al alumno la realizacin de un programa que simule el conocido juego de la guerra de barcos.
El ordenador deber generar al azar la posicin de 7 barcos situados horizontalmente en un tablero de
8 8 casillas.
Los barcos tendrn el siguiente tamao: 1 de 4 casillas, 1 de 3 casillas, 2 de 2 casillas y 3 de 1 casilla.
En cada tirada, el jugador introducir un disparo. El programa responder agua, tocado o tocado y
hundido segn corresponda.
El ordenador detectar cundo el jugador a hundido todos los barcos y declar su victoria.

Ejemplo 105: Las formaciones multidimensionales se procesan de la misma manera que las formaciones
unidimensionales, actuando sobre los elementos de la formacin uno a uno. Sin embargo, se requiere
algn cuidado cuando se pasan formaciones multidimensionales a una funcin. En particular, las
declaraciones de argumentos formales dentro de la definicin de funcin deben incluir especificaciones
explcitas de tamao en todos los ndices excepto en el primero. Estas especificaciones deben ser
consistentes con las correspondientes especificaciones de tamao en el programa que hace la llamada. El
primer ndice puede ser escrito como un par de corchetes vacos, como en una formacin unidimensional.
Los prototipos correspondientes de funcin deben escribirse de la misma manera. Supongamos que
queremos leer dos tablas de nneros enteros y calcular la suma de los elementos correspondientes:
c[i][j]= a[i][j] + b[i][j]
y a continuacin escribir la nueva tabla que contiene estas sumas. Supondremos que todas las tablas
contendrn el mismo nmero de filas y de columnas, no excediendo de 20 filas y 30 columnas. Haremos
uso de las siguientes definiciones de formaciones y variables.
a, b , c = formaciones bidimensionales con el mismo nmero de filas y el mismo nmero de
columnas, no excediendo de 20 filas y 3 O columnas.
nfilas = variable entera que indica el nmero real de filas en cada tabla.
ncols = variable entera que indica el nmero real de columnas en cada tabla.
fila = contador entero que indica el nmero de fila.
col = contador entero que indica el nmero de columna.
El programa estar organizado mediante el uso de funciones separadas para leer la formacin, calcular la
suma de los elementos de la formacin y escribir la formacin. Llamaremos a estas funciones leerentrada,
calcularsuma y escribirsalida, respectivamente.
/* calcular la suma de los elementos en dos tablas de enteros */
#include <stdio.h>
#define MAXFIL 20
#define MAXCOL 30
void leerentrada(int a[][MAXCOL], int nfilas, int ncols);

- 93 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void calcularsuma(int a[][MAXCOL], int b[][MAXCOL],int c[][MAXCOL],


int nfilas, int ncols);
void escribirsalida(int c[][MAXCOL], int nfilas, int ncols);
main()
{
int nfilas, ncols;
/* definiciones de formaciones */
int a[MAXFIL][MAXCOL], b[MAXFIL][MAXCOL], c[MAXFIL][MAXCOL];
printf("Cuantas filas? ");
scanf("%d", &nfilas);
printf("Cuantas columnas? ");
scanf("%d", &ncols);
printf("\n\nPrimera tabla:\n");
leerentrada(a, nfilas, ncols);
printf("\n\nSegunda tabla:\n");
leerentrada(b, nfilas, ncols);
calcularsuma(a, b, c, nfilas, ncols);
printf("\n\nSumas de los elementos:\n\n");
escribirsalida(c, nfilas, ncols);
}
/* leer una tabla de enteros */
void leerentrada(int a[][MAXCOL], int m, int n)
{
int fila, col;
for (fila = 0; fila < m; ++fila) {
printf("\nIntroducir datos para la fila n %2d\n", fila + 1);
for (col = 0; col < n; ++col)
scanf("%d", &a[fila][col]);
}
return;
}
/* sumar los elementos de dos tablas de enteros */
void calcularsuma(int a[][MAXCOL], int b[][MAXCOL], int c[][MAXCOL],
int m, int n)
{
int fila, col;

- 94 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for (fila = 0; fila < m; ++fila)


for (col = 0; col < n; ++col)
c[fila][col] = a[fila][col] + b[fila][col];
return;
}
/* escribir una tabla de enteros */
void escribirsalida(int a[][MAXCOL] , int m, int n)
{

33

int fila, col;


for (fila = 0; fila < m; ++fila) {
for (col = 0; col < n; ++col)
printf("%4d", a[fila][col]);
printf("\n") ;
}
return;

}
Observaciones:
Las definiciones de fonnaciones se expresan en tnninos de las constantes simblicas MAXFIL y
MAXCOL, cuyos valores se especifican como 20 y 30, respectivamente, al principio del programa.
Obsrvese la manera de escribir las declaraciones de los argumentos fonnales dentro de cada
definicin de funcin. Por ejemplo, la primera lnea de la funcin leerentrada se escribe como: void
leerentrada(int a[][MAXCOL], int m, int n). El nombre de la formacin, a, est seguido por dos pares
de corchetes. El primer par est vaco porque el nmero de filas no necesita ser especificado
explcitamente. Sin embargo, el segundo par contiene la constante simblica MAXCOL, que facilita
una especificacin de tamao explcita para el nmero de columnas.
Estudiaremos con mayor detalle cmo pasar formaciones a funciones cuando veamos, en las
secciones siguientes, el uso de punteros.

Ejemplo 106: Gestin de ventas por empleado. Completa el siguiente programa que permite la gestin
del nmero de artculos que vende cada empleado de una empresa. Suponemos que la empresa se
compone de 3 empleados y existen 50 tipos de artculos diferentes. Podemos, por lo tanto, representar el
problema mediante una formacin bidimensional que representa en las columnas a los tres empleados y
en las filas a los 50 artculos. Para poder calcular al final de cada da el nmero de ventas de cada
empleado y el nmero total de artculos de cada clase que se han vendido, definimos dos formaciones
unidimensionales, una para los empleados y otra para las ventas, que tendrn un tamao equivalente al
nmero de filas y columnas de la formacin bidimensional.
#define EMPLEADOS 3
#define ARTICULOS 30
main()
{
int matriz_ventas[EMPLEADOS][ARTICULOS]
int ventas_emp [EMPLEADOS];
int ventas_art [ARTICULOS;

- 95 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

int i, j;
/* Se inicializan las formaciones */
for(i = 0; i<EMPLEADOS; i++)
ventas_emp[i]=0;
for(j = 0; j<ARTICULOS; j++)
ventas_artculos[j]=0;
/* Se rellenan por teclado las formacin matriz_ventas*/

/* clculo de ventas totales por empleado */


for(i = 0; i<EMPLEADOS; i++){
for(j = 0; j<ARTICULOS; j++){
ventas_emp[i] = ventas_emp[i]+matriz_ventas[i][j];
}
}
/* Clculo de ventas totales por artculo */
for(i = 0; i<EMPLEADOS; i++){
for(j = 0; j<ARTICULOS; j++){
ventas_art[j] = ventas_art[j] + matriz_ventas[i][j];
}
}
Cadenas de caracteres. Ya hemos visto que una cadena de caracteres puede ser representada por una
formacin unidimensional de caracteres. Cada carcter de la cadena ser almacenado en un elemento de
la formacin. Algunos problemas requieren que los caracteres de la cadena sean procesados
individualmente. Uno de ellos el el recuento de la longitud de una cadena.
Ejemplo 107: Longitud de una cadena de caracteres almacenada en una formacin. Para llevar a
cabo este programa, basta con recorrer la cadena:
#include<stdio.h>
#include<conio.h>
void main()
{
char cadena[36];
int i=0, cont=0;/*i es el indice que recorre la cadena, cont sirve
para contar los caracteres introducidos, excepto
el nulo*/
printf(Introduce una cadena de 36 caracteres como mximo: );
gets(cadena);/*esta funcion de biblioteca permite introducir una
cadena de caracteres en cadena. Incluye
automaticamente el carcter nulo al final*/
for(i=0; cadena[i] = \0; i++){
if(cadena[i] = \0)
cont++;
}
printf(\nHay %d caracteres sin contar el nulo. \n, cont);
}

- 96 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

444

Proponemos al sufrido lector que modifque el programa con el fin de hacer un recuento de :
El nmero de vocales.
El nmero de frases.
El nmero de palabras.
Ejemplo 108: Copia de los caracteres de una formacin en otra. Se recorre la cadena de origen y, si el
contenido de una posicin es distinto del carcter nulo, dicho carcter quedar asignado en la misma
posicin de la cadena destino:
#include<stdio.h>
#include<conio.h>
void main()
{
char cad_origen[]= Federico, cad_destino[80];
int i=0;
for(i=0; cad_origen[i] = \0; i++){
cad_destino[i]=cad_origen[i];
}
cad_destino[i]=\0;/*Cuando se localice el carcter nulo, se dejara
de ejecutar el bucle y se introducira al
carcter nulo en la posicion i de la cadena
de destino*/
printf(\nLa cadena copiada es \%s\ \n,cad_destino);
}
El lector puede intentar modificar este programa para que slo pase a la cadena de destino los nombres
propios.
No obstante, hay muchos otros problemas en los que se requiere que las cadenas de caracteres se procesen
como entidades completas. Tales problemas pueden simplificarse considerablemente utilizando funciones
especiales de biblioteca orientadas a cadenas de caracteres:

funcin
strcat()

utilidad
Concatena dos cadenas en una sola:

biblioteca
string.h

strcat(cadena_destino, cadena_origen).

strcmp()

cadena_destino: la cadena que va a recibir los caracteres de la cadena


de origen (se unirn a continuacin de los ya existentes).
cadena_origen: contiene los caracteres que se incluyen en
cadena_destino, al final de los ya existentes.
Compara dos cadenas. Dicha comparacin se realiza carcter a string.h
carcter hasta encontrar uno que las diferencie. Dependiendo de la
posicin del carcter diferenciador de ambas cadenas dentro de la
tabla ACII la funcin devolver un nmero ser:

0 si ambas cadenas son iguales.

- 97 -

55

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

<0 si el carcter diferenciador de la1 cadena posee un cdigo


ASCII menor que el de la segunda.
>0 si el carcter diferenciador de la1 cadena posee un cdigo
ASCII mayor que el de la segunda.

strcmp(cadena1, cadena2).
strcpy()

Permtie copiar el contenido de una cadena de caracteres en otra:

string.h

strcpy(cadena_destino, cadena_origen).
strlen()

Calcula el nmero de caracteres de un cadena, sin tener en cuenta el string.h


carcter nulo (\0) que indica la finalizacin de la misma:
strcat(cadena).

Ejemplo 109: Manipulacin de cadenas de caracteres. Supongamos que queremos leer una lista de
cadenas de caracteres, reordenarlas alfabticamente y escribir la lista reordenada. La estrategia para hacer
esto es muy parecida a la ya utilizada para reordenar una lista de nmeros en orden ascendente. Sin
embargo, ahora existe la complicacin adicional de comparar cadenas de caracteres completas, en lugar
de valores numricos simples. Por tanto, almacenaremos las cadenas de caracteres en una formacin
bidimensional de caracteres. Cada cadena se almacenar en una fila distinta de la formacin. Para
simplificar el proceso, hacemos uso de las funciones de biblioteca strcmp y strcpy. Permitiemos que el
programa acepte un nmero no especificado de cadenas hasta que se introduzca una cadena cuyos tres
primeros caracteres sean FIN (tanto en minsculas como en maysculas).
/* ordenar alfabticamente una lista de cadenas de caracteres,
utilizando una formacin bidimensional */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void reordenar(int n, char x[][12]); /* prototipo de funcin */
main()
{
int i, n = 0;
char x[10][12];
printf("Introducir debajo cada cadena en una lnea\n\n");
printf("Escribir \'FIN\' para terminar\n\n");
/* leer la lista de cadenas de caracteres */
do {
printf("cadena %d: ", n + 1);
scanf("%s", x[n]);
} while (strcmp(x[n++], "FIN"));

- 98 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/* ajustar el valor de n */
n--;
/* reordenar la lista de cadenas de caracteres */
reordenar(n, x);
/* escribir la lista reordenada de cadenas ,de caracteres */
printf("\n\nLista reordenada de cadenas:\n");
for (i = 0; i < n; ++i)
printf("\ncadena %d: %s", i + 1, x[i]);
}
/* reordena la lista de cadenas de caracteres */
void reordenar(int n, char x[] [12J])
{
char temp[12];
int i, elem;
for (elem = 0; elem < n -1; ++elem)
/* encontrar la menor de las cadenas restantes */
for (i = elem + 1; i < n; ++i)
if (strcmp(x[elem], x[i]) > 0) {
/* intercambiar las dos cadenas */
strcpy(temp, x[elem]);
strcpy(x[elem], x[i]);
strcpy(x[i], temp);
}
return;
}

11. Punteros.
Imaginemos por un momento cmo se organiza la informacin en la memoria del ordenador. Es como
un enorme armario lleno de cajitas idnticas, numeradas consecutivamente (algo parecido a la pared de
los apartados de correo de una gran oficina de reparto de correspondencia). Cada dato almacenado ocupa
una o ms cajitas (celdas contiguas de memoria) (es decir, palabras o bytes adyacentes). El nmero de
celdas de memoria requeridas para almacenar un dato depende de su tipo. Por ejemplo, un carcter se
almacenar normalmente en un byte (8 bits) de memoria; un entero usualmente necesita dos bytes
contiguos (uno para el signo y otro para el nmero en si). un nmero en coma flotante puede necesitar
cuatro bytes contiguos y una cantidad en doble precisin puede requerir ocho bytes contiguos.
De cada celda podemos llegar a conocer dos cosas:
Su direccin, un nmero que indica dnde esta la celda.
Su contenido; es el dato que hay en dicha celda.
En ocasiones puede resultar til utilizar una celda de memoria para almacenar como dato la direccin de
otra posicin. Un puntero es justamente eso, una variable que representa la posicin (en lugar del valor)
de otro dato.

66

Ejemplo 110: Supongamos que u es una variable que representa almacena un nmero entero. Vamos a
llamar pu a la variable en la que guardaremos la direccin de v:
#include<stdio.h>
- 99 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

main()
{
int u = 3;
int *pu;
pu = &u;
printf(u = %d &u = %X ,u, &u);
printf(*pu = %d pu = %X ,*pu, pu);
}
La direccin de memoria de u puede ser determinada mediante la expresin &u donde u es un operador
monario llamado operador direccin, que proporciona la direccin del operando. El programa asigna el
valor de esta direccin a la variable pu; diremos entonces que pu es un puntero a u, puesto que apunta a la
posicin de memoria donde se almacena v.

nombre
direccin
contenido

122

123

u
124
3

125

126

pu
127
124

128

Cuando el programa ejecuta la instruccin pu = &u; asigna a la variable pu la direccin de u (124) El dato
almacenado en u (3) puede ser recuperado indirectamente a travs de pu mediante el operador indireccin
*pu.
Los punteros, como cualquier otra variable, deben ser declarados antes de ser usados dentro de un
programa en C, aunque siempre despus de la declaracin de la variable a la que apunta. Sin embargo,
la interpretacin de una declaracin de puntero es un poco diferente de la declaracin de otras variables:

77

Cuando se declara una variable puntero, el nombre de la variable debe ir precedido por un asterisco
(*). ste identifica a la variable como un puntero: int *pu;
El tipo de datos que aparece en la declaracin se refiere al objeto del puntero, esto es, el dato que se
almacena en la direccin representada por el puntero. Por ejemplo, en la declaracin
float u, p;
float *pv;
el puntero pv no contiene un nmero en coma flotante y , sin embargo, se declara como tal porque
apunta a una variable que s lo contiene.
Es posible asignar la direccin a un puntero en la instruccin de declaracin: float *pv;

Ejemplo 111: Es importante recalcar que el operador direccin (&) slo puede actuar sobre operandos con
direccin nica tales como variables ordinarias o elementos individuales de una formacin. Por
consiguiente, el operador direccin no puede actuar sobre expresiones aritmtica, tales como 2*(u+v).
El operador indireccin (*) slo puede actuar sobre operandos que sean punteros. Sin embargo, si pv
apunta a v (esto es, pv = &v), entonces una expresin como * p v puede intercambiarse con la
correspondiente variable v y aparecer en lugar de una variable (por ejemplo v) dentro de una expresin
ms complicada:

- 100 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include <stdio.h>
main()
{
int ul, u2;
int v = 3;
int *pv;
ul = 2 * (v + 5);
pv = &v;
u2 = 2 * (*pv + 5);
printf("\nul=%d u2=%d", ul, u2);
}

/* pv apunta a v */
/* expresin ordinaria */
/* asigna direccin de v a pv */
/* expresin equivalente */

Este programa involucra el uso de dos expresiones enteras. La primera, 2*(v+5), es una expresin
aritmtica ordinaria, mientras que la segunda, 2*(*pv+5), implica el uso de un puntero. Las expresiones
son equivalentes, ya que v y *pv representan el mismo valor entero.
Ejemplo 112: Una referencia indirecta puede aparecer tambin en la parte izquierda de una instruccin de
asignacin. Esto proporciona otro mtodo para asignar un valor a una variable o a un elemento de una
formacin:
main()
{
int v = 3;
int *pv;
pv = &V;
printf("\n*pv=%d v=%d", *pv, v);
*pv = O;
printf("\n\n*pv=%d v=%d", *pv, v);

/*pv apunta a v*/


/*reasigna v indirectamente*/

}
El programa empieza asignando un valor inicial de 3 a la variable entera v y asignando la direccin de v a
la variable puntero pv. La expresin *pv representa por tanto el valor 3. A continuacin de la primera
instruccin printf, el valor de *pv es puesto a cero. Por tanto, v tendr reasignado el valor 0.
Ejemplo 113: Paso de argumentos por referencia. Ya hemos estudiado anteriorrmente que si pasamos
el valor de una variable a una funcin mediante el conocido como paso por valor, las modificaciones
efectuadas en la funcin no son reconocidas en la parte del programa desde la que se hizo la llamada a la
funcin. Esto es debido a que, cuando se pasa un argumento por valor, la funcin recibe una copia del
dato (pero no el original); cualquier alteracin realizada en la funcin afecta a dicha copia, pero no al
valor original. Imaginemos ahora que lo que pasamos como argumento no es el nombre de la variable
sino su direccin. El contenido de dicha direccin puede ser abordado libremente, bien desde la funcin
principal, bien desde la funcin a la que el programa llama. Por ello, los cambios llevados a cabo de este
modo son reconocidos globalmente ya que afectan a la celda donde originalmente hemos guardado el
dato. Esta forma de pasar informacin a una funcin recibe el nombre de paso por referencia. A
continuacin se muestra un ejemplo que ilustra la diferencia entre argumentos ordinarios, que son pasados
por valor, y argumentos puntero, que son pasados por referencia:

- 101 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include <stdio.h>
void func1(int u, int v);
/* prototipo de funcin */
void func2(int *pu, int *pv);
/* prototipo de funcin */
main()
{
int u = 1;
int v = 3;
printf("\nAntes de la llamada a func1: u=%d v=%d", u, v);
funcl(u, v);
printf("\nDespus de la llamada a funcl: u=%d v=%d", u, v);
printf("\n\nAntes de la llamada a func2: u=%d v=%d", u, v);
func2(&u, &v);
printf("\nDespus de la llamada a func2: u=%d v=%d", u, v);
}
void func1(int u, int v)
{
u = 0;
v = 0;
printf("\nDentro de funcl: u=%d v=%d", u, v);
return;
}
void func2(int *pu, int *pv)
{
*pu = 0;
*pv = 0;
printf("\nDentro de func2: *pu=%d *pv=%d", *pu, *pv);
return;
}

88

Observaciones:
Este programa contiene dos funciones, llamadas funcl y func2.
La primera funcin, funcl, recibe dos variables enteras como argumentos. Estas variables tienen
originalmente asignados los valores 1 y 3, respectivamente. Los valores son alterados a 0, 0 dentro de
funcl. Sin embargo, los nuevos valores no son reconocidos en main, porque los argumentos fueron
pasados por valor.
La segunda funcin, func2, recibe dos punteros a variables enteras como argumentos. Los
argumentos son identificados como punteros por los operadores de indireccin (los asteriscos) que
aparecen en la declaracin de los argumentos: void func2(int *pu, int *pv); Adems, la declaracin
de argumentos indica que los punteros representan direcciones de cantidades enteras.
Dentro de func2 los contenidos de las direcciones apuntadas son reasignados con valores 0, 0. Como
las direcciones son reconocidas tanto en func2 como en main, los valores reasignados sern
reconocidos dentro de main tras la llamada a func2.
Las variables puntero pu y pv no han sido declaradas en ninguna parte dentro de main. Esto est
permitido en el prototipo de la funcin, ya que pu y pv son argumentos ficticios en vez de
argumentos reales.

8
8
8

- 102 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 114: Punteros y formaciones. En C existe una relacin muy estrecha entre punteros y
formaciones. En realidad, el nombre de una formacin siempre es un puntero que seala la direccin de la
celdilla que contiene el primer elemento de la formacin. Por lo tanto, cuando pasamos una formacin
como argumento a una funcin, siempre lo hacemos por referencia; si un elemento de la formacin es
alterado dentro de la funcin, esta alteracin ser reconocida en la parte del programa desde la que se hizo
la llamada:
#include<stdio.h>
void modificar(int a[]); /*prototipo de la funcion*/
void main()
{
int cont, a[3];
/*defincion de formacion*/
printf (\nDesde main, antes de llamar a la funcion:\n);
for(cont = 0; cont <= 2; ++cont) {
a[cont] = cont + 1;
printf(a[%d] = %d\n, cont, a[cont]);
}
modificar(a);
printf (\nDesde main, despus de llamar a la funcion;\n);
for(cont = 0; cont <= 2; ++cont) {
printf(a[%d] = %d\n, cont, a[cont]);
}
}
void modificar(int a[]) /*definicion de la funcion*/
{
int cont;
printf(\nDesde la funcin, despus de modificar:\n);
for(cont = 0; cont <= 2; ++cont) {
a[cont] = -9;
printf(a[%d] = %d\n, cont, a[cont]);
}
return;
}
Ejemplo 115: La funcin scanf requiere que los argumentos que sean variables ordinarias vayan
precedidos por el smbolo &. No obstante, los nombres de formacin estn exentos de este requerimiento.
Esto pudo parecer algo misterioso en las secciones anteriores, pero adquiere sentido al considerar ahora lo
que sabemos de nombres de formacin y direcciones. As, la funcin scanf requiere que se especifique la
direccin de los elementos que vayan a ser introducidos en la memoria de la computadora. Los
ampersands (&) proporcionan un medio para acceder a las direcciones de las variables ordinarias
univaluadas. Los ampersands no son necesarios con nombres de formaciones, ya que estos mismos
nombres representan direcciones.
#includ<stdio.h>
void main()
{

- 103 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

char concepto[20];
int no_partida;
float coste;

scanf(%s %d %f, concepto, &no_partida, &coste);


...
}
Como concepto es el nombre de una formacin representa una direccin por lo que no necesita (no puede)
ir precedido de un ampersand dentro de la instruccin scanf. Si se utiliza la funcin scanf para introducir
un solo elemento de la formacin en vez de toda la formacin, el nombre del elemento de la formacin
debe ir precedido por un ampersand:
for(cont=0; cont <= tamano; ++cont) {
printf(\nElemento %d= , cont);
scanf(%f, &vector[cont]);
}
Se puede pasar una parte de una formacin, en lugar de toda la formacin, a una funcin. Para llevarlo a
cabo, la direccin del primer elemento que vayamos a pasar deber ser especificada como argumento. El
resto de la formacin, comenzando por el elemento especificado, ser entonces pasado a la funcin:
#include<stdio.h>
void procesar(float z[]);
void main(){

procesar(&z[50]);/*pasamos la direccin de z[50] a la funcin


procesar. As, los ltimos 0 elementos de z
estarn disponibles para procesar*/

}
void procesar(float f[]){

/*procesar los elementos de f*/

}
El hecho de que el nombre de cualquier formacin es realmente un puntero al primer elemento de la
formacin nos permite acceder a las celdillas donde se encuentra almacenada los elementos de la
formacin de dos formas equivalentes:
Elemento
Elemento 0
Elemento 1
Elemento 2

contenido
x[0]
x[1]
x[2]

*x
*(x+1)
*(x+2)

- 104 -

direccin
&x[0]
x
&x[1]
x+1
&x[2]
x+2

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Elemento i

x[i]

*(x+i)

&x[i]

x+i

Se puede, por lo tanto, sumar un valor entero al nombre de una formacin para acceder a un elemento
individual de la formacin. Supongamos, por ejemplo, que px es una variable puntero que representa la
direccin de una variable x. Podemos escribir expresiones tales como ++px, --px, (px + 3), (px + i) y (px i), donde i es una variable entera. Cada expresin representar una direccin localizada a cierta distancia
de la posicin original representada por px. La distancia exacta ser el producto de la cantidad entera
por el nmero de bytes que ocupa cada elemento al cual apunta px. Por ejemplo, cuando escribimos px +
3, el compilador interpreta que nos estamos refiriendo a la direccin del elemento de la formacin que
est desplazado 3 elementos con respecto al primer elemento de la formacin. Si la formacin es de
nmeros enteros, esto supondr una distancia de 6 bytes (dos por elemento:
direcciones

117

118

118

120

121

122

123

124

117
px
2 bytes

px+3
Los siguientes programa ejemplifica la relacin existente entre los elementos de una formacin y sus
direcciones:
#include <stdio.h>
main( )
{
static int x[10] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
int i;
for (i = o; i <= 9; ++i){
/* escribir un elemento de la formacin */
printf("\ni= %d x[i]= %d *(x+i)= %d", i, x[i], *(x+i));
/* escribir la correspondiente direccin de la formacin */
printf(" &x[i]= %X
x+i= %X", &x[i], (x+i));
}
}
Este programa define una formacin x unidimensional de 10 elementos enteros, que tienen asignados
valores 10, 11, ..., 19. El programa muestra mediante un bucle el valor y la direccin correspondiente para
cada elemento de la formacin. Obsvese que el valor de cada elemento de la formacin se especifica de
dos formas distintas, como x[i] y como *(x+i) con el fin de demostrar su equivalencia. Anlogamente, la
direccin de cada elemento de la formacin est expresada en dos formas diferentes,
como &x[i] y como (x+i), por la misma razn. Por tanto, el valor y la direccin de cada elemento de la
formacin aparecer dos veces
El siguiente programa escribe los valores y direcciones asociados con cuatro tipos diferentes de variables:
#include <stdio.h>

- 105 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

main()
{
int *px; /* puntero a un entero */
int i = 1;
float f = 0.3;
double d = 0.005;
char c = '*';
px = &i;
printf("Valores: i=%i f=%f d=%f c=%c\n\n", i, f, d, c);
printf("Direcciones: &i=%X &f=%X &d=%X &c=%X\n\n",
printf("Valores de punteros:
px=%X px + l=%X
printf ("px + 3=%X", px, px+1, px+2, px + 3);

&i, &f, &d, &c);


px + 2=%X");

}
Debe tenerse en cuenta que cada tipo de variable requiere una reserva de memoria distinta
Ejemplo 116: Cosas que se pueden hacer y cosas que no.
1.
2.
3.
4.
5.
6.

A una variable puntero se le puede asignar la direccin de una variable ordinaria


A una variable puntero se le puede asignar el valor de otra variable puntero siempre
que ambos punteros apunten al mismo tipo de datos
A una variable puntero se le puede asignar un valor nulo (cero)
A una variable puntero se le puede sumar o restar una cantidad entera

pv =&v
pv =px

pv= NULL
pv + 3;
++pv;
Una variable puntero puede ser restada de otra con tal que ambas apunten a elementos pv -px
de la misma formacin.
Dos variables puntero pueden ser comparadas siempre que ambas apunten a datos del px = py
mismo tipo.
px >= py

Veamos algunos ejemplos de instrucciones correctas e incorrectas en C con respecto al unos de


formaciones y punteros. Imaginemos el siguiente encabezado para un programa en C:
#include<stidio.h>
void main(){
int linea[80];
int *pl;

Nos gustara escribir una instruccin de asignacin que asignara el valor del segundo elemento de la
formacin (linea[1]) al tercer elemento de la formacin (linea[2]). Este propsito se podra conseguir
mediante cualquiera de las cuatro instrucciones siguientes:
linea[2] = linea[1];
linea[2] = *(linea + 1);

- 106 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

*(linea + 2) = linea[1];
*(linea + 2) = *(linea + 1);
Imaginemos ahora que deseamos pasar la direccin del segundo elemento al puntero pl; podramos
hacerlo de dos formas distintas:
pl = &linea[1];
pl = linea + 1;
Sin embargo, sera incorrecto asignar la direccin de un elemento de la formacin a otro elemento de la
formacin de este modo:
&linea[2]= &linea[1]; /*esta asignacin es incorrecta*/
Una forma correcta de asignar el valor de un elemento de la formacin a otro mediante un puntero sera:
pl = &linea[1]; /*volcamos en el puntero pl la direccin de linea[1]*/
linea[2]=*pl;/*asignamos a linea[2] el contenido de la direccion pl*/
Ejemplo 117: Asignacin dinmica de memoria. Acabamos de estudiar que cualquier expresin en la
que aparezca un vector y un subndice se puede escribir como un puntero y un desplazamiento. Sin
embargo, existe una diferencia fundamental entre una y otra forma de trabajar. La definicin
convencional de una formacin produce la reserva de un bloque fijo de memoria al principio de la
ejecucin del programa. As,
int x[10];
reserva un bloque de memoria cuyo tamao es equivlente a 10 cantidades enteras (10 enteros a 2 bytes
por cada entero es igual a 20 bytes).
Sin embargo, esto no ocurre si la formacin se representa mediante una variable puntero. Por
consiguiente, el uso de una variable puntero para representar una formacin requiere algn tipo de
asignacin inicial de memoria, antes de que los elementos de la formacin sean procesados. Esto se
conoce como asignacin dinmica de memoria mediante la funcin de biblioteca malloc (<stdlib.h>):
x = (int *) malloc(10 * sizeof(int));
Esta funcin reserva un bloque de memoria de 20 bytes (10 por lo que ocupa un entero). Tal y como est
escrita, la funcin devuelve un puntero a un entero, que indica el comienzo del bloque de memoria.
Cuando el ordenador no dispone de un hueco suficientemente grande de memoria, la funcin malloc
devuelve 0, por lo que tras la peticin de memoria es siempre conveniente escribir una sentencia
condicional que detecte este hecho y lo comunique:
#define NULL 0

if(x == NULL)
printf(\nNo hay memoria suficiente);
Si deseramos almacenar 10 cantidades en doble presicin, escribiramos:

- 107 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

x = (double *) malloc(10 * sizeof(double));

Observaciones:
Si la declaracin de una formacin incluye la asignacin de valores iniciales, entonces la formacin
no puede ser definida como un puntero:

int x[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


Cuando hemos acabado de usar la memoria asignada dinmicamente mediante la funcin malloc, esa
memoria no es liberada automticamente; tiene que ser el programador el que se encargue de
liberarla mediante la instruccin free:
free(x);

Ejemplo 118: Reordenacin de una lista de nmeros mediante asignacin dinmica de memoria.
Cuando se programa en C es usual utilizar expresiones con punteros en vez de referencias a elementos
individuales de la formacin. El programa resultante puede aparecer extrao al principio, pero deja de
serio al acostumbramos a acceder a valores almacenados en direcciones especficas. Para ilustrar el uso de
punteros con asignacin dinmica de memoria, consideremos de nuevo el problema de reordenar una lista
de enteros, pero ahora utilizando expresiones de punteros para acceder a valores individuales en vez de
referimos explcitamente a elementos individuales de la formacin:
#inc1ude <stdio.h>
#include <std1ib.h>
void reordenar(int n, int *x);
main()
{
int i, n, *x;
/*leer el valor de n*/
printf("\nCuantos nmeros sern introducidos? ");
scanf("%d", &n);
printf("\n") ;
/* reserva de memoria */
x = (int *) malloc(n * sizeof(int));
/* leer la lista de nmeros */
for (i = 0; i < n; ++i) {
printf("i = %d
x = ", i + 1);
scanf("%d", x + i);
}
/* reordenar todos los elementos de la formacin */
reordenar(n, x);
/* escribir la lista reordenada de nmeros */
printf("\n\nLista de nmeros reordenada:\n\n");
for (i = 0; i < n; ++i)
printf("i = %d
x = %d\n", i + 1, *(x + i));

- 108 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
void reordenar(int n, int *x)
/* reordenar la lista de nmeros */
{
int i, elem, temp;
for (elem = 0; elem < n -1; ++elem)
/* encontrar el menor del resto de los elementos */
for (i = elem + 1; i < n; ++i)
if (*(x +i) < *(x + elem)) {
/* intercambiar los dos elementos */
temp = *(x + elem);
*(x + elem) = *(x + i);
*(x + i) = temp;
}
return;
}

Observaciones:
En este programa la formacin de enteros es definida como un puntero a un entero. En todo el
programa se utiliza la notacin de punteros para procesar los elementos individuales de la formacin.
Por ejemplo, el prototipo de funcin especifica ahora que el segundo argumento es un puntero a una
cantidad entera en vez de una formacin de enteros. Este puntero identificar el comienzo de la
formacin de enteros.
La asignacin inicial de memoria para la variable puntero se hace mediante la funcin de biblioteca
malloc.
la funcin scanf especifica ahora la direccin del i-simo elemento como x + i en vez de &x[i].
Anlogamente, en la funcin printf se representa ahora el valor del i-simo elemento como *(x + i)
en vez de x [i].
Dentro de la funcin reordenar vemos que el segundo argumento formal se define ahora como una
variable puntero en vez de como una formacin de enteros. Esto es consistente con el prototipo de
funcin.
Las diferencias ms pronunciadas se encuentran en la instruccin if, en la que x[i] se ha sustituido
por la expresin equivalente *(x+i), y x[elem] por *(x+elem).

::
:
:

Ejemplo 119: Punteros y formaciones multidimensionales. Como una formacin unidimensional puede
ser representada en trminos de un puntero (el nombre de la formacin) y de un desplazamiento (el
ndice), es razonable esperar que las formaciones multidimensionales pueden ser representadas tambin
con una notacin equivalente de punteros.
Una formacin bidimensional puede ser vista como una formacin cuyos elementos son punteros a otras
formaciones unidimensionales. Por ejemplo
int *x[10];
Obsrvese que el nombre de la formacin va precedido por un asterisco. Esto significa que: (a) Realmente
estamos definiendo una formacin y, (b) Esa formacin contiene, como elementos, punteros a otras
direcciones.
0

- 109 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Formacin de punteros

112

132

152

112

132

152

113

133

153

114

134

154

115

135

155

116

136

156

Un elemento individual, tal como el x[2][5] puede ser llamado escribiendo:


*(x[2]+5)
donde x[2] es el contenido del tercer elemento de la formacin x. Es un puntero al primer elemento de la
fila 2, de modo que (x[2]+5) apunta al elemento 5 de la fila 2. El contenido de dicha casilla se obtendr
mediante el operador indireccin, es decir *(x[2]+5)
Ejemplo 120: Lectura/escritura de una tabla. Analicemos el problema ya estudiado de leer una
formacin bidimensional y luego mostrarla por pantalla
#include<stdio.h>
#include<stdlib.h>
#define MAXCOL 10
#define MAXFIL 10
void main(){
int matriz[MAXFIL][MAXCOL];
int fila,col, nfilas, ncols;
nfilas=2;
ncols= 4;
/*Introduce los datos*/
for(fila = 0; fila < nfilas; ++fila){
printf("\nIntroduce los datos de la fila n %2d\n", fila+1);
for(col =0; col< ncols; ++col)
scanf("%d", (*(matriz + fila) + col));/*equivalente a
&matriz[fila][col]*/
};
/*Escribir salida*/
printf("\nEscritura de la tabla:\n");
for(fila = 0; fila < nfilas; ++ fila){
for(col =0; col< ncols; ++col){
printf("%4d", *(*(matriz+fila)+col));}; /*equivalente a
matriz[fila][col]*/
printf("\n");
};
- 110 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
Podemos sacar fuera del cuerpo principal la lectura y la escritura de la tabla:
#include<stdio.h>
#include<stdlib.h>
#define MAXCOL 10
#define MAXFIL 10
/*prototipos de las funciones*/
void leertabla(int matriz[][MAXFIL], int nfilas, int ncols);
void escribirtabla(int matriz[][MAXFIL], int nfilas, int ncols);
int matriz[MAXFIL][MAXCOL];
void main()
{
int nfilas, ncols;
printf("\nnumero de filas: ");
scanf("%d", &nfilas);
printf("\nnumero de columnas: ");
scanf("%d", &ncols);
/*Leer tabla*/
printf("\nLectura de la tabla:\n");
leertabla(matriz, nfilas, ncols);
/*Escribir salida*/
printf("\nEscritura de la tabla:\n");
escribirtabla(matriz, nfilas, ncols);
}
void leertabla(int matriz[][MAXCOL], int nfilas, int ncols){
int fila, col;
for(fila = 0; fila < nfilas; ++ fila){
printf("\nIntroduce los datos de la fila n %2d\n", fila+1);
for(col =0; col< ncols; ++col)
scanf("%d", &matriz[fila][col]);
};
return;
}
void escribirtabla(int matriz[][MAXCOL], int nfilas,int ncols){
int fila, col;
for(fila = 0; fila < nfilas; ++ fila){
for(col =0; col< ncols; ++col)
printf("%4d", matriz[fila][col]);

- 111 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\n");
};
}
Ahora vamos a sustituir la definicin de la formacin por otra basada en una formacin unidimensional
de punteros. Hemos de tener en cuenta que ser preciso reservar memoria mediante la instruccin malloc:
#include<stdio.h>
#include<stdlib.h>
#define MAXCOL 10
#define MAXFIL 10
/*prototipos de las funciones*/
void leertabla(int *matriz[MAXFIL], int nfilas, int ncols);
void escribirtabla(int *matriz[MAXFIL], int nfilas, int ncols);
int *matriz[MAXCOL];
void main()
{
int fila, nfilas, ncols;
printf("\nnumero de filas: ");
scanf("%d", &nfilas);
printf("\nnumero de columnas: ");
scanf("%d", &ncols);
/*Reserva de memoria*/
for(fila =0; fila<nfilas; ++fila){
matriz[fila]=(int *) malloc(ncols* sizeof(int));
};
/*Leer tabla*/
printf("\nLectura de la tabla:\n");
leertabla(*matriz, nfilas, ncols);
/*Escribir salida*/
printf("\nEscritura de la tabla:\n");
escribirtabla(*matriz, nfilas, ncols);
}
void leertabla(int *matriz[MAXFIL], int nfilas, int ncols){
int fila, col;
for(fila = 0; fila < nfilas; ++fila){
printf("\nIntroduce los datos de la fila n %2d\n", fila+1);
for(col =0; col< ncols; ++col)
scanf("%d", (*(matriz + fila) + col));
};
return;

- 112 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
void escribirtabla(int *matriz[MAXFIL], int nfilas,int ncols){
int fila, col;
for(fila = 0; fila < nfilas; ++ fila){
for(col =0; col< ncols; ++col)
printf("%4d", *(*(matriz+fila)+col));
printf("\n");
};

;;

}
Se proponen los siguientes ejercicios:
Crear un programa que lea 2 formaciones unidimensionales de 5 elementos y las vuelque en una
nica formacin bidimensional de 2 por 5 elementos, definida a partir de una formacin de punteros.
Redactar un programa que sirva para almacenar en una formacin bidimensional el DNI y la edad
de 10 empleados de una empresa, para luego mostrarlos en una lista del mayor al ms joven.
Utilcese una formacin unidimensional de punteros.

;;

Ejemplo 121: Suma de dos tablas de nmeros. En este ejemplo evitamos el uso de formaciones
bidimensionales, utilizando en su lugar una formacin de punteros:
Proponemos al lector interesado que intente escribir por s mismo el programa siguiendo los siguientes
pasos:
Escribir un programa que lea dos matrices cuyo nmero de filas y columnas sea previamente
especificado por el usuario.
Redactar una funcin que sume los elementos correspondientes de ambas matrices y los almacene en
una tercera. Puede utilizarse el procedimiento
c[fila][col]= a[fila][col]+b[fila][col];
Redactar una funcin que escriba la matriz suma.
Sustituye la definicin convencional de las formaciones bidimensionales por punteros a conjuntos de
formaciones: int *a[MAXFIL];
Realiza la consiguiente reserva de memoria mediante la funcin malloc:
a[fila] = (int *) malloc (ncols * sizeof(int));
Sustituye el procedimiento de suma por otro que utilice los operadores indireccin:
*(*(c+fila)+col))= *(*(a+fila)+col))+ *(*(b+fila)+col));

;;
;;

/*calcular la suma de los elementos en dos tablas de enteros*/


/*cada formacin bidimensional se procesa como un puntero a un conjunto de formaciones
unidimensionales de enteros*/
#include <stdio.h>
#include <stdlib.h>
#define MAXFIL 20
/* prototipos de funciones */
void leerentrada(int *a[MAXFIL] , int nfilas, int ncols);
void calcularsuma(int *a[MAXFIL], int *b[MAXFIL],
int *c[MAXFIL], int nfilas, int ncols);

- 113 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void escribirsalida(int *c[MAXFIL] , int nfilas, int ncols);


void main( )
{
int fila, nfilas, ncols;
/* definiciones de punteros */
int *a[MAXFIL] , *b[MAXFIL] , *c[MAXFIL];
printf("Cuantas filas? ");
scanf("%d", &nfilas);
printf("Cuantas columnas? ");
scanf("%d", &ncols);
/* reserva inicial de memoria */
for (fila = 0; fila < nfilas; ++fila) {
a[fila] = (int *) malloc (ncols * sizeof(int));
b[fila] = (int *) malloc (ncols * sizeof(int));
c[fila] = (int *) malloc (ncols * sizeof(int));
}
printf("\n\nPrimera tabla:\n");
leerentrada(a, nfilas, ncols);
printf("\n\nSegunda tabla:\n");
leerentrada(b, nfilas, ncols);
calcularsuma(a, b, c, nfilas, ncols);
printf("\n\nSumas de los elementos:\n\n");
escribirsalida(c, nfilas, ncols);
}
void leerentrada(int *a[MAXFIL], int m, int n)
/* leer una tabla de enteros */
{
int fila, col;
for (fila = 0; fila < m; ++fila) {
printf("\nlntroducir datos para la fila n %2d\n", fila+1);
for (col = 0; col < n; ++col)
scanf("%d", (*(a + fila) + col));
}
return;
}
void calcularsuma(int *a[MAXFIL], int *b[MAXFIL], int *c[MAXFIL],
int m, int n)
/* sumar los elementos de dos tablas de enteros */
{
int fila, col;

- 114 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

for (fila = 0; fila < m; ++fila)


for (col = 0; col < n; ++col)
*(*(c+fila)+col) = *(*(a+fila)+col)+*(*(b+fila)+col);
return;
}
void escribirsalida(int *a[MAXFIL], int m, int n)
/* escribir una tabla de enteros */
{
int fila, col;
for (fila = 0; fila < m; ++fila) {
for (col = 0; col < n; ++col)
printf("%4d", *(*(a + fila) + col));
printf("\n");
}
return;
}

<<

Observaciones:
En este programa a, b y c son definidas como una formacin de punteros a enteros. Cada formacin
tiene un mximo de MAXFIL elementos.
Puesto que cada elemento de a, b y c es un puntero, debemos reservar memoria inicial para cada fila
de enteros, utilizando la funcin malloc:

<
<

a[fila] = (int *) malloc (ncols * sizeof(int));


donde a[0] apunta a la primera fila a[1] a la segunda fila y as sucesivamente. Por tanto, cada
elemento de la formacin apunta a un bloque de memoria suficientemente grande para almacenar una
fila de enteros (ncols enteros).
Los elementos individuales de la formacin se procesan utilizando el operador indireccin
repetidamente. En leerentrada, por ejemplo, cada elemento de la formacin es referenciado como
scanf("%d", (* (a + fila) + col));
De igual modo, la suma de los elementos de la formacin dentro de calcularsuma se escribe como
*(*(c + fila) + col) = *(*(a + fila) + col) + *(*(b + fila) + col);

<

y la primera instruccin printf dentro de escribirsalida est escrita como


printf("%4d", *(*(a + fila) + col));
Por supuesto, podramos haber utilizado la notacin ms convencional dentro de las funciones. As,
en leerentrada podramos haber escrito
scanf("%d", a[fila] [col]);

en vez de

- 115 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

scanf("%d", (*(a + fila) + col));


Anlogamente, en calcularsuma podramos haber escrito
c[fila] [col] = a[fila] [col] + b[fila] [col];
en vez de
*(*(c + fila) + col) = *(*(a + fila) + col) + *(*(b + fila) + col);
y en escribirsalida podramos haber escrito
printf("%4d", a[fila] [col]);
Ejemplo 122: Formaciones de punteros y cadenas de caracteres. Las formaciones de punteros ofrecen
un mtodo particularmente til para almacenar cadenas de caracteres. Cada elemento de la formacin es
considerado un puntero de tipo carcter que indica dnde comienza la cadena:
#include <stdio.h>
#include <stdlib.h>
#INCLUDE<string.h>
#define TAMANO 12
void main()
{
int i, n = 0;
char *x[10];
printf(Introducir debajo cada cadena en una linea\n\n");
printf("Escribir \'FIN\' para terminar\n\n");
/*leer la lista de cadenas de caracteres*/
do{
/* reservar memoria */
x[n] = (char *) malloc(TAMANO *sizeof(char));
printf("cadena %d: ", n+1);
scanf("%s", x[n]);
} while (strcmp(x[n++], "FIN"));
/*escribir la de cadenas de caracteres*/
printf("\n\nLista de cadenas,\n");
for(i =0;i<n;++i)
printf("\ncadena %d, %s", i+1, x[i]);
)
Aprovechando la versatilidad que nos proporcionan los punteros, resulta particularmente simple aadir
una llamada a una funcin que reordene alfabticamente las cadenas. Para ello se utiliza la funcin de
biblioteca strcmp y se intercambian los punteros ( y no las cadenas).

- 116 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include <stdio.h>
#include <stdlib.h>
#INCLUDE<string.h>
#define TAMANO 12
/*prototipo de la funcion reordenar*/
void reordenar(int n, char *x[]);
void main()
{
int i, n = 0;
char *x[10];
printf(Introducir debajo cada cadena en una linea\n\n");
printf("Escribir \'FIN\' para terminar\n\n");
/*leer la lista de cadenas de caracteres*/
do{
/* reservar memoria */
x[n] = (char *) malloc(TAMANO *sizeof(char));
printf("cadena %d: ", n+1);
scanf("%s", x[n]);
} while (strcmp(x[n++], "FIN"));
/*llamada a la funcin reordenar*/
reordenar(--n,x);
/*escribir la lista reordenada de cadenas de caracteres*/
printf("\n\nLista de cadenas,\n");
for(i =0;i<n;++i)
printf("\ncadena %d, %s", i+1, x[i]);
)

void reordenar(int n, char *x[]){


char *temp;
int i, elem;
for(elem = 0; elem< n-1; ++elem)
/*encuentra la menor de las cadenas restantes*/
for(i = elem+1; i<n; ++i)
if(strcmp(x[elem], x[i])>0){
temp =x[elem];
x[elem]= x[i];
x[i]=temp;
}
return;
}

- 117 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 123: Interpretacin de declaraciones que involucran punteros: Trata de razonar el sentido de
las siguientes declaraciones.
int *p;
int *p[10];
int (*p)[10];
int *p(void);
int p(char *a);
int *p(char *a);
int (*p)(char *a);
int (*p(char *a)) [10];
int p(char (*a) []);
int p(char *a[]);
int *p(char a[]);
int *p(char (*a) []);
int *p(char *a[]);
int (*p) (char (*a) []);
int *(*p)(char (*a)[]);

int *(*p) (char *a[]);


int (*p[10]) (void);

/*p es un puntero a una cantidad entera*/


/*p es una formacin de 10 punteros a enteros*/
/*p es un puntero a una formacin de 10 enteros*/
/*p es una funcin que devuelve un puntero a entero*/
/*p es una funcin que acepta un argumento que es un puntero a
carcter y devuelve un entero*/
/*p es una funcin que acepta un argumento que es un puntero a
carcter y devuelve un puntero a un entero*/
/* p es un puntero a una funcin que acepta un argumento que es un
puntero a carcter y devuelve un entero */
/* p es una funcin que acepta un argumento que es un puntero a
carcter y devuelve un puntero a una formacin de diez enteros */
/* p es una funcin que acepta un argumento que es un puntero a una
formacin de caracteres y devuelve un entero */
/* p es una funcin que acepta un argumento que es una formacin
de punteros a caracteres y devuelve un entero */
/* p es una funcin que acepta un argumento que es una formacin
de caracteres y devuelve un puntero a entero */
/* p es una funcin que acepta un argumento que es un puntero a una
formacin de caracteres y devuelve unpuntero a entero */
/* p es una funcin que acepta un argumento que es una formacin
de punteros a caracteres y devuel ve un puntero a entero * /
/* p es un puntero a una funcin que acepta un argumento que es un
puntero a una formacin de caracteres y devuelve un entero */
/* p es un puntero a una funcin que acepta un argumento que es un
puntero a una formacin de caracteres y devuelve un puntero a
entero */
/* p es un puntero a una funcin que acepta un argumento que es una
formacin de punteros a caracteres y devuelve un puntero a entero */
/* p es una formacin de 10 punteros a funcin; cada funcin
devuelve un entero */

- 118 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

12. Estructuras.

==

Supongamos que deseamos llevar a cabo una aplicacin capaz de gestionar las cuentas de los clientes
de un banco. De cada cuenta guardaremos:

==

El nmero de cuenta, un nmero de entero que identifica una cuenta del resto.
El tipo de cuenta; viene identificado por una letra, l o r, correspondientes a las iniciales de
cuenta y libreta.
El nombre del cliente; una cadena de caracteres con un mximo de 80.
El saldo; es un nmero en coma flotante, ya que la cuenta est en euros.

Es evidente que en este caso no podemos utilizar una formacin bidimensional ya que los datos son de
distinto tipo. Necesitaramos un nuevo tipo de variable que agrupara datos de diferente tipo. En C, a esto
se le llama estructura.
Ejemplo 123: Definicin de una estructura. Tipos de datos definidos por el usuario. Cuando
definimos una estructura, estamos determinando la forma genrica de organizar un conjunto de datos
dispares. Para ello se utiliza la siguiente sintaxis:
struct nombre {
tipo nombre_miembro1;
tipo nombre_miembro2;

};
Por ejemplo,
struct cuenta {
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
}
define exactamente el tipo de estructura que nosotros necesitbamos para guardar los datos de los clientes
del banco. Una vez definida la estructura, podemos declarar las variables a las que vamos a darles dicha
forma. Para ello podemos aadir
struct cuenta antiguocliente, nuevo cliente;
As, son variables de tipo cuenta es decir, antiguocliente y nuevo cliente tienen una estructura de cuenta.
Es posible combinar en una misma instruccin la declaracin de la estructura y la de las variables que
poseen dicha estructura:
struct cuenta {
int no_cuenta;
char tipo_cuenta;
- 119 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

char nombre[80];
float saldo;
} antiguocliente, nuevo cliente;
En este caso hubiera sido posible omitir el nombre de la estructura (cuenta):
struct {
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
} antiguocliente, nuevo cliente;
La intruccin typedef permite al programador definir nuevos tipos de datos que sean equivalentes a los
tipos de datos ya existentes. Si escribimos
typedef int edad;
estamos indicndole al compilador que edad es un tipo de datos equivalente al tipo int. Por ello, cuando
declaramos
edad varon, mujer;
es equivalente a escribir
int varon, mujer
Anlogamente, las declaraciones
typedef float altura[100];
altura hombres, mujeres;
define como una formacin de 100 elementos en coma flotante. La intruccin typedef resulta
especialemente til cuando se definen estructuras, ya que hace innecesario el escribir struct nombre tantas
veces como variables con dicha estructura declaremos. As en,
typedef struct {
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
} registro;
registro anteriorcliente, nuevocliente;
la primera declaracin define registro como un tipo de datos definido por el programador. La segunda
declaracin define anteriorcliente y nuevocliente como variables con la estructura del tipo registro.

- 120 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 124: Una estructura como miembro de otra estructura. Una variable de estructura puede
contener como miembro de otra estructura. En tales situaciones, la declaracin de la estructura interna
debe aparecer antes que la declaracin de la estructura externa:
struct fecha{
int mes;
int dia;
int anio;
};
struct cuenta{
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
struct fecha ultimopago;
} antiguocliente, nuevocliente;
El siguiente esquema aclara cmo se encuentran organizados los datos:

cuenta
no_cuenta
tipo_cuenta
nombre[80]
saldo
ultimopago
mes

dia

anio
Ejemplo 125: Inicializacin de una variable de tipo estructura. A los miembros de una variable con
una cierta estructura se les pueden asignar valores iniciales de un modo muy parecido a como se haca
con las formaciones:
struct fecha{
int mes;
int dia;
int anio;
};
struct cuenta{

- 121 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
struct fecha ultimopago;
};
static struct cuenta cliente ={12345,C,Manuel Bentez, 6000.37, 5, 23, 2002}
En este ejemplo cliente es una variable cuya estructura corresponde al modelo cuenta y cuyos valores
iniciales son los que aparecen entre llaves.
Ejemplo 126: Formaciones de estructuras Normalmente lo que queremos es guardar la informacin de
muchos clientes y no nicamente la referente a Manuel Bentez. Para ello necesitaramos una formacin
de datos, todos ellos con la estructura de cuenta:
struct fecha{
int mes;
int dia;
int anio;
};
struct cuenta{
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
struct fecha ultimopago;
} cliente[5];
Una formacin de estructuras puede tener asignados valores iniciales:
static struct cuenta cliente ={12,C,Manuel, 6000.37, 5, 23, 2002,
13, C,Ana, 4568.23, 3, 12, 2002,
14, L,Miguel,2541.98, 1, 1, 2001,
15, C, Juan, 234.34, 11, 25, 2001,
16, L,Nicolasa, 3167.12, 1, 1,2000};
Algunos programadores prefieren incluir cada conjnto de constantes dentro de un par de llaves para
separar claramente los elementos individuales de la formacin. As, la declaracin anterior podra
escribirse tambin del siguiente modo:
static struct cuenta cliente ={
{12,C,Manuel, 6000.37, 5, 23, 2002},
{13, C,Ana, 4568.23, 3, 12, 2002},
{14, L,Miguel,2541.98, 1, 1, 2001},
{15, C, Juan, 234.34, 11, 25, 2001},
{16, L,Nicolasa, 3167.12, 1, 1,2000}
};

- 122 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 127: Procesamiento de estructuras. Con este ejemplo vamos a aprender cmo se puede acceder
a los diferentes datos almacenados en una variable de tipo estructura.
struct fecha{
int mes;
int dia;
int anio;
};
struct cuenta{
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
struct fecha ultimopago;
};
static struct cuenta cliente ={12345,C,Manuel Bentez, 6000.37, 5, 23, 2002}

En este ejemplo cliente posee una estructura de tipo cuenta. Si deseamos acceder al nmero de cuenta de
cliente, el 12345, tendremos que escribir
cliente.no_cuenta
Sobre pueden actuar otros operadores estudiados en secciones anteriores
++cliente.no_cuenta
&cliente.no_cuenta
El punto es una operacin que tiene prioridad frente al resto de los operadores. Esto quiere decir que las
expresiones anteriores son equivalentes a las siguientes:
++(cliente.no_cuenta)
&(cliente.no_cuenta)
Si deseamos acceder al mes en que se ha realizado el ltimo pago, (5), utilizaremos la expresin:
cliente.ultimopago.mes
En el caso del miembro nombre, se trata de una cadena de caracteres a los que puedo acceder
individualmente. Si escribo
cliente.nombre[2]
nos estamos refiriendo al tercer carcter dentro de la formacin nombre, es decir n. Si estamos
trabajando con una formacin de estructuras como la definida en el ejemplo 126 la expresiones que
podramos utilizar seran del estilo de las siguientes:

- 123 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

expresin
cliente[3].nombre
cliente[3].nombre[7]

significado
El nombre del cliente 4
El nombre del octavo carcter del
nombre del cliente 4
El mes del ltimo pago del cliente 4.
Incrementa en una unidad el valor
del da del ltimo pago del cliente 4.
Asigna el valor 0 al saldo del cliente
4.
Actualiza el valor del saldo del
cliente 4 restndole los ltimos
pagos.
Muestra el nombre del cliente 4
Si la cuenta es de tipo L escribe
Libreta n antes del nmero de
cuenta. Si no lo es, escribe Cartilla
n antes del nmero d ecuenta.

cliente[4].ultimopago.mes
++cliente[3].ultimopago.dia
cliente[3].saldo=0
cliente[3].saldo -= pagos

printf(Nombre: %s\n, cliente[3].nombre);


if(cliente[4].tipo_cuenta == L)
printf(Libreta n : %d\n,
cliente[4].no_cuenta);
else
printf(Cartilla n : %d\n,
cliente[4].no_cuenta);

Ejemplo 128: Procesamiento de estructuras completas. Supongamos que anteriorcliente y nuevo


cliente son variables con la misma estructura:
struct fecha{
int mes;
int dia;
int anio;
};
struct cuenta{
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
struct fecha ultimopago;
} anteriorcliente, nuevocliente;
Previamente hemos asignado valores a todos los miembros de anteriorcliente. En al mayora de las nuevas
versiones de C es posible copiar estos valores en nuevocliente simplemente mediante la instruccin de
asignacin:
nuevocliente = anteriorcliente;
Un poco ms adelante veremos cmo se pueden pasar, bien los miembros individuales, bien una
estructura completa, a una funcin. Pero antes es necesario que estudiemos la relacin entre punteros y
estructuras.

- 124 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 129: Estructuras y punteros. Consideremos la siguiente declaracin de estructura:


typedef struct {
int mes;
int dia;
int anio;
} fecha;
typedef struct {
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
fecha ultimopago
} cuenta;
cuenta cliente, *pc;
En este ejemplo cliente es una variable con la estructura de tipo cuenta y pc es un puntero que seala la
direccin de una variable con la estructura de tipo cuenta. Podemos asignar la direccin de comienzo de
cliente a este puntero mediante la instruccin:
pc = &cliente;
Podamos haber incluido las declaraciones del puntero pc y la variable cliente dentro de la declaracin de
la estructura:
typedef struct {
int no_cuenta;
char tipo_cuenta;
char nombre[80];
float saldo;
} cliente, *pc;
Para acceder a un miembro individual de cliente por medio del puntero pc , por ejemplo, al nombre del
cliente, escribiremos
pc -> nombre
que es equivalente a
cliente.nombre
Otras formas equivalentes son las detalladas en la siguiente tabla. Intenta averiguar a qu dato se refieren
y razona por qu son realmente equivalentes:

cliente.no_cuenta

es equivalente a
pc->no_cuenta

- 125 -

tambin es equivalente a
(*pc).no_cuenta

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

cliente.saldo
cliente.ultimopago.mes

pc->saldo
pc->ultimopago.mes

(*pc).saldo
(*pc).ultimopago.mes

cliente.nombre[2]
*(cliente.nombre+2)

pc->nombre[2]
pc->(nombre+2)

(*pc).nombre[2]
*((*pc).nombre+2)

Ejemplo 130: Ms sobre estructuras y punteros. Una estructura puede incluir tambin uno o ms
punteros como miembros:
#include<stdio.h>
void main(){
int n =3333;
char t = A;
float b = 99.99;
typedef struct {
int mes;
int dia;
int anio;
} fecha;
struct {
int *no_cuenta;
char *tipo_cuenta:
char *nombre;
float *saldo;
fecha ultimopago;
} cliente, *pc = &cliente;
cliente.no_cuenta = &n;
cliente.tipo_cuenta = &t;
cliente.nombre = Lzaro;
cliente.saldo = &b;
printf(%d %c %s %f.2f\n, *cliente.no_cuenta, *cliente.tipo_cuenta,
cliente.nombre, *cliente.saldo);
printf(%d %c %s %f.2f\n, *pc->no_cuenta,, *pc->tipo_cuenta,
pc->nombre, *pc->saldo);
Dentro de la segunda estructura, los miembros no_cuenta, tipo_cuenta y saldo son punteros.
Ejemplo 131: Reserva de memoria para una estructura. El nmero de bytes reservado para una
estructura particular puede determinarse mediante el uso de la instruccin sizeof.
#include<stdio.h>

- 126 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void main(){
typedef struct {
int mes;
int dia;
int anio;
} fecha;
struct {
int no_cuenta;
char tipo_cuenta:
char nombre[8];
float saldo;
fecha ultimopago;
} cliente, *pc = &cliente;
printf(Nmero de bytes (dec): %d\n, sizeof, *pt);
printf(Nmero de bytes (hex): %x\n\n, sizeof, *pt);
printf(Direccin de comienzo (hex): %x\n, pt);
printf(Direccin incrementada (hex): %x\n, ++pt);
}
miembro
no_cuenta
tipo_cuenta
nombre[8]
saldo

nmero de bytes

ultimopago
Cuando la estructura slo contiene punteros, el compilador no reserva memoria para almacenar el
contenido que deseamos introducir. Debido a esta situacin, la funcin que rellena la estructura debe
encargarse de la peticin de memoria para introducir los datos. Esta peticin puede hacerse de manera que
el tamao de cada vector se ajuste exactamente al tamao de la memoria que se necesita. Para ello se
utiliza una variable auxiliar que permite la introduccin de datos de tamao cualquiera y, una vez que el
usuario ha introducido el dato que realmente desea almacenar, se calcula el tamao de dicho datoy se
ajusta la peticin de memoria a ese tamao mediante la funcin malloc.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char *cod_art;
char *nombre;
char *desc;
float *precio;
int *existencias;
char *cod_prov;
char *cod_notas;
- 127 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

} articulo;
void leerarticulo(articulo *ar); /*prototipo*/
void main(){

static articulo art;


leerarticulo(&art);
}
void leerarticulo(articulo *ar)
{
char aux[50];
float precio;
int existencias;
/*Codigo de articulo*/
printf("\nIntroduzca el cdigo del artculo: ");
scanf(" %[^\n]", aux);
ar->cod_art = (char *)malloc( (strlen(aux)+1) * sizeof(char));
/*Con strlen solo se cuentan los caracteres pero no el \0 del final
de la cadena. Por eso ponemos strlen(aux)+1*/
if(ar->cod_art == NULL){
("\nNo hay memoria disponible");
exit(0) ;
}
strcpy (ar->cod_art , aux);
/*Nombre de artculo*/
printf("\nIntroduzca el nombre del artculo: ");
scanf(" %[^\n]", aux);
ar->nombre = (char *)malloc( (strlen(aux)+1) * sizeof(char));
if(ar->nombre == NULL){
printf("No hay memoria disponible");
exit(0);
}
strcpy(ar->nombre, aux);
/*Descripcion del articulo*/
printf("\nIntroduzca la descripcin del artculo: ");
scanf(" %[^\n]", aux);
ar->desc = (char *)malloc((strlen(aux)+1) * sizeof(char));
if(ar->desc == NULL){
("No hay memoria disponible");
exit(0);
}
strcpy(ar->desc, aux);

- 128 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/*Precio del articulo*/


printf("\nIntroduzca el precio del artculo: ");
scanf(" %f", &precio);
ar->precio=(float *)malloc(sizeof(float));
/*Existencias*/
printf("\nIntroduzca las existencias del artculo: ");
scanf(" %f", &existencias);
ar->existencias=(int *)malloc(sizeof(int));
/*Codigo del proveedor*/
printf("\nIntroduzca el cdigo del proveedor: ");
scanf(" %[^\n]", aux);
ar->cod_prov = (char *) malloc(strlen(aux) * sizeof(char));
if(ar->cod_prov == NULL){
printf("No hay memoria disponible");
exit(0);
}
strcpy(ar->cod_prov, aux);

>>

/*Comentarios sobre el articulo*/


printf("\nIntroduzca comentarios sobre el artculo: ");
scanf(" %[^\n]", aux);
ar->cod_notas = (char *)malloc((strlen(aux)+1) * sizeof(char));
if(ar->cod_notas == NULL){
printf("No hay memoria disponible");
exit(0);
}
strcpy(ar->cod_notas, aux);

}
Completa el programa anterior con una funcin externa que reciba un puntero a la estructura
artculo y muestre por pantalla los diferentes miembros.
Modifcalo para que puedan existir 5 artculos.

El problema de la gestin dinmica es la no destruccin por parte del compilador de la reserva de


memoria cuando se acaba la validez de la variable. El compilador liberar la memoria ocupada por los
punteros, pero no la memoria a la que apuntan dichos punteros. Es decir, debemos liberar esa memoria
nosotros mismos "a mano":
void DestruirArticulo(struct articulo *ar)
{
free(ar->cod_art);
free (ar->nombre) ;
free(ar->desc);
free(ar->cod-prov);
free(ar->cod_notas);

- 129 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
Escribe un programa que sea capaz de almacenar los datos de un alumno en una estructura
apropiada, haciendo uso de punteros y de la reserva dinmica de memoria.

Ejemplo 132: Paso de estructuras a una funcin: transferencia de miembros individuales. Los
miembros individuales de una estructura se pueden pasar a una funcin como argumentos en la llamada
de la funcin. Por ejemplo, en el siguiente programa,
#include<stdio.h>
void visualizar(char nombre[], float saldo); /*prototipo de la funcin*/
void main(){
typedef struct {
int mes;
int dia;
int anio;
} fecha;
struct {
int no_cuenta;
char tipo_cuenta;
char nombre[8];
float saldo;
fecha ultimopago;
};
static struct cuenta cliente ={12345,C,Manuel, 6000.37, 5, 23, 2002};
visualizar(cliente.nombre, cliente.saldo);
}
void visualizar(char nombre[], float saldo) {
printf(\nEl saldo del cliente %s es de %f euros, nombre, saldo);
}
los miembros nombre y saldo han sido pasados por valor a la funcin visualizar con el fin de mostrar por
pantalla el saldo del cliente. Debe quedar claro que los miembros pasados de esta forma no conservarn
las alteraciones que puedan sufrir fuera del cuerpo principal de la funcin.
Proponemos al lector que aada una funcin al programa anterior, llamada anuladora que ponga el
saldo a cero. Mustrese por pantalla el valor del saldo tres veces:

???

Antes de llamar a la funcin anuladora, desde cuerpo principal del programa


Despues de anular el saldo, desde la funcin anuladora.
Despus de llamar a la funcin, desde el programa principal.

- 130 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 133: Paso de estructuras a una funcin: transferencia de estructuras completas. Si


deseamos pasar la estructura completa, lo ms simple es pasar como argumento un puntero a la direccin
donde comienza la estructura.
#include<stdio.h>
typedef struct {
char *nombre;
int no_cuenta;
char tipo_cuenta;
float saldo;
} registro;
void ajustar(registro *pt);

/* prototipo de funcin */

main() /* transferir un puntero a estructura a una funcin */


{
static registro cliente = {"Lzaro", 3333, 'A', 33.33};
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
ajustar(&cliente);
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
}

void ajustar(registro *pt)


{
pt->nombre = "Jos";
pt->no_cuenta = 9999;
pt->tipo_cuenta = 'R';
pt->saldo = 99.99;
return;
}

/* definicin de funcin */

Obsrvese que los valores asignados a los miembros de cliente dentro de ajustar son reconocidos dentro
del cuerpo principal del programa, tal y como como esperbamos. Comprese el programa anterior con el
siguiente. En l se transfiere una estructura completa, en lugar de un puntero.
#include <stdio.h>
typedef struct {
char *nombre;
int no_cuenta;
char tipo_cuenta;
float saldo;
} registro;

- 131 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void ajustar(registro cliente);

/* prototipo de funcin */

main() /* transferir una estructura a una funcin */


{
static registro cliente = {"Lzaro", 3333, 'A', 33.33};
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
ajustar(cliente) ;
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
}
void ajustar(registro clien) /* definicin de funcin */
{
clien.nombre = "Jos";
clien.no_cuenta = 9999;
clien.tipo_cuenta = 'R';
clien.saldo = 99.99;
return;
}

Ejemplo 134: Paso de estructuras a una funcin: devolucin de un puntero. Una funcin, tal y como
hemos visto en el ejemplo anterior, puede recibir como argumento un puntero que seale el inicio de una
estructura. Pero, adems, una funcin tambin puede devolver un puntero a la parte del programa desde la
que se hizo la llamada. Esta caracterstica puede ser especialmente til cuando se pasen varias
estructuras a una funcin y slo una de ellas sea devuelta.
El siguiente programa busca todos los datos sobre un cliente a partir de su nmero de cuenta. Obsrvese
que cliente es una formacin de N elementos, cada uno de los cuales posee una estructura tipo registro
(cada elemento de la formacin ser una estructura independiente). La estrategia bsica ser introducir un
nmero de cuenta y transferirlo con la formacin de registros a la funcin llamada buscar. Dentro de
buscar, el nmero de cuenta ser comparado con el nmero de cuenta almacenado dentro de cada registro,
hasta que se produzca una coincidencia o hasta que se haya buscado en toda la lista de registros. Si se
produce una coincidencia, se devuelve a main un puntero a ese elemento de la formacin (la estructura
que contiene el registro del cliente deseado) y se muestran los contenidos del registro. Si no se produce
una coincidencia despus de la bsqueda en toda la formacin, entonces la funcin devuelve el valor
NULL (cero) a main. El programa muestra entonces un mensaje de error pidiendo al usuario que
reintroduzca el nmero de cuenta. Esta bsqueda continuar hasta que se introduzca el valor cero como
nmero de cuenta.
/* encontrar un registro de cliente que corresponda a un nmero de
cuenta especificado */
#include<stdio.h>
#define N 3
#define CERO 0

- 132 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

typedef struct {
char *nombre;
int no_cuenta;
char tipo_cuenta;
float saldo;
} registro;
registro *buscar(registro tabla[], int ncuenta); /* prototipo de
funcin */
main()
{
static registro cliente[N] = {
{"Lzaro", 3333, 'A', 33.33},
{"Jos", 6666, 'R', 66.66},
{"Rafael", 9999, 'D', 99.99}
};/* formacin de estructuras */
int ncuenta;
registro *pt;

/* declaracin de variable */
/* declaracin de puntero */

printf("Localizador de cuenta de cliente\n");


printf("Para FIN, introducir O para el nmero de cuenta\n");
printf("\nCuenta n: ");
/* introducir primer nmero de cuenta */
scanf("%d", &ncuenta);
while (ncuenta != 0) {
pt = buscar(cliente, ncuenta);
if (pt != CERO) {/* se encontr una coincidencia */
printf("\n\nNombre: %s", pt->nombre);
printf("\nCuenta n: %d", pt->no_cuenta);
printf("\nTipo de cuenta: %c", pt->tipo_cuenta);
printf("\nSaldo: %.2f\n", pt->saldo);
}
else
printf("\nERROR -Por favor pruebe de nuevo\n");
printf("\nCuenta n: ");
/* introducir siguiente
nmero de cuenta */
scanf("%d", &ncuenta);
}
}
registro *buscar(registro tabla[N], int ncuenta) /*definicin de
funcin */
/* acepta una formacin de estructuras y un nmero de cuenta, devuelve un puntero a una estructura
particular (un elemento de la formacin) si el nmero de cuenta coincide con un elemento de la formacin
*/

- 133 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

{
int cont;
for (cont = 0; cont < N; ++cont)
if (tabla[cont].no_cuenta == ncuenta) /* encontrada una
coincidencia */
return(&tabla[cont]);
/* devuelve un puntero al
elemento de la formacin */
return(CERO);
}
Ejemplo 135: Paso de estructuras a una funcin: devolucin de una estructura completa. Observa las
diferencias entre los dos siguientes programas:
#include <stdio.h>
typedef struct {
char *nombre;
int no_cuenta;
char tipo_cuenta;
float saldo;
} registro;
void ajustar(registro cliente);

/* prototipo de funcin */

main()

/* transferir una estructura a una


funcin */

{
static registro cliente = {"Lzaro", 3333, 'A', 33.33};
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
ajustar(cliente) ;
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
}
void ajustar(registro clien) /* definicin de funcin */
{
clien.nombre = "Jos";
clien.no_cuenta = 9999;
clien.tipo_cuenta = 'R';
clien.saldo = 99.99;
return;
}

En este caso ajustar acepta una estructura del tipo registro y no devuelve nada.

- 134 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include <stdio.h>
typedef struct {
char *nombre;
int no_cuenta;
char tipo_cuenta;
float saldo;
} registro;
registro ajustar(registro cliente);

/* prototipo de funcin */

main() /* transferir una estructura a una funcin y devolver


la estructura */
{
static registro cliente = {"Lzaro", 3333, 'A', 33.~3};
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
cliente = ajustar(cliente);
printf("%s %d %c %.2f\n", cliente.nombre, cliente.no_cuenta,
cliente.tipo_cuenta, cliente.saldo);
}
registro ajustar(registro clien)
{
clien.nombre = "Jos";
clien.no_cuenta = 9999;
clien.tipo_cuenta = 'R';
clien.saldo = 99.99;
return(clien);
}

/* definicin de funcin */

Obsrvese que ajustar devuelve ahora una estructura del tipo registro a main. Debido a que ahora la
estructura alterada se devuelve a la parte del programa donde se encuentra la llamada, las alteraciones
hechas dentro de ajustar son reconocidas tambn en main.

13. Organizacin de la informacin mediante listas.


La reserva dinmica de memoria junto con el uso de la definicin de estructuras que contienen punteros
como miembros, permiten ajustar la memoria reservada a la utilizada realmente. No obstante, siempre
debemos determinar por adelantado el nmero de elementos que poseen tal estructura. Dicho de otro
modo, si escribimos un programa para gestionar los clientes de una empresa, deberamos conocer de
antemano cuntos clientes va a tener con el fin de reservar tantas direcciones de memoria como clientes.
Este nmero no podra ser incrementado durante la ejecucin del programa. El diseo de bases de datos
requiere de una nueva forma de organizar la memoria.
En la mayor parte de los ejemplos que hemos visto hasta ahora reservbamos la memoria que bamos
a usar al comenzar el programa (al definir las variables). El problema surge a la hora de hacer un
- 135 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

programa al estilo de una agenda. No sabemos a priori cuntos nombres vamos a meter en la agenda, as
que si usamos un vector para este programa podemos quedarnos cortos o pasarnos. Si, por ejemplo
creamos, una agenda con un array de mil elementos (que pueda contener mil nmeros) y usamos slo 100
estamos desperdiciando una cantidad de memoria importante. Si por el contrario decidimos crear una
agenda con slo 100 elementos para ahorrar memoria y necesitamos 200 nos vamos a quedar cortos. La
mejor solucin para este tipo de programas son las listas enlazadas.
En una lista enlazada la memoria se va tomando segn se necesita. Cuando queremos aadir un nuevo
elemento reservamos memoria para l y lo aadimos a la lista. Cuando queremos eliminar el elemento
simplemente lo sacamos de la lista y liberamos la memoria usada.
Las listas enlazadas pueden ser simples, dobles o circulares.
Ejemplo 136: Estructuras que incluyen como un miembro un puntero a otra estructura del mismo
tipo.. A veces es deseable incluir dentro de una estructura un miembro que sea un puntero a este tipo de
estructura. En trminos generales esto puede ser expresado como
struct marca {
miembro 1;
miembro 2;
...
struct marca *nombre;
};
donde nombre se refiere el nombre de la variable puntero. As, la estructura del tipo marca contendr un
miembro que apunta a otra estructura del tipo marca. Veamos un ejemplo:
struct lista_elementos {
char elem[40];
struct lista_elementos *sig;
};
sta es una estructura del tipo 1 i sta_elementos. La estructura contiene dos miembros: una formacin de
40 caracteres, llamada elem, y un puntero a otra estructura del mismo tipo (un puntero a otra estructura
del tipo lista_elementos) llamado sig.
Ejemplo 137: Listas enlazadas. Creacin de una lista simple. Las estructuras con punteros a estructuras
del mismo tipo son utilizadas para enlazar unos datos con otros. La idea bsica de una estructura de
datos enlazada es que cada componente dentro de la estructura incluye un puntero indicando dnde est
el siguiente componente. Por tanto, el orden relativo de los componentes puede ser fcilmente cambiado.
Adems, los componentes individuales pueden ser fcilmente aadidos o borrados, simplemente alterando
los punteros. Como resultado, una estructura de datos enlazada no se limita a un nmero mximo de
componentes, sino que puede ser expandida o contraida segn sea necesario. El siguiente programa crea
una lista enlazada simple y la muestra por pantalla.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<ctype.h>

- 136 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#define CERO 0
struct lista_elementos{
char elem[40]; /* dato de este nodo */
struct lista_elementos *sig; /*puntero al siguiente nodo */
};

typedef struct lista_elementos nodo;


/*nodo es un dato con la estructura del tipo lista_de elementos*/
void crear(nodo *pt); /*prototipo de la funcion crear*/
void mostrar(nodo *pt); /*prototipo de la funcion mostrar*/

void main(){
nodo *registro, *principio;
char r;

/*puntero al principio de la lista*/

printf("\nVamos a crear una lista enlazada");


registro = (nodo *) malloc(sizeof(nodo));
principio = registro;
do{
/*pide el dato*/
printf("\nDato: ");
scanf(" %[^\n]", registro->elem);
/* reservar espacio para el siguiente registro */
printf("\nDesea aadir un nuevo elemento?(s/n): ");
scanf(" %c", &r);
if(r == 's'){
registro->sig = (nodo *) malloc(sizeof(nodo));
registro = registro->sig;
}
else {
registro->sig = CERO;
printf("\nCadena finalizada");
break;
}
} while((r != 'N')||(r != 'n'));
printf("\nLa lista enlazada que hemos creado es:");
mostrar(principio);
}

/*Muestra la lista enlazada*/

- 137 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void mostrar(nodo *principio) {


/*El argumento apunta al registro actual*/
nodo *registro; /*puntero al principio de la lista*/
registro = principio;
do{
printf("\nDireccion= %x ", registro);
printf("Dato = %s, apunta a= %x", registro->elem,
registro->sig); /*muestra el dato*/
if(registro->sig != CERO){
registro = registro->sig;
}
else {
printf("\nCadena finalizada");
break;
}
} while(registro != CERO);
return;

Escribe un programa que almacene en una lista enlazada los nombres, apellidos y datos telefnicos
de tu grupo de amigos.

Otra versin del mismo programa que utiliza, en este caso, un procedimiento recursivo:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CERO 0
struct lista_elementos {
char elem[40]; /*
dato de este nodo */
struct lista_elementos *sig;
/*puntero al siguiente nodo */
};

typedef struct lista_elementos nodo;


/*nodo es un dato con la estructura del tipo lista_de elementos*/
void crear(nodo *pt); /*prototipo de la funcion crear*/
void mostrar(nodo *pt); /*prototipo de la funcion mostrar*/

- 138 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void main(){
nodo *prin;

/*puntero al principio de la lista*/

printf("\nVamos a crear una lista enlazada");


prin = (nodo *) malloc(sizeof(nodo));
crear(prin);
printf("\nLa lista enlazada que hemos creado es:");
mostrar(prin);
}
void crear(nodo *registro) /* crea una lista enlazada */
/* el argumento apunta al nodo actual */
{
printf("\nDato (escribir \'FIN\' para terminar): ");
scanf(" %[^\n]", registro->elem);
if (strcmp(registro->elem, "FIN") == 0)
registro->sig = CERO;
else {
/* reservar espacio para el siguiente nodo */
registro->sig = (nodo *) malloc(sizeof(nodo));
/* crear el siguiente nodo */
crear(registro->sig) ;
}
return;
}
/*Muestra la lista enlazada*/
void mostrar(nodo *registro) {
/*El argumento apunta al nodo actual*/
if (registro -> sig != CERO) {
printf(" \nDato = %s, siguiente= %x",registro->elem,
registro->sig); /*muestra el dato*/
mostrar(registro->sig); /*toma el siguiente elemento*/
}
return;
}

La representacin de la lista sera:


puntero al principio
de la lista
dato 1

dato 2

- 139 -

dato 3

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

puntero al
siguiente nodo

puntero al
siguiente nodo

puntero al
siguiente nodo
null

AA

Ejemplo 138: Listas enlazadas. Insercin de un nuevo dato. Vamos a aadir en el programa anterior la
posibilidad de incluir un nuevo dato. Habr que distinguir dos casos:
El dato facilitado por el usuario es el primero de la lista. Por lo tanto, tendremos que colocar nuestro
dato antes del primero.
El dato facilitado por el usuario no es el primero de la lista. En este caso hay que cambiar los
punteros para incrustar el nuevo dato en la posicin adecuada.

Esa tarea se puede dividir en los siguientes pasos:


/*Pedir al usuario el nuevo dato y su posicion (el dato siguiente)*/
dato usuario, posicin
/*Reservar memoria para el nuevo nodo*/

/*Asignar el dato introducido por el usuario al nodo creado*/


dato usuario

/*Averiguar si la posicin deseada es la primera de la lista*/


/*Asignar como elemento siguiente el primero de la antigua lista*/
nuevo primer
dato

posicin
(antiguo primer dato)

puntero al
siguiente nodo

puntero al siguiente nodo

/*El puntero que seala la direccin de este nodo se convierte en


el primero de la lista*/
puntero al principio
de la lista

- 140 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

nuevo primer
dato

posicin
(antiguo primer dato)

puntero al
siguiente nodo

puntero al siguiente nodo

/*Si no lo es, localizamos el nodo precedente*/


puntero al principio
de la lista
dato 1
puntero al
siguiente nodo

dato 2
puntero al
siguiente nodo

posicin
puntero al
siguiente nodo
null

dato usuario

/*Conectar con posicin*/


puntero al principio
de la lista
dato 1
puntero al
siguiente nodo

dato 2
puntero al
siguiente nodo

posicin
puntero al
siguiente nodo
null

dato usuario
puntero al
siguiente nodo

/*Asignar como elemento anterior el precedente*/


puntero al principio
de la lista
dato 1
puntero al
siguiente nodo

dato 2
puntero al
siguiente nodo

dato usuario
puntero al
siguiente nodo

posicin
puntero al
siguiente nodo
null

- 141 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Desarrollemos cada uno de los apartados:


nodo *insertar(nodo *primero) { /*funcin insertar de tipo nodo.
Recibe un puntero al primer nodo.
Devuelve un puntero al primer nodo
de la lista modificada*/
char nuevodato[40];
char siguiente[40];
/*Pedir al usuario el nuevo dato y su posicion (el dato siguiente)*/
printf("Nuevo dato: ");
scanf("%[^\n]", nuevodato);
printf("Colocar delante de (escribir \'FIN\' si es el ltimo): ");
scanf("%[^\n]", siguiente);
/*Reservar memoria para el nuevo nodo*/
nuevoregistro = (nodo *) malloc(sizeof(nodo));
/*Asignar el dato introducido por el usuario al nodo creado*/
strcpy(nuevoregistro->elem, nuevodato);
if (strcmp(primero->elem, siguiente) == 0) {

/*Averigua si la
posicin deseada es la
primera de la lista*/
/*Asigna como elemento
siguiente el primero de
la antigua lista*/
/*El puntero que seala
la direccin del nuevo
nodo se convierte en el
primero de la lista*/

nuevoregistro->sig = primero;

primero = nuevoregistro;

}
else {
marca = localizar(primero,siguiente);

/*localiza el nodo
PRECEDENTE del nodo que
el usuario nos ha
indicado como
siguiente*/

if (marca == CERO)
printf("\No se encuentra el registro especificado");
else {
nuevoregistro->sig = marca->sig;

marca->sig = nuevoregistro;

- 142 -

/*El puntero del


nuevo registro
apunta al marcado
por el usuario como
siguiente*/
/*El puntero del
registro precedente

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

apunta a la
direccin del nuevo
registro*/
}
}
return(primero); /*Devolvemos el puntero al primer nodo*/

BB

}
Nos hemos vistos obligados a definir una nueva funcin, localizar, la cual recibe dos argumentos:
El primero es un puntero que seala al nodo actual.
El segundo es la cadena siguiente, que indica dnde hay que situar el nuevo nodo.
nodo *localizar(nodo *registro, char objetivo[]){

/*funcin localizar
de tipo nodo. Recibe
un puntero al nodo
actual y la cadena
siguiente. Devuelve
un puntero al nodo
anterior al nodo
donde est situada
la cadena
siguiente*/

if (strcmp(registro->sig->elem, objetivo) == 0)

/*coincidencia
encontrada*/

return(registro);
else
if (registro->sig->sig == NULL) /* fin de lista */
return(NULL);
else
localizar(registro->sig, objetivo); /*es recursiva. Lo
intenta en el
siguiente nodo*/
}
Ejemplo 139: Listas enlazadas. Borrado de un dato. Visto lo anterior, no nos resultar muy complicado
escribir una funcin que borre un determinado nodo. Tendremos que tener en cuenta que la memoria
reservada para el nodo deber ser liberada mediante la instruccin free. De nuevo ser necesario tratar de
forma separada el caso en el que el nodo que deseamos borrar sea el primero de la lista.
nodo *eliminar(nodo *primero) {

/*eliminar (borrar) un componente de


la lista enlazada; devuelve un
puntero al principio de la lista
modificada*/
/*el argumento apunta al primer nodo*/

- 143 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

nodo *localizar(nodo *, char[]);


nodo *marca;
nodo *temp;
char siguiente[40];
printf("Dato que deseamos borrar, ");
scanf("%[^\n]", siguiente);

/*declaracin de funcin*/
/*puntero al nodo ANTERIOR al
buscado*/
/*puntero temporal*/
/*dato que queremos borrar*/

if (strcmp(primero->elem, siguiente) == 0) {
/*borra el primer nodo*/
/* toma la direccin a la que apunta el primer nodo y la
guarda en una variable temporal */
temp = primero->sig;
/* liberar el espacio del nodo primero*/
free(primero);
/* ajustar el puntero a la direccion del que es ahora el
primer nodo */
primero = temp;
}
else {
/* borrar otro dato distinto del primero */
/* localizar el nodo PRECEDENTE del nodo indicado por el
usuario */
marca = localizar (primero, siguiente);
if (marca == NULL)
printf("\nNo se encuentra coincidencia\n");
else {
/* toma la direccin a la que apunta dicho nodo y la
guarda en una variable temporal*/
temp = marca->sig->sig;
/* liberar el espacio del nodo */
free(marca->sig);
/* ajustar el enlace para el siguiente nodo */
marca->sig = temp;
}

}
return(primero) ;

Ejemplo 140: Listas enlazadas. Otros tipos de listas. Es posible considerar una lista formada por nodos
que contengan dos punteros, uno al nodo siguiente y otro al nodo anterior. Este doble conjunto de
punteros permite recorrer la lista en cualquier direccin.

- 144 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CERO 0
struct lista_elementos {
char elem[40]; /*
dato de este nodo */
struct lista_elementos *ant;
/*puntero al anterior nodo */
struct lista_elementos *sig;
/*puntero al siguiente nodo */
};

typedef struct lista_elementos nodo;


/*nodo es un dato con la estructura del tipo lista_de elementos*/
El siguiente ejemplo permite crear una lista doblemente enlazada de cadenas:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<ctype.h>
#define CERO 0
struct lista_elementos{
char elem[40]; /*
dato de este nodo */
struct lista_elementos *ant;
/*puntero al anterior nodo */
struct lista_elementos *sig;
/*puntero al siguiente nodo */
};

typedef struct lista_elementos nodo;


/*nodo es un dato con la estructura del tipo lista_de elementos*/
void mostrar(nodo *pt); /*prototipo de la funcion mostrar*/

void main(){
nodo *registro, *principio, *anterior;
char r;

/*puntero al principio de la lista*/

printf("\nVamos a crear una lista enlazada");


registro = (nodo *) malloc(sizeof(nodo));
principio = registro;
anterior =CERO;
do{
/*pide el dato*/

- 145 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("\nDato: ");
scanf(" %[^\n]", registro->elem);
/* reservar espacio para el siguiente registro */
printf("\nDesea aadir un nuevo elemento?(s/n): ");
scanf(" %c", &r);
if(r == 's'){
registro->ant = anterior;
anterior = registro;
registro->sig = (nodo *) malloc(sizeof(nodo));
registro = registro->sig;
}
else {
registro->ant = anterior;
anterior = registro;
registro->sig = CERO;
printf("\nCadena finalizada");
break;
}
} while((r != 'N')||(r != 'n'));

DD

printf("\nLa lista enlazada que hemos creado es:");


mostrar(principio);

}
Escribe un programa que genere una lista enlazada en la que el ltimo nodo apunte al primero. Los
nodos contendrn un mensaje corto, que aparecer por pantalla (publicidad).
Escribe un programa mediante el que se generen nodos con dos punteros a otros dos nodos hijos.

14. Uniones.
Hemos visto que las estructuras toman una parte de la memoria y se la reparten entre sus miembros. Cada
miembro tiene reservado un espacio para l solo. El tamao total que ocupa una estructura en memoria es
la suma del tamao que ocupa cada uno de sus miembros. Las uniones tienen un aspecto similar en cuanto
a cmo se definen, pero tienen una diferencia fundamental con respecto a las estructuras: los miembros
comparten el mismo trozo de memoria. El espacio que ocupa en memoria una unin es el espacio que
ocupa el campo ms grande.
Una unin se define como
union nombre_de_la_unin
{
campos de la unin
};
Ejemplo 141: Una ejemplo de unin. Creemos una unin formada por dos miembros: un nombre de 10
bytes (nombre[10]) y la inicial (1 byte).
union persona {
- 146 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

char nombre[10];
char inicial;
};

Como hemos dicho la unin ocupa el espacio de su elemento ms grande, en este caso nombre. Por lo
tanto la unin ocupa 10 bytes. Las variables nombre e inicial comparten el mismo sitio de la memoria. Si
accedemos a nombre estaremos accediendo a los primeros 10 bytes de la unin (es decir, a toda la unin),
si accedemos a inicial lo que tendremos es el primer byte de la unin.
Unin
Nombre
Incial

P
P
P

e
e

d
d

r
r

o
o

#include <stdio.h>
union _persona
{
char nombre[10];
char inicial;
} pers;
int main()
{
printf("Escribe tu nombre: ");
gets(pers.nombre);
printf("\nTu nombre es: %s\n", pers.nombre);
printf("Tu inicial es: %c\n", pers.inicial);
}
Para comprender mejor eso de que comparten el mismo espacio en memoria vamos a ampliar el ejemplo.
Obsrvese lo que ocurre si aadimos unas lneas al final que modifiquen slo la inicial e impriman el
nuevo nombre:

#include <stdio.h>
union _persona
{
char nombre[10];
char inicial;
} pers;
int main()
{
printf("Escribe tu nombre: ");
gets(pers.nombre);
printf("\nTu nombre es: %s\n", pers.nombre);

- 147 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

printf("Tu inicial es: %c\n", pers.inicial);


/* Cambiamos la inicial */
pers.inicial='Z';
printf("\nAhora tu nombre es: %s\n", pers.nombre);
printf("y tu inicial es: %c\n", pers.inicial);
}
Un miembro individual de una unin puede ser accedido de la misma forma que un miembro de una
estructura, usando los operadores . y ->.

15. Archivos de datos.


Hasta ahora no habamos estudiado ninguna forma de guardar permanentemente los datos y resultados de
nuestros programas. La forma habitual de poder almacenar permanentemente informacin es mediante el
uso de archivos de datos. Es importante indicar que los ficheros no son nicamente los archivos que
guardamos en el disco duro; en C todos los dispositivos del ordenador pueden ser tratados como ficheros:
la impresora, el teclado, la pantalla,...
Ejemplo 142: Nuestro primer fichero. Para entrar en materia vamos a analizar un ejemplo que lee un
fichero de texto y lo muestra en la pantalla. Naturalmente, para poder abrir un fichero de texto,
previamente tendremos que crearlo. Abre el editor de textos Word y escribe una frase de texto. Antes de
cerrar el fichero, gurdalo con el nombre origen.txt en formato de texto.

#include <stdio.h>

int main()
{
FILE *fichero;
char letra;
fichero = fopen("origen.txt","r");
if (fichero==NULL)
{
printf( "No se puede abrir el fichero.\n" );
exit(1);
}
printf( "Contenido del fichero:\n" );
letra=getc(fichero);
while (feof(fichero)==0)
{
printf( "%c",letra );
letra=getc(fichero);
}
if (fclose(fichero)!=0)
printf( "Problemas al cerrar el fichero\n" );
- 148 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
Vamos a analizar el ejemplo con detalle:
FILE *fichero
En este ejemplo estamos trabajando con un tipo de archivo que recibe el nombre de secuencial. Cuando
se trabaja con archivos de este tipo, se establece un rea de bffer, en donde la informacin permanece
temporalmente mientras se est transfiriendo entre el ordenador y el archivo de datos. Todas las funciones
de entrada/salida estndar usan este tipo especial de puntero para conseguir informacin sobre el fichero
abierto. Este puntero no apunta al archivo sino a una estructura que contiene informacin sobre l. El
tipo de estructura FILE est definida en en el archivo stdio.h. (Esta estructura incluye entre otras cosas
informacin sobre el nombre del archivo, la direccin de la zona de memoria donde se almacena el
fichero, tamao del buffer,...).
fichero = fopen("origen.txt","r");
Ahora nos toca abrir el fichero. Para ello usamos la funcin fopen. Esta funcin tiene el siguiente
formato:

puntero = fopen("nombre_archivo.dat", "tipo");


El nombre de fichero se puede indicar directamente (como en el ejemplo) o usando una variable. El
fichero se puede abrir de diversas formas. Esto se especifica con el parmetro "tipo". Los modos posibles
son:
r
w
a

Abre un fichero existente para lectura.


Crea un fichero nuevo (o borra su contenido si existe) y lo abre para escritura.
Abre un fichero (si no existe lo crea) para escritura. El puntero se sita al final del archivo, de
forma que se puedan aadir datos si borrar los existentes

.
Se pueden aadir una serie de modificadores siguiendo a los modos anteriores:
b
t
+

Abre el fichero en modo binario.


Abre el fichero en modo texto.
Abre el fichero para lectura y escritura.

Ejemplos de combinaciones:
*
*
*

rb+
w+
rt

Abre el fichero en modo binario para lectura y escritura.


Crea (o lo borra si existe) un fichero para lectura y escritura.
Abre un archivo existente en modo texto para lectura.

La funcin fopen devuelve un puntero al principio del rea bffer asociada con el archivo. Si no puede
abrir el archivo devuelve el valor NULL.

- 149 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

if (fichero==NULL)
{
printf( "No se puede abrir el fichero.\n" );
exit(1);
}
Una cosa muy importante despus de abrir un fichero es comprobar si realmente est abierto. El sistema
no es infalible y pueden producirse fallos: el fichero puede no existir, estar daado o no tener permisos de
lectura. Si intentamos realizar operaciones sobre un puntero tipo FILE cuando no se ha conseguido abrir
el fichero puede haber problemas. Por eso es importante comprobar si se ha abierto con xito. Si el
fichero no se ha abierto el puntero fichero (puntero a FILE) tendr el valor NULL, si se ha abierto con
xito tendr un valor distinto de NULL. Por lo tanto para comprobar si ha habido errores nos fijamos en
el valor del puntero. Para salir utilizamos la funcin exit(1), que indica al sistema operativo que se han
producido errores.
printf( "Contenido del fichero:\n" );
letra=getc(fichero);
while (feof(fichero)==0)
{
printf( "%c",letra );
letra=getc(fichero);
}
Ahora ya podemos empezar a leer el fichero. Para ello podemos utilizar la funcin getc, que lee los
caracteres uno a uno. Se puede usar tambin la funcin fgetc. Adems de estas dos existen otras funciones
como fgets, fread que leen ms de un carcter y que veremos ms adelante. Tomamos un carcter de
fichero, lo almacenamos en letra y el puntero se coloca en el siguiente carcter. Cuando entramos en el
bucle while, la lectura se realiza hasta que se encuentre el final del fichero. Para detectar el final del
fichero se pueden usar dos formas:
*
*

con la funcin feof()


comprobando si el valor de letra es EOF.

En el ejemplo hemos usado la funcin feof. Esta funcin comprueba si se ha llegado al final de fichero en
cuyo caso devuelve un valor distinto de 0. Si no se ha llegado al final de fichero devuelve un cero.
if (fclose(fichero)!=0)
printf( "Problemas al cerrar el fichero\n" );
}
Una vez realizadas todas las operaciones deseadas sobre el fichero hay que cerrarlo. Es importante no
olvidar este paso pues el fichero podra corromperse. Al cerrarlo se vacan los buffers y se guarda el
fichero en disco. Un fichero se cierra mediante la funcin fclose(fichero). Si todo va bien fclose devuelve
un cero, si hay problemas devuelve otro valor. Estos problemas se pueden producir si el disco est lleno,
por ejemplo. Muchos compiladores cierran automticamente los archivos de datos al finalizar la ejecucin
del programa.

- 150 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Ejemplo 143: Un ejemplo simple de creacin y lectura de un fichero. Comenta el siguiente programa,
indicando la utilidad de cada una de las instrucciones que en l aparecen:
#include<stdio.h>
#include<ctype.h>
void main(){
FILE *fpt;
char c;
fpt = fopen("muestra.dat", "w");
do{
putc(toupper(c = getchar()), fpt);
}while (c != '\n');
fclose(fpt);
}
El programa anlogo para la lectura de un fichero sera el siguiente:
#include<stdio.h>
#include<ctype.h>
#define NULL 0
void main(){
FILE *fpt;
char c;
if((fpt =fopen("muestra.dat","r")) == NULL)
printf("\nERROR- No es posible abrir el fichero indicado\n");
else
do
putchar(c=getc(fpt));
while (c != '\n');

fclose(fpt);

}
Escribe un programa que copie, carcter a carcter, un fichero en otro.

#include<stdio.h>
#define NULL 0
int main(int argc, char * argv[]){
FILE *fich1, *fich2;
char c;

- 151 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

if((fich1 = fopen(arv[1], "r")) == NULL) {


printf("No se pudo abrir el fichero %s \n", argv[1]);
exit(1);
}
if((fich2 = fopen(argv[2],"w")) == NULL) {
printf("No se pudo abrir el fichero %s \n", argv[2]);
exit(1);
}
while((c=getc(fich1)) != '\n')
putc(c,fich2);
fclose(fich1);
fclose(fich2);
exit(0);
}
}
Enumeremos las funciones ms utilizadas para leer y escribir de un fichero:
funcin
getc()

putc()

fgets(texto, n, fichero)

Uso
lee los caracteres uno a uno del dispositivo de entrada determinado:
FILE *fichero1;
...
do{
putchar(c = getc(fichero1));
while(c != '\n');
escribe los caracteres uno a uno en el dispositivo de salida determinado:
FILE *fichero1;
...
do{
putc((c = getchar()), fichero1);
while(c != '\n');
lee desde el fichero hasta que encuentra un carcter '\n' o hasta que lee n-1
caracteres y aade '\0' al final de la cadena. La cadena leda la almacena en la
cadena texto. Si se encuentra EOF antes de leer ningn carcter o si se
produce un error la funcin devuelve NULL, en caso contrario devuelve la
direccin de buffer.
#include <stdio.h>
int main()
{
FILE *fichero;
char texto[100];
fichero=fopen("origen.txt","r");
if (fichero==NULL)
{
printf( "No se puede abrir el fichero.\n" );

- 152 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

exit(1);
}
printf("Contenido del fichero:\n");
fgets(texto,100,fichero);
while (feof(fichero)==0)/*feof() devuelve 0 si no
archivo*/
{
printf("%s",texto);
fgets(texto,100,fichero);
}
if (fclose(fichero)!=0)
printf( "Problemas al cerrar el fichero\n" );
}
fputs(texto, fichero)

encuentra el final del

escribe la cadena texto en el fichero fichero:


FILE *origen, *destino;
char cadena[50];
origen=fopen("origen.txt","r");
destino=fopen("destino.txt","w");
...
while ((fgets(cadena,50, origen)) != NULL)
fputs(cadena, destino);

fprintf(fichero, ...)

fclose(origen);
fclose(destino);
...
}
Trabaja igual que su equivalente printf. La nica diferencia es que podemos
especificar el fichero sobre el que operamos.
FILE *nombres;
...
nombres = fopen("nombres.dat","w");
printf("\nNombre: ");
scanf("%s", nombre);
fprintf(nombres, "\n%s\n", nombre);
...
fclose(nombres);

fscanf(fichero, ...)

Trabaja igual que su equivalente scanf. La nica diferencia es que podemos


especificar el fichero sobre el que operamos.
FILE *nombres;
...
nombres = fopen("nombres.dat","r");

- 153 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

fscanf(nombres, "\n%s\n", nombre);


printf("\nNombre: %s", nombre);
...
fclose(nombres);
fread(&p_bloque,
n_bloques,p_ archivo)

tamao,

Se utiliza para leer informacin de un fichero. Los argumentos que debe


recibir son: p_bloque, que es un puntero a la direccin donde se van a
guardar los datos en la memoria dinmica del ordenador, tamao,es el tamao
de memoria que ocupa uno de los bloques de datos, n_bloques, el nmero de
esos bloques que se van a leer y p_ archivo el punteroal rea del buffer
vinculada con el archivo:
#include <stdio.h>
struct {
char nombre[20];
char apellido[20];
char telefono[15];
} registro;
int main(){
FILE *fichero;
fichero = fopen( "nombres.txt", "r" );
while (!feof(fichero)){
if(fread(registro, sizeof(registro),1, fichero )){
printf( "Nombre: %s\n", registro.nombre );
printf( "Apellido: %s\n", registro.apellido);
printf( "Telfono: %s\n", registro.telefono);
}
}
fclose( fichero );
}

fwrite(&p_bloque,tamao,
n_bloques,p_ archivo)

Se utiliza para escribirr informacin en un fichero. Los argumentos que debe


recibir son: p_bloque, que es un puntero a la direccin donde se van a
guardar los datos en la memoria dinmica del ordenador, tamao,es el tamao
de memoria que ocupa uno de los bloques de datos, n_bloques, el nmero de
esos bloques que se van a leer y p_ archivo el punteroal rea del buffer
vinculada con el archivo:
#include <stdio.h>
struct {
char nombre[20];
char apellido[20];
char telefono[15];
} registro;

- 154 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

int main()
{
FILE *fichero;
fichero = fopen( "nombres.txt", "a" );
do {
printf( "Nombre: " ); fflush(stdout);
gets(registro.nombre);
if (strcmp(registro.nombre,"")){
printf( "Apellido: " ); fflush(stdout);
gets(registro.apellido);
printf( "Telfono: " ); fflush(stdout);
gets(registro.telefono);
fwrite(registro,sizeof(registro),1,fichero );
}
}while (strcmp(registro.nombre,"")!=0);
fclose( fichero );
}
fseek(p_archivo,
desplazamiento,modo)

Nos permite situarnos en la posicin que queramos de un fichero abierto.


Cuando leemos un fichero hay un 'puntero' que indica en qu lugar del fichero
nos encontramos. Cada vez que leemos datos del fichero este puntero se
desplaza. Con la funcin fseek podemos situar este puntero en el lugar que
deseemos. p_ archivo es el puntero al rea del buffer vinculada con el archivo;
desplazamiento son las posiciones (o bytes) que queremos desplazar el
puntero. Este desplazamiento puede ser de tres tipos, dependiendo del valor
del modo:
SEEK_SET
SEEK_CUR
SEEK_END

El puntero se desplaza desde el principio del fichero.


El puntero se desplaza desde la posicin actual del fichero.
El puntero se desplaza desde el final del fichero.

Si se produce algn error al intentar posicionar el puntero, la funcin devuelve


un valor distinto de 0. Si todo ha ido bien el valor devuleto es un 0. En el
siguiente ejemplo se muestra el funcionamiento de fseek. Se trata de un
programa que lee la letra que hay en la posicin que especifica el usuario.

#include <stdio.h>
int main(){
FILE *fichero;
long posicion;
int resultado;
fichero = fopen( "origen.txt", "r" );
printf("Qu posicin quieres leer?" );

- 155 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

fflush(stdout);
scanf("%D", &posicion );
resultado = fseek( fichero, posicion, SEEK_SET );
if (!resultado)
printf("En la posicin %D est la letra %c.\n",
posicion, getc(fichero));
else
printf( "Problemas posicionando el cursor.\n" );
fclose(fichero);
}
fseek(p_archivo)

Es complementaria a fseek, devuelve la posicin actual dentro del fichero. El


valor que nos da ftell puede ser usado por fseek para volver a la posicin
actual.

Ejemplo 144: Gestin de la base de datos de clientes. Inicializacin. Vamos a utilizar las instruccin
fprint para procesar los datos de los clientes de una empresa:
#include <stdio.h>
#include <string.h>
#define CIERTO 1
#define NULL 0
typedef struct {
int mes;
int dia;
int anio;
} fecha;
typedef struct {
char nombre[80];
char calle[80];
char ciudad[80];
int no_cuenta;
int tipo_cuenta,
float anteriorsaldo;
float nuevosaldo;
floar pago;
fecha ultimopago;
} registro;

/"(entero positivo) */
/*A (Al dia), R (retrasada) o D (morosa) */
/*(cantidad no negativa) */
/*(cantidad no negativa) */
/*(cantidad no negativa) */

registro leerpantalla(registro cliente); /*prototipo de funcion */


void escribirarchivo(registro cliente); /*prototipo de funcion */
FILE *fpt;
/* puntero a la estructura predefinida FILE */
main(){
int indicador = CIERTO;
/*declaracion de variable */

- 156 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

registro cliente;
/* declaracion de variable de estructura */
/* abrir un archivo nuevo solo para escritura */
fpt = fopen("registro.dat", "w");
/* introducir fecha y asignar valores iniciales */
printf("SISTEMA DE FACTURACION DE CLIENTES -INICIALIZACION\n\n");
printf ("Introduzca la fecha actual (mm/dd/aaaa): ");
scanf("%d/%d/%d", &cliente.ultimopago.mes,
&cliente.ultimopago.dia,
&clienteultimopago.anio);
cliente.nuevosaldo = 0;
cliente.pago = 0;
cliente tipo_cuenta = 'A';
/* bucle principal */
while (indicador) {
/*introducir el nombre del cliente y escribirlo en el
archivo */
printf("\nNombre (introducir \'FIN\' para terminar): ");
scanf("%[^\n]", cliente.nombre);
fprintf(fpt, "\n%s\n", cliente.nombre);
/* comprobacion de la condicion de parada */
if (strcmp(clientenombre, "FIN") == 0)
break;
cliente = leerpantalla(cliente) ;
escribirarchivo(cliente) ;
}
fclose(fpt);
}

registro leerpantalla(registro cliente)


/* lee el resto de datos */
{
printf("Calle: ");
scanf("%[^\n]", cliente.calle);
printf("Ciudad: ");
scanf("%[^\n]", cliente.ciudad);
printf("Nmero de cuenta: ");
scanf("%d", &cliente.no_cuenta);
printf("Saldo actual: ");
scanf("%f", &cliente.anteriorsaldo);
return(cliente) ;
}
void escribirarchivo(registro cliente)
/* escribe el resto de los datos en el archivo */
{

- 157 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

fprintf(fpt, "%s\n", cliente.calle) ;


fprintf(fpt, "%s\n", cliente.ciudad) ;
fprintf(fpt, "%d\n", cliente.no_cuenta) ;
fprintf(fpt, "%c\n", cliente.tipo_cuenta) ;
fprintf(fpt, "%.2f\n", cliente.anteriorsaldo) ;
fprintf(fpt, "%.2f\n", cliente.nuevosaldo) ;
fprintf(fpt, "%.2f\n", cliente.pago) ;
fprintf(fpt, "%d/%d/%d\n", cliente.ultimopago.mes,
cliente.ultimopago.dia,
cliente.ultimopago.anio) ;
return;
}

Completa el programa anterior con una funcin que lea los registros introducidos en el fichero y que
los muestre por pantalla.

Ejemplo 145: Gestin de la base de datos de clientes. Actualizacin. Vamos a modificar el programa
propuesto en el ejemplo 144 para permitir la actualizacin de los datos de los clientes. Observa
detalladamente las diferencias entre ambos programas:
/*actualizar un archivo de datos que contenga registros de clientes */
#include <stdio.h>
#include <string.h>
#define NULL 0
#define CIERTO 1
typedef struct {
int mes;
int dia;
int anio;
} fecha;
typedef struct {
char nombre[80];
char calle[80];
char ciudad[80] ;
int no_cuenta;
/* (entero positivo)
*/
int tipo_cuenta;
/* A (Al da), R (retrasada) o D (morosa) */
float anteriorsaldo; /* (cantidad no negativa) */
float nuevosaldo;
/* (cantidad no negativa) */
float pago;
/* (cantidad no negativa) */
fecha ultimopago;
} registro;
registro leerarchivo(registro cliente);
registro actualizar(registro cliente) ;

/* prototipo de funcin */
/* prototipo de funcin */

- 158 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

void escribirarchivo(registro cliente);


/* prototipo de funcin */
FILE *antpt, *nuept;
/* punteros a la estructura predefinida
FILE */
int mes, dia, anio; /* declaracin de variables globales */
main(){
int indicador = CIERTO;
registro cliente;

/* declaracin de variable local */


/*declaracin de variable de estructura*/

/* abrir archivos de datos */


if ((antpt = fopen("registro.ant", "r")) == NULL)
printf("\nERROR -No se puede abrir archivo especificado\n");
else {
nuept = fopen("registro.nue", "w");
/* introducir fecha actual */
printf("SISTEMA DE FACTURACION DE CLIENTES");
printf("- ACTUALIZACION\n\n");
printf("Introduzca la fecha actual (rnm/dd/aaaa): ");
scanf("%d/%d/%d", &mes, &dia, &anio);
/* bucle principal */
while (indicador) {
/* leer un nombre del archivo antiguo y escribirlo en el
nuevo archivo */
fscanf(antpt, " %[^\n]", cliente.nombre);
fprintf(nuept, "\n%s\n", cliente.nombre);
/* comprobacin de la condicin de parada */
if (strcmp(cliente.nombre, "FIN") == 0)
break;
/*leer el resto de la informacin del archivo antiguo */
cliente = leerarchivo(cliente);
/* solicitar la informacin actualizada */
cliente = actualizar(cliente);
/* escribir la informacin actualizada en el nuevo
archivo*/
escribirarchivo(cliente) ;
}
fclose(antpt) ;
fclose(nuept) ;
} /* fin del else */
}
registro leerarchivo(registro cliente)

/* leer el resto de datos del


antiguo archivo */

{
fscanf(antPt, " %[^\n]", cliente,calle) ;
fscanf(antpt, " %[^\n]", cliente.ciudad) ;
fscanf(antpt, " %d", &cliente. no_cuenta) ;

- 159 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

fscanf(antpt, " %c", &cliente tipo_cuenta);


fscanf(ontpt, " %f", &cliente.anteriorsaldo) ;
fscanf(antpt, " %f", &cliente,nuevosaldo) ;
fscanf(antpt, " %f", &cliente.pago);
fscanf(antpt, " %d/%d/%d", &cliente.u1timopago.mes,
&cliente.u1timopago.dia,
&cliente.u1timopagoanio);
return(cliente) ;
}
registro actualizar(registro cliente) /*solicita la nueva
informacion, actualiza el
registro y muestra el
resumen de datos*/
{
printf("\n\nNombre,
%s", cliente nombre);
printf(" Numero de cuenta, %d\n", cliente.no_cuenta);
printf("\nSaldo anterior, %7.2f", cliente.anteriorsaldo) ;
printf(" Pago actual: ");
scanf("%f", &cliente.pago);
if (cliente.pago > 0) {
cliente.ultimopago.mes = mes;
cliente.ultimopago.dia = dia;
cliente.ultimopago.anio = anio;
cliente.tipo_cuenta = (cliente.pago < 0.1 * cliente.anteriorsaldo)
? 'R' : 'A';
}
else
cliente.tipo_cuenta = (cliente.anteriorsaldo > 0) ? 'D' : 'A';
clientenuevosaldo = cliente.anteriorsaldo - cliente.pago;
printf("Nuevo saldo:
%7.2f", cliente.nuevosaldo) ;
printf("Estado de la cuenta:
");
switch (cliente.tipo_cuenta) {
case 'A':
printf("AL DIA\n");
break;
case' R';
printf("RETRASADA\n");
break;
case 'D';
printf("MOROSA\n");
break;
default;
printf ("ERROR\n");
}
return(cliente);

- 160 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

}
void escribirarchivo(registro cliente)
/* escribe la informacion actualizada en el archivo */
{
fprintf(fpt, "%s\n", cliente.calle) ;
fprintf(fpt, "%s\n", cliente.ciudad) ;
fprintf(fpt, "%d\n", cliente.no_cuenta) ;
fprintf(fpt, "%c\n", cliente.tipo_cuenta) ;
fprintf(fpt, "%.2f\n", cliente.anteriorsaldo) ;
fprintf(fpt, "%.2f\n", cliente.nuevosaldo) ;
fprintf(fpt, "%.2f\n", cliente.pago) ;
fprintf(fpt, "%d/%d/%d\n", cliente.ultimopago.mes,
cliente.ultimopago.dia,
cliente.ultimopago.anio) ;
return;
}
Ejemplo 146: Gestin de la base de datos de clientes. Archivos sin formato. Vamos a modificar el
programa propuesto en el ejemplo 144 para permitir la escritura de cada registro de cliente mediante la
instruccin fwrite:
/* crear un archivo de datos sin formato que contenga registros de
clientes */
#include <stdio.h>
#include <string.h>
#define CIERTO 1
typedef struct {
int mes;
int dia;
int anio;
} fecha;
typedef struct {
char nombre[80];
char calle[80];
char ciudad[80];
int no_cuenta;
/* (entero positivo) */
int tipo_cuenta;
/* A (Al da), R (retrasada) o D (morosa) */
float anteriorsaldo; /* (cantidad no negativa) */
float nuevosaldo;
/* (cantidad no negativa) */
float pago;
/* (cantidad no negativa) */
fecha ultimopago;
} registro;
registro leerpantalla(registro cliente}; /* prototipo de funcin */

- 161 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

FILE *fpt;

/* puntero a la estructura predefinida FILE */

main(){
int indicador = CIERTO; /* declaracin de variable */
registro cliente;
/*declaracin de variable de estructura*/
/* abrir un archivo nuevo solo para escritura */
fpt = fopen("datos.bin", "w");
/* introducir datos y asignar valores iniciales */
printf("SISTEMA DE FACTURACION DE CLIENTES -INICIALIZACION\n\n"};
printf (" Introduzca la fecha actual (mm/dd/aaaa): ");
scanf("%d/%d/%d", &cliente.ultimopago.mes,
&cliente.ultimopago.dia,
&cliente.ultimopago.anio) ;
cliente.nuevosaldo = 0;
cliente.pago = 0;
cliente.tipo_cuenta = 'A';
/* bucle principal */
while (indicador) {
/* introducir el nombre del cliente */
printf("\nNombre (introducir \'FIN\' para terminar): ");
scanf(" %["\n]", cliente.nombre);
/* comprobacin de la condicin de parada */
if (strcmp(cliente.nombre, "FIN") == 0)
break;
/*introducir el resto y escribirlo en el archivo*/
cliente = leerpantalla(cliente);
fwrite(&cliente, sizeof(registro), 1, fpt);
/* borrar cadenas de caracteres */
strset(cliente.nombre, ' ');/*reemplaza lo que hay por ' '*/
strset(cliente.calle, ' ');
strset(cliente.ciudad, ' ');
}
fclose(fpt) ;
}
registro leerpantalla(registro cliente) /* leer el resto de datos */
{
printf("Calle: ");
scanf(" %["\n]", cliente.calle);
printf("Ciudad: ");
scanf(" %["\n]", cliente.ciudad);
printf("Nmero de cuenta: ");
scanf("%d", &cliente.no_cuenta);
printf("Saldo actual: ");
scanf("%f", &cliente.anteriorsaldo);
return(cliente) ;
}

- 162 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Una vez que se ha creado un archivo de datos sin formato, surge la pregunta de cmo detectar una
condicin de fin de archivo. La funcin de biblioteca feof sirve para este propsito. (Realmente feof
indicar una condicin de fin de archivo para cualquier archivo de datos secuencial, no slo para uno sin
formato.) Esta funcin devuelve un valor distinto de cero (CIERTO) si se detecta una condicin de fin de
archivo y un valor cero (FALSO) si no se detecta. Por tanto, un programa que lee un archivo de datos sin
formato puede utilizar un bucle que lea los sucesivos registros hasta que el valor devuelto por feof sea no
CIERTO.
#include <stdio.h>
#define NULL 0
typedef struct {
int mes;
int dia;
int anio;
} fecha;
typedef struct {
char nombre[80];
char calle[80];
char ciudad[80];
int no_cuenta;
/*
int tipo_cuenta;
float anteriorsaldo;
float nuevosaldo; /*
float pago;
/*
fecha ultimopago;
} registro;

(entero positivo) */
/*
(cantidad no negativa) */
(cantidad no negativa) */
(cantidad no negativa) */

registro actualizar(registro cliente); /* prototipo de funcin */


FILE *antpt, *nuept;
/* punteros a la estructurapredefinida FILE */
int mes, dia, anio;
/* declaracin de variables globales */
main()
{
registro cliente; /* declaracin de variable de estructura */
/* abrir archivos de datos */
if ((antpt = fopen("datos.ant", "r")) == NULL)
printf("\nERROR-No se puede abrir el archivo\n");
else {
nuept = fopen("datos.nue", "w");
/* introducir fecha actual */
printf("SISTEMA DE FACTURACION DE CLIENTES-ACTUALIZACION\n\n");
printf("Introduzca la fecha actual (mm/dd/aaaa): ");
scanf("%d/%d/%d", &mes, &dia, &anio);
/* leer el primer registro del archivo antiguo de datos */
fread(&cliente, sizeof(registro) , 1, antpt);
/* bucle principal (continuar hasta que se detecte fin-de-archivo */
while (!feof(antpt)) {

- 163 -

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

/* solicitar la informacin actualizada ./


cliente= actualizar(cliente);
/*escribir la informacin actualizada en el nuevo archivo ./
fwrite(&cliente, sizeof(registro), 1, nuept);
/* leer el siguiente registro del archivo antiguo de datos ./
fread(&cliente, sizeof,registro) , 1, antpt);
}
fclose(antpt);
fclose(nuept);
} /* fin del else ./
}
registro actualizar(registro cliente) /* solicitar la nueva informacin, actualizar el registro y mostrar el
resumen de datos */
{
printf("\n\nNombre: %s", cliente.nombre);
printf(" Nmero de cuenta:
%d\n", cliente.no_cuenta);
printf("\nSaldo anterior, '%7.2f", cliente.anteriorsaldo);
printf(" Pago actual: ");
scanf("%f", &cliente.pago);
if (cliente.pago > 0) {
cliente.ultimopago.mes=mes;
cliente.ultimopago.dia=dia;
cliente.ultimopago.anio=anio;
cliente.tipo_cuenta= (cliente.pago < 0.1 *cliente.anteriorsaldo)
? 'R' , 'A';
}
else
cliente.tipo_cuenta = (cliente.anteriorsaldo > 0) ? 'D' , 'A';
cliente.nuevosaldo = cliente.anteriorsaldo - cliente.pago;
printf("Nuevo saldo: %7.2f",
cliente.nuevosaldo);
printf("Estado de la cuenta:
");
switch(cliente.tipo_cuenta) {
case 'A':
printf("AL DlA\n");
break;
case 'R':
printf("RETRASADA\n");
break;
case 'D':
printf("MOROSA\n");
break;
default:
printf("ERROR\n");
}
return(cliente);
}

- 164 -

GG

GG

GG

AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C

Escribe un programa que guarde en un fichero los nombre y las notas obtenidas por los estudiantes
de una clase en un examen.
Crea una funcin de bsqueda que lea las notas previamente escritas y devuelva el nombre del
estudiante con la nota ms alta.
Crea una funcin que lea las notas previamente escrita y devuelva la media de la clase.
Crea una funcin que grabe en el fichero aprobados.dat los datos de los alumnos con una nota
superior a 5.
Crea otra funcin que permita modificar la nota de un estudiante introduciendo su nombre.
Crea un programa que lea las notas de dos clases, guardadas en dos ficheros, y las copie en uno
slo.
Lo mismo que antes, pero ordenando previamente los registros.

- 165 -