Académique Documents
Professionnel Documents
Culture Documents
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);
}
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*/
}
float procesar(float r)
/* definicion de funcion*/
{
float a;
a = PI * r * r;
return(a);
}
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
-3-
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
-4-
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
/* declaracion de variables*/
/* declaracion de formaciones*/
-5-
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
}
n = --i;
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*/
-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
int
char
float
descripcin
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
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
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
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.
1.
0.000743
0.006e-3
0.2
12.3
1.6667E+8
827.602
315.0066
.12121212e12
-9-
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
3E 10
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:
300000
.3e6
3e5
0.3E6
3e+5
30E4
3E5
30.E+4
3.0e+5
300e4
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.
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
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;
/* 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);
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
a
1
r
2
a
3
g
4
o
5
z
6
a
7
\0
8
- 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
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
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 -
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
++
<
<=
>
>=
==
if (b == a)
!=
distinto que
if (b != a)
&&
y (lgico)
||
o (lgico)
(a > b) || (b > a)
!(a > b)
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
(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
toupper(c)
int
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);
}
- 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
- 25 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 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
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
- 32 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 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
- 36 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
Bucles: 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
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
- 41 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
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.
- 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
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
10 3 1.475772
1.28225
1.38344
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
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.
%%
%% %
%%
%%
%
- 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
- 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
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.
- 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 */
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
}
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
}
}
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 */
}
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
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
- 55 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
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
/* 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
- 63 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 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
- 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;
int contlinea(void);
main()
{
int n;
- 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
- 73 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
xi
nuevo xi
xd
nuevo xd
xd
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
- 76 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
0
11
1
-2
2
4
3
1
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
- 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*/
- 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
- 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
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
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];
/* 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
/* 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;
..
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
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
- 94 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
33
}
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*/
- 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()
- 97 -
55
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
strcmp(cadena1, cadena2).
strcpy()
string.h
strcpy(cadena_destino, cadena_origen).
strlen()
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);
}
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;
}
void procesar(float 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);
}
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.
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
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
Observaciones:
Si la declaracin de una formacin incluye la asignacin de valores iniciales, entonces la formacin
no puede ser definida como un puntero:
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
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));
;;
;;
- 113 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 114 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
<<
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:
<
<
<
en vez de
- 115 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 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]);
)
- 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)[]);
- 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
- 124 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
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(){
- 128 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
>>
}
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.
- 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:
???
- 130 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
/* prototipo de funcin */
/* 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
/* prototipo de funcin */
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 */
- 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()
{
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 */
/* 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.
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 */
};
void main(){
nodo *registro, *principio;
char r;
- 137 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
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 */
};
- 138 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
void main(){
nodo *prin;
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.
posicin
(antiguo primer dato)
puntero al
siguiente nodo
- 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
dato 2
puntero al
siguiente nodo
posicin
puntero al
siguiente nodo
null
dato usuario
dato 2
puntero al
siguiente nodo
posicin
puntero al
siguiente nodo
null
dato usuario
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
/*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 -
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) {
- 143 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
/*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 */
};
void main(){
nodo *registro, *principio, *anterior;
char r;
- 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
}
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
#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:
.
Se pueden aadir una serie de modificadores siguiendo a los modos anteriores:
b
t
+
Ejemplos de combinaciones:
*
*
*
rb+
w+
rt
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:
*
*
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
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)
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, ...)
- 153 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
tamao,
fwrite(&p_bloque,tamao,
n_bloques,p_ archivo)
- 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)
#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)
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) */
- 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);
}
- 157 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
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
{
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
- 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;
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) */
- 163 -
AAPPU
UN
NTTEESS D
DEE C
CLLAASSEE SSO
OBBRREE
PPRRO
OG
GRRAAM
MAAC
CII
N
N EEN
NC
C
- 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 -