Escolar Documentos
Profissional Documentos
Cultura Documentos
a la programacin
con C
Andrs Marzal
Isabel Gracia
Departamento de leng
uajes y sistemas informticos
Codis dassignatura
II04 i IG04
In
II
III
Introduccin a la prog
IV
Introduccin a la progr
Introduccin a la programa
VI
Introduccin a la progr
1
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
Nada mejor que un ejemplo de programa en los dos lenguajes para que te lleve
s una
primera impresin de cun diferentes son Python y C. . . y cun semejantes. Estos dos
programas, el primero en Python y el segundo en C, calculan el valor de
b
i
i=a
para sendos valores enteros de a y b introducidos por el usuario y tales que 0 a
b.
from math import
Pedir lmites inferior y superior.
a int raw input
while a 0
print
a int raw input
b int raw input
while b a
print
b int raw input
a b s
include
include
int main void
int a b i
float s
Pedir lmites inferior y superior.
printf
scanf
a
while a 0
printf
printf
scanf
a
printf
scanf
while b
printf
b
a
a
printf
scanf
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
troduccin a la programacin con C -UJI
cUJI
2
In
a b s
return 0
En varios puntos de este captulo haremos referencia a estos dos programas. No
los
pierdas de vista.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 Compara los programas
y
. Analiza sus semejanzas y
diferencias. Qu funcin desempean las llaves en
? Qu funcin crees
que desempean las lneas 6 y 7 del programa C? A qu elemento de Python se parecen
las dos primeras lneas de
? Qu simi
litudes y diferencias aprecias entre
las estructuras de control de Python y C? Cmo crees que se interpreta el bucle for
del
programa C? Por qu algunas lneas de
finalizan en punto y coma y otras
no? Qu diferencias ves entre los comentarios Python y los comentarios C?
...............................................................................
...
) con
su
cdigo fuente. Si deseamos ejecutar
, por ejemplo, hemos
de escribir
en la lnea de rdenes Unix. Como resultado, el i
ntrprete
va leyendo y ejecutando paso a paso el programa. Para volver a ejecuta
rlo, has
de volver a escribir
en la lnea de rdenes, con
lo que
se repite el proceso completo de traduccin y ejecucin paso a paso. Aunqu
e no
modifiquemos el cdigo fuente, es necesario interpretarlo (traducir y ej
ecutar paso
a paso) nuevamente.
Intrprete
Python
Resultados
C es un lenguaje compilado: antes de ejecutar un programa escrito por
nosotros,
suministramos su cdigo fuente (en un fichero con extensin ) a un compilad
or
de C. El compilador lee y analiza todo el programa. Si el programa est
correctamente escrito segn la definicin del lenguaje, el compilador genera un
nuevo
fichero con su traduccin a cdigo de mquina, y si no, muestra los errores
que ha
detectado. Para ejecutar el programa utilizamos el nombre del fichero
generado. Si
no modificamos el cdigo fuente, no hace falta que lo compilemos nuevame
nte para
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
3
c
UJI
Introduccin a la prog
ramacin con C - UJI
Un poco de historia
C ya tiene sus aitos. El nacimiento de C est estrechamente vinculado al del
sistema
operativo Unix. El investigador Ken Thompson, de AT&T, la compaa telefnica e
stadounidense, se propuso disear un nuevo sistema operativo a principios de l
os setenta.
Dispona de un PDP-7 en el que codific una primera versin de Unix en lenguaje
ensamblador. Pronto se impuso la conveniencia de desarrollar el sistema en
un lenguaje de
programacin de alto nivel, pero la escasa memoria del PDP-7 (8K de 18 bits
) hizo que
ideara el lenguaje de programacin B, una versin reducida de un lenguaje ya
existente:
BCPL. El lenguaje C apareci como un B mejorado, fruto de las demandas impu
estas
por el desarrollo de Unix. Dennis Ritchie fue el encargado del diseo del l
enguaje C y
de la implementacin de un compilador para l sobre un PDP-11.
C ha sufrido numerosos cambios a lo largo de su historia. La primera
versin estable
del lenguaje data de 1978 y se conoce como K&R C, es decir, C de Kernighan y
Ritchie. Esta versin fue descrita por sus autores en la primera edicin del l
ibro The
C Programming Language (un autntico best-seller de la informtica). La adopcin d
e
Unix como sistema operativo de referencia en las universidades en los aos
80 populariz
enormemente el lenguaje de programacin C. No obstante, C era atractivo por
s mismo
y pareca satisfacer una demanda real de los programadores: disponer de un
lenguaje
de alto nivel con ciertas caractersticas propias de los lenguajes de bajo
nivel (de ah
que a veces se diga que C es un lenguaje de nivel intermedio).
La experiencia con lenguajes de programacin diseados con anterioridad,
como
Lisp o Pascal, demuestra que cuando el uso de un lenguaje se extiende es
muy probable
que proliferen variedades dialectales y extensiones para aplicaciones muy
concretas,
lo que dificulta enormemente el intercambio de programas entre diferentes
grupos de
programadores. Para evitar este problema se suele recurrir a la creacin de
un comit
de expertos que define la versin oficial del lenguaje. El comit ANSI X3J9 (
ANSI
son las siglas del American National Standards Institute), creado en 1983
, considera
la inclusin de aquellas extensiones y mejoras que juzga de suficiente inte
rs para la
comunidad de programadores. El 14 de diciembre de 1989 se acord qu era el C
estndar y se public el documento con la especificacin en la primavera de 1990
.
El estndar se divulg con la segunda edicin de The C Programming Language,
de Brian Kernighan y Dennis Ritchie. Un comit de la International Standard
s Office
(ISO) ratific el documento del comit ANSI en 1992, convirtindolo as en un estn
dar
4
Empezaremos por presentar de forma concisa cmo traducir la mayor parte de los pro
gramas Python que aprendimos a escribir en los captulos 3 y 4 del primer volumen
a
programas equivalentes en C. En secciones posteriores entraremos en detalle y no
s dedicaremos a estudiar las muchas posibilidades que ofrece C a la hora de selecciona
r tipos
de datos, presentar informacin con sentencias de impresin en pantalla, etc.
2
La versin 3.2 de es la primera en ofrecer un soporte suficiente de C99. Si
usas una versin anterior,
es posible que algunos (pocos) programas del libro no se compilen correctamente.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
5
Introduccin a la programacin con C - UJI
UJI
c
1.
include
bloque.
Toda variable debe declararse antes de ser usada. La declaracin de la va
float x y z
3
Recuerda que no estudiaremos las variables de tipo cadena hasta el prximo ca
ptulo.
4
En versiones de C anteriores a C99 s era obligatorio que las declaraciones s
e hicieran al principio de
un bloque. C99 permite declarar una variable en cualquier punto del programa, si
empre que ste sea anterior
al primer uso de la misma.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
6
return 0
3.
Las sentencias de asignacin C son similares a las sentencias de asignac
in Python:
a mano izquierda del smbolo igual ( ) se indica la variable a la que se
va a asignar
el valor que resulta de evaluar la expresin que hay a mano derecha. Cad
a sentencia
de asignacin debe finalizar con punto y coma.
include
int main void
int a
float b
a
b
2
0.2
return 0
Como puedes ver, los nmeros enteros y flotantes se representan igual qu
e en
Python.
4.
Python.
tivamente, con
, y ;
No hay operador de exponenciacin (que en Python era
).
Hay operadores para la conversin de tipos. Si en Python escri
bamos float x
para convertir el valor de x a flotante, en C escribiremos f
loat x para expresar lo mismo. Fjate en cmo se disponen los parntesis: los op
eradores de
conversin de tipos son de la forma tipo .
include
int main void
int a
float b
a
b
13 2
2.0 1.0
return 0
7
13 2
2.0 1.0
printf
1
a b
return 0
La cadena con formato debe ir encerrada entre comillas dobles, no simp
les. El
carcter de retorno de carro ( ) es obligatorio si se desea finalizar la
impresin
con un salto de lnea. (Observa que, a diferencia de Python, no hay oper
ador de
formato entre la cadena de formato y las expresiones: la cadena de for
mato se
separa de la primera expresin con una simple coma).
Como puedes ver, todas las sentencias de los programas C que estamos p
resentando
finalizan con punto y coma.
6. Para leer datos de teclado has de usar la funcin scanf . Fjate en este ejemp
lo:
include
int main void
int a
float b
scanf
scanf
printf
a
b
a b
return 0
La lnea 8 lee de teclado el valor de un entero y lo almacena en a. La ln
ea 9 lee
de teclado el valor de un flotante y lo almacena en b. Observa el uso
de marcas
de formato en el primer argumento de scanf :
seala la lectura de u
n int y
la de un float. El smbolo que precede al identificador de la variable e
n la que
se almacena el valor ledo es obligatorio para variables de tipo escalar
.
Si deseas mostrar por pantalla un texto que proporcione informacin acer
ca de lo
que el usuario debe introducir, hemos de usar nuevas sentencias printf
:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
8
include
int main void
int a
float b
printf
scanf
printf
scanf
a
b
printf
a b
return 0
7.
if a 2
printf
printf
return 0
Ten en cuenta que:
la condicin va encerrada obligatoriamente entre parntesis;
y el bloque de sentencias cuya ejecucin est supeditada a la sati
sfaccin de
la condicin va encerrado entre llaves (aunque matizaremos esta
afirmacin
ms adelante).
Naturalmente, puedes anidar sentencias if.
include
int main void
int a
printf
scanf
if a
Andrs aMarzal/Isabel
a
2
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
9
printf
if a 0
printf
return 0
Tambin hay sentencia if else en C:
include
int main void
int a
printf
scanf
if a 2
printf
a
0
else
printf
return 0
No hay, sin embargo, sentencia if elif, aunque es fcil obtener el mismo
efecto con
una sucesin de if else if:
include
int main void
int a
printf
scanf
if a 0
printf
else if a
printf
else if a
printf
else
printf
return 0
0
0
Introduccin
Andrs aMarzal/Isabel
la programacin con -C ISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
10
10
8.
La sentencia while de C es similar a la de Python, pero has de tener en
cuenta
la obligatoriedad de los parntesis alrededor de la condicin y que las sen
tencias
que se pueden repetir van encerradas entre un par de llaves:
include
int main void
int a
printf
scanf
while a
printf
a
1
0
a
printf
return 0
9.
b 2
while b a
if a b 0
break
b
if b
a
printf
else
printf
1
a
a
return 0
10.
ncia
ogramas: include
ciones de
11
11
Las funciones matemticas pueden importarse del mdulo matemtico con includ
e
y sus nombres son los mismos que vimos en Python (sin para
el seno,
cos para el coseno, etc.).
include
include
int main void
float b
printf
scanf
if b
0.0
printf
sqrt b
else
printf
return 0
No hay funciones predefinidas en C. Muchas de las que estaban predefin
idas en
Python pueden usarse en C, pero importndolas de bibliotecas. Por ejempl
o, abs
(valor absoluto) puede importarse del mdulo
es
decir, biblioteca estndar).
Las (aproximaciones a las) constantes y e se
lioteca
matemtica, ero sus identificadores son ahora
, res ectivament
e.
No est mal: ya sabes traducir rogramas Python sencillos a C (aunque no sabe
mos
traducir rogramas con definiciones de funcin, ni con variables de ti o cadena, n
i con
listas, ni con registros, ni con acceso a ficheros. . . ). Qu tal racticar con un
os ocos
ejercicios?
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 Traduce a C este programa Python.
a
int raw input
b
int raw input
if a b
maximo
else
maximo
a
b
print
3
maximo
Traduce a C este programa Python.
100
n m
else
print
n m
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
12
12
ramacin con C - UJI
Introduccin a la prog
c
UJI
5
a
b
dy
distancia
if a
0
x
b a
print
else
if b
0
print
else
print
6
log x
Andrs Marzal/Isabel
Introduccin
Gracia
13
13
Introd
if opcion 1
diametro 2 radio
print
diametro
elif opcion 2
perimetro 2 pi radio
print
perimetro
elif opcion 3
area pi radio
2
print
area
elif opcion 0 or opcion 4
print
opcion
...............................................................................
...
Ya es hora, pues, de empezar con los detalles de C.
a 1
5
El valor 0 se toma, por un convenio, como seal de que el programa finaliz cor
rectamente. El sistema
operativo Unix recibe el valor devuelto con el return y el intrprete de rdenes, po
r ejemplo, puede tomar
una decisin acerca de qu hacer a continuacin en funcin del valor devuelto.
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
14
14
el compilador muestra el siguiente mensaje (u otro similar, segn la versin del com
pilador
que utilices):
a
b
c
a
c
b
c
minimo
Este programa podra haberse escrito como sigue y sera igualmente correcto:
include
int main void
int a b c minimo
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
15
15
scanf
a
scanf
b
scanf
c
if a b
if a c
minimo
else minimo c
else
if b c
minimo
else minimo c
printf
return 0
b
minimo
b
minimo
De hecho, como if else es una nica sentencia, tambin podemos suprimir las llaves
restantes:
include
int main void
int a b c minimo
scanf
scanf
scanf
if a b
if a
else
else
if b
else
a
b
c
c minimo
minimo c
c minimo
minimo c
printf
return 0
minimo
Debes tener cuidado, no obstante, con las ambigedades que parece producir un
slo
else y dos if:
E
E
include
int main void
int a b c minimo
scanf
scanf
scanf
if a b
if a c
printf
else
printf
printf
return 0
a
b
c
minimo
Cul de los dos if se asocia al else? C usa una regla: el else se asocia al if ms prx
imo
(en el ejemplo, el segundo). Segn esa regla, el programa anterior no es correcto.
El
sangrado sugiere una asociacin entre el primer if y el else que no es la que inte
rpreta
C. Para que C entienda la intencin del autor es necesario que explicites con llaves
el
alcance del primer if:
include
int main void
int a b c minimo
scanf
scanf
scanf
if a b
if a c
printf
else
printf
printf
return 0
a
b
c
minimo
Ahora que has adquirido la prctica de indentar los programas gracias a la dis
ciplina
impuesta por Python, sguela siempre, aunque programes en C y no sea necesario.
Una norma: las sentencias C acaban con un punto y coma. Y una excepcin a la
norma: no hace falta poner punto y coma tras una llave cerrada.6
Dado que las sentencias finalizan con punto y coma, no tienen por qu ocupar u
na
lnea. Una sentencia como a 1 podra escribirse, por ejemplo, en cuatro lneas:
Habr una excepcin a esta norma: las construcciones struct, cuya llave de cierr
e debe ir seguida de un
6
punto y coma.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
17
17
1
b
c
1
2
1
b
c
1
2
e) Y an otro, con las llaves a la misma altura que el contenido del bloq
ue:
if a
1
b
c
1
2
Increble, no? En este texto hemos optado por el primer estilo de la lista
(que, naturalmente, es el correcto
) para todas las construcciones del lenguaje
a excepcin
de la definicin de funciones (como main), que sigue el convenio de inden
tacin que
relacionamos en tercer lugar.
a
1
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
18
18
Pero aunque sea lcito escribir as esa sentencia, no tienen ningn sentido y hace ms
difcil la comprensin del programa. Recuerda: vela siempre por la legibilidad de lo
s
programas.
Tambin podemos poner ms de una sentencia en una misma lnea, pues el compilador
sabr dnde empieza y acaba cada una gracias a los puntos y comas, las llaves, etc.
El
programa
, por ejemplo, podra haberse escrito as:
include
include
int main void
printf
int a b i float s
scanf
a while a 0
printf
scan
printf
f
a
printf
scanf
b while b a
pr
intf
a printf
scanf
b
Calcular el sumatorio de la raz cuadrada de i para i entre
a y b.
s 0.0 for i a i
b i
s
sqrt i
Mostrar
el resultado
printf
printf
a b s return 0
Obviamente, hubiera sido una mala eleccin: un programa escrito as, aunque correcto
, es
completamente ilegible.7
Un programador de C experimentado hubiera escrito sumatorio c utilizando lla
ves
slo donde resultan necesarias y, probablemente, utilizando unas pocas lneas menos.
Estudia las diferencias entre la primera versin de sumatorio c y esta otra:
include
include
int main void
int a b i
float s
anf
a
sc
scanf
a
b
Mostrar el resultado.
printf
a b s
return 0
7
Quiz hayas reparado en que las lneas que empiezan con include son especiales
y que las tratamos
de forma diferente: no se puede jugar con su formato del mismo modo que con las
dems: cada sentencia
include debe ocupar una lnea y el carcter debe ser el primero de la lnea.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
19
19
y
. Usando un diccionario espaol y diferentes nmeros
de
palabras obtendrs, entre otros, stos:
o
.
Ya sabes: puedes escribir programas ilegibles en C. Procura que tus pr
ogramas no
merezcan una mencin de honor en el concurso!
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
20
20
Los lenguajes de programacin en los que el cdigo no debe seguir un formato det
erminado de lneas y/o bloques se denominan de formato libre. Python no es un lengua
je
de formato libre; C s.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9 Este programa C incorrecto tiene varios errores que ya puedes detectar. Indica
cules son:
include
int a b
scanf
while a
scanf
scanf
printf
scanf
b
a
b
a b
a
b
b
a
b
printf
return 0
a b
...............................................................................
...
Un programa de ejemplo.
Propsito: mostrar algunos efectos que se pueden lograr con
comentarios de C
include
Programa principal
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
21
21
ramacin con C - UJI
Introduccin a la prog
c
UJI
Lectura de un nmero
printf
scanf
a
... de otro ...
printf
scanf
b
... y de otro ms.
printf
scanf
c
if a b
if a c
En este caso a > b y a > c.
m a
else Y en este otro caso b < a c.
m c
else
if b c
En este caso a b y b > c.
m b
else Y en este otro caso a b c.
m c
Impresin del resultado.
printf
a b c m
return 0
Uno de los comentarios empieza al principio de la lnea 1 y finaliza al final
de la
lne 6 (sus dos ltimos caracteres visibles son un asterisco y una barra). Hay otro
que
empieza en la lnea 10 y finaliza en al lnea 12. Y hay otros que usan las marcas
y
en lneas como la 20 o la 22, aunque hubisemos podidos usar en ambos casos la marca
.
Los comentarios encerrados entre
y
no se pueden anidar. Este frag
mento de
programa es incorrecto:
Por qu? Parece que hay un comentario dentro de otro, pero no es as: el comentario
que empieza en el primer par de caracteres
acaba en el primer par de caracte
res ,
no en el segundo. El texto del nico comentario aparece aqu enmarcado:
As pues, el fragmento
no forma parte de comentario alguno y no tien
e
sentido en C, por lo que el compilador detecta un error.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11 Haciendo pruebas durante el desarrollo de un programa hemos decidido comentar
una lnea del programa para que, de momento, no sea compilada. El programa nos que
da
as:
include
int main void
int a b i j
scanf
scanf
i a
a
b
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
2222
Introduccin a la prog
ramacin con C - UJI
c
UJI
j 1
while i
b
printf
j 2
i 1
i j
printf
return 0
a
b
while i
b
printf
j
2
i 1
i j
printf
return 0
a
b
while i
b
printf
j
2
i 1
printf
return 0
13
i j
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
roduccin a la programacin con C - UJI
c
UJI
2323
Int
...............................................................................
...
Por valores literales nos referimos a valores de nmeros, cadenas y caracteres dad
os
explcitamente. Afortunadamente, las reglas de escritura de literales en C son sim
ilares a
las de Python.
Los nmeros en coma flotante siguen la misma sintaxis que los flotantes de Python.
Un
nmero flotante debe presentar parte decimal y/o exponente. Por ejemplo, 20.0 es u
n
flotante porque tiene parte decimal (aunque sea nula) y 2e1 tambin lo es, pero po
rque
tiene exponente (es decir, tiene una letra e seguida de un entero). Ambos repres
entan al
nmero real 20.0. (Recuerda que 2e1 es 2 101 .) Es posible combinar en un nmero par
te
decimal con exponente: 2.0e1 es un nmero en coma flotante vlido.
As como en Python puedes optar por encerrar una cadena entre comillas simples o
dobles, en C slo puedes encerrarla entre comillas dobles. Dentro de las cadenas p
uedes
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
24
24
Valor
(alerta): produce un aviso audibl
e o visible.
(backspace, espacio atrs): el curs
or retrocede un espacio a la izquierda.
(form feed, alimentacin de pgina):
pasa a una nueva pgina.
(newline, nueva lnea): el cursor p
asa a la primera posicin de la
siguiente lnea.
(carriage return, retorno de carr
o): el cursor pasa a la primera posicin
de la lnea actual.
(tabulador): desplaza el cursor a
la siguiente marca de tabulacin.
nmero_octal
o IsoLatin) es el nmero octal
equivale a
emplo,
tambin equivale a
, pues 48 en decimal es 30 en
hexadecimal.
muestra el interrogante.
Introduccin a la prog
c
UJI
la que deseamos trabajar, as que nos permite tomar decisiones acerca del compromi
so
entre rango/precisin y ocupacin de memoria: a menor rango/precisin, menor ocupacin
de memoria.
No obstante, nosotros limitaremos nuestro estudio a cinco tipos de datos esc
alares: int,
unsigned int, float, char y unsigned char. Puedes consultar el resto de tipos es
calares
en el apndice A. Encontrars una variedad enorme: enteros con diferente nmero de
bits, con y sin signo, flotantes de precisin normal y grande, booleanos, etc. Esa
enorme
variedad es uno de los puntos fuertes de C, pues permite ajustar el consumo de m
emoria
a las necesidades de cada programa. En aras de la simplicidad expositiva, no obs
tante,
no la consideraremos en el texto.
int
El tipo de datos int se usa normalmente para representar nmeros enteros. La espec
ificacin de C no define el rango de valores que podemos representar con una variab
le
de tipo int, es decir, no define el nmero de bits que ocupa una variable de tipo
int. No
obstante, lo ms frecuente es que ocupe 32 bits. Nosotros asumiremos en este texto
que
el tamao de un entero es de 32 bits, es decir, 4 bytes.
Como los enteros se codifican en complemento a 2, el rango de valores que po
demos
representar es [2147483648, 2147483647], es decir, [231 , 231 1]. Este rango es suf
iciente
ara las a licaciones que resentaremos. Si resulta insuficiente o excesivo ara
alguno
de tus rogramas, consulta el catlogo de ti os que resentamos en el a ndice A.
En C, tradicionalmente, los valores enteros se han utilizado ara codificar
valores
booleanos. El valor 0 re resenta el valor lgico falso y cualquier otro valor re res
enta
cierto. En la ltima revisin de C se ha introducido un ti o booleano, aunque no lo
usaremos en este texto orque, de momento, no es frecuente encontrar rogramas q
ue lo
usen.
unsigned int
Para qu des erdiciar el bit ms significativo en una variable entera de 32 bits que
nunca almacenar valores negativos? C te ermite definir variables de ti o entero s
in
signo. El ti o tiene un nombre com uesto or dos alabras: unsigned int (aunque la
alabra unsigned, sin ms, es sinnimo de unsigned int).
Gracias al a rovechamiento del bit extra es osible aumentar el rango de val
ores
ositivos re resentables, que asa a ser [0, 232 1], o sea, [0, 4294967295].
float
El ti o de datos float re resenta nmeros en coma flotante de 32 bits. La codifica
cin
de coma flotante ermite definir valores con decimales. El mximo valor que uedes
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
26
26
char
El ti o char, aunque tenga un nombre que arezca sugerir el trmino carcter (que
en ingls es character) designa en realidad a una variante de enteros: el conjunto d
e
nmeros que odemos re resentar (en com lemento a 2) con un solo byte (8 bits). El
rango de valores que uede tomar una variable de ti o char es muy limitado: [128,
127].
Es frecuente usar variables de ti o char ara almacenar caracteres (de ah su
nombre)
codificados en ASCII o alguna de sus extensiones (como IsoLatin1). Si una variab
le a es
de ti o char, la asignacin a
es absolutamente equivalente a la asignacin a
48,
ues el valor ASCII del dgito 0 es 48.
unsigned char
Y del mismo modo que haba una versin ara enteros de 32 bits sin signo, hay una
versin de char sin signo: unsigned char. Con un unsigned char se uede re resenta
r
cualquier entero en el rango [0, 255].
Las reglas ara construir identificadores vlidos son las mismas que sigue Python:
un
identificador es una sucesin de letras (del alfabeto ingls), dgitos y/o el carcter d
e
subrayado ( ) cuyo rimer carcter no es un dgito. Y al igual que en Python, no ue
des
usar una alabra reservada como identificador. He aqu la relacin de alabras reser
vadas
del lenguaje C: auto, break, case, char, const, continue, default, do, double, e
lse, enum,
extern, float, for, goto, if, int, long, register, return, short, signed, sizeof
, static, struct,
switch, ty edef, union, unsigned, void, volatile y while.
a de
declaracin se arando sus identificadores con comas. Este fragmento, or ejem lo,
declara
tres variables de ti o entero y otras dos de ti o flotante.
int x y z
float u v
En
se declaran tres variables de ti o int, a, b
y i, y una de ti o float,
s.
Una variable declarada como de ti o entero slo uede almacenar valores de ti
o
entero. Una vez se ha declarado una variable, es im osible cambiar su ti o, ni s
iquie
ra volviendo a declararla. Este rograma, or ejem lo, es incorrecto or el inte
nto de
redeclarar el ti o de la variable a:
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
UJI
c
27
27
ro
determinada, ero que falle estre itosamente cuando lo com ilamos y ejecu
tamos en una
lataforma diferente. O, eor an, uede que el error ase inadvertido dura
nte mucho
tiem o: el rograma no abortar la ejecucin y roducir resultados incorrectos
que
odemos no detectar. Es un roblema muy grave.
Los roblemas relacionados con la garanta de oder ejecutar un mismo
rograma en
diferentes lataformas se conocen como roblemas de ortabilidad. Pese a
los muchos
roblemas de ortabilidad de C, es el lenguaje de rogramacin en el que se
ha escrito
buena arte de los rogramas que hoy ejecutamos en una gran variedad de
lataformas.
include
int main void
int a
float a
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con C ISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
28
28
a 2
return 0
Al com ilarlo obtenemos este mensaje de error:
Debes tener resente que el valor inicial de una variable declarada est indefinid
o. Jams
debes acceder al contenido de una variable que no haya sido reviamente iniciali
zada.
Si lo haces, el com ilador no detectar error alguno, ero tu rograma resentar un
com ortamiento indeterminado: a veces funcionar bien, y a veces mal, lo cual es
eor que
un funcionamiento siem re incorrecto, ues odras llegar a dar or bueno un rogr
ama
mal escrito. En esto C se diferencia de Python: Python abortaba la ejecucin de un
rograma cuando se intentaba usar una variable no inicializada; C no aborta la e
jecucin,
ero resenta un com ortamiento indeterminado.
Puedes inicializar las variables en el momento de su declaracin. Para ello, b
asta con
aadir el o erador de asignacin y un valor a continuacin de la variable en cuestin.
Mira este ejem lo:
include
int main void
int a 2
float b 2.0 c d
1.0 e
return 0
En l, las variables a, b y d se inicializan en la declaracin y las variables c y e
no
tienen valor definido al ser declaradas.
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
UJI
c
29
29
r
intf
Marcas de formato ara nmeros
Para mostrar nmeros enteros o flotantes has de usar necesariamente cadenas con fo
rmato.
Afortunadamente, las marcas que a rendiste al estudiar Python se utilizan en C.
Eso s,
hay algunas que no te hemos resentado an y que tambin se recogen en esta tabla:
Ti o
int
unsigned int
float
char
unsigned char
Marca
Por ejem lo, si a es una variable de ti o int con valor 5, b es una variable de
ti o float
con valor 1.0, y c es una variable de ti o char con valor 100, esta llamada a la
funcin
rintf :
rintf
a b c
muestra or
antalla esto:
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
UJI
c
30
30
10
muestra en pantalla:
10
muestra en pantalla:
Un nmero que empieza por cero: reserva tantos espacios como indique el
nmero
para representar el entero y muestra el valor alineado a la derecha. L
os espacios
que no ocupa el entero se rellenan con ceros.
Ejemplo: la sentencia
printf
10
muestra en pantalla:
10
Introduccin a la prog
c
UJI
Tipo
float
float
Notacin
Convencional
Cientfica
Marca
La forma convencional muestra los nmeros con una parte entera y una decimal separ
adas
por un punto. La notacin cientfica representa al nmero como una cantidad con una
sola cifra entera y una parte decimal, pero seguida de la letra e y un valor enter
o.
Por ejemplo, en notacin cientfica, el nmero 10.1 se representa con 1.010000e 01 y s
e
interpreta as: 1.01 101 .
Tambin puedes usar modificadores para controlar la representacin en pantalla d
e
los flotantes. Los modificadores que hemos presentado para los enteros son vlidos
aqu.
Tienes, adems, la posibilidad de fijar la precisin:
Un punto seguido de un nmero: indica cuntos decimales se mostrarn.
Ejemplo: la sentencia
printf
10.1
muestra en pantalla:
Marca
Atencin! La marca
muestra como carcter un nmero entero. Naturalmente, el carcter
que se muestra es el que corresponde al valor entero segn la tabla ASCII (o, en t
u
ordenador, IsoLatin1 si el nmero es mayor que 127). Por ejemplo, la sentencia
printf
97
muestra en pantalla:
printf
En pantalla se muestra esto:
32
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
Introduccin a la programacin con C - UJI
c
UJI
32
i i i
f
f f
return 0
El resultado de ejecutar el programa es la impresin por pantalla del siguiente te
xto:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16 Qu muestra por pantalla cada uno de estos programas?
a)
include
int main void
char i
for i
printf
printf
return 0
i
i
b)
include
int main void
char i
for i 65 i
printf
printf
return 0
c)
include
90 i
i
i
i
d)
include
int main void
int i
for i
printf
printf
return 0
i
i i
i
int i
e)
include
int main void
char i
for i
printf
printf
return 0
Ojo: la
es minscula.
...............................................................................
...
3434
Antes de presentar con cierto detalle la entrada de datos por teclado mediante s
canf , nos
conviene detenernos brevemente para estudiar algunas cuestiones relativas a las
variables
y la memoria que ocupan.
Recuerda que la memoria es una sucesin de celdas numeradas y que una direccin
de memoria no es ms que un nmero entero. La declaracin de una variable supone la
reserva de una zona de memoria lo suficientemente grande para albergar su conten
ido.
Cuando declaramos una variable de tipo int, por ejemplo, se reservan 4 bytes de
memoria
en los que se almacenar (codificado en complemento a 2) el valor de dicha variabl
e.
Modificar el valor de la variable mediante una asignacin supone modificar el patrn
de
32 bits (4 bytes) que hay en esa zona de memoria.
Este programa, por ejemplo,
include
int main void
int a b
a
b
0
a
return 0
reserva 8 bytes para albergar dos valores enteros.9 Imagina que a ocupa los byte
s 1000
1003 y b ocupa los bytes 10041007. Podemos representar la memoria as:
996:
01010010
10101000
01110011
1000:
01011010
00111101
00111010
1004:
10111011
10010110
01010010
1008:
11010111
01000110
11110010
11110010
11010111
01010011
01011101
35
35
11
11110010
00
00000000
10
01010011
10
01011101
996:
01010010
10101000
011100
1000:
00000000
00000000
000000
1004:
10111011
10010110
010100
1008:
11010111
01000110
111100
11110010
00
00000000
00
00001000
10
01011101
996:
01010010
10101000
011100
1000:
00000000
00000000
000000
1004:
00000000
00000000
000000
1008:
11010111
01000110
111100
Hemos supuesto que a est en la direccin 1000 y b en la 1004, pero podemos saber
en qu direcciones de memoria se almacenan realmente a y b? S: el operador permite
conocer la direccin de memoria en la que se almacena una variable:
include
int main void
int a b
a
b
0
a
printf
unsigned int
printf
unsigned int
a
b
return 0
Observa que usamos la marca de formato
para mostrar el valor de la dire
ccin de
memoria, pues debe mostrarse como entero sin signo. La conversin a tipo unsigned
int
evita molestos mensajes de aviso al compilar.11
Al ejecutar el programa tenemos en pantalla el siguiente texto (puede que si
ejecutas
t mismo el programa obtengas un resultado diferente):
O sea, que en realidad este otro grfico representa mejor la disposicin de las
variables en memoria:
3221222572:
01010010
10101000
0111
3221222576:
b
3221222580:
00000000
a
3221222584:
01011101
00000000
00000000
0000
00000000
00000000
0000
11010111
01000110
1111
0011
11110010
0000
00001000
0000
0010
11
363
c
Las direcciones de memoria de las variables se representarn con flechas que apunt
an a
sus correspondientes cajas:
&a
a
0
&b
b
8
Ahora que hemos averiguado nuevas cosas acerca de las variables, vale la pen
a que
reflexionemos brevemente sobre el significado de los identificadores de variable
s all
donde aparecen. Considera este sencillo programa:
include
int main void
int a b
a 0
b a
scanf
a a
b
b
return 0
Cmo se interpreta la sentencia de asignacin a 0? Se interpreta como almacena el
valor 0 en la direccin de memoria de a. Y b a?, cmo se interpreta? Como almacena
una copia del contenido de a en la direccin de memoria de b. Fjate bien, el identif
icador
a recibe interpretaciones diferentes segn aparezca a la izquierda o a la derecha
de una
asignacin:
a la izquierda del igual, significa la direccin de a,
y a la derecha, es decir, en una expresin, significa el contenido de a.
La funcin scanf necesita una direccin de memoria para saber dnde debe depositar
un resultado. Como no estamos en una sentencia de asignacin, sino en una expresin,
es necesario que obtengamos explcitamente la direccin de memoria con el operador b
.
As, para leer por teclado el valor de b usamos la llamada scanf
b .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18 Interpreta el significado de la sentencia a a b.
................................................................................
..
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
37
37
ramacin con C - UJI
Introduccin a la prog
c
UJI
Marca
Muchos de los smbolos que representan a los operadores de Python que ya conoces s
on
los mismos en C. Los presentamos ahora agrupados por familias. (Consulta los niv
eles
de precedencia y asociatividad en la tabla de la pgina 44.) Presta especial atenc
in a
los operadores que no conoces por el lenguaje de programacin Python, como son los
operadores de bits, el operador condicional o los de incremento/decremento.
Operadores aritmticos Suma ( ), resta ( ), producto ( ), divisin ( ), mdulo o resto
de
la divisin ( ), identidad ( unario), cambio de signo ( unario).
No hay operador de exponenciacin.12
12
Pero hay una funcin de la biblioteca matemtica que permite calcular la pot
encia de un nmero: pow.
38
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
38
(o, equivale
ntemente,
el valor 98, ya que
equivale a 97). (Recuerda, no obstante, que u
n carcter no
es una cadena en C, as que
1 no es
.)
Operadores lgicos Negacin o no-lgica ( ), y-lgica o conjuncin (
-lgica o
) y o
disyuncin ( ).
Los smbolos son diferentes de los que aprendimos en Python. La negacin er
a all
not, la conjuncin era and y la disyuncin or.
C sigue el convenio de que 0 significa falso y cualquier otro valor sig
nifica cierto.
As pues, cualquier valor entero puede interpretarse como un valor lgico,
igual que
en Python.
Operadores de comparacin Igual que ( ), distinto de ( ), menor que ( ), mayor que
( ), menor o igual que ( ), mayor o igual que ( ). Son viejos conocidos. Un
a diferencia con respecto a Python: slo puedes usarlos para comparar valores escal
ares.
No puedes, por ejemplo, comparar cadenas mediante estos operadores.
13
Y la suma y la resta trabajan tambin con punteros. Ya estudiaremos la denomi
nada aritmtica de
punteros ms adelante.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
39
39
-Wall
Cuando escribimos un texto en castellano podemos cometer tres tipos de err
ores:
Errores lxicos: escribimos palabras incorrectamente, con errores
ortogrficos, o
usamos palabras inexistentes. Por ejemplo:
.
Errores sintcticos: las palabras son vlidas y estn bien escritas, p
ero faltan componentes de una frase (como el sujeto o el verbo), los component
es no concuerdad
o no ocupan la posicin adecuada, etc. Por ejemplo:
,
.
Errores semnticos: la frase est correctamente construida pero care
ce de significado vlido en el lenguaje. Por ejemplo:
,
.
Lo mismo ocurre con los programas C; pueden contener errores de los tres t
ipos:
Errores lxicos: usamos carcteres no vlidos o construimos incorrecta
mente componentes elementales del programa (como identificadores, cadenas
, palabras clave,
etc.). Por ejemplo: ,
.
Errores sintcticos: construimos mal una sentencia aunque usamos p
alabras vlidas. Por ejemplo: while a 10 a
1 , b 2
3 .
Errores semnticos: la sentencia no tiene un significado vlido. Por
ejemplo, si a es
de tipo float, estas sentencias contienen errores semnticos: scanf
a
(no se puede leer a como entero), if a 1.0
a 2.0 (no compa
ra el valor
de a con 1.0: le asigna 1.0 a a).
El compilador de C no deja pasar errores lxicos o sintcticos: informa y no t
raduce el
programa a cdigo de mquina. Con los errores semnticos, sin embargo, el compil
ador es
ms indulgente: la filosofa de C es suponer que el programador puede tener un
a razn
para hacer lo que expresa en los programas, aunque no tenga un significado
correcto
a primera vista. No obstante, y para segn qu posibles errores, el compilador
puede
emitir avisos (warnings). El nivel de avisos es configurable. La opcin
(Warning
all) activa todos los avisos, lo que incluye algunos potenciales errores se
mnticos. Este
programa errneo, por ejemplo, no genera ningn aviso al compilarse sin Wall:
E
include
int main void
float a
scanf
if a 0.0
return 0
a
a 2.0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
40
40
b
a b
Tambin podemos especificar con cierto detalle cmo esperamos que el usuario i
ntroduzca la informacin. Por ejemplo, con scanf
a b indicamos que el u
suario
debe separar los enteros con un guin; y con scanf
a b especi
ficamos que esperamos encontrar los enteros encerrados entre parntesis y separa
dos por
comas.
Lee la pgina de manual de scanf (escribiendo
en el intrp
rete de
rdenes Unix) para obtener ms informacin.
41
41
es
), asignacin con d
lazamiento a derecha (
), asignacin con y ( ), asignacin con o ( ),
asignacin con o exclusiva ( ).
Puede resultarte extrao que la asignacin se considere tambin un o erador.
Que
sea un o erador ermite escribir asignaciones mlti les como sta:
a
42
42
Por ejem lo, si deseas efectuar una divisin entre enteros que no ierda
decimales al
convertir el resultado a un flotante, uedes hacerlo como te muestra es
te rograma:
include
int main void
float x
int a 1 b
x
float b
10
100
200
43
43
nto y
necesitaremos escoger entre
reincremento y ostincremento.
Asociatividad
ostfijo
jo
izquierda
jo
derecha
izquierda
sizeof
ti o
refijo
ostfi
refi
izquierda
izquierda
izquierda
izquierda
izquierda
izquierda
izquierda
izquierda
izquierda
izquierda
derecha
izquierda
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
44
44
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19 Sean a, b y c tres variables de tipo int cuyos valores actuales son 0, 1 y 2,
respectivamente. Qu valor tiene cada variable tras ejecutar esta secuencia de asignacion
es?
a
a
c
a
b
b
c
a
c
b
a b
b c
a 0
a 2
a
2
a b c
scanf
a
printf
sc
anf
b
printf
r
b
scanf
a
printf
return 0
21 Haz un programa que solicite el valor de x y muestre por pantalla el resultad
o de
evaluar x 4 x 2 + 1. (Recuerda que en C no hay o erador de ex onenciacin.)
22 Disea un programa C que solicite la longitud del lado de un cuadrado y muestre
por pantalla su permetro y su rea.
23 Disea un programa C que solicite la longitud de los dos lados de un rectngulo
y muestre por pantalla su permetro y su rea.
24 Este programa C es problemtico:
include
int main void
int a b
a 2147483647
b a a
printf
a
printf
b
return 0
Al compilarlo y ejecutarlo hemos obtenido la siguiente salida por pantalla:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
45
45
ramacin con C - UJI
Introduccin a la prog
c
UJI
Qu ha ocurrido?
25 Disea un programa C que solicite el radio r de una circunferencia y muestre
por pantalla su permetro (2r) y su rea (r 2 ).
26 Si a es una variable de tipo char con el valor 127, qu vale a? Y qu vale
a? Y si a es una variable de tipo unsigned int con el valor 2147483647, qu vale a
?
Y qu vale a?
27 Qu resulta de evaluar cada una de estas dos expresiones?
a) 1
b) 1
1
1
0
0
1
1
scanf
printf
scanf
a
b
a
b
a
printf
printf
b
a
b
a
b
return 0
(Nota: la forma en que hace lo que hace viene de un viejo truco de la pr
ogramacin
en ensamblador, donde hay ricos juegos de instrucciones para la manipulacin de da
tos
bit
. . . a. .bit.)
.....................................................................
........
46
46
c
UJI
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
ntroduccin a la programacin con C - UJI
5
3
2?
Recuerda que en Python podamos combinar operadores de comparacin para for
mar
expresiones como 5 3 2. Esa, en particular, se evala a True, pues 5 es m
ayor que 3 y
3 es mayor que 2. C tambin acepta esa expresin, pero con un significado c
ompletamente
diferente basado en la asociatividad por la izquierda del operador : en
primer lugar
evala la subexpresin 5 3, que proporciona el valor cierto; pero como cierto e
s
1 (valor por defecto) y 1 no es mayor que 2, el resultado de la evaluac
in es 0, o sea,
falso.
Ojo con la interferencia entre ambos lenguajes! Problemas como ste su
rgirn con
frecuencia cuando aprendas nuevos lenguajes: construcciones que signifi
can algo en el
lenguaje que conoces bien tienen un significado diferente en el nuevo.
c d
antalla:
Introduccin a la prog
c
UJI
include
int main void
int a b
char c d
unsigned char e f
a 384
b 256
c a
d b
e a
f b
printf
printf
c d
e f
return 0
...............................................................................
...
Si asignamos un entero a una variable flotante, el entero promociona a su va
lor
equivalente en coma flotante. Por ejemplo, esta asignacin almacena en x el valor
2.0 (no
el entero 2).
x
2
Si asignamos un valor flotante a un entero, el flotante se convierte en su
equivalente
entero (si lo hay!). Por ejemplo, la siguiente asignacin almacena el valor 2 en i
(no el
flotante 2.0).
i
2.0
Y esta otra asignacin almacena en i el valor :
i
0.1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31 Qu valor se almacena en las variables i (de tipo int) y x (de tipo float) tras
ejecutar cada una de estas sentencias?
a) i
) x
2
2.0
4.0
c) i
g) x
2
2
4
4
b) i 1 2
d) i 2.0 4
f) x 2.0 4
h) x 1 2
...............................................................................
...
Aunque C se encarga de efectuar implcitamente muchas de las conversiones de
tipo,
puede que en ocasiones necesites indicar explcitamente una conversin de tipo. Para
ello, debes preceder el valor a convertir con el tipo de destino encerrado entre
parntesis.
As:
i
int 2.3
En este ejemplo da igual poner int que no ponerlo: C hubiera hecho la conversin i
mplcitamente. El trmino int es el operador de conversin a enteros de tipo int. Hay un
operador de conversin para cada tipo: char , unsigned int float , etc. . . Recuer
da
que el smbolo tipo es un operador unario conocido como operador de coercin o
conversin de tipos.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32 Qu valor se almacena en las variables i (de tipo int) y x (de tipo float) tras
ejecutar estas sentencias?
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
4848
Introduccin a la pro
gramacin con C - UJI
c
UJI
a) i
float 2
e) x
2.0
int 4.0
i) x
float 1
2
b) i
1
float 2
f) x
int 2.0
4
j) x 1
float 2
c) i
int
2
4
g) x
int 2.0
4
d) i
int 2
float 4
h) x 2
float 4
...............................................................................
...
Las lneas que empiezan con una palabra predecida por el carcter son especiales.
Las palabras que empiezan con se denominan directivas. El compilador no llega a
ver
nunca las lneas que empiezan con una directiva. Qu queremos decir exactamente con
que no llega a verlas? El compilador
es, en realidad, un programa que control
a varias
etapas en el proceso de traduccin de C a cdigo de mquina. De momento, nos interesa
considerar dos de ellas:
el preprocesador,
y el traductor de C a cdigo de mquina (el compilador propiamente dicho).
Preprocesador
ompilador
El preprocesador es un programa independiente, aunque es infrecuente invocarlo d
irectamente. El preprocesador del compilador
se llama
.
Las directivas son analizadas e interpretadas por el preprocesador. La direc
tiva include
seguida del nombre de un fichero (entre los caracteres y ) hace que el preproces
ador
sustituya la lnea en la que aparece por el contenido ntegro del fichero (en ingls in
clude significa incluye). El compilador, pues, no llega a ver la directiva, sino el re
sultado
de su sustitucin.
Nosotros slo estudiaremos, de momento, dos directivas:
define, que permite definir constantes,
e include, que permite incluir el contenido de un fichero y que se usa
para importar
funciones, variables, constantes, etc. de bibliotecas.
define
Una diferencia de C con respecto a Python es la posibilidad que tiene el primero
de
definir constantes. Una constante es, en principio15 , una variable cuyo valor n
o puede ser
modificado. Las constantes se definen con la directiva define. As:
define
valor
3.14
rintf
scanf
a
r
i
rintf
r
a
return 0
Pero la osibilidad de declarar constantes con const no nos libra de la dire
ctiva
define, ues no son de a licacin en todo lugar donde conviene usar una constante.
Ms adelante, al estudiar la declaracin de vectores, nos referiremos nuevamente a e
sta
cuestin.
17
A qu es eras
18
Ms adelante estudiaremos una estructura de seleccin que no es if y que se usa
normalmente ara
es ecificar este ti o de acciones.
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
UJI
c
50
50
3.14
El resultado es esto:
int main void
int a 3.14
return 0
Como uedes ver, una vez re rocesado, no queda ninguna directiva en el rog
rama y
la a aricin del identificador ha sido sustituida or el texto . Un erro
r t ico es
confundir un define con una declaracin normal de variables y, en consecuenc
ia, oner
una asignacin entre el identificador y el valor:
define
3.14
if o cion
1
Cdigo ara cargar registros
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con C ISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
51
51
else if o cion
2
Cdigo ara guardar registros
else if o cion
El cdigo resulta un tanto ilegible orque no vemos la relacin entre los valores nu
mricos
y las o ciones de men. Es ms legible recurrir a constantes:
define
1
define
2
define
3
define
4
define
5
define
6
define
7
if o cion
Cdigo ara cargar registros
else if o cion
Cdigo ara guardar registros
else if o cion
Puedes ahorrarte la retahla de defines con los denominados ti os enumerados.
Un
ti o enumerado es un conjunto de valores con nombre. Fjate en este ejem lo:
enum
if o cion
Cargar
Cdigo ara cargar registros
else if o cion
Guardar
Cdigo ara guardar registros
else if o cion
Anyadir
La rimera lnea define los valores Cargar, Guardar, . . . como una sucesin de
valores
correlativos. La asignacin del valor 1 al rimer elemento de la enumeracin hace qu
e la
sucesin em iece en 1. Si no la hubisemos escrito, la sucesin em ezara en 0.
Es habitual que los enum a arezcan al rinci io, tras los include y define.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33 Qu valor tiene cada identificador de este tipo enumerado?
enum
Primera
Segunda Tercera Penultima
Ultima
(No te hemos explicado qu hace la segunda asignacin. Comprueba que la explicacin que das es correcta con un programa que muestre por pantalla el valor de cada
identificador.)
...............................................................................
...
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
5252
Introduccin a la prog
ramacin con C - UJI
c
UJI
Los tipos enumerados sirven para algo ms que asignar valores a opciones de me
n.
Es posible definir identificadores con diferentes valores para series de element
os como
los das de la semana, los meses del ao, etc.
enum
Lunes Martes Miercoles Jueves Viernes Sabado Domingo
enum
Invierno Primavera Verano Otonyo
enum
Rojo Verde Azul
i
nclude
En C, los mdulos reciben el nombre de bibliotecas (o libreras, como traduccin fonticamente similar del ingls library). La primera lnea de
es sta:
include
Con ella se indica que el programa hace uso de una biblioteca cuyas funciones, v
ariables, tipos de datos y constantes estn declaradas en el fichero
, que
es abreviatura de standard input/output (entrada/salida estndar). En particular, el progra
ma
usa las funciones printf y scanf de
. Los ficheros con
extensin
se denominan ficheros cabecera (la letra es abreviatura de header, que en ingls
significa cabecera).
A diferencia de Python, C no permite importar un subconjunto de las funcione
s proporcionadas por una biblioteca. Al hacer include de una cabecera se importan tod
as
sus funciones, tipos de datos, variables y constantes. Es como si en Python ejec
utaras la
sentencia from mdulo import .
Normalmente no basta con incluir un fichero de cabecera con include para pod
er
compilar un programa que utiliza bibliotecas. Es necesario, adems, compilar con o
pciones
especiales. Abundaremos sobre esta cuestin inmediatamente, al presentar la librera
matemtica.
, el compi
Lo cierto es que son de tipo double (vase el apndice A), pero no hay problema
si las usas con valores
19
y variables de tipo float, ya que hay conversin automtica de tipos.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
53
53
Funcin C
sqrt x
sin x
cos x
tan x
asin x
acos x
atan x
exp x
exp10 x
log x
log10 x
log2 x
pow x y
fabs x
round x
Funcin matemtica
raz cuadrada de x
seno de x
coseno de x
tangente de x
arcoseno de x
arcocoseno de x
arcotangente de x
el nmero e elevado a x
10 elevado a x
logaritmo en base e de x
logaritmo en base 10 de x
logaritmo en base 2 de x
x elevado a y
valor absoluto de x
redondeo al entero ms prximo a
ceil x
floor x
redondeo superior de x
redondeo inferior de x
Valor
una aproximacin
del nmer
una
aproximacin
del nmer
una
una
una
a roximacin
a roximacin
a roximacin
una
una
a roximacin
a roximacin
de /2
de /4
de 1/
de 2
de log2
una
a roximacin
de log1
o e
o
e
0 e
Tabla 1.2: Algunas constantes dis onibles en la bibliotec
a
ue
debe ser, etc.). Una vez que se com rueba que el rograma es correcto, se roced
e a ge
nerar el cdigo de mquina, y ah es necesario egar (enlazar) el cdigo de mquina
de las funciones matemticas que hemos utilizado. El cuer o ya com ilado de sqrt,
or
ejem lo, se encuentra en
(
es abreviatura de math libra
ry). El
enlazador es el rograma que enlaza el cdigo de mquina de nuestro rograma con el
cdigo de mquina de las bibliotecas que usamos. Con la o cin
le indicamos al
en
lazador que debe resolver las referencias indefinidas a funciones matemticas util
izando
.
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la rogramacin con C
Introduccin a la rogramacin con C UJI
c
UJI
54
54
La o cin
El
Com ila
Enlazador
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
34 Disea un programa C que solicite la longitud delos tres lados de un tringulo
(a, b y c) y muestre por pantalla su permetro y su rea ( s(s a)(s b)(s c), donde
s = (a + b + c)/2.).
Com ila y ejecuta el rograma.
35 Disea un programa C que solicite el radio r de una circunferencia y muestre
por pantalla su permetro (2r) y su rea (r 2 ). Utiliza la a roximacin a redefinida
en la biblioteca matemtica.
Com ila y ejecuta el rograma.
...............................................................................
...
Las estructuras de control de C son arecidas a las de Python. Bueno, hay alguna
ms
y todas siguen unas reglas sintcticas diferentes. Em ecemos estudiando las estruc
turas
de control condicionales.
La sentencia de seleccin if
La estructura de control condicional fundamental es el if. En C se escribe as:
if condicin
sentencias
Los arntesis que encierran a la condicin son obligatorios. Como en Python no lo s
on,
es fcil que te equivoques or no onerlos. Si el bloque de sentencias consta de u
na sola
sentencia, no es necesario encerrarla entre llaves:
if condicin
sentencia
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con ISBN:
C
9788469301432
55
55
ramacin con C UJI
Introduccin a la rog
c
UJI
uedes eliminar l
if condicin
sentencias si
else
sentencia no
if condicin
sentencia si
else
sentencia no
Ojo: la indentacin no significa nada ara el com ilador. La onemos nicamente
ara facilitar la lectura. Pero si la indentacin no significa nada nos enfrentamo
s a un
roblema de ambigedad con los if anidados:
if
condicin
if otra condicin
sentencias si
else
??????
sentencias no
A cul de los dos if ertenece el else? Har el com ilador de C una inter retacin
como la que sugiere la indentacin en el ltimo fragmento o como la que sugiere este
otro?:
if condicin
if otra condicin
sentencias si
else
???
???
sentencias no
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
56
56
else
sentencias no
A esar de que el if externo contiene una sola sentencia (otro y, or tanto,
las llaves
son redundantesif), las llaves son necesarias ara indicar que el else va asocia
do a la
condicin exterior.
No hay sentencia elif: la combinacin else if
C no tiene una estructura elif como la de Python,
es usar
else if donde hubieras uesto un elif en Python:
if condicin
sentencias si
else if condicin2
sentencias si2
else if condicin3
sentencias si3
else
sentencias no
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36 Disea un programa C que pida por teclado un nmero entero y diga si es par o
impar.
37 Disea un programa que lea dos nmeros enteros y muestre por pantalla, de estos
tres mensajes, el que convenga:
.
38 Tambin en C es problemtica la divisin por 0. Haz un programa C que resuelva
la ecuacin ax + b = 0 solicitando por teclado el valor de a y b (ambos de tipo fl
oat).
El programa detectar si la ecuacin no tiene solucin o si tiene infinitas soluciones
y,
en cualquiera de los dos casos, mostrar el pertinente aviso.
39 Disea un programa que solucione ecuaciones de segundo grado. El programa
detectar y tratar por separado las siguientes situaciones:
la ecuacin tiene dos soluciones reales;
la ecuacin tiene una nica solucin real;
la ecuacin no tiene solucin real;
la ecuacin tiene infinitas soluciones.
40 Realiza un programa que proporcione el desglose en billetes y monedas de una
cantidad exacta de euros. Hay billetes de 500, 200, 100, 50, 20, 10 y 5 euros y
monedas
de 1 y 2 euros.
Por ejemplo, si deseamos conocer el desglose de 434 euros, el programa mostra
r por
pantalla el siguiente resultado:
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
5757
c
UJI
Introduccin a la prog
ramacin con C - UJI
opcion
58
58
c
UJI
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
switch opcion
case 1
printf
break
case 2
printf
break
default
printf
break
return 0
Aunque resulta algo ms elegante esta otra versin, que hace uso de tipos enum
erados:
include
enum
Saludar 1 Despedirse
Saludar 1 Despedirse
opcion
switch opcion
case Saludar
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
59
59
printf
case Despedirse
printf
default
printf
return 0
Si seleccionas la opcin 1, no sale un nico mensaje por pantalla, salen tres:
,
y
! Y si seleccionas la opcin 2, salen dos mensajes:
y
! Si no hay break, el flujo de control que entra en un case
ejecuta las
acciones asociadas al siguiente case, y as hasta encontrar un break o salir del s
witch
por la ltima de sus lneas.
El compilador de C no seala la ausencia de break como un error porque, de hec
ho,
no lo es. Hay casos en los que puedes explotar a tu favor este curioso comportam
iento
del switch:
include
enum
El bucle while
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
60
60
scanf
scanf
printf
x
n
x n r
return 0
El bucle do while
Hay un bucle iterativo que Python no tiene: el do while:
do
sentencias
while condicin
El bucle do while evala la condicin tras cada ejecucin de su bloque, as que es segur
o
que ste se ejecuta al menos una vez. Podramos reescribir
para us
ar un
bucle do while:
include
include
int main void
int a b i
float s
Pedir lmites inferior y superior.
do
printf
if a 0 printf
while a 0
scanf
scanf
do
printf
if b a printf
while b a
for i a i
b i
sqrt i
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
61
61
Mostrar el resultado.
printf
a b s
return 0
Los bucles do while no aaden potencia al lenguaje, pero s lo dotan de mayor ex
presividad. Cualquier cosa que puedas hacer con bucles do while, puedes hacerla tam
bin
con slo bucles while y la ayuda de alguna sentencia condicional if, pero probable
mente
requerirn mayor esfuerzo por tu parte.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
44 Escribe un programa que muestre un men en pantalla con dos opciones: saludar
y salir. El programa pedir al usuario una opcin y, si es vlida, ejecutar su accin
asociada. Mientras no se seleccione la opcin salir, el men reaparecer y se solicitar
nuevamente una opcin. Implementa el programa haciendo uso nicamente de bucles
do while.
45 Haz un programa que pida un nmero entero de teclado distinto de 1. A continuacin, el programa generar una secuencia de nmeros enteros cuyo primer nmero es el
que hemos ledo y que sigue estas reglas:
si el ltimo nmero es par, el siguiente resulta de dividir a ste por la mi
tad;
si el ltimo nmero es impar, el siguiente resulta de multiplicarlo por 3
y aadirle
1.
Todos los nmeros se irn mostrando por pantalla conforme se vayan generando. El
proceso se repetir hasta que el nmero generado sea igual a 1. Utiliza un bucle do
while.
...............................................................................
...
El bucle for
El bucle for de Python existe en C, pero con importantes diferencias.
for inicializacin condicin incremento
sentencias
Los parntesis de la primera lnea son obligatorios. Fjate, adems, en que los tres ele
mentos entre parntesis se separan con puntos y comas.
El bucle for presenta tres componentes. Es equivalente a este fragmento de cdi
go:
inicializacin
while condicin
sentencias
incremento
Una forma habitual de utilizar el bucle for es la que se muestra en este ejem
plo, que
imprime por pantalla los nmeros del 0 al 9 y en el que suponemos que i es de tipo
int:
for i 0 i
printf
10 i
i
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
6262
Introduccin a la prog
ramacin con C - UJI
c
UJI
Comparaciones y asignaciones
Un error frecuente es sustituir el operador de comparacin de igualdad por
el de asignacin en una estructura if o while. Analiza este par de sentencias:
?
a 0
if a 0
Correcto.
i 0
while i
printf
i
10
i
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46 Implementa el programa de clculo de x n (para x y n entero) con un bucle for.
47 Implementa un programa que dado un nmero de tipo int, ledo por teclado, se
asegure de que slo contiene ceros y unos y muestre su valor en pantalla si lo int
erpretamos como un nmero binario. Si el usuario introduce, por ejemplo, el nmero 1101,
el
programa mostrar el valor 13. Caso de que el usuario introduzca un nmero formado p
or
nmeros de valor diferente, indica al usuario que no puedes proporcionar el valor
de su
interpretacin como nmero binario.
48 Haz un programa que solicite un nmero entero y muestre su factorial. Utiliza u
n
entero de tipo long long para el resultado. Debes usar un bucle for.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
63
63
ramacin con C - UJI
Introduccin a la prog
c
UJI
127 b
a
1024 c i
printf
a 2147483647
for i 0 i 8 sizeof a
printf
c a
a
1
c
i
0
printf
a 1
for i 0 i 8 sizeof a i
if c a
0 c 1
else c 1
a
1
a 2147483647
for i 0 i 8 sizeof a
printf
c a
a
1
i
0
printf
return 0
51 Cuando no era corriente el uso de terminales grficos de alta resolucin era comn
representar grficas de funciones con el terminal de caracteres. Por ejemplo, un p
eriodo
de la funcin seno tiene este aspecto al representarse en un terminal de caractere
s (cada
punto es un asterisco):
Andrs Marzal/Isabel
Introduccin
Gracia
64
64
Haz un programa C que muestre la funcin seno utilizando un bucle que recorre el p
eriodo
2 en 24 asos (es decir, re resentndolo con 24 lneas).
52 Modifica el programa para que muestre las funciones seno (con asteriscos) y
coseno (con sumas) simultneamente.
...............................................................................
...
Variables de bucle de usar y tirar
C99 ha copiado una buena idea de C : permitir que las variables de bucle
se definan
all donde se usan y dejen de existir cuando el bucle termina. Fjate en este
programa:
include
int main void
int a
for int i 0 i
printf
a
1
32 i
i a
return 0
La variable i, el ndice del bucle, se declara en la mismsima zona de inicia
lizacin del
bucle. La variable i slo existe en el mbito del bucle, que es donde se usa.
Hacer un bucle que recorra, por ejemplo, los nmeros pares entre 0 y 10 es senc
illo:
basta sustituir el modo en que se incrementa la variable ndice:
for i 0 i
10 i
i
2
printf
i
aunque la forma habitual de expresar el incremento de i es esta otra:
for i 0 i
10 i
2
printf
i
Un bucle que vaya de 10 a 1 en orden inverso presenta este aspecto:
for i 10 i
0 i
printf
i
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53 Disea un programa C que muestre el valor de 2n para todo n entre 0 y un valor
entero proporcionado por teclado.
54 Haz un programa que pida al usuario una cantidad de euros, una tasa de inters
y un nmero de aos y muestre por pantalla en cunto se habr convertido el capital
inicial transcurridos esos aos si cada ao se aplica la tasa de inters introducida.
Recuerda que un capital C a un inters del x por cien durante n aos se conviert
e
en C (1 + x/100)n .
(Prueba tu programa sabiendo que 10 000 euros al 4.5% de inters anual se conv
ierten
en 24 117.14 euros al cabo de 20 aos.)
55 Un vector en un espacio tridimensional es una tripleta de valores reales (x,
y, z).
Deseamos confeccionar un programa que permita operar con dos vectores. El usuari
o ver
en pantalla un men con las siguientes opciones:
Tras la ejecucin de cada una de las acciones del men ste reaparecer en pantalla,
a
menos que la opcin escogida sea la nmero 9. Si el usuario escoge una opcin diferent
e,
el programa advertir al usuario de su error y el men reaparecer.
Las opciones 4 y 5 pueden proporcionar resultados distintos en funcin del ord
en
de los operandos, as que, si se escoge cualquiera de ellas, aparecer un nuevo men
que permita seleccionar el orden de los operandos. Por ejemplo, la opcin 4 mostra
r el
siguiente men:
Clculo
(x1 + x2 , y1 + y2
(x1 x2 , y1 y2 , z1
x1 x2 + y1 y2 + z1 z
(y1 z2 z1 y2 , z1 x2
180
x1 x2 + y1 y2 + z1 z2
ngulo entre (x1 , y1 , z1 ) y (x2 , y2 , z2 )
arccos
x + y2 + z 2 x 2 + y2 + z 2
2
1
2
Longitud de (x, y, z)
x 2 + y2 + z 2
Ten en cuenta que tu rograma debe contem lar toda osible situacin exce cion
al:
divisiones or cero, races con argumento negativo, etc..
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con ISBN:
C
9788469301432
Introduccin a la
66
66
La sentencia break tambin est dis onible en C. De hecho, ya hemos visto una a lica
cin
suya en la estructura de control switch. Con ella uedes, adems, abortar al insta
nte la
ejecucin de un bucle cualquiera (while, do while o for).
Otra sentencia de C que uede resultar til es continue. Esta sentencia finali
za la
iteracin actual, ero no aborta la ejecucin del bucle.
Por ejem lo, cuando en un bucle while se ejecuta continue, la siguiente sent
encia a
ejecutar es la condicin del bucle; si sta se cum le, se ejecutar una nueva iteracin
del
bucle.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
56 Qu muestra por pantalla este programa?
include
int main void
int i
i 0
while i 10
if i 2
0
i
continue
printf
i
for i 0 i 10 i
if i 2
0
continue
printf
i
return 0
57
58
or car
car
and opcion
print
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
67
67
ramacin con C - UJI
Introduccin a la prog
c
UJI
print
opcion raw
if opcion
diametro
print
elif opcion
perimetro
print
elif opcion
area pi
print
else
print
opcion
59
input
2
radio
diametro
2
pi
radio
perimetro
radio
2
area
100
0 or anyo
400
0
print
else
print
60
limite
anyo
anyo
Traduce a C este programa Python.
int raw input
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
6868
Me llamo Alicia, Majestad dijo Alicia con mucha educacin; pero aadi
para sus adentros: Vaya!, en realidad no son ms que un mazo de cartas.
No tengo por qu tenerles miedo!.
LEWIS CARROLL, Alicia en el Pas de
las Maravillas.
En este captulo vamos a estudiar algunas estructuras que agrupan varios datos, pe
ro cuyo
tamao resulta conocido al compilar el programa y no sufre modificacin alguna duran
te
su ejecucin. Empezaremos estudiando los vectores, estructuras que se pueden asimi
lar a
las listas Python. En C, las cadenas son un tipo particular de vector. Manejar c
adenas en
C resulta ms complejo y delicado que manejarlas en Python. Como contrapartida, es
ms
fcil definir en C vectores multidimensionales (como las matrices) que en Python.
En este
captulo nos ocuparemos tambin de ellos. Estudiaremos adems los registros en C, que
permiten definir nuevos tipos como agrupaciones de datos de tipos no necesariame
nte
idnticos. Los registros de C son conceptualmente idnticos a los que estudiamos en
Python.
Un vector (en ingls, array) es una secuencia de valores a los que podemos acceder
mediante ndices que indican sus respectivas posiciones. Los vectores pueden asimi
larse
a las listas Python, pero con una limitacin fundamental: todos los elementos del
vector
han de tener el mismo tipo. Podemos definir vectores de enteros, vectores de flo
tantes,
etc., pero no podemos definir vectores que, por ejemplo, contengan a la vez ente
ros y
flotantes. El tipo de los elementos de un vector se indica en la declaracin del v
ector.
C nos permite trabajar con vectores estticos y dinmicos. En este captulo nos o
cupamos nicamente de los denominados vectores estticos, que son aquellos que tienen
tamao fijo y conocido en tiempo de compilacin. Es decir, el nmero de elementos del
vector no puede depender de datos que suministra el usuario: se debe hacer explci
to mediante una expresin que podamos evaluar examinando nicamente el texto del programa
.
69
Sin cortes
Los vectores C son mucho ms limitados que las listas Python. A los problem
as relacionados con el tamao fijo de los vectores o la homogeneidad en el tipo
de sus
elementos se une una incomodidad derivada de la falta de operadores a los
que nos
hemos acostumbrado como programadores Python. El operador de corte, por e
jemplo, no
existe en C. Cuando en Python desebamos extraer una copia de los elementos
entre i
y j de un vector a escribamos a i j 1 . En C no hay operador de corte. . .
ni operador
de concatenacin o repeticin, ni sentencias de borrado de elementos, ni se e
ntienden
como accesos desde el final los ndices negativos, ni hay operador de perte
nencia, etc.
Echaremos de menos muchas de las facilidades propias de Python.
Puede que consideres vlida esta otra declaracin que prescinde de constantes de
finidas con define y usa constantes declaradas con const, pero no es as:
const int talla
80
char a talla
Una variable const es una variable en toda regla, aunque de slo lectura.
Una vez creado un vector, sus elementos presentan valores arbitrarios. Es un err
or suponer
que los valores del vector son nulos tras su creacin. Si no lo crees, fjate en est
e programa:
1
Como siempre, hay excepciones: C99 permite declarar la talla de un vector
con una expresin cuyo valor
slo se conoce en tiempo de ejecucin, pero slo si el vector es una variable local a
una funcin. Para evitar
confusiones, no haremos uso de esa caracterstica en este captulo y lo considerarem
os incorrecto.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
70
70
include
define
i
a i
Observa que el acceso a elementos del vector sigue la misma notacin de Python: us
amos
el identificador del vector seguido del ndice encerrado entre corchetes. En una e
jecucin
del programa obtuvimos este resultado en pantalla (es probable que obtengas resu
ltados
diferentes si repites el experimento):
10
i
i
a i
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67 Declara e inicializa un vector de 100 elementos de modo que los componentes d
e
ndice par valgan 0 y los de ndice impar valgan 1.
68 Escribe un programa C que almacene en un vector los 50 primeros nmeros de
Fibonacci. Una vez calculados, el programa los mostrar por pantalla en orden inve
rso.
69 Escribe un programa C que almacene en un vector los 50 primeros nmeros de
Fibonacci. Una vez calculados, el programa pedir al usuario que introduzca un nmer
o
y dir si es o no es uno de los 50 primeros nmeros de Fibonacci.
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
7171
Introduccin a la prog
ramacin con C - UJI
c
UJI
b 10
i
i
i
i
10
10
10
10
i
i
i
i
a i
0
b i
0
printf
printf
a i
b i
10
10
b TALLA B
for i 0
for i 0
for i 0
i
i
i
TALLA
TALLA
TALLA
A
B
A
i
i
i
a i
0
b i
0
printf
for i 0
TALLA
printf
a i
b i
return 0
Si ahora has de modificar a para que tenga 20 elementos, basta con editar
la lnea
3: cambia el 10 por un 20. Ms rpido y con mayor garanta de no cometer errore
s.
Por qu en Python no nos preocup esta cuestin? Recuerda que en Python no haba
declaracin de variables, que las listas podan modificar su longitud durante
la ejecucin
de los programas y que podas consultar la longitud de cualquier secuencia
de valores
con la funcin predefinida len. Python ofrece mayores facilidades al progra
mador, pero
a un precio: menos velocidad de ejecucin y ms consumo de memoria.
5
0 0 0 0 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
2
72
7
int b
1 2 3 4 5
0 0 0 0 0
1 2 3 4 5
100
i j
Inicializacin
criba 0
0
for i 1 i
i
criba i
1
Criba de Eratstenes
for i 2 i
i
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
73
73
if criba i
for j 2 i j
criba i j
j
0
100
Queremos efectuar estadsticas con una serie de valores (las edades de 15 personas
), as
que vamos a disear un programa que nos ayude. En una primera versin, solicitaremos
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
7474
Introduccin a la prog
ramacin con C - UJI
c
UJI
100
8 1
Inicializacin
criba 0
254
o el primero.
for i 1 i
criba i
8 i
255
Criba de Eratstenes
for i 2 i
i
if criba i 8
1
el bit en posicin i vale 1.
for j 2 i j
j
criba i j 8
Pone a 0 el bit en posicin i j.
i 8
1
Pregunta si
i j
75
75
donde xi e la edad del individuo nmero i.2 La moda e la edad que m vece aparec
e
(i do o m edade aparecen mucha vece con la mxima frecuencia, aumiremo que
una cualquiera de ella e la moda). La mediana e la edad tal que el 50% de la
edade
on inferiore o iguale a ella y el retante 50% on mayore o iguale.
Empezamo por la declaracin del vector que albergar la 15 edade y por leer l
o
dato:
include
define
15
Lectura de edade
for i 0 i
i
printf
i 1
canf
edad i
return 0
Vale la pena que te detenga a obervar cmo indicamo a canf que lea la celda de
ndice i en el vector edad: uamo el operador delante de la exprein edad i . E l
o
que caba eperar: edad i e un ecalar de tipo int, y ya abe que canf epera
u
direccin de memoria.
Paamo ahora a calcular la edad media y la deviacin tpica (no te ha de upo
ner
dificultad alguna con la experiencia adquirida al aprender Python):
include
include
define
15
uma edad
media uma edad
edad i
float
76
76
for i 0 i
i
suma desviacion edad i
media
media
desviacion sqrt suma desviacion
Im resin de resultados
rintf
rintf
edad
media
desviacion
return 0
El clculo de la moda (la edad ms frecuente) resulta ms roblemtica. Cmo abor
dar el clculo? Vamos a resentar dos versiones diferentes. Em ezamos or una que
consume demasiada memoria. Dado que trabajamos con edades, odemos asumir que
ninguna edad iguala o su era los 150 aos. Podemos crear un vector con 150 contado
res,
uno ara cada osible edad:
include
include
define
define
15
150
Clculo de la moda
for i 0 i
i
s contadores.
contador i
0
for i 0 i
i
contador edad i
dor asociado a edad i .
moda
1
edad
Inicializacin de lo
Incrementamos el conta
frecuencia 0
for i 0 i
i
edad con mayor valor en contador).
if contador i
frecuencia
frecuencia contador i
moda i
Buscamos la moda (
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
7
77
7
Im resin de resultados
rintf
rintf
rintf
media
desviacion
moda
return 0
Esta solucin consume un vector de 150 elementos enteros cuando no es estrictam
ente
necesario. Otra osibilidad asa or ordenar el vector de edades y contar la lon
gitud de
cada secuencia de edades iguales. La edad cuya secuencia sea ms larga es la moda:
include
include
define
15
edad i
media
desviacion sqrt suma desviacion
Clculo de la moda
for i 0 i
1 i
Ordenacin mediante
burbuja.
for j 0 j
i j
if edad j
edad j 1
aux edad j
edad j
edad j 1
edad j 1
aux
frecuencia 0
frecuencia moda 0
moda
1
for i 0 i
1 i
s ms larga.
if edad i
edad i 1
frecuencia
if frecuencia frecuencia moda
frecuencia moda frecuencia
Introduccin a la rogramacin con C
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
78
78
cUJI
Introduccin a la
moda
edad i
else
frecuencia
Im resin de resultados
rintf
rintf
rintf
media
desviacion
moda
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71 Contiene en cada instante la variable frecuencia el verdadero valor de la frecuencia de aparicin de un valor? Si no es as, qu contiene? Afecta eso al clculo
efectuado? Por qu?
72 Esta nueva versin del programa presenta la ventaja adicional de no fijar un
lmite mximo a la edad de las personas. El programa resulta, as, de aplicacin ms
general. Son todo ventajas? Ves algn aspecto negativo? Reflexiona sobre la velocida
d
de ejecucin del programa comparada con la del programa que consume ms memoria.
...............................................................................
...
Slo nos resta calcular la mediana. Mmmm. No hay que hacer nuevos clculos para
conocer la mediana: gracias a que hemos ordenado el vector, la mediana es el val
or que
ocupa la posicin central del vector, es decir, la edad de ndice
2.
include
include
define
15
edad i
Clculo de la media
suma edad 0
for i 0 i
i
suma edad
edad i
media suma edad
float
Clculo de la desviacion tpica
suma desviacion 0.0
for i 0 i
i
suma desviacion
edad i
edad i
media
desviacion sqrt suma desviacion
media
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
7979
Introduccin a la prog
ramacin con C - UJI
c
UJI
Clculo de la moda
for i 0 i
1 i
iante burbuja.
for j 0 j
i j
if edad j
edad j 1
aux edad j
edad j
edad j 1
edad j 1
aux
Ordenacin med
frecuencia 0
frecuencia moda 0
moda
1
for i 0 i
1 i
if edad i
edad i 1
if
frecuencia frecuencia moda
Ver ejercicio 73.
frecuencia moda frecuencia
moda edad i
else
frecuencia
Clculo de la mediana
mediana edad
Impresin de resultados
printf
printf
printf
printf
media
desviacion
moda
mediana
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73 Fjate en la lnea 44 del programa y comprala con las lneas 44 y 45 de su
versin anterior. Es correcto ese cambio? Lo sera este otro?:
if frecuencia
frecuencia moda
...............................................................................
...
Bueno, vamos a modificar ahora el programa para que el usuario introduzca cua
ntas
edades desee hasta un mximo de 20. Cuando se introduzca un valor negativo para la
edad, entenderemos que ha finalizado la introduccin de datos.
include
include
define
20
for i 0 i
printf
i
i 1
scanf
edad i
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
8080
Introduccin a la prog
ramacin con C - UJI
c
UJI
if edad i
break
return 0
Mmmm. Hay un problema: si no damos 20 edades, el vector presentar toda una serie
de valores sin inicializar y, por tanto, con valores arbitrarios. Sera un grave e
rror tomar
esos valores por edades introducidas por el usuario. Una buena idea consiste en
utilizar
una variable entera que nos diga en todo momento cuntos valores introdujo realmen
te
el usuario en el vector edad:
include
include
define
20
i
i 1
return 0
La constante que hasta ahora se llamaba
ha pasado a llamarse
.
Se pretende reflejar que su valor es la mxima cantidad de edades de personas que
podemos manejar, pues el nmero de edades que manejamos realmente pasa a estar en
la variable entera personas.
Una forma alternativa de hacer lo mismo nos permite prescindir del ndice i:
include
include
define
int main void
20
int edad
personas j aux suma edad
float suma desviacion media desviacion
int moda frecuencia frecuencia moda mediana
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
81
81
s 1
Lectura de edades
personas 0
do
printf
personas 1
scanf
edad personas
personas
while personas
0
personas
edad persona
return 0
Imagina que se han introducido edades
punta
(conceptualmente) al final de la serie de
fectuar
los clculos pertinentes:
0
1
2
11 12 13 14 15 16 17 18
edad
7 -55 0 391 0
personas
MAX PERSONAS
18 30 18 19 19 31
-6 89 322 -2
10
27 66 -1 88
10
20
Ya podemos calcular la edad media, pero con un cuidado especial por las posi
bles
divisiones por cero que provocara que el usuario escribiera una edad negativa com
o edad
de la primera persona (en cuyo caso personas valdra 0):
include
include
define
20
s 1
Lectura de edades
personas 0
do
printf
personas 1
scanf
edad personas
personas
while personas
0
personas
edad persona
if personas 0
Clculo de la media
suma edad 0
for i 0 i personas i
suma edad
edad i
media suma edad
float personas
Clculo de la desviacion tpica
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
82
82
cUJI
Introduccin a la programacin con C -UJI
Ordenacin
frecuencia 0
frecuencia moda 0
moda
1
for i 0 i personas 1 i
if edad i
edad i 1
if
frecuencia frecuencia moda
frecuencia moda frecuencia
moda edad i
else
frecuencia
Clculo de la mediana
mediana edad personas 2
Impresin de resultados
printf
printf
printf
printf
media
desviacion
moda
mediana
else
printf
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74 Cuando el nmero de edades es par no hay elemento central en el vector ordenado
,
as que estamos escogiendo la mediana como uno cualquiera de los elementos centrale
s.
Utiliza una definicin alternativa de edad mediana que considera que su valor es l
a media
de las dos edades que ocupan las posiciones ms prximas al centro.
75 Modifica el ejercicio anterior para que, caso de haber dos o ms valores con la
mxima frecuencia de aparicin, se muestren todos por pantalla al solicitar la moda.
76 Modifica el programa anterior para que permita efectuar clculos con hasta 100
personas.
77 Modifica el programa del ejercicio anterior para que muestre, adems, cuntas
edades hay entre 0 y 9 aos, entre 10 y 19, entre 20 y 29, etc. Considera que ning
una
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
8383
Introduccin a la prog
ramacin con C - UJI
c
UJI
el programa mostrar (adems de la media, desviacin tpica, moda y mediana), la siguiente tabla:
80 Modifica el programa del ejercicio anterior para que muestre el mismo histogr
ama
de esta otra forma:
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
roduccin a la programacin con C - UJI
c
UJI
84
84
Int
Deseamos implementar una calculadora para polinomios de grado menor o igual que
10.
Un polinomio p(x) = p0 + p1 x + p2 x 2 + p3 x 3 + + p10 x 10 puede representarse
con un
vector en el que se almacenan sus 11 coeficientes (p0 , p1 , . . . , p10 ). Vamo
s a construir un
programa C que permita leer por teclado dos polinomios p(x) y q(x) y, una vez led
os,
calcule los polinomios s(x) = p(x) + q(x) y m(x) = p(x) q(x).
Empezaremos definiendo dos vectores p y q que han de poder contener 11 valor
es en
coma flotante:
include
define
11
include
define
11
Lectura de p
do
printf
scanf
grado
while grado 0
grado
for i 0 i grado i
printf
i scanf
p i
Lectura de q
do
printf
scanf
grado
while grado 0
grado
for i 0 i grado i
printf
i scanf
return 0
q i
85
85
Lectura de p
do
printf
1
scanf
grado
while grado 0
grado
for i 0 i grado i
printf
i scanf
p i
for i grado 1 i
p i 0.0
return 0
Ahora que hemos ledo los polinomios, calculemos la suma. La almacenaremos en
un
nuevo vector llamado s. La suma de dos polinomios de grado menor que
es un polinomio de grado tambin menor que
, as que el vector s
tendr
talla
.
11
scanf
grado
while grado 0
grado
for i 0 i grado i
printf
i scanf
p i
for i grado 1 i
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
86
86
p i
0.0
Lectura de q
do
printf
1
scanf
grado
while grado 0
grado
for i 0 i grado i
printf
i scanf
q i
for i grado 1 i
q i 0.0
Clculo de la suma
for i 0 i
s i
p i
q i
i
i
return 0
Aqu tienes un ejemplo de uso del programa con los polinomios p(x) = 5 + 3x + 5x 2
+ x 3
y q(x) = 4 4x 5x 2 + x 3 :
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81 Modifica el programa anterior para que no se muestren los coeficientes nulos.
82 Tras efectuar los cambios propuestos en el ejercicio anterior no aparecer nada
por pantalla cuando todos los valores del polinomio sean nulos. Modifica el prog
rama
para que, en tal caso, se muestre por pantalla
.
83 Tras efectuar los cambios propuestos en los ejercicios anteriores, el polinom
io
empieza con un molesto signo positivo cuando s0 es nulo. Corrige el programa par
a que
el primer trmino del polinomio no sea precedido por el carcter .
84 Cuando un coeficiente es negativo, por ejemplo 1, el rograma anterior muestra
su corres ondiente trmino en antalla as:
. Modifica el rograma ante
rior ara que un trmino con coeficiente negativo como el del ejem lo se muestre a
s:
.
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con ISBN:
C
9788469301432
8787
Introduccin a la rog
ramacin con C UJI
c
UJI
...............................................................................
...
Nos queda lo ms difcil: el roducto de los dos olinomios. Lo almacenaremos en
un
vector llamado m. Como el roducto de dos olinomios de grado menor o igual que
n es un
olinomio de grado menor o igual que 2n, la talla del vector m no es
:
q
s
float m 2
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85 Entiendes por qu hemos reservado 2
1 elementos para m y
no 2
?
...............................................................................
...
El coeficiente mi , para valores de i entre 0 y el grado mximo de m(x), es de
cir, entre
los enteros 0 y 2
2, se calcula as:
i
mi =
pj
qij .
j=0
Deberemos tener cuidado de no acceder errneamente a elementos de
rango de ndices vlidos.
Im lementemos ese clculo:
include
define
11
q
s
float m 2
int grado
int i j
Lectura de
do
rintf
1
scanf
grado
while grado 0
grado
for i 0 i grado i
o q fuera del
rintf
i scanf
for i grado 1 i
i 0.0
Lectura de q
do
rintf
1
scanf
grado
while grado 0
grado
for i 0 i grado i
rintf
i scanf
q i
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con ISBN:
C
9788469301432
88
88
ramacin con C UJI
Introduccin a la rog
c
UJI
for i grado 1 i
q i 0.0
Clculo de la suma
for i 0 i
s i
i
q i
i
i
1 i
i j
ue
efecta estadsticas de una serie de edades), necesitas alguna variable au
xiliar que
te permita saber en todo momento cuntas de las celdas contienen informa
cin. Si
aades un elemento, has de incrementar t mismo el valor de esa variable.
Ya sabes lo suficiente sobre vectores para poder hacer frente a estos ejer
cicios:
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
8989
c
UJI
Introduccin a la prog
ramacin con C - UJI
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
87 Disea un programa que pida el valor de 10 nmeros enteros distintos y los
almacene en un vector. Si se da el caso, el programa advertir al usuario, tan pro
nto sea
posible, si introduce un nmero repetido y solicitar nuevamente el nmero hasta que s
ea
diferente de todos los anteriores. A continuacin, el programa mostrar los 10 nmeros
por pantalla.
88 En una estacin meteorolgica registramos la temperatura (en grados centgrados)
cada hora durante una semana. Almacenamos el resultado en un vector de 168 compo
nentes (que es el resultado del producto 7 24). Disea un programa que lea los dat
os
por teclado y muestre:
La mxima y mnima temperaturas de la semana.
La mxima y mnima temperaturas de cada da.
La temperatura media de la semana.
La temperatura media de cada da.
El nmero de das en los que la temperatura media fue superior a 30 grados
.
El da y hora en que se registr la mayor temperatura.
89 La cabecera
incluye la declaracin de funciones para generar nmero
s
aleatorios. La funcin rand, que no tiene parmetros, devuelve un entero positivo al
eatorio. Si deseas generar nmeros aleatorios entre 0 y un valor dado , puedes evalu
ar
rand
1 . Cuando ejecutas un programa que usa rand, la semilla del gener
ador de
nmeros aleatorios es siempre la misma, as que acabas obteniendo la misma secuencia
de nmeros aleatorios. Puedes cambiar la semilla del generador de nmeros aleatorios
pasndole a la funcin srand un nmero entero sin signo.
Usa el generador de nmeros aleatorios para inicializar un vector de 10 elemen
tos
con nmeros enteros entre 0 y 4. Muestra por pantalla el resultado. Detecta y mues
tra, a
continuacin, el tamao de la sucesin ms larga de nmeros consecutivos iguales.
(Ejemplo: si los nmeros generados son
, el tramo ms l
argo
formado por nmeros iguales es de talla 3 (los tres doses al final de la secuencia
), as
que por pantalla aparecer el valor 3.)
90
Modifica el ejercicio anterior para que trabaje con un vector de 100
elementos.
91 Genera un vector con 20 nmeros aleatorios entre 0 y 100 y muestra por pantalla
el vector resultante y la secuencia de nmeros crecientes consecutivos ms larga.
(Ejemplo: la secuencia
es la secuencia creciente ms
larga
en la serie de nmeros
.)
92 Escribe un programa C que ejecute 1000 veces el clculo de la longitud de la
secuencia ms larga sobre diferentes secuencias aleatorias (ver ejercicio anterior
) y que
muestre la longitud media y desviacin tpica de dichas secuencias.
93 Genera 100 nmeros aleatorios entre 0 y 1000 y almacnalos en un vector.
Determina a continuacin qu nmeros aparecen ms de una vez.
94 Genera 100 nmeros aleatorios entre 0 y 10 y almacnalos en un vector. Determina
a continuacin cul es el nmero que aparece ms veces.
95 Disea un programa C que almacene en un vector los 100 primeros nmeros
primos.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
90
90
ramacin con C - UJI
Introduccin a la prog
c
UJI
Es importante que conozcas bien cmo se disponen los vectores en memoria. Cuando s
e
encuentra esta declaracin en un programa:
int a 5
el compilador reserva una zona de memoria contigua capaz de albergar 5 valores d
e tipo
int. Como una variable de tipo int ocupa 4 bytes, el vector a ocupar 20 bytes.
Podemos comprobarlo con este programa:
include
define
sizeof
a 0
printf
return 0
sizeof a
Cada byte de la memoria tiene una direccin. Si, pongamos por caso,
a
empieza en la direccin 1000, a 0 se almacena en los bytes 10001003, a
bytes 10041007, y as sucesivamente. El ltimo elemento, a 4 , ocupar
1019:
996:
1000:
1004:
1008:
el vector
1 en los
los bytes 1016
a[0]
a[1]
a[2]
1012:
1016:
a[3]
a[4]
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
91
91
Big-endian y little-endian
Lo bueno de los estndares es. . . que hay muchos donde elegir. No hay form
a de ponerse
de acuerdo. Muchos ordenadores almacenan los nmeros enteros de ms de 8 bits
disponiendo los bits ms significativos en la direccin de memoria ms baja y o
tros, en
la ms alta. Los primeros se dice que siguen la codificacin big-endian y los s
egundos,
little-endian.
Pongamos un ejemplo. El nmero 67586 se representa en binario con cuatr
o bytes:
Supongamos que ese valor se almacena en los cuatro bytes que empiezan en
la direccin
1000. En un ordenador big-endian, se dispondran en memoria as (te indicamos b
ajo
cada byte su direccin de memoria):
Veamos qu direccin ocupa cada elemento de un vector cuando ejecutamos un programa sobre un computador real:
include
define
for i 0 i
printf
i
i
unsigned int
a i
return 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
92
92
for i 0 i
printf
i
scanf
i
a i
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98 Qu problema presenta esta otra versin del mismo programa?
include
define
i
i
a i
...............................................................................
...
Analiza este programa:
include
define
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
93
93
ramacin con C - UJI
Introduccin a la prog
c
UJI
for i 0 i
printf
nsigned int a i
i
i
printf
return 0
unsigned int a
unsigned int a
0 0 0 0 0
sta
cuestin.
0 0 0 0 0
&a[0]
Cmo encuentra C la direccin de memoria de un elemento del vector cuando accedemos a travs de un ndice? Muy sencillo, efectuando un clculo consistente en sumar
al puntero que seala el principio del vector el resultado de multiplicar el ndice
por
el tamao de un elemento del vector. La expresin a 2 , por ejemplo, se entiende com
o
accede al valor de tipo int que empieza en la direccin a con un desplazamiento de
2 4 bytes. Una sentencia de asignacin como a 2
0 se interpreta como almacena
el valor 0 en el entero int que empieza en la direccin de memoria de a ms 2 4 byte
s.
for i 0 i
v i i
w i
10
printf
printf
printf
printf
i i
printf
printf
w 0 w
printf
w 1 w
printf
w 2 w
printf
printf
v 0 v
printf
v 1 v
printf
v 2 v
i
i
unsigned int
unsigned int
0
unsigned int
1
unsigned int
2
unsigned int
0
unsigned int
1
unsigned int
2
int
int
int
int
int
int
printf
printf
v
2 v 2
printf
v
3 v 3
printf
v
4 v 4
printf
w 5 w 5
printf
w
1 w 1
printf
v
5 v 5
printf
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
return 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
95
95
3
10
11
12
i
w[0]
w[1]
w[2]
0
1
2
v[0]
v[1]
v[2]
96
96
UJI
c
1 2 3 4 5 6 7 8 9 10
original
return 0
Si com ilas el rograma, obtendrs un error en la lnea 8 que te im edir obtener un
ejecutable:
. El mensaje de error nos indi
ca que
no es osible efectuar asignaciones entre ti os vectoriales.
Nuestra intencin era que antes de ejecutar la lnea 8, la memoria resentara es
te
as ecto:
0 1 2 3 4
5
6 7 8 9
original
5
1 2 3 4 5 6 7 8 9 10
0 1 2 3 4
9
co ia
1 2 3 4 5 6 7
8 9 10
0
4
co ia
1 2 3 4 5 6 7
8 9 10
2.
al:
9
original
1 2 3 4 5 6 7
8 9 10
0
4
9
co ia
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
97
97
Violacin de segmento
Los errores de acceso a zonas de memoria no reservada se cuentan entre los
eores. En
el ejem lo, hemos accedido a la zona de memoria de un vector salindonos del
rango
de indexacin vlido de otro, lo cual ha roducido resultados desconcertantes.
Pero odra habernos ido an eor: si tratas de escribir en una zona de me
moria
que no ertenece a ninguna de tus variables, cosa que uedes hacer asignan
do un valor
a un elemento de vector fuera de rango, es osible que se genere una exce
cin du
rante la ejecucin del rograma: intentar escribir en una zona de memoria qu
e no ha
sido asignada a nuestro roceso dis ara, en Unix, una seal de violacin de seg
men
to (segmentation violation) que rovoca la inmediata finalizacin de la ejecu
cin del
rograma. Fjate en este rograma:
E
include
int main void
int a 10
a 10000
return 0
Cuando lo ejecutamos en un ordenador bajo Unix, obtenemos este mensaje
antalla:
or
y
el d
da
re
define
10
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
98
98
1 2 3 4 5 6 7 8 9 10
i
original i
return 0
1 2 3
1 1 1 3
Su onemos que todos los elementos son iguales dos a
dos.
i 0
while i
son iguales
if co ia i
original i
Pero basta con que dos elementos no
sean iguales...
son iguales 0
... ara que los vectores sean distinto
s.
i
if son iguales
rintf
else
rintf
return 0
99
99
A sim le vista,
ocu a 6 bytes, ues contamos en ella 6 caracteres,
ero no es
as. En realidad,
ocu a 7 bytes: los 6 que corres onden a los 6 caract
eres que
ves ms uno corres ondiente a un carcter nulo al final, que se denomina terminador
de
cadena y es invisible.
Al declarar e inicializar una cadena as:
char a 10
la memoria queda de este modo:
0
6
9
a
c a d e n a \0
9
a
c a d e n a \0
a esta otra:
0
6
t e x t o \0
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
100
100
y \0
Recuerda:
Las comillas sim les definen un carcter y un carcter ocu a un sol
o byte.
Las comillas dobles definen una cadena. Toda cadena incluye un
carcter nulo
invisible al final.
Fjate en que la zona de memoria asignada a a sigue siendo la misma. El truco del
terminador ha ermitido que la cadena decrezca. Podemos conseguir tambin que crez
ca
a voluntad. . . ero siem re que no se rebase la ca acidad del vector.
Hemos re resentado las celdas a la derecha del terminador como cajas vacas,
ero
no es cierto que lo estn. Lo normal es que contengan valores arbitrarios, aunque
eso no
im orta mucho: el convenio de que la cadena termina en el rimer carcter nulo hac
e que
el resto de caracteres no se tenga en cuenta. Es osible que, en el ejem lo ante
rior, la
memoria resente realmente este as ecto:
0
1 2 3 4 5
6
7 8
9
a
t e x t o \0 a u \0 x
Por comodidad re resentaremos las celdas a la derecha del terminador con cajas v
acas,
ues no im orta en absoluto lo que contienen.
Qu ocurre si intentamos inicializar una zona de memoria reservada ara slo 10
!
chars con una cadena de longitud mayor que 9?
char a 10
Mal!
s u
e r c a l i f r a g i l i s t i c o e s
i a l i d o s o \0
Como resulta que en una variable con ca acidad ara, or ejem lo, 80 caracte
res
slo caben realmente 79 caracteres a arte del nulo, ado taremos una curiosa rctica
al
declarar variables de cadena que nos ermitir almacenar los 80 caracteres (adems d
el
nulo) sin crear una constante confusin con res ecto al nmero de caracteres que cab
en
en ellas:
include
define
80
Reservamos 81 caractere
return 0
Las cadenas se muestran con rintf y la adecuada marca de formato sin que se re
senten
dificultades es eciales. Lo que s resulta roblemtico es leer cadenas. La funcin sc
anf
resenta una seria limitacin: slo uede leer alabras, no frases. Ello nos obligar
a resentar una nueva funcin (gets). . . que se lleva fatal con scanf .
Salida con rintf
Em ecemos or considerar la funcin rintf , que muestra cadenas con la marca de f
ormato
. Aqu tienes un ejem lo de uso:
include
define
80
rintf
cadena
return 0
Al ejecutar el rograma obtienes en antalla esto:
80
rintf
cadena
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
UJI
c
102
102
rintf
rintf
cadena
cadena
return 0
80
i 0
while cadena i
rintf
i
cadena i
return 0
Este es el resultado de la ejecucin:
80
103
103
char cadena
scanf
rintf
1
cadena
caden
a
return 0
Ojo! No hemos uesto el o erador delante de cadena! Es un error? No. Con las
cadenas no hay que oner el carcter del identificador al usar scanf . Por qu? Porqu
e
scanf es era una direccin de memoria y el identificador, or la dualidad vector
untero,
es una direccin de memoria!
Recuerda: cadena 0 es un char, ero cadena, sin ms, es la direccin de memoria
en la que em ieza el vector de caracteres.
Ejecutemos el rograma e introduzcamos una alabra:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
99 Es vlida esta otra forma de leer una cadena? Prubala en tu ordenador.
include
define
80
1
cadena 0
cad
ena
return 0
...............................................................................
...
Cuando scanf recibe el valor asociado a cadena, recibe una direccin de memori
a y,
a partir de ella, deja los caracteres ledos de teclado. Debes tener en cuenta que
si los
caracteres ledos exceden la capacidad de la cadena, se producir un error de ejecuc
in.
Y por qu printf no muestra por pantalla una simple direccin de memoria cuando
ejecutamos la llamada printf
cadena ? Si e
s cierto
lo dicho, cadena es una direccin de memoria. La explicacin es que la marca
es
interpretada por printf como me pasan una direccin de memoria en la que empieza
una cadena, as que he de mostrar su contenido carcter a carcter hasta encontrar un
carcter nulo.
Lectura con gets
Hay un problema prctico con scanf : slo lee una palabra, es decir, una secuencia de
caracteres no blancos. Hagamos la prueba:
E
E
include
define
80
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
104
104
ramacin con C - UJI
Introduccin a la prog
c
UJI
1
cadena
cadena
return 0
Si al ejecutar el programa tecleamos un par de palabras, slo se muestra la primer
a:
11
1
gets a
gets b
a b
return 0
Ejecutemos el programa:
uso el
bloqueo de la ejecucin del programa. Los detalles son bastante escabrosos. Si tie
nes
curiosidad, te los mostramos en el apartado B.3.
Presentaremos en este captulo una solucin directa que debers aplicar siempre qu
e
tu programa alterne la lectura de cadenas con blancos y valores escalares (algo
muy
frecuente). La solucin consiste en:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
105
105
Overflow exploit
El manejo de cadenas C es complicado. . . y peligroso. La posibilidad de
que se almacenen ms caracteres de los que caben en una zona de memoria reservada para
una
cadena ha dado lugar a una tcnica de cracking muy comn: el overflow exploit
(que
significa aprovechamiento del desbordamiento), tambin conocido por smash the
stack
(machacar la pila).
Si un programa C lee una cadena con scanf o gets es vulnerable a este
tipo de
ataques. La idea bsica es la siguiente. Si c es una variable local a una f
uncin (en
el siguiente captulo veremos cmo), reside en una zona de memoria especial:
la pila.
Podemos desbordar la zona de memoria reservada para la cadena c escribien
do un texto
ms largo del que cabe en ella. Cuando eso ocurre, estamos ocupando memoria
en una
zona de la pila que no nos pertenece. Podemos conseguir as escribir informac
in en
una zona de la pila reservada a informacin como la direccin de retorno de l
a funcin.
El exploit se basa en asignar a la direccin de retorno el valor de una dir
eccin en
la que habremos escrito una rutina especial en cdigo mquina. Y cmo conseguimo
s
introducir una rutina en cdigo mquina en un programa ajeno? En la propia cad
ena
que provoca el desbordamiento, codificndola en binario! La rutina de cdigo
mquina
suele ser sencilla: efecta una simple llamada al sistema operativo para qu
e ejecute un
intrprete de rdenes Unix. El intrprete se ejecutar con los mismos permisos qu
e el
programa que hemos reventado. Si el programa atacado se ejecutaba con per
misos de
, habremos conseguido ejecutar un intrprete de rdenes como
. El
ordenador
es nuestro!
Y cmo podemos proteger a nuestros programas de los overflow exploit? Pu
es,
para empezar, no utilizando nunca scanf o gets directamente. Como es posi
ble leer
de teclado carcter a carcter (lo veremos en el captulo dedicado a ficheros),
podemos
definir nuestra propia funcin de lectura de cadenas: una funcin de lectura
que controle
que nunca se escribe en una zona de memoria ms informacin de la que cabe.
Dado que gets es tan vulnerable a los overflow exploit, el compilador
de C te dar
un aviso cuando la uses. No te sorprendas, pues, cuando veas un mensaje c
omo ste:
80
40
1
1
printf
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
106
106
gets linea
sscanf linea
sscanf linea
printf
gets frase
printf
gets linea
printf
printf
a b
frase
return 0
En el programa hemos definido una variable auxiliar, linea, que es una caden
a con
capacidad para 80 caracteres ms el terminador (puede resultar conveniente reserva
r ms
memoria para ella en segn qu aplicacin). Cada vez que deseamos leer un valor escala
r,
leemos en linea un texto que introduce el usuario y obtenemos el valor escalar c
on la
funcin sscanf . Dicha funcin recibe, como primer argumento, la cadena en linea; co
mo
segundo, una cadena con marcas de formato; y como tercer parmetro, la direccin de
la
variable escalar en la que queremos depositar el resultado de la lectura.
Es un proceso un tanto incmodo, pero al que tenemos que acostumbrarnos. . . d
e
momento.
Este programa, que pretende copiar una cadena en otra, parece correcto, pero no
lo es:
define
10
int main void
char original
char copia
copia
1
1
original
return 0
Si compilas el programa, obtendrs un error que te impedir obtener un ejecutable. R
ecuerda: los identificadores de vectores estticos se consideran punteros inmutable
s y, a
fin de cuentas, las cadenas son vectores estticos (ms adelante aprenderemos a usar
vectores dinmicos). Para efectuar una copia de una cadena, has de hacerlo carcter
a
carcter.
define
10
int main void
char original
char copia
int i
for i 0 i
i
copia i original i
return 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
107
107
Fjate en que el bucle recorre los 10 caracteres que realmente hay en original per
o, de
hecho, slo necesitas copiar los caracteres que hay hasta el terminador, incluyndol
e a
l.
define
10
int main void
char original
char copia
int i
1
1
for i 0 i
i
copia i
original i
if copia i
break
return 0
0
3
9
original
c a d e n a \0
0
1
2
copia
c a d e n a \0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
100 Qu problema presenta esta otra versin del mismo programa?
define
10
int main void
char original
char copia
int i
for i 0 i
if copia i
break
else
copia i
1
1
i
original i
return 0
...............................................................................
...
An podemos hacerlo mejor:
define
10
int main void
char original
char copia
int i
for i
0 original i
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
108
108
ramacin con C - UJI
Introduccin a la prog
c
UJI
copia i
copia i
original i
return 0
Ves? La condicin del for controla si hemos llegado al terminador o no. Como el
terminador no llega a copiarse, lo aadimos tan pronto finaliza el bucle. Este tip
o de
bucles, aunque perfectamente legales, pueden resultar desconcertantes.
El copiado de cadenas es una accin frecuente, as que hay funciones predefinida
s
para ello, accesibles incluyendo la cabecera
:
include
define
10
1
1
Copia el contenido de or
return 0
Ten cuidado: strcpy (abreviatura de string copy) no comprueba si el destino de la
copia
tiene capacidad suficiente para la cadena, as que puede provocar un desbordamient
o. La
funcin strcpy se limita a copiar carcter a carcter hasta llegar a un carcter nulo.
Tampoco est permitido asignar un literal de cadena a un vector de caracteres
fuera
de la zona de declaracin de variables. Es decir, este programa es incorrecto:
define
10
int main void
char a
!Mal!
return 0
Si deseas asignar un literal de cadena, tendrs que hacerlo con la ayuda de strcpy
:
include
define
10
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
101 Disea un programa que lea una cadena y copie en otra una versin encriptada.
La encriptacin convertir cada letra (del alfabeto ingls) en la que le sigue en la t
abla
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
109
109
ramacin con C - UJI
Introduccin a la prog
c
UJI
10
1
1
Copia, a lo su
return 0
Pero tampoco strncpy es perfecta. Si la cadena original tiene ms caracteres
de los que
puede almacenar la cadena destino, la copia es imperfecta: no acabar en
. De
todos modos, puedes encargarte t mismo de terminar la cadena en el ltimo carc
ter,
por si acaso:
include
define
10
1
1
1
return 0
as.
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
110
110
El convenio de terminar una cadena con el carcter nulo permite conocer fcilmente l
a
longitud de una cadena:
include
define
80
printf
gets a
i 0
while a i
i
printf
return 0
Calcular la longitud de una cadena es una operacin frecuentemente utilizada, a
s
que est predefinida en la biblioteca de tratamiento de cadenas. Si inclumos la cab
ecera
, podemos usar la funcin strlen (abreviatura de string length):
include
include
define
80
printf
gets a
l strlen a
printf
return 0
Has de ser consciente de qu hace strlen: lo mismo que haca el primer programa,
es
decir, recorrer la cadena de izquierda a derecha incrementando un contador hasta
llegar al
terminador nulo. Esto implica que tarde tanto ms cuanto ms larga sea la cadena. Ha
s de
estar al tanto, pues, de la fuente de ineficiencia que puede suponer utilizar di
rectamente
strlen en lugares crticos como los bucles. Por ejemplo, esta funcin cuenta las voc
ales
minsculas de una cadena leda por teclado:
include
include
define
80
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
111
111
El estilo C
El programa que hemos presentado para calcular la longitud de una cadena e
s un
programa C correcto, pero no es as como un programador C expresara esa misma
idea.
No hace falta que el bucle incluya sentencia alguna!:
include
define
80
printf
gets a
i 0
while a i
y sentencia alguna en el while.
printf
Observa que no ha
i 1
return 0
El operador de postincremento permite aumentar en uno el valor de i justo
despus de
consultar el valor de a i . Eso s, hemos tenido que modificar el valor most
rado como
longitud, pues ahora i acaba valiendo uno ms.
Es ms, ni siquiera es necesario efectuar comparacin alguna. El bucle se p
uede
sustituir por este otro:
i 0
while a i
El bucle funciona correctamente porque el valor
significa falso cua
ndo se interpreta como valor lgico. El bucle itera, pues, hasta llegar a un valor falso
, es decir, a un
terminador.
0 0 0 0 0
i
int i contador
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
112
112
while o for
Los bucles while pueden sustituirse muchas veces por bucles for equivalen
tes, bastante
ms compactos:
include
define
80
printf
gets a
for i 0 a i
ncia alguna en el for.
printf
return 0
Tambin aqu es superflua la comparacin:
for i 0 a i
Todas las versiones del programa que hemos presentado son equivalentes
. Escoger
una u otra es cuestin de estilo.
a i
printf
gets a
contador 0
for i 0 i strlen a i
if a i
a i
a i
contador
printf
a i
contador
return 0
Pero tiene un problema de eficiencia. Con cada iteracin del bucle for se llama a
strlen
y strlen tarda un tiempo proporcional a la longitud de la cadena. Si la cadena t
iene,
pongamos, 60 caracteres, se llamar a strlen 60 veces para efectuar la comparacin,
y
para cada llamada, strlen tardar unos 60 pasos en devolver lo mismo: el valor 60.
Esta
nueva versin del mismo programa no presenta ese inconveniente:
include
include
define
80
113
113
a i
a i
con
tador
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
104 Disea un programa que lea una cadena y la invierta.
105 Disea un programa que lea una palabra y determine si es o no es palndromo.
106 Disea un programa que lea una frase y determine si es o no es palndromo.
Recuerda que los espacios en blanco y los signos de puntuacin no se deben tener e
n
cuenta a la hora de determinar si la frase es palndromo.
107 Escribe un programa C que lea dos cadenas y muestre el ndice del carcter de
la primera cadena en el que empieza, por primera vez, la segunda cadena. Si la s
egunda
cadena no est contenida en la primera, el programa nos lo har saber.
(Ejemplo: si la primera cadena es
y la segu
nda es
, el programa mostrar el valor 3.)
108 Escribe un programa C que lea dos cadenas y muestre el ndice del carcter de
la primera cadena en el que empieza por ltima vez una aparicin de la segunda caden
a.
Si la segunda cadena no est contenida en la primera, el programa nos lo har saber.
(Ejemplo: si la primera cadena es
y la segu
nda es
, el programa mostrar el valor 16.)
109 Escribe un programa que lea una lnea y haga una copia de ella eliminando los
espacios en blanco que haya al principio y al final de la misma.
110 Escribe un programa que lea repetidamente lneas con el nombre completo de
una persona. Para cada persona, guardar temporalmente en una cadena sus iniciales
(las
letras con maysculas) separadas por puntos y espacios en blanco y mostrar el resul
tado
en pantalla. El programa finalizar cuando el usuario escriba una lnea en blanco.
111 Disea un programa C que lea un entero n y una cadena a y muestre por pantalla
el valor (en base 10) de la cadena a si se interpreta como un nmero en base n. El
valor
de n debe estar comprendido entre 2 y 16. Si la cadena a contiene un carcter que
no
corresponde a un dgito en base n, notificar el error y no efectuar clculo alguno.
Ejemplos:
si a es
si a es
;
si a es
112 Disea un programa C que lea una lnea y muestre por pantalla el nmero de
palabras que hay en ella.
...............................................................................
...
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
114
114
ramacin con C - UJI
Introduccin a la prog
c
UJI
80
printf
gets a
printf
gets b
longa strlen a
longb strlen b
for i 0 i longb i
a longa i b i
a longa longb
printf
return 0
Pero es mejor usar la funcin de librera strcat (por string concatenate):
include
include
define
80
printf
gets a
printf
gets b
strcat a b
b
printf
return 0
Si quieres dejar el resultado de la concatenacin en una variable distinta, de
bers
actuar en dos pasos:
include
include
define
int main void
80
char a
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
115
115
printf
gets a
printf
gets b
strcpy c a
strcat c b
printf
return 0
Recuerda que es responsabilidad del programador asegurarse de que la cadena
que
recibe la concatenacin dispone de capacidad suficiente para almacenar la cadena r
esultante.
Por cierto, el operador de repeticin de cadenas que encontrbamos en Python (op
erador ) no est disponible en C ni hay funcin predefinida que lo proporcione.
Un carcter no es una cadena
Un error frecuente es intentar aadir un carcter a una cadena con strcat o a
signrselo
como nico carcter con strcpy:
char linea 10
char caracter
!
!
strcat linea caracter
strcpy linea
Mal!
Mal!
Recuerda: los dos datos de strcat y strcpy han de ser cadenas y no es ace
ptable que
uno de ellos sea un carcter.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
113 Escribe un programa C que lea el nombre y los dos apellidos de una persona e
n
tres cadenas. A continuacin, el programa formar una sla cadena en la que aparezcan
el nombre y los apellidos separados por espacios en blanco.
114 Escribe un programa C que lea un verbo regular de la primera conjugacin y
lo muestre por pantalla conjugado en presente de indicativo. Por ejemplo, si lee
el texto
, mostrar por pantalla:
...............................................................................
...
No slo
contiene funciones tiles ara el tratamiento de cadenas. En
encontrars unas funciones que ermiten hacer cmodamente reguntas acerca de los
caracteres, como si son maysculas, minsculas, dgitos, etc:
isalnum carcter : devuelve cierto (un entero cualquiera distinto de cer
o) si carcter
es una letra o dgito, y falso (el valor entero 0) en caso contrario,
isal ha carcter : devuelve cierto si carcter es una letra, y falso en ca
so contrario,
n tabu
ontrario,
iss ace carcter : devuelve cierto si carcter es un es acio en blanco, un
salto de
lnea, un retorno de carro, un tabulador, etc., y falso en caso contrari
o,
islower carcter : devuelve cierto si carcter es una letra minscula, y fal
so en
caso contrario,
isu er carcter : devuelve cierto si carcter es una letra mayscula, y fal
so en
caso contrario.
Tambin en
encontrars un ar de funciones tiles ara convertir caracteres d
e
minscula a mayscula y viceversa:
tou er carcter : devuelve la mayscula asociada a carcter, si la tiene; s
i no,
devuelve el mismo carcter,
tolower carcter : devuelve la minscula asociada a carcter, si la tiene; s
i no,
devuelve el mismo carcter.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
118 Qu problema presenta este programa?
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
117
117
ramacin con C - UJI
Introduccin a la prog
c
UJI
include
include
int main void
char b 2
if isalpha b
printf
else
printf
return 0
...............................................................................
...
sprintf
Hay una funcin que puede simplificar notablemente la creacin de cadenas cuyo contenido se debe calcular a partir de uno o ms valores: sprintf , disponible incluy
endo la
cabecera
(se trata, en cierto modo, de la operacin complementaria de s
scanf ).
La funcin sprintf se comporta como printf , salvo por un detalle: no escribe texto
en
pantalla, sino que lo almacena en una cadena.
Fjate en este ejemplo:
include
define
80
1
1
1
sprintf c
printf
a b
return 0
Si ejecutas el programa aparecer lo siguiente en pantalla:
Como puedes ver, se ha asignado a c el valor de a seguido de un espacio en b
lanco y
de la cadena b. Podramos haber conseguido el mismo efecto con llamadas a strcpy c
a ,
strcat c
y strcat c b , pero sprintf resulta ms legible y no cuesta mucho
aprender a usarla, pues ya sabemos usar printf . No olvides que t eres responsable de
que la
informacin que se almacena en c quepa.
En Python hay una accin anloga al sprintf de C: la asignacin a una variable
de una cadena formada con el operador de formato. El mismo programa se podra habe
r
118
118
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
119 Qu almacena en la cadena a la siguiente sentencia?
sprintf a
1 48 2
120 Escribe un programa que pida el nombre y los dos apellidos de una persona. C
ada
uno de esos tres datos debe almacenarse en una variable independiente. A continu
acin,
el programa crear y mostrar una nueva cadena con los dos apellidos y el nombre
(separado de los apellidos por una coma). Por ejemplo, Juan Prez Lpez dar lugar a l
a
cadena
.
...............................................................................
...
Vamos a implementar un programa que lee por teclado una lnea de texto y muestra
por pantalla una cadena en la que las secuencias de blancos de la cadena origina
l
(espacios en blanco, tabuladores, etc.) se han sustituido por un slo espacio en b
lanco.
Si, por ejemplo, el programa lee la cadena
,
mostrar por pantalla la cadena normalizada
.
include
include
include
define
80
isspace a i
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
121 Modifica
para que elimine, si
los hay, los blancos inicial y final
de la cadena normalizada.
122 Haz un programa que lea una frase y construya una cadena que slo contenga
sus letras minsculas o maysculas en el mismo orden con que aparecen en la frase.
123 Haz un programa que lea una frase y construya una cadena que slo contenga
sus letras minsculas o maysculas en el mismo orden con que aparecen en la frase, p
ero
sin repetir ninguna.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
119
119
ramacin con C - UJI
Introduccin a la prog
c
UJI
124 Lee un texto por teclado (con un mximo de 1000 caracteres) y muestra por
pantalla la frecuencia de aparicin de cada una de las letras del alfabeto (consid
era
nicamente letras del alfabeto ingls), sin distinguir entre letras maysculas y minscu
las
(una aparicin de la letra y otra de la letra cuentan como dos ocurrencias de la l
etra
).
...............................................................................
...
for i 0 i 10 i
for j 0 j 5 j
a i j 0
for i 0 i 3 i
for j 0 j 2 j
for k 0 k 4 k
b i j k 0.0
return 0
Tambin puedes inicializar explcitamente un vector multidimensional:
int c 3
3
1 0 0
0 1 0
0 0 1
decir,
36 bytes. He aqu cmo se disponen las celdas en memoria, suponiendo que la zona de
memoria asignada empieza en la direccin 1000:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
120
120
996:
1000:
a[0][0]
1004:
a[0][1]
1008:
a[0][2]
1012:
a[1][0]
1016:
a[1][1]
1020:
a[1][2]
1024:
a[2][0]
1028:
a[2][1]
1032:
a[2][2]
1036:
Cuando accedemos a un elemento a i j , C sabe a qu celda de memoria acceder
sumando a la direccin de a el valor i 3 j 4 (el 4 es el tamao de un int y el 3 es
el
nmero de columnas).
Aun siendo conscientes de cmo representa C la memoria, nosotros trabajaremos c
on
una representacin de una matriz de 3 3 como sta:
0
1
2
0
a
1
2
i
j
10 i j
for j 0 j
printf
j
a 0
return 0
...............................................................................
...
b
p
Lectura de la matriz a
for i 0 i
i
for j 0 j
j
printf
a i
Lectura de la matriz b
for i 0 i
i
for j 0 j
j
printf
b i
i j
scanf
i j
scanf
j
Clculo de la suma
for i 0 i
i
for j 0 j
j
s i j
a i j
Clculo del producto
for i 0 i
i
for j 0 j
j
p i j
0.0
for k 0 k
k
p i j
a i k
b i
b k
122
122
a ver este programa y lo modificaremos para que use funciones definidas por noso
tros.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
126 En una estacin meteorolgica registramos la temperatura (en grados centgrados)
cada hora durante una semana. Almacenamos el resultado en una matriz de 7 24 (ca
da
fila de la matriz contiene las 24 mediciones de un da). Disea un programa que lea
los
datos por teclado y muestre:
La mxima y mnima temperaturas de la semana.
La mxima y mnima temperaturas de cada da.
La temperatura media de la semana.
La temperatura media de cada da.
El nmero de das en los que la temperatura media fue superior a 30 grados
.
127 Representamos diez ciudades con nmeros del 0 al 9. Cuando hay carretera que
une directamente a dos ciudades i y j, almacenamos su distancia en kilmetros en l
a celda
d i j de una matriz de 10 10 enteros. Si no hay carretera entre ambas ciudades,
el valor almacenado en su celda de d es cero. Nos suministran un vector en el qu
e se
describe un trayecto que pasa por las 10 ciudades. Determina si se trata de un t
rayecto
vlido (las dos ciudades de todo par consecutivo estn unidas por un tramo de carret
era)
y, en tal caso, devuelve el nmero de kilmetros del trayecto. Si el trayecto no es
vlido,
indcalo con un mensaje por pantalla.
La matriz de distancias debers inicializarla explcitamente al declararla. El v
ector
con el recorrido de ciudades debers leerlo de teclado.
128 Disea un programa que lea los elementos de una matriz de 4 5 flotantes
y genere un vector de talla 4 en el que cada elemento contenga el sumatorio de l
os
elementos de cada fila. El programa debe mostrar la matriz original y el vector
en este
formato (evidentemente, los valores deben ser los que correspondan a lo introduc
ido por
el usuario):
...............................................................................
...
El programa que hemos presentado adolece de un serio inconveniente si nuest
ro objetivo era construir un programa general para multiplicar matrices: slo puede traba
jar
con matrices de TALLA TALLA, o sea, de 3 3. Y si quisiramos trabajar con matrices
de tamaos arbitrarios? El primer problema al que nos enfrentaramos es el de que la
s
matrices han de tener una talla mxima: no podemos, con lo que sabemos por ahora,
reservar un espacio de memoria para las matrices que dependa de datos que nos sumi
nistra
el usuario en tiempo de ejecucin. Usaremos, pues, una constante
con u
n valor
razonablemente grande: pongamos 10. Ello permitir trabajar con matrices con un nme
ro
de filas y columnas menor o igual que 10, aunque ser a costa de malgastar memoria
.
include
define
10
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
123
123
ramacin con C - UJI
Introduccin a la prog
c
UJI
b
p
0
2
9
0
a
1
2
3
4
las a
5
6
7
8
9
10
Lectura de la matriz a
printf
filas a
printf
columnas a
scanf
scanf
for i 0 i filas a i
for j 0 j columnas a j
printf
scanf
a i
j
i j
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
124
124
I
cUJI
include
define
10
a i
Lectura de la matriz a
printf
printf
for i 0 i filas a i
for j 0 j columnas a j
printf
j
scanf
scanf
filas a
columnas a
i j
scanf
Lectura de la matriz b
Clculo de la suma
if filas a
filas b
columnas a
filas s filas a
columnas s columnas a
for i 0 i filas s i
for j 0 j filas s j
s i j
a i j
b i j
columnas b
10
b
p
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
125
125
scanf
filas
printf
scanf
colum
a
nas a
for i 0 i filas a i
for j 0 j columnas a j
printf
a i j
i j
scanf
Lectura de la matriz b
printf
scanf
filas
printf
scanf
colum
b
nas b
for i 0 i filas b i
for j 0 j columnas b j
printf
b i j
Clculo de la suma
if filas a
filas b
columnas a
filas s filas a
columnas s columnas a
for i 0 i filas s i
for j 0 j filas s j
s i j
a i j
b i j
Clculo del producto
if columnas a
filas b
filas p filas a
columnas p columnas b
for i 0 i filas p i
for j 0 j columnas p j
p i j
0.0
for k 0 k columnas a k
p i j
a i k b k
i j
columnas b
scanf
if columnas a
filas b
printf
for i 0 i filas p i
for j 0 j columnas p j
printf
p i j
Introduccin
Andrs aMarzal/Isabel
la programacin con -C ISBN: 978-84-693-0143-2
Gracia
6
12
12
printf
else
printf
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
129 Extiende el programa de calculadora matricial para efectuar las siguientes o
peraciones:
Producto de una matriz por un escalar. (La matriz resultante tiene la
misma dimensin que la original y cada elemento se obtiene multiplicando el esca
lar por la
celda correspondiente de la matriz original.)
Transpuesta de una matriz. (La transpuesta de una matriz de n m es una
matriz
de m n en la que el elemento de la fila i y columna j tiene el mismo v
alor que
el que ocupa la celda de la fila j y columna i en la matriz original.)
130 Una matriz tiene un valle si el valor de una de sus celdas es menor que el d
e
cualquiera de sus 8 celdas vecinas. Disea un programa que lea una matriz (el usua
rio
te indicar de cuntas filas y columnas) y nos diga si la matriz tiene un valle o no
. En
caso afirmativo, nos mostrar en pantalla las coordenadas de todos los valles, sus
valores
y el de sus celdas vecinas.
La matriz debe tener un nmero de filas y columnas mayor o igual que 3 y menor
o igual que 10. Las casillas que no tienen 8 vecinos no se consideran candidatas
a ser
valle (pues no tienen 8 vecinos).
Aqu tienes un ejemplo de la salida esperada para esta matriz de 4 5:
1 2 9 5 5
3 2 9 4 5
6 1 8 7 6
6 3 8 0 9
Para la misma matriz del ejemplo del ejercicio anterior se obtendra esta sali
da:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
127
127
ramacin con C - UJI
Introduccin a la prog
c
UJI
...............................................................................
...
Por lo dicho hasta el momento, est claro que un vector de cadenas es una matriz d
e
caracteres. Este fragmento de programa, por ejemplo, declara un vector de 10 cad
enas
cuya longitud es menor o igual que 80:
define
80
char v 10
1
Cada fila de la matriz es una cadena y, como tal, debe terminar en un carcter nul
o.
Este fragmento declara e inicializa un vector de tres cadenas:
define
80
char v 3
80
for i 0 i 3 i
printf
gets v i
printf
v i
return 0
Vamos a desarrollar un programa til que hace uso de un vector de caracteres:
un
pequeo corrector ortogrfico para ingls. El programa dispondr de una lista de palabra
s
en ingls (que encontrars en la pgina web de la asignatura, en el fichero
),
solicitar al usuario que introduzca por teclado un texto en ingls y le informar de
qu palabras considera errneas por no estar includas en su diccionario. Aqu tienes un
ejemplo de uso del programa:
Introduccin a la programacin
Andrs Marzal/Isabel
con- CISBN: 978-84-693-0143-2
Gracia
c
128
128
El fichero
s ahora las primeras y
ltimas lneas:
define
define
char diccionario
45378
28
1
129
129
c
UJI
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
define
define
define
1000
100
100
strlen frase
isalpha frase i
palabra palabras j
frase i
palabra palabras j
El terminador es responsabilida
d nuestra.
Incrementar el contador de palabras.
palabras
if palabras
Y finalizar si ya no caben ms p
alabras
break
Saltarse las no-letras que separan esta palabra de la siguiente
(si las hay).
while i lonfrase
isalpha frase i
for i 0 i palabras i
printf
palabra i
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
132 Un programador, al copiar el programa, ha sustituido la lnea que reza as:
while i lonfrase
isalpha frase i
i
Saltarse las no-letras iniciales.
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
130
130
c
UJI
Introduccin a la prog
ramacin con C - UJI
isalpha frase i
isalpha frase i
palabra palabras
isalpha frase i
palabra palabras
?
Estn todas las palabras en el diccionario?
for i 0 i palabras i
encontrada 0
?
for j 0 j
j
if strcmp palabra i diccionario j
Es palabra i igual que diccionario j ?
encontrada 1
break
if
palabra i
return 0
encontrada
printf
Ten en cuenta lo que hace strcmp: recorre las dos cadenas hasta encontrar alguna
diferencia entre ellas o concluir que son idnticas. Es, por tanto, una operacin bastante
costosa
en tiempo. Podemos reducir el nmero de comparaciones? Claro! Como el diccionario
est ordenado alfabticamente, podemos abortar el recorrido cuando llegamos a una vo
z
del diccionario posterior (segn el orden alfabtico) a la que buscamos:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
131
131
?
Estn todas las palabras en el diccionario?
for i 0 i palabras i
encontrada 0
?
for j 0 j
j
Es palabra i igual que diccionario j ?
if strcmp palabra i diccionario j
0
encontrada 1
break
else if strcmp palabra i
?palabra i diccionario j ?
break
if encontrada
printf
labra i
diccionario j
pa
return 0
Con esta mejora hemos intentado reducir a la mitad el nmero de comparaciones con
cadenas del diccionario, pero no hemos logrado nuestro objetivo: aunque, en prome
dio,
efectuamos comparaciones con la mitad de las palabras del diccionario, estamos l
lamando
dos veces a
! Es mejor almacenar el resultado de una sola llamada a
en
una variable:
?
Estn todas las palabras en el diccionario?
for i 0 i palabras i
encontrada 0
for j 0 j
j
?
comparacion strcmp palabra i diccionario j
if comparacion
0
Es palabra i igual que dicciona
rio j ?
encontrada 1
break
?
else if comparacion
or que diccionario j ?
break
if
encontrada
printf
Es palabra i men
pa
labra i
return 0
(Recuerda declarar comparacion como variable de tipo entero.)
El diccionario tiene 45378 palabras. En promedio efectuamos, pues, 22689 com
paraciones por cada palabra de la frase. Mmmm. An podemos hacerlo mejor. Si la lista
est
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
32
1
13
for i 0 i palabras i
encontrada 0
izquierda 0
derecha
while izquierda derecha
j
izquierda derecha 2
comparacion strcmp palabra i
dicci
onario j
if comparacion 0
derecha j
else if comparacion 0
izquierda j 1
else
encontrada 1
break
if
encontrada
printf
palabra i
return 0
(Debes declarar derecha e izquierda como enteros.)
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
136 Escribe un programa C que lea un texto (de longitud menor que 1000) y obteng
a
un vector de cadenas en el que cada elemento es una palabra distinta del texto (
con un
mximo de 500 palabras). Muestra el contenido del vector por pantalla.
137 Modifica el programa del ejercicio anterior para que el vector de palabras s
e
muestre en pantalla ordenado alfabticamente. Debers utilizar el mtodo de la burbuja
para ordenar el vector.
138
Representamos la baraja de cartas con un vector de cadenas. Los palos s
on
,
,
y
. Las cartas con nmeros entre 2 y 9
se describen con el texto
(ejemplo:
,
). Los ases
se describen con la cadena
, las sotas con
,
los caballos
con
y los reyes con
.
Escribe un programa que genere la descripcin de las 40 cartas de la baraja. U
sa
bucles siempre que puedas y compn las diferentes partes de cada descripcin con str
cat
o sprintf . A continuacin, baraja las cartas utilizando para ello el generador de
nmeros
aleatorios y muestra el resultado por pantalla.
139 Disea un programa de ayuda al diagnstico de enfermedades. En nuestra base
de datos hemos registrado 10 enfermedades y 10 sntomas:
char enfermedades 10
20
char sintomas 10 20
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
133
133
ramacin con C - UJI
Introduccin a la prog
c
UJI
El programa leer una lnea y mostrar por pantalla su traduccin a cdigo Morse. Ten
en cuenta que las letras se deben separar por pausas (un espacio blanco) y las p
alabras
por pausas largas (tres espacios blancos). Los acentos no se tendrn en cuenta al
efectuar
la traduccin (la letra , por ejemplo, se representar con ) y la letra
se mos
trar
como una
. Los signos que no aparecen en la tabla (comas, admiraciones, et
c.) no se
traducirn, excepcin hecha del punto, que se traduce por la palabra
. Te con
viene
pasar la cadena a maysculas (o efectuar esta transformacin sobre la marcha), pues
la
tabla Morse slo recoge las letras maysculas y los dgitos.
Por ejemplo, la cadena
se traducir por
................................................................................
..
Los vectores permiten agrupar varios elementos de un mismo tipo. Cada elemento d
e un
vector es accesible a travs de un ndice.
En ocasiones necesitars agrupar datos de diferentes tipos y/o preferirs accede
r a
diferentes elementos de un grupo de datos a travs de un identificador, no de un nd
ice. Los
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
134
134
de 9 caracteres).
persona antes de la aparicin de main:
40
9
struct Persona
char nombre
1
int edad
char dni
1
Fjate en el punto y coma: es fcil olvidarse de ponerlo.
La definicin de un registro introduce un nuevo tipo de datos en nuestro programa.
En el
ejemplo hemos definido el tipo struct Persona (la palabra struct forma parte del
nombre
del tipo). Ahora puedes declarar variables de tipo struct Persona as:
struct Persona pepe juan ana
En tu programa puedes acceder a cada uno de los campos de una variable de tipo s
truct
separando con un punto el identificador de la variable del correspondiente ident
ificador
del campo. Por ejemplo, pepe edad es la edad de Pepe (un entero que ocupa cuatro
bytes), juan nombre es el nombre de Juan (una cadena), y ana dni 8 es la letra d
el DNI
de Ana (un carcter).
Cada variable de tipo struct Persona ocupa, en principio, 55 bytes: 41 por e
l nombre,
4 por la edad y 10 por el DNI. (Si quieres saber por qu hemos resaltado lo de en
principio, lee el cuadro Alineamientos.)
Este programa ilustra cmo acceder a los campos de un registro leyendo por tec
lado
sus valores y mostrando por pantalla diferentes informaciones almacenadas en l:
include
include
define
define
struct Persona
char nombre
int edad
char dni
int main void
40
9
1
1
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
135
135
Alineamientos
El operador sizeof devuelve el tamao en bytes de un tipo o variable. Analiz
a este
programa:
include
struct Registro
char a
int b
int main void
printf
sizeof str
uct Registro
return 0
Parece que vaya a mostrar en pantalla el mensaje
, pues un
char ocupa 1 byte y un int ocupa 4. Pero no es as:
printf
printf
ejemplo edad
printf
printf
printf
printf
printf
longitud strlen ejemplo nombre
for i 0 i longitud i
if ejemplo nombre i
ejemplo nombre i
printf
ejemplo nombre i
ejemplo nombre
ejemplo edad
ejemplo dni
printf
printf
longitud strlen ejemplo dni
if ejemplo dni longitud 1
ejemplo dni longitud 1
printf
else
printf
ejemplo dni longitud 1
return 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
6
136
13
Los registros pueden copiarse ntegramente sin mayor problema. Este programa,
por
ejemplo, copia el contenido de un registro en otro y pasa a minsculas el nombre d
e la
copia:
include
include
include
define
define
40
9
struct Persona
char nombre
int edad
char dni
1
1
Copia
una nombre
una edad
una dni
printf
printf
printf
copia nombre
copia edad
copia dni
return 0
Observa que la copia se efecta incluso cuando los elementos del registro son
vectores.
O sea, copiar vectores con una mera asignacin est prohibido, pero copiar registros
es
posible. Un poco incoherente, no?
Por otra parte, no puedes comparar registros. Este programa, por ejemplo, ef
ecta una
copia de un registro en otro para, a continuacin, intentar decirnos si ambos son
iguales
o no:
E
E
include
define
define
40
9
struct Persona
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
137
137
char nombre
int edad
char dni
1
1
printf
una
edad
copia
una
if copia
printf
else
printf
Copia
una
Comparacin ilegal.
return 0
Pero ni siquiera es posible compilarlo. La lnea 24 contiene un error que el compi
lador
seala como
, o sea, operandos invlidos para la
operacin binaria . Entonces, cmo podemos decidir si dos registros son iguales?
Comprobando la igualdad de cada uno de los campos de un registro con el correspo
ndiente
campo del otro:
include
define
define
40
9
struct Persona
char nombre
int edad
char dni
1
1
printf
una
edad
copia
una
Copia
copia edad
na edad
strcmp copia dni una dni 0
printf
else
printf
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
138
138
return 0
struct Algo
struct Algo
a
b
2.0
x 1 nombre
struct Algo
nombre
struct Algo
2.
0
1
dx 1
strcpy d nombre
2.0
d y 2.0
Los vectores estticos tienen una talla fija. Cuando necesitamos un vector cuya ta
lla vara
o no se conoce hasta iniciada la ejecucin del programa usamos un truco: definimos
un vector cuya talla sea suficientemente grande para la tarea que vamos a aborda
r y
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
cUJI
Introduccin a la programacin con C -UJI
139
139
mantenemos la talla real en una variable. Lo hemos hecho con el programa que calcu
la
algunas estadsticas con una serie de edades: definamos un vector edad con capacida
d
para almacenar la edad de
y una variable personas, cuyo valor si
empre era
menor o igual que
, nos indicaba cuntos elementos del vector cont
enan
realmente datos. Hay algo poco elegante en esa solucin: las variables edad y pers
onas
son variables independientes, que no estn relacionadas entre s en el programa (sal
vo
por el hecho de que nosotros sabemos que s lo estn). Una solucin ms elegante pasa
por crear un registro que contenga el nmero de personas y, en un vector, las edad
es.
He aqu el programa que ya te presentamos en su momento convenientemente modificad
o
segn este nuevo principio de diseo:
include
include
define
20
struct ListaEdades
int edad
edades.
int talla
Lectura de edades
personas talla 0
do
printf
personas talla 1
scanf
personas edad personas talla
personas talla
while personas talla
personas edad personas talla
0
personas talla
if
personas talla 0
Clculo de la media
suma edad 0
for i 0 i personas talla i
suma edad
personas edad i
media suma edad
float personas talla
Clculo de la desviacion tpica
suma desviacion 0.0
for i 0 i personas talla i
suma desviacion
personas edad i media
perso
nas edad i
media
desviacion sqrt suma desviacion personas talla
Clculo de la moda
for i 0 i personas talla 1 i
Ordenacin mediante burbuja.
for j 0 j personas talla i j
if personas edad j
personas edad j 1
aux personas edad j
personas edad j personas edad j 1
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
140
140
personas edad j 1
aux
frecuencia 0
frecuencia moda 0
moda
1
for i 0 i personas talla 1 i
if personas edad i
personas edad i 1
if
frecuencia frecuencia moda
frecuencia moda frecuencia
moda personas edad i
else
frecuencia
Clculo de la mediana
mediana personas edad personas talla 2
Impresin de resultados
printf
printf
printf
printf
media
desviacion
moda
mediana
else
printf
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
143 Modifica el programa de clculo con polinomios que sirvi de ejemplo en el
apartado 2.1.5 para representar los polinomios mediante registros. Cada registro
contendr
dos campos: el grado del polinomio y el vector con los coeficientes.
...............................................................................
...
Hay mtodos estadsticos que permiten obtener una recta que se ajusta de forma ptima
a una serie de puntos en el plano.
y = mx + b
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
141
141
ramacin con C - UJI
Introduccin a la prog
c
UJI
i=1 xi yi
.
Las frmulas asustan un oco, ero no contienen ms que sumatorios. El rograma que
vamos a escribir lee una serie de untos (con un nmero mximo de, ongamos, 1000),
y
muestra los valores de m y b.
Modelaremos los untos con un registro:
struct Punto
float x y
El vector de untos, al que en rinci io denominaremos , tendr talla 1000:
define
1000
struct Punto
Pero 1000 es el nmero mximo de untos. El nmero de untos dis onibles efectivamente
ser menor o igual y su valor deber estar accesible en alguna variable. Olvidmonos d
el
vector : nos conviene definir un registro en el que se almacenen vector y talla
real del
vector.
struct ListaPuntos
struct Punto unto
int talla
Observa que estamos anidando structs.
Necesitamos ahora una variable del ti o que hemos definido:
include
define
1000
struct Punto
float x y
struct ListaPuntos
struct Punto unto
int talla
int main void
struct ListaPuntos lista
Reflexionemos brevemente sobre cmo
able
lista:
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
Introduccin a la rogramacin con C UJI
cUJI
142
142
Ex resin
lista
Puntos. Contiene un vector
Ti o y significado
Es un valor de ti o struct Lista
de 1000 untos y un entero.
Es un entero. Indica cuntos eleme
lista talla
ntos del vector contie
nen informacin.
Es un vector de 1000 valores de
lista unto
ti o struct Punto.
lista unto 0
y es de ti o struct Punto,
Es el
vector. Su ti o es float.
Error! Si lista unto es un vecto
lista unto x
r, no odemos acceder
al cam o x.
Error! Si lo anterior era incorre
lista unto x 0
cto, sto lo es an ms.
lista unto 0 x
erador de inde
lista 0 unto
vector, as que no uedes
1000
resolver
hayan
de cl
untos
float m b
int i
Lectura de untos
rintf
for i 0 i lista talla i
rintf
lista unto i x
rintf
lista unto i y
scanf
lista talla
i
scanf
scanf
143
c
UJI
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la rogramacin con C UJI
143
lista unto i y
sxx 0.0
for i 0 i lista talla i
sxx lista unto i x
lista unto i x
struct Coleccion
struct CompactDisc cd
int cantidad
struct Coleccion mis cds
Nuestro programa permitir efectuar las siguientes acciones:
Aadir un CD a la base de datos.
Listar toda la base de datos.
Listar los CDs de un intrprete.
Suprimir un CD dado su ttulo y su intrprete.
(El programa no resultar muy til hasta que aprendamos a utilizar ficheros en C, pu
es
al finalizar cada ejecucin se pierde toda la informacin registrada.)
He aqu el programa completo:
include
include
define
80
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
145
145
define
define
define
1000
80
40
enum
struct Tiempo
int minutos
int segundos
struct CompactDisc
char titulo
1
char interprete
struct Tiempo duracion
int anyo
struct Coleccion
struct CompactDisc cd
int cantidad
int main void
struct Coleccion mis cds
int opcion i j
char titulo
1
interprete
char linea
1
scanf.
Inicializacin de la coleccin.
mis cds cantidad 0
Bucle principal: men de opciones.
do
do
printf
printf
printf
printf
printf
printf
printf
printf
gets linea sscanf linea
if opcion 1
opcion 5
printf
while opcion 1
opcion 5
switch opcion
case Anyadir
Aadir un CD.
if mis cds cantidad
printf
else
printf
gets mis cds cd mis cds cantidad titulo
printf
gets mis cds cd mis cds cantidad interprete
opcion
printf
Introduccin
Andrs aMarzal/Isabel
la programacin con -C ISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
146
146
gets linea
sscanf linea
printf
gets linea
sscanf linea
printf
gets linea
sscanf linea
idad anyo
mis cds cantidad
break
case ListadoCompleto
Listar todo.
for i 0 i mis cds cantidad i
printf
mis cds cd i titulo
mis cds cd i interprete
mis cds cd i duracion minutos
mis cds cd i duracion segundos
mis cds cd i anyo
break
case ListadoPorInterprete
Listar por intrprete.
printf
gets interprete
for i 0 i mis cds cantidad i
if strcmp interprete mis cds cd i interprete
0
printf
mis cds
mis cds cd
mis cds cd
mis cds cd
mis cds cd
i
cd i titulo
i interprete
i duracion minutos
i duracion segundos
i anyo
break
case Suprimir
Suprimir CD.
printf
gets titulo
printf
gets interprete
for i 0 i mis cds cantidad i
if strcmp titulo mis cds cd i titulo
0
strcmp interprete mis cds cd i interprete
0
break
if i mis cds cantidad
for j i 1 j mis cds cantidad j
mis cds cd j 1
mis cds cd j
mis cds cantidad
break
while opcion
printf
return 0
Salir
147
147
struct CompactDisc cd
int cantidad
mis cds
Declara la variable mis cds como de tipo struct Coleccion.
Apuntemos ahora cmo enriquecer nuestro programa de gestin de una coleccin de
discos compactos almacenando, adems, las canciones de cada disco. Empezaremos por
definir un nuevo registro: el que modela una cancin. De cada cancin nos interesa e
l
ttulo, el autor y la duracin:
struct Cancion
char titulo
1
char autor
1
struct Tiempo duracion
Hemos de modificar el registro struct CompactDisc para que almacene hasta, d
igamos,
20 canciones:
define
20
struct CompactDisc
char titulo
1
char interprete
1
struct Tiempo duracion
int anyo
struct Cancion cancion
Vector de canciones.
int canciones
Nmero de canciones que realmente hay.
Cmo leemos ahora un disco compacto? Aqu tienes, convenientemente modificada,
la porcin del programa que se encarga de ello:
int main void
int segundos
switch opcion
case Anyadir
Aadir un CD.
if mis cds cantidad
printf
else
printf
gets mis cds cd mis cds cantidad titulo
printf
gets mis cds cd mis cds cantidad interprete
printf
gets linea sscanf linea
mis cds cd mis cds cantidad
anyo
do
printf
gets linea
sscanf linea
ones
while mis cds cd mis cds cantidad canciones
for i 0 i mis cds cd mis cds cantidad canciones i
printf
i
gets mis cds cd mis cds cantidad cancion i titulo
printf
i
148
148
gets linea
sscanf linea
mis cds cd mis cds cantidad cancion i dur
acion minutos
printf
gets linea
sscanf linea
mis cds cd mis cds cantidad cancion i dur
acion segundos
segundos 0
for i 0 i mis cds cd mis cds cantidad canciones i
segundos 60 mis cds cd mis cds cantidad cancion i du
racion minutos
mis cds cd mis cds cantidad cancion i du
racion segundos
mis cds cd mis cds cantidad duracion minutos segundos 6
0
mis cds cd mis cds cantidad duracion segundos segundos
60
mis cds cantidad
break
Observa cmo se calcula ahora la duracin del compacto como suma de las duraciones
de todas sus canciones.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
146 Disea un programa C que gestione una agenda telefnica. Cada entrada de la
agenda contiene el nombre de una persona y hasta 10 nmeros de telfono. El programa permitir aadir nuevas entradas a la agenda y nuevos telfonos a una entrada ya
existente. El men del programa permitir, adems, borrar entradas de la agenda, borra
r
nmeros de telfono concretos de una entrada y efectuar bsquedas por las primeras
letras del nombre. (Si, por ejemplo, tu agenda contiene entradas para Jos Martnez,
Josefa Prez y Jaime Primero, una bsqueda por Jos mostrar a las dos primeras
personas y una bsqueda por J las mostrar a todas.)
...............................................................................
...
Los registros son nuevos tipos de datos cuyo nombre viene precedido por la palab
ra struct.
C permite definir nuevos nombres para los tipos existentes con la palabra clave
typedef.
He aqu un posible uso de typedef:
define
80
define
40
struct Tiempo
int minutos
int segundos
1
1
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
149
149
ramacin con C - UJI
Introduccin a la prog
c
UJI
1
1
typedef struct
char titulo
1
char interprete
1
TipoTiempo duracion
int anyo
TipoCancion cancion
Vector de canciones.
int canciones Nmero de canciones que realmente hay.
TipoCompactDisc
typedef struct
TipoCompactDisc cd
int cds
TipoColeccion
int main void
TipoColeccion mis cds
Observa que, sistemticamente, hemos utilizado iniciales maysculas para los nom
bres
de tipos de datos (definidos con typedef y struct o slo con struct). Es un buen c
onvenio
para no confundir variables con tipos. Te recomendamos que hagas lo mismo o, en
su
defecto, que adoptes cualquier otro criterio, pero que sea coherente. El renombr
amiento
de tipos no slo sirve para eliminar la molesta palabra clave struct, tambin permit
e
disear programas ms legibles y en los que resulta ms fcil cambiar tipos globalmente.
Imagina que en un programa nuestro representamos la edad de una persona con
un
valor entre 0 y 127 (un char). Una variable edad se declarara as:
char edad
No es muy elegante: una edad no es un carcter, sino un nmero. Si definimos un nuevo
tipo, el programa es ms legible:
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
150
150
cUJI
Introduccin a la programacin con C -UJI
mi edad
mi edad
mi edad
Mal!
mi edad
Mal!
return 0
Y por qu es errneo? Porque debiramos haber modificado adems las marcas de
es un
lenguaje idneo para este tipo de modificaciones. Otros lenguajes, como C s
oportan
de forma mucho ms flexible la posibilidad de cambiar tipos de datos, ya qu
e no obligan
al programador a modificar un gran nmero de lneas del programa.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
151
151
En C no hay una palabra reservada (como def en Python) para iniciar la definicin
de
una funcin. El aspecto de una definicin de funcin en C es ste:
tipo de retorno identificador
parmetros
cuerpo de la funcin
El cuerpo de la funcin puede contener declaraciones de variables locales (tpicamen
te
en sus primeras lneas).
Aqu tienes un ejemplo de definicin de funcin: una funcin que calcula el logaritm
o
en base b (para b entero) de un nmero x. La hemos definido de un modo menos compa
cto
de lo que podemos hacer para ilustrar los diferentes elementos que puedes encont
rar en
una funcin:
float logaritmo float x int b
float logbase resultado
152
152
logbase log10 b
resultado log10 x
return resultado
logbase
logbase
logbase
int b
logbase log10 b
resultado log10 x
return resultado
logbase
1
Eso en el caso de parmetros escalares. Los parmetros de tipo vectorial se
estudiarn ms adelante.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
153
153
logbase
logbase
logbase
154
154
return 0
Si ejecutamos el programa tenemos:
log10 b ;
Cuerpo
resultado
log10 x
logbase
return resultado
Sentencia de devolucin de valor
..
.
int main void
float y
..
.
y
logaritmo 128.0 2
ocacin o activacin
..
.
eales
Identificador
Llamada, inv
Argumentos o parmetros r
Ah! Te hemos dicho antes que la funcin logaritmo no es muy compacta. Podramos
haberla definido as:
float logaritmo float x int b
return log10 x
log10 b
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
147 Define una funcin que reciba un int y devuelva su cuadrado.
Nuevamente hemos de matizar una afirmacin: en realidad slo es necesario que se
haya declarado el
2
prototipo de la funcin. Ms adelante daremos ms detalles.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
155
155
ramacin con C - UJI
Introduccin a la prog
c
UJI
.
A2 + B 2
Disea una funcin que reciba los valores que definen una recta y los valores que de
finen
un punto y devuelva la distancia del punto a la recta.
...............................................................................
...
Veamos otro ejemplo de definicin de funcin:
int minimo int a int b int c
d=
if a
b
if a
c
return a
else
return c
else
if b
c
return b
else
return c
La funcin minimo devuelve un dato de tipo int y recibe tres datos, tambin de tipo
int. No hay problema en que aparezca ms de una sentencia return en una funcin. El
comportamiento de return es el mismo que estudiamos en Python: tan pronto se eje
cuta,
finaliza la ejecucin de la funcin y se devuelve el valor indicado.
Observa que main es una funcin. Su cabecera es int main void . Qu significa
void? Significa que no hay parmetros. Pero no nos adelantemos. En este mismo captu
lo
hablaremos de funciones sin parmetros.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
154 Define una funcin que, dada una letra minscula del alfabeto ingls, devuelva
su correspondiente letra mayscula. Si el carcter recibido como dato no es una letr
a
minscula, la funcin la devolver inalterada.
155 Qu error encuentras en esta funcin?
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
156
156
ramacin con C - UJI
Introduccin a la prog
c
UJI
...............................................................................
...
Cada funcin puede definir sus propias variables locales definindolas en su cuerpo.
C
permite, adems, definir variables fuera del cuerpo de cualquier funcin: son las va
riables
globales.
Las variables que declaramos justo al principio del cuerpo de una funcin son vari
ables
locales.
Este programa, por ejemplo, declara dos variables locales para calcular el
sumatorio bi=a i. La variable local a sumatorio con identificador i nada tiene que v
er con la
variable del mismo nombre que es local a main:
include
int sumatorio int a int b
int i s
s 0
for i a i
s
i
return s
return 0
Las variables locales
.
La zona en la que
slo son
visibles en el cuerpo
. . . . . . . . . . .
OS . . . . . . . . . . . . .
156 Disea una funcin que
157 Disea una funcin que
que si n es negativo, x n es
. . . . . . . . . . . . . . . . . . . . . . .
calcule el factorial de un entero n.
calcule x n , para n entero y x de tipo float. (Recuerda
el resultado de multiplicar 1/x por s mismo n veces.)
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con ISBN:
C
9788469301432
157
157
ramacin con C UJI
Introduccin a la rog
c
UJI
i j
return 0
La variable j slo existe en el bloque en el que se ha declarado, es decir,
en la zona
sombreada. Ese es su mbito. La variable i tiene un mbito que engloba al de
j.
Puedes com robar, ues, que una variable local a una funcin es tambin u
na variable
local a un bloque: slo existe en el bloque que corres onde al cuer o de la
funcin.
Como ya te dijimos en un cuadro del ca tulo 1, C99 ermite declarar va
riables de
ndice de bucle de usar y tirar. Su mbito se limita al bucle. Aqu tienes un e
jem lo en
el que hemos sombreado el mbito de la variable j:
include
int main void
int i
for i 0 i 3 i
for int j 0 j 3 j
rintf
rintf
i j
return 0
que hay
formas ms eficientes de calcular x/1!, x 2 /2!, x 3 /3!, . . . , sabes cmo? Plantate
cmo
generar un trmino de la forma x i /i! a partir de un trmino de la forma x i1 /(i 1)
!.)
159 El valor de la funcin coseno puede aproximarse con el desarrollo de Taylor:
cos(x) 1
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
x2 x4 x6
+
+
2! 4! 6!
158
158
Disea una funcin que aproxime el coseno de un valor x usando n trminos del desarrol
lo
de Taylor, siendo n un nmero entero positivo.
160 Disea una funcin que diga si un nmero es perfecto o no. Si el nmero es
perfecto, devolver cierto (el valor 1) y si no, devolver falso (el valor 0). Un nmero
es perfecto si es igual a la suma de todos sus divisores (excepto l mismo).
161 Disea una funcin que diga si un nmero entero es o no es capica.
................................................................................
..
Las variables globales se declaran fuera del cuerpo de cualquier funcin y son acc
esibles desde cualquier punto del programa posterior a su declaracin. Este fragmento
de
programa, por ejemplo, define una variable global i y una variable local a main
con el
mismo identificador:
include
int i
Variable global i.
Variable local i.
for i 0 i 5 i
Referencias a la variable local i.
printf
doble
Ojo: el valor mostrado corresponde a la i g
lobal.
return 0
Fjate en la prdida de legibilidad que supone el uso del identificador i en dif
erentes
puntos del programa: hemos de preguntarnos siempre si corresponde a la variable
local
o global. Te desaconsejamos el uso generalizado de variables globales en tus pro
gramas. Como evitan usar parmetros en funciones, llegan a resultar muy cmodas y es fci
l
que abuses de ellas. No es que siempre se usen mal, pero se requiere una cierta
experiencia para formarse un criterio firme que permita decidir cundo resulta conve
niente
usar una variable global y cundo conviene suministrar informacin a funciones media
nte
parmetros.
Como estudiante te pueden parecer un recurso cmodo para evitar suministrar in
for-
macin a las funciones mediante parmetros. Ese pequeo beneficio inmediato es, crenos,
un lastre a medio y largo plazo: aumentar la probabilidad de que cometas errores
al
intentar acceder o modificar una variable y las funciones que definas en un prog
rama
sern difcilmente reutilizables en otros. Ests aprendiendo a programar y pretendemos
evitar que adquieras ciertos vicios, as que te prohibimos que las uses. . . salvo
cuando
convenga que lo hagas.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
162 Qu muestra por pantalla este programa?
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
159
159
ramacin con C - UJI
Introduccin a la prog
c
UJI
include
int contador
Variable global.
contador
0 contador
Puedes definir una funcin sin parmetros dejando la palabra void como contenido de
la lista de parmetros. Esta funcin definida por nosotros, por ejemplo, utiliza la
funcin
rand de
para devolver un nmero aleatorio entre 1 y 6:
int dado void
return rand
devuelve un entero:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
160
160
include
include
int dado void
return rand
1
La constante
double rand
1
doubl
d. La
divisin hace que el nmero generado est en el intervalo [0, 1[.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
163 El programa
siempre genera la misma se
cuencia de nmeros aleatorios.
Para evitarlo, debes proporcionar una semilla diferente con cada ejecucin del pro
grama.
El valor de la semilla se suministra como nico argumento de la funcin srand. Modif
ica
para que solicite al usuario la introduccin del valor semilla.
................................................................................
..
Un uso tpico de las funciones sin parmetros es la lectura de datos por teclado
que deben satisfacer una serie de restricciones. Esta funcin, por ejemplo, lee un
nmero
entero de teclado y se asegura de que sea par:
int lee entero par void
int numero
scanf
numero
while numero 2
0
printf
numero
numero scanf
numero
return numero
Otro uso tpico es la presentacin de mens de usuario con lectura de la opcin
seleccionada por el usuario:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
161
161
amacin con C - UJI
Introduccin a la progr
c
UJI
opcion
return opcion
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
164 Disea una funcin que lea por teclado un entero positivo y devuelva el valor
ledo. Si el usuario introduce un nmero negativo, la funcin advertir del error por
pantalla y leer nuevamente el nmero cuantas veces sea menester.
...............................................................................
...
Un procedimiento, como recordars, es una funcin que no devuelve valor alguno. Los
procedimientos provocan efectos laterales, como imprimir un mensaje por pantalla, m
odificar
variables globales o modificar el valor de sus parmetros.
Los procedimientos C se declaran como funciones con tipo de retorno void. Mi
ra este
ejemplo:
include
void saludos void
printf
En un procedimiento puedes utilizar la sentencia return, pero sin devolver v
alor alguno. Cuando se ejecuta una sentencia return, finaliza inmediatamente la ejecucin
del
procedimiento.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
165 Disea un procedimiento que reciba un entero n y muestre por pantalla n asteriscos seguidos con un salto de lnea al final.
166 Disea un procedimiento que, dado un valor de n, dibuje con asteriscos un tringulo rectngulo cuyos catetos midan n caracteres. Si n es 5, por ejemplo, el proce
dimiento
mostrar por pantalla este texto:
Introduccin a la prog
c
UJI
do
b
num
2
while num
return b
int main void
unsigned int numero
int bitsnumero
printf
scanf
numero
bitsnumero
printf
return 0
bits numero
bitsnumero numero
Llegados a este punto conviene que nos detengamos a estudiar cmo se gestiona
la
memoria en las llamadas a funcin.
163
163
bits
num 128
llamada desde lnea
21
bitsnumero
main
numero 128
Tras ejecutar el bucle de bits, la variable b vale 8. Observa que aunque num ha
modificado
su valor y ste provena originalmente de numero, el valor de numero no se altera:
b
bits
num
0
llamada desde lnea
21
bitsnumero
main
numero 128
La trama de activacin de bits desaparece ahora, pero dejando constancia del valor
devuelto por la funcin:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
164
164
return 8
bitsnumero
main
numero 128
Y, finalmente, el valor devuelto se copia en bitsnumero:
bitsnumero
main
numero 128
Como ves, las variables locales slo viven durante la ejecucin de cada funcin. C
obtiene una copia del valor de cada parmetro y la deja en la pila. Cuando modific
amos
el valor de un parmetro en el cuerpo de la funcin, estamos modificando el valor de
la
copia, no el de la variable original.
Este otro programa declara numero como una variable global y trabaja directa
mente
con dicha variable:
include
unsigned int numero
int bits void
int b
do
b
numero 2
while numero
return b
int main void
int bitsnumero
printf
scanf
numero
bitsnumero
bits
printf
bitsnumero numero
return 0
Las variables globales residen en una zona especial de la memoria y son acce
sibles
desde cualquier funcin. Representaremos dicha zona como un rea enmarcada con una
lnea discontnua. Cuando se inicia la ejecucin del programa, sta es la situacin:
variables globales
main
bitsnumero
numero
165
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
Introduccin a la programacin con C - UJI
c
UJI
165
main
numero 128
bitsnumero
bitsnumero
El clculo de bits modifica el valor de numero. Tras la primera iteracin del bucle
while,
sta es la situacin:
bits
variables globales
b
1
llamada desde lnea 22
main
numero
bitsnumero
64
bitsnumero
0
main
numero
bitsnumero
0
Bueno. Ahora sabes qu pasa con las variables globales y cmo acceder a ellas
desde las funciones. Pero repetimos lo que te dijimos al aprender Python: pocas
veces
est justificado acceder a variables globales, especialmente cuando ests aprendiend
o.
Evtalas.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
168 Estudia este programa y muestra grficamente el contenido de la memoria cuando
se van a ejecutar por primera vez las lneas 24, 14 y 5.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
166
166
ramacin con C - UJI
Introduccin a la prog
c
UJI
include
int cuadrado int i
return i
sumatorio i j
169 Este programa muestra por pantalla los 10 primeros nmeros primos. La funcin
siguiente genera cada vez un nmero primo distinto. Gracias a la variable global u
ltimoprimo la funcin recuerda cul fue el ltimo nmero primo generado. Haz una traza
paso a paso del programa (hasta que haya generado los 4 primeros primos). Muestr
a el
estado de la pila y el de la zona de variables globales en los instantes en que
se llama
a la funcin siguienteprimo y cuando sta devuelve su resultado
include
int ultimoprimo
do
ultimoprimo
esprimo 1
for i 2 i ultimoprimo 2 i
if ultimoprimo i 0
esprimo 0
break
while esprimo
return ultimoprimo
int main void
int i
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
ntroduccin a la programacin con C - UJI
c
UJI
167
167
printf
for i 0 i 10 i
printf
siguienteprimo
return 0
...............................................................................
...
No hay problema con que las variables locales a una funcin sean vectores. Su
contenido se almacena siempre en la pila. Este programa, por ejemplo, cuenta la
cantidad
de nmeros primos entre 1 y el valor que se le indique (siempre que no supere cier
ta
constante ) con la ayuda de la criba de Eratstenes. El vector con el que se efecta
la
criba es una variable local a la funcin que efecta el conteo:
include
define
10
n.
char criba
int i j numprimos
Comprobemos que el argumento es vlido
if n
return 1
Devolvemos 1 si no es vlido.
Inicializacin
criba 0
0
for i 1 i n i
criba i
1
Criba de Eratstenes
for i 2 i n i
if criba i
for j 2 i j n j
criba i j 0
Conteo de rimos
num rimos 0
for i 0 i n i
if criba i
num rimos
return num rimos
scanf
hasta
1
else
rintf
hasta cantid
ad
return 0
Introduccin a la rogramacin con C
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
cUJI
Introduccin a la
168
168
hasta
6
main
cantidad
Se efecta entonces (lnea 40) la llamada a cuenta rimos, con lo que se crea un
a
nueva trama de activacin. En ella se reserva memoria ara todas las variables loc
ales
de cuenta rimos:
n
0
2
9
criba
cuenta_ rimos
j
i
num rimos
llamada
desde lnea 40
hasta
main
cantidad
Observa que el vector criba ocu a memoria en la ro ia trama de activacin. Com le
ta
t mismo el resto de acciones ejecutadas or el rograma ayudndote de una traza de
la
ila de llamadas a funcin con grficos como los mostrados.
Te hemos dicho en el a artado 2.1 que los vectores han de tener talla conocida e
n tiem o
de com ilacin. Es hora de matizar esa afirmacin. Los vectores locales a una funcin
ueden determinar su talla en tiem o de ejecucin. Veamos un ejem lo:
include
int cuenta rimos int n
y n.
char criba n
int i j num rimos
Inicializacin
criba 0
0
for i 1 i n i
criba i
1
Criba de Eratstenes
for i 2 i n i
Introduccin
Andrs aMarzal/Isabel
la rogramacin con CISBN: 9788469301432
Gracia
169
169
cUJI
Introduccin a la rogramacin con C UJI
if criba i
for j 2 i j n j
criba i j 0
Conteo de rimos
num rimos 0
for i 0 i n i
if criba i
num rimos
return num rimos
scanf
has
ta
cantidad
rintf
idad
return 0
Fjate en cmo hemos definido el vector criba: la talla no es un valor constante
, sino
n, un armetro cuyo valor es desconocido hasta el momento en que se ejecute la fu
ncin.
Esta es una caracterstica de C99 y su one una mejora interesante del lenguaje.
i
i v i
armetros vectorial
incrementa v
rintf
Introduccin
Andrs aMarzal/Isabel
la rogramacin con CISBN: 9788469301432
Gracia
Introduccin a la rogramacin con C UJI
cUJI
170
170
for i 0 i
rintf
return 0
i
i v i
2
v
main
2
i
a
incrementa
i
0
llamada d
esde lnea 23
0
1
v
main
1
2
i
Andrs Marzal/Isabel
Introduccin
Gracia
a la rogramacin con CISBN: 9788469301432
171
171
Ves? El armetro a a unta a v. Los cambios sobre elementos del vector a que tienen
lugar al ejecutar la lnea 10 tienen efecto sobre los corres ondientes elementos d
e v,
as que v refleja los cambios que ex erimenta a. Tras ejecutar el bucle de increme
nta,
tenemos esta situacin:
a
incrementa
i
3
llamada de
sde lnea 23
0
1
2
v
main
3
i
2
v
main
3
i
int v
void incrementa int a
int i
for i 0 i
a i
int main void
int i
rintf
for i 0 i
v i
i
rintf
incrementa v
rintf
for i 0 i
rintf
return 0
i v i
i v i
Andrs aMarzal/Isabel
Introduccin
Gracia
la rogramacin con CISBN: 9788469301432
172
172
main
1
i
0
llamada desde lnea 24
main
1
i
3
llamada desde lnea 24
main
2
main
2
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
170 Disea un programa C que manipule polinomios de grado menor o igual que
10. Un polinomio se representar con un vector de float de tamao 11. Si p es un vec
tor
que representa un polinomio, p i es el coeficiente del trmino de grado i. Disea un
procedimiento suma con el siguiente perfil:
void suma float p
fl
oat q
float r
El procedimiento modificar r para que contenga el resultado de sumar los polinomi
os p
y q.
171 Disea una funcin que, dada una cadena y un carcter, diga cuntas veces
aparece el carcter en la cadena.
...............................................................................
...
Hemos visto cmo pasar vectores a funciones. Has de ser consciente de que no h
ay
forma de saber cuntos elementos tiene el vector dentro de una funcin: fjate en que
no
se indica cuntos elementos tiene un parmetro vectorial. Si deseas utilizar el valo
r de
la talla de un vector tienes dos posibilidades:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
173
173
ramacin con C - UJI
Introduccin a la prog
c
UJI
1.
saberlo de antemano,
2.
o proporcionarlo como parmetro adicional.
Estudiemos la primera alternativa. Fjate en este fragmento de programa:
include
define
define
20
10
i
z i
Ojo!
!
imprime x
imprime y
Ojo!
return 0
Siguiendo esta aproximacin, la funcin inicializa slo se puede utilizar con vectores
de
int de talla
, como x. No puedes llamar a inicializa con y: si lo haces (y
C te deja
hacerlo!) cometers un error de acceso a memoria que no te est reservada, pues el b
ucle
recorre
componentes, aunque y slo tenga
. Ese error puede abor
tar la
ejecucin del programa o, peor an, no hacindolo pero alterando la memoria de algn
modo indefinido.
Este es el resultado obtenido en un ordenador concreto:
174
174
10
int b
!
printf
printf
a b 0
a 0 b
Ojo!
i y
10
for i 0 i
i
printf
procedimiento y x
printf
procedimiento x y
printf
procedimiento x 0
x i
x 1
return 0
Esta es la salida resultante de su ejecucin:
eros.
include
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
175
175
define
define
void inicializa int z
20
10
int talla
int i
for i 0 i talla i
z i 0
void imprime int z
int talla
int i
for i 0 i talla i
printf
z i
printf
Correcto.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
172 Disea un procedimiento ordena que ordene un vector de enteros. El procedimiento recibir como parmetros un vector de enteros y un entero que indique el tamao
del vector.
173 Disea una funcin que devuelva el mximo de un vector de enteros. El tamao
del vector se suministrar como parmetro adicional.
174 Disea una funcin que diga si un vector de enteros es o no es palndromo
(devolviendo 1 o 0, respectivamente). El tamao del vector se suministrar como parme
tro
adicional.
175 Disea una funcin que reciba dos vectores de enteros de idntica talla y diga si
son iguales o no. El tamao de los dos vectores se suministrar como parmetro adicion
al.
176 Disea un procedimiento que reciba un vector de enteros y muestre todos sus
componentes en pantalla. Cada componente se representar separado del siguiente co
n
una coma. El ltimo elemento ir seguido de un salto de lnea. La talla del vector se
indicar con un parmetro adicional.
177 Disea un procedimiento que reciba un vector de float y muestre todos sus
componentes en pantalla. Cada componente se representar separado del siguiente co
n
una coma. Cada 6 componentes aparecer un salto de lnea. La talla del vector se ind
icar
con un parmetro adicional.
...............................................................................
...
b
b
b
incrementa
a
llamada desde lnea
14
main
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
177
177
Qu pasara si en lugar de a
1 hubisemos escrito a
1? Se hubiera incrementado la direccin de memoria a la que apunta el puntero, nada ms.
Y si hubisemos escrito a ? Lo mismo: hubisemos incrementado el valor de la
direccin almacenada en a. Y a ?, funcionara? A primera vista diramos que s, pero
no funciona como esperamos. El operador
postfijo tiene mayor nivel de pre
cedencia
que el operador unario , as que a (post)incrementa la direccin a y accede a su
contenido, por ese rden. Nuevamente habramos incrementado el valor de la direccin
de memoria, y no su contenido. Si quieres usar operadores de incremento/decremen
to,
tendrs que utilizar parntesis para que los operadores se apliquen en el orden dese
ado:
a
.
Naturalmente, no slo puedes acceder as a variables locales, tambin las variable
s
globales son accesibles mediante punteros:
include
int b
Variable global.
incrementa
printf
return 0
b
b
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
8
178
17
variables globales
incrementa
a
llamada desde lnea 14
main
b 1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
178 Disea un procedimiento que modifique el valor del parmetro de tipo float para
que valga la inversa de su valor cuando ste sea distinto de cero. Si el nmero es c
ero,
el procedimiento dejar intacto el valor del parmetro.
Si a vale 2.0, por ejemplo, inversa a har que a valga 0.5.
179 Disea un procedimiento que intercambie el valor de dos nmeros enteros.
Si a y b valen 1 y 2, respectivamente, la llamada intercambia a b har que a
pase a valer 2 y b pase a valer 1.
180 Disea un procedimiento que intercambie el valor de dos nmeros float.
181 Disea un procedimiento que asigne a todos los elementos de un vector de
enteros un valor determinado. El procedimiento recibir tres datos: el vector, su
nmero
de elementos y el valor que que asignamos a todos los elementos del vector.
182 Disea un procedimiento que intercambie el contenido completo de dos vectores
de enteros de igual talla. La talla se debe suministrar como parmetro.
183 Disea un procedimiento que asigne a un entero la suma de los elementos de
un vector de enteros. Tanto el entero (su direccin) como el vector se suministrarn
como
parmetros.
...............................................................................
...
Un uso habitual del paso de parmetros por referencia es la devolucin de ms
de un valor como resultado de la ejecucin de una funcin. Vemoslo con un ejemplo.
Diseemos una funcin que, dados un ngulo (en rdines) y un rdio r, clcule el
vlor de x = r cos() e y = r sin():
r
y
No podemos diser un funcin que devuelv los dos vlores. Hemos de diser un procedimiento que devuelv los vlores resultntes como prmetros psdos por refere
nci:
include
include
rdio
rdio
cos lf
sin lf
Introduccin l progr
pr
b
b
Andrs Mrzl/Isbel
Introduccin
Grci
l progrmcin con -CISBN: 978-84-693-0143-2
Introduccin l progrmcin con C -UJI
cUJI
180
180
scnf
printf
r
clcul xy ngulo r
printf
verticl
ngulo
scnf
horizontl
verticl
horizontl
return 0
Ves? Ls vribles horizontl y verticl no se inicilizn en min: reciben vlor
es como
resultdo de l llmd clcul xy.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
184 Disea una funcin que calcule la inversa de calcula xy, es decir, que obtenga
el valor del radio y del ngulo a partir de x e y.
185 Disea una funcin que reciba dos nmeros enteros a y b y devuelva, simultneamente, el menor y el mayor de ambos. La funcin tendr esta cabecera:
void minimax int a int b int
min int
max
186 Disea una funcin que reciba un vector de enteros, su talla y un valor de tipo
entero al que denominamos buscado. La funcin devolver (mediante return) el valor 1
si
buscado tiene el mismo valor que algn elemento del vector y 0 en caso contrario.
La
funcin devolver, adems, la distancia entre buscado y el elemento ms prximo a l.
La cabecera de la funcin ha de ser similar a sta:
int busca int vector
int talla int buscado int
distancia
Te ponemos un par de ejemplos para que veas qu debe hacer la funcin.
include
define
for i 0 i
printf
i
scanf
printf
scanf
encontrado busca v
istancia
if encontrado
v i
buscado
buscado
printf
uscado
else
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
181
181
c
UJI
Introduccin a la prog
ramacin con C - UJI
printf
distancia
printf
scanf
buscado
encontrado busca v
if encontrado
printf
else
printf
distancia
buscado
distancia
buscado
return 0
Al ejecutar el programa obtenemos esta salida por pantalla:
187 Modifica la funcin del ejercicio anterior para que, adems de la distancia al
elemento ms prximo, devuelva el valor del elemento ms prximo.
188 Modifica la funcin del ejercicio anterior para que, adems de la distancia al
elemento ms prximo y el elemento ms prximo, devuelva el valor de su ndice.
...............................................................................
...
No slo puedes pasar escalares y vectores como argumentos, tambin puedes pasar registros. El paso de registros es por valor, o sea, copiando el contenido en la p
ila, a menos
que t mismo pases un puntero a su direccin de memoria.
Este programa, por ejemplo, define un tipo de datos para representar puntos
en un
espacio de tres dimensiones y una funcin que calcula la distancia de un punto al
origen:
include
include
struct Punto
float x y z
float distancia struct Punto p
return sqrt p x p x
py py
pz pz
18
182
pto x
pto y
pto z
1
1
1
printf
return 0
distancia pto
traslacion x
traslacion y
traslacion z
Observa cmo hemos accedido a los campos de p. Ahora p es una direccin de memoria
(es de tipo struct Punto ), y p es la variable apuntada por p (y por tanto, es d
e tipo
struct Punto). El campo x es accedido con p x: primero se accede al contenido de
la
direccin de memoria apuntada por p, y luego al campo x del registro p, de ah que
usemos parntesis.
Es tan frecuente la notacin p x que existe una forma compacta equivalente:
void traslada struct Punto
p struct Punto traslacion
p
p
x
y
traslacion x
traslacion y
traslacion z
183
183
Acabemos este apartado mostrando una rutina que pide al usuario que introduz
ca las
coordenadas de un punto:
void lee punto struct Punto
p
printf
printf
printf
scanf
scanf
scanf
p
p
p
x
y
z
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
189 Este ejercicio y los siguientes de este bloque tienen por objeto construir u
na serie
de funciones que permitan efectuar transformaciones afines sobre puntos en el pl
ano. Los
puntos sern variables de tipo struct Punto, que definimos as:
struct Punto
float x y
Disea un procedimiento muestra punto que muestre por pantalla un punto. Un punto
p
tal que p x vale 2.0 y p y vale 0.2 se mostrar en pantalla as: 2.000000 0.200000 .
El
procedimiento muestra punto recibir un punto por valor.
Disea a continuacin un procedimiento que permita leer por teclado un punto. El
procedimiento recibir por referencia el punto en el que se almacenarn los valores
ledos.
190 La operacin de traslacin permite desplazar un punto de coordenadas (x, y) a
(x + a, y + b), siendo el desplazamiento (a, b) un vector (que representamos con
otro
punto). Implementa una funcin que reciba dos parmetros de tipo punto y modifique e
l
primero de modo que se traslade lo que indique el vector.
191 La operacin de escalado transforma un punto (x, y) en otro (ax, ay), donde a
es
un factor de escala (real). Implementa una funcin que escale un punto de acuerdo
con el
factor de escala a que se suministre como parmetro (un float).
192 Si rotamos un punto (x, y) una cantidad de radianes alrededor del origen,
obtenemos el punto
(x cos y sin , x sin + y cos ).
Define una funcin ue rote un punto la cantidad de grados ue se especifiue.
193 La rotacin de un punto (x, y) una cantidad de radianes alrededor de un punto
(a, b) se puede efectuar con una traslacin con el vector (a, b), una rotacin de
radianes con respecto al origen y una nueva traslacin con el vector (a, b). Disea
una
funcin ue permita trasladar un punto un nmero dado de grados alrededor de otro
punto.
194 Disea una funcin que diga si dos puntos son iguales.
195 Hemos definido un tipo registro para representar complejos as:
struct Complejo
float real
float imag
Disea e implementa los siguientes procedimientos para su manipulacin:
Introduccin a la prog
c
UJI
el mdulo de un complejo (|a + bi| =
a2 + b2 );
bcad
c2 +d2
i
).
196 Define un tipo registro y una serie de funciones para representar y manipula
r
fechas. Una fecha consta de un da, un mes y un ao. Debes implementar funciones que
permitan:
mostrar una fecha por pantalla con formato dd mm aaaa (por ejemplo, el
7 de junio
de 2001 se muestra as: 07 06 2001);
mostrar una fecha por pantalla como texto (por ejemplo, el 7 de junio
de 2001 se
muestra as:
);
include
define
i
j
m
j
return m
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la programacin con C UJI
Introduccin a la programacin con C
c
UJI
185
185
i
j
i j
printf
return 0
maximo matriz
El compilador no acepta ese programa. Por u? Fjate en la declaracin del parmetro.
Qu hay de malo? C no puede resolver los accesos de la forma a i j . Si recuerdas,
a i j significa accede a la celda cuya direccin se obtiene sumando a la direccin
a el valor i
+ j, donde
es el nmero de columnas de la mat
riz
a (en nuestro caso, sera
). Pero, cmo sabe la funcin cuntas columnas tiene a?
No hay forma de saberlo viendo una definicin del estilo int a
!
La versin correcta del programa debe indicar explcitamente cuntas columnas tien
e
la matriz. Hela au:
include
define
i
j
m
j
return m
int main void
int matriz
int i j
for i 0 i
for j 0 j
matriz i
printf
return 0
i
j
j
i j
maximo matriz
No ha sido necesario indicar cuntas filas tiene la matriz (aunue somos libres de
hacerlo).
La razn es sencilla: el nmero de filas no hace falta para calcular la direccin en l
a ue
reside el valor a i j .
As pues, en general, es necesario indicar explcitamente el tamao de cada una de
las
dimensiones del vector, excepto el de la primera (ue puedes declarar o no, a vo
luntad).
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
cUJI
Introduccin a la programacin con C UJI
186
186
Slo as obtiene C informacin suficiente para calcular los accesos a elementos del ve
ctor
en el cuerpo de la funcin.
Una consecuencia de esta restriccin es ue no podremos definir funciones capa
ces
de trabajar con matrices de tamao arbitrario. Siempre hemos de definir explcitamen
te
el tamao de cada dimensin excepto de la primera. Habr una forma de superar este
inconveniente, pero tendremos ue esperar al siguiente captulo para poder estudia
rla.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
197 Vamos a disear un programa capaz de jugar al tres en raya. El tablero se
representar con una matriz de 3 3. Las casillas sern caracteres. El espacio en bla
nco
representar una casilla vaca; el carcter
repr
esentar una casilla ocupada con un
crculo y el carcter
representar una casilla marcada con un
a cruz.
Disea una funcin que muestre por pantalla el tablero.
Disea una funcin que detecte si el tablero est lleno.
Disea una funcin que detecte si algn jugador consigui hacer tres en raya.
Disea una funcin que solicite al usuario la jugada de los crculos y modif
ique el
tablero adecuadamente. La funcin debe detectar si la jugada es vlida o n
o.
Disea una funcin que, dado un tablero, realice la jugada que corresponde
a las
cruces. En una primera versin, haz que el ordenador ponga la cruz en la
primera
casilla libre. Despus, modifica la funcin para que el ordenador realice
la jugada
ms inteligente.
Cuando hayas diseado todas las funciones, monta un programa que las use y permita
jugar al tres en raya contra el computador.
198 El juego de la vida se juega sobre una matriz cuyas celdas pueden estar viva
s o
muertas. La matriz se modifica a partir de su estado siguiendo unas sencilla reg
las que
tienen en cuenta los, como mucho, 8 vecinos de cada casilla:
Si una celda viva est rodeada por 0 o 1 celdas vivas, muere de soledad.
Si una celda viva est rodeada por 4 celdas vivas, muere por superpoblac
in.
Si una celda viva est rodeada por 2 o 3 celdas vivas, sigue viva.
Una celda muerta slo resucita si est rodeada por 3 celdas vivas.
Disea una funcin que reciba una matriz de 1010 celdas en la que el valor 0 represen
ta
celda muerta y el valor 1 representa celda viva. La funcin modificar la matriz de
acuerdo con las reglas del juego de la vida. (Avisos: Necesitars una matriz auxil
iar. Las
celdas de los bordes no tienen 8 vecinos, sino 3 o 5.)
A continuacin, monta un programa que permita al usuario introducir una dispos
icin
inicial de celdas y ejecutar el juego de la vida durante n ciclos, siendo n un v
alor
introducido por el usuario.
Aqu tienes un ejemplo de partida de 3 ciclos con una configuracin inicial curios
a:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
187
187
ramacin con C - UJI
Introduccin a la prog
c
UJI
Las casillas con un punto no han sido descubiertas an. Las casillas con un nmero h
an
sido descubiertas y sus casillas vecinas contienen tantas bombas como se indica
en el
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
c
UJI
Introduccin a la programacin con C - UJI
188
188
Una funcin puede devolver valores de cualquier tipo escalar o de registros, pero
no puede
devolver vectores3 . La razn es simple: la asignacin funciona con valores escalare
s y
registros, pero no con vectores.
Ya hemos visto cmo devolver valores escalares. A ttulo ilustrativo te presenta
mos un
ejemplo de definicin de registro y definicin de funcin que recibe como parmetros un
punto (x, y) y un nmero y devuelve un nuevo punto cuyo valor es (ax, ay):
struct Punto
float x y
struct Punto escala struct Punto p float a
struct Punto q
qx
qy
a
a
px
py
return q
Eso es todo. . . por el momento. Volveremos a la cuestin de si es posible devolve
r vectores
cuando estudiemos la gestin de memoria dinmica.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
200 Vuelve a implementar las funciones de manipulacin de puntos en el plano
(ejercicios 189194) para que no modifiquen el valor del registro struct Punto que
se
suministra como parmetro. En su lugar, devolvern el punto resultante como valor de
Introduccin a la prog
c
UJI
201 Implementa nuevamente las funciones del ejercicio 195, pero devolviendo un n
uevo
complejo con el resultado de operar con el complejo o complejos que se suministr
an como
parmetros.
...............................................................................
...
El tablero se muestra como una serie de casillas. Arriba tienes letras para
identificar
las columnas y a la izquierda nmeros para las filas. El ordenador nos informa de
que
an quedan 5 nufragos por rescatar y que disponemos de 20 sondas. Se ha detenido
mostrando el mensaje
: est esperando a que digamos en qu coordenadas lanzamos una sonda. El ordenador acepta una cadena que contenga un dgito y
una letra (en cualquier orden) y la letra puede ser minscula o mayscula. Lancemos
nuestra primera sonda: escribamos
y pulsemos la tecla de retorno de carro. H
e aqu
el resultado:
4
El nombre y la descripcin puede que te hagan concebir demasiadas esperanzas
: se trata de un juego
muy sencillito y falto de cualquier efecto especial. Galaxis fue concebido por C
hristian Franz y escrito para
el Apple Macintosh. Ms tarde, Eric Raymond lo reescribi para que fuera ejecutable
en Unix.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
190
190
En la casilla de coordenadas
aparece un uno: la sonda ha detectado la pres
encia
de un nufrago en alguna de las 4 direcciones. Sigamos. Probemos en :
Dos nufragos detectados. Parece probable que uno de ellos est en la columna .
Lancemos otra sonda en esa columna. Probemos con 2 :
Introduccin a la programacin
Andrs Marzal/Isabel
con- CISBN: 978-84-693-0143-2
Gracia
c
Introduccin a la programacin con C - UJI
UJI
191
191
9
20
9
20
define
void inicializa tablero char tablero
Inicializa el tablero de juego marcando todas las casillas como no
sondeadas.
int i j
for i 0 i
for j 0 j
tablero i
i
j
j
Introduccin a la programacin
Andrs Marzal/Isabel
con- CISBN: 978-84-693-0143-2
Gracia
c
Introduccin a la programacin con C - UJI
UJI
192
192
9
20
define
i
i
80
No hemos usado el nombre espacio, sino tablero, con el nico objetivo de resal
tar que el parmetro puede
5
ser cualquier matriz (siempre que su dimensin se ajuste a lo esperado), aunque no
sotros slo usaremos
la matriz espacio como argumento. Si hubisemos usado el mismo nombre, es probable
que hubisemos
alimentado la confusin entre parmetros y argumentos que experimentis algunos.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
UJI
c
193
193
char coordenadas
printf
scanf
coordenadas
Como ves, las coordenadas se leern en una cadena. Nos convendr disponer, pues, de
una funcin que traduzca esa cadena a un par de nmeros y otra que haga lo contrario:
void de fila y columna a numero y letra int fila int columna char coordenad
as
Convierte una fila y columna descritas numricamente en una fila y co
lumna descritas
como una cadena con un dgito y una letra.
coordenadas 0
coordenadas 1
coordenadas 2
fila
columna
isal
isal
return 0
La primera funcin (de fila y columna a numero y letra) es muy sencilla: recibe el
valor de la fila y el valor de la columna y modifica el contenido de un puntero
a una cadena.
Observa que es responsabilidad nuestra terminar correctamente la cadena coordena
das.
La segunda funcin es algo ms complicada. Una razn para ello es que efecta cierto
tratamiento de errores. Por qu? Porque la cadena coordenadas ha sido introducida p
or
el usuario y puede contener errores. Usamos un convenio muy frecuente en los pro
gramas
C:
Los valores se devuelven en la funcin mediante parmetros pasados por refe
rencia,
scanf
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
coordenadas
194
194
fila
columna
printf
scanf
coordenadas
define
struct Naufrago
int fila columna
int encontrado
?
Coordenadas
Ha sido encontrado ya?
struct GrupoNaufragos
struct Naufrago naufrago
int cantidad
El tipo registro struct Naufrago mantiene la posicin de un nufrago y permite saber si sigue perdido o si, por el contrario, ya ha sido encontrado. El tipo regi
stro
struct GrupoNaufragos mantiene un vector de nufragos de talla
. Aunque el juego indica que hemos de trabajar con 5 nufragos, usaremos un campo adici
onal
con la cantidad de nufragos realmente almacenados en el vector. De ese modo resul
tar
sencillo modificar el juego (como te proponemos en los ejercicios al final de es
ta seccin)
para que se juegue con un nmero de nufragos seleccionado por el usuario.
Guardaremos los nufragos en una variable de tipo struct GrupoNaufragos:
char espacio
struct GrupoNaufragos losNaufragos
inicializa tablero espacio
muestra tablero espacio
return 0
El programa debera empezar realmente por inicializar el registro losNaufragos ubi
cando
a cada nufrago en una posicin aletoria del tablero. Esta funcin (errnea) se encarga
de ello:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
195
195
include
void pon naufragos struct GrupoNaufragos grupoNaufragos int cantidad
Situa aleatoriamente cantidad nufragos en la estructura grupoNaufrag
os.
PERO LO HACE MAL.
int fila columna
grupoNaufragos cantidad 0
while grupoNaufragos cantidad cantidad
fila rand
columna rand
grupoNaufragos naufrago grupoNaufragos cantidad fila fila
grupoNaufragos naufrago grupoNaufragos cantidad columna columna
grupoNaufragos naufrago grupoNaufragos cantidad encontrado 0
grupoNaufragos cantidad
paso. Emreferencia
indica
la cantid
de fila al campo fila de un elemento del vector naufrago del registro que es apu
ntado por
grupoNaufragos. El resto de la funcin te debe resultar fcil de leer ahora. Volvamo
s a la
cuestin principal: por qu est mal diseada esa funcin? Fcil: porque puede ubicar dos
nufragos en la misma casilla del tablero. Cmo corregimos el problema? Asegurndonos
de que cada nufrago ocupa una casilla diferente. Tenemos dos posibilidades:
Generar las posiciones de cinco nufragos al azar y comprobar que son tod
as
diferentes entre s. Si lo son, perfecto: hemos acabado; si no, volvemos
a repetir
todo el proceso.
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
196
196
cantidad columna
cantidad encontra
a
columna
do 0
grupoNaufragos cantidad
Nos vendr bien disponer de una funcin que muestre por pantalla la ubicacin y estado
de cada nufrago. Esta funcin no resulta til para el juego (pues perdera toda la grac
ia),
pero s para ayudarnos a depurar el programa. Podramos, por ejemplo, ayudarnos con
llamadas a esa funcin mientras jugamos partidas de prueba y, una vez dado por bue
no
el programa, no llamarla ms. En cualquier caso, aqu la tienes:
void muestra naufragos struct GrupoNaufragos grupoNaufragos
Muestra las coordenadas de cada nufrago e informa de si sigue perdid
o.
til para depuracin del programa.
int i
char coordenadas 3
for i 0 i grupoNaufragos cantidad i
de fila y columna a numero y letra grupoNaufragos naufrago i fila
grupoNaufragos naufrago i columna
coordenadas
printf
i coordenadas
if grupoNaufragos naufrago i encontrado
printf
else
printf
La funcin est bien, pero podemos mejorarla. Fjate en cmo pasamos su parmetro:
por valor. Por qu? Porque no vamos a modificar su valor en el interior de la funcin
.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
197
197
Es posible usar el adjetivo const para dejar claro que pasamos el puntero po
r eficiencia, pero no porque vayamos a modificar su contenido:
void muestra naufragos const struct GrupoNaufragos
grupoN
aufragos
Hagamos una prueba para ver si todo va bien por el momento:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
198
198
199
199
c
UJI
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
define
define
define
void lanzar sonda int fila int columna char tablero
const struct GrupoNaufragos grupoNaufragos
Lanza sonda en las coordenadas indicadas. Actualiza el tablero con
el resultado del
sondeo. Si se detecta un nufrago en el punto de lanzamiento de la so
nda, lo rescata.
int detectados
0 i
Recorrer la vertical
for i 0 i
i
if hay naufrago i columna grupoNaufragos
detectados
if tablero i columna
tablero i columna
Recorrer la horizontal
for i 0 i
i
if hay naufrago fila i grupoNaufragos
detectados
if tablero fila i
tablero fila i
if
X.
rescate fila columna grupoNaufragos
else
tablero fila
columna
no, el nmero de nufragos detectados.
detectados
Y si
Esta funcin se ayuda con otras dos: hay naufrago y rescate. La primera nos in
dica
si hay un nufrago en una casilla determinada:
int hay naufrago int fila int columna const struct GrupoNaufragos grupoNauf
ragos
Averigua si hay un nufrago perdido en las coordenadas fila columna .
Si lo hay devuelve 1; si no lo hay, devuelve 0.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
200
200
int i
for i 0 i grupoNaufragos cantidad i
if fila
grupoNaufragos naufrago i fila
columna
grupoNaufragos naufrago i columna
return 1
return 0
Y la segunda lo marca como rescatado:
void rescate int fila int columna struct GrupoNaufragos grupoNaufragos
Rescata al nufrago que hay en las coordenadas indicadas.
int i
for i 0 i grupoNaufragos cantidad i
if fila grupoNaufragos naufrago i fila
columna
grupoNaufragos naufrago i columna
grupoNaufragos naufrago i encontrado 1
Ya podemos ofrecer una versin ms completa del programa principal:
int main void
char espacio
struct GrupoNaufragos losNaufragos
char coordenadas
1
int fila columna
srand time 0
pon naufragos losNaufragos 5
inicializa tablero espacio
muestra tablero espacio
ila
while
printf
scanf
coordenadas
while de numero y letra a fila y columna coordenadas
columna
printf
scanf
coordenadas
lanzar sonda fila columna espacio
muestra tablero espacio
losNaufragos
return 0
Cundo debe finalizar el bucle while exterior? Bien cuando hayamos rescatado a todo
s
los nufragos, bien cuando nos hayamos quedado sin sondas. En el primer caso habre
mos
vencido y en el segundo habremos perdido:
define
20
grupoNaufragos
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
201
201
0 i
olumna
printf
scanf
coordenadas
losNaufragos
son
return 0
Hemos definido una nueva funcin, perdidos, que calcula el nmero de nufragos que
permanecen perdidos.
Y ya est. Te mostramos finalmente el listado completo del programa:
include
include
include
include
include
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
202
202
define
define
define
define
define
9
20
80
5
20
define
define
define
Conversin entre los dos modos de expresar coordenadas
void de fila y columna a numero y letra int fila int columna char coordenad
as
Convierte fila y columna descritas numricamente en fila y columna de
scritas
como una cadena con un dgito y una letra.
coordenadas 0
coordenadas 1
coordenadas 2
fila
columna
Nufragos
struct Naufrago
int fila columna
int encontrado
?
Coordenadas
Ha sido encontrado ya?
isalph
isalph
struct GrupoNaufragos
struct Naufrago naufrago
int cantidad
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
203
203
cantidad columna
cantidad encontra
a
columna
do 0
grupoNaufragos cantidad
int hay naufrago int fila int columna const struct GrupoNaufragos grupoNauf
ragos
Averigua si hay un nufrago perdido en las coordenadas fila columna .
Si lo hay devuelve 1; si no lo hay, devuelve 0.
int i
for i 0 i grupoNaufragos cantidad i
if fila
grupoNaufragos naufrago i fila
columna
grupoNaufragos naufrago i columna
return 1
return 0
0 i
204
204
return contador
void muestra naufragos const struct GrupoNaufragos grupoNaufragos
Muestra las coordenadas de cada naufrago e informa de si sigue perd
ido.
til para depuracin del programa.
int i
char coordenadas 3
for i 0 i grupoNaufragos cantidad i
de fila y columna a numero y letra grupoNaufragos naufrago i fila
grupoNaufragos naufrago i columna
coordenadas
printf
i coordenadas
if grupoNaufragos naufrago i encontrado
printf
else
printf
Tablero
void inicializa tablero char tablero
Inicializa el tablero de juego marcando todas las casillas como no
sondeadas.
int i j
for i 0 i
for j 0 j
tablero i
i
j
j
Sonda
i
i
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
205
205
0 i
Recorrer la vertical
for i 0 i
i
if hay naufrago i columna grupoNaufragos
detectados
if tablero i columna
tablero i columna
Recorrer la horizontal
for i 0 i
i
if hay naufrago fila i grupoNaufragos
detectados
if tablero fila i
tablero fila i
Ver si acertamos y hay una nufrago en esta misma casilla.
if hay naufrago fila columna grupoNaufragos
tablero fila columna
En tal caso, ponemos una X.
rescate fila columna grupoNaufragos
else
tablero fila
columna
o, nmero de nufragos detectados.
detectados
Si n
losNaufrago
s
muestra tablero espacio
sondas disponibles
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
206
206
if perdidos losNaufragos
printf
0
sondas disponibles
else
printf
perdidos losNaufragos
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
202 Reescribe el programa para que no se use una variable de tipo struct GrupoNa
ufragos
como almacn del grupo de nufragos, sino una matriz paralela a la matriz espacio.
Cada nufrago se representar con un
mi
entras permanezca perdido, y con una
cuando haya sido descubierto.
203 Siempre que usamos rand en miniGalaxis calculamos un par de nmeros aleatorios. Hemos definido un nuevo tipo y una funcin:
struct Casilla
int fila columna
struct Casilla casilla al azar void
struct Casilla casilla
casilla fila rand
casilla columna rand
return casilla
Y proponemos usarlos as:
void pon naufragos struct GrupoNaufragos grupoNaufragos int cantidad
Situa aleatoriamente cantidad nufragos en la estructura grupoNaufrag
os.
int fila columna ya hay uno ahi i
struct Casilla casilla
grupoNaufragos cantidad 0
while grupoNaufragos cantidad
cantidad
casilla casilla al azar
ya hay uno ahi 0
for i 0 i grupoNaufragos cantidad i
if casilla fila
grupoNaufragos naufrago i fila
casilla columna
grupoNaufragos naufrago i columna
ya hay uno ahi 1
break
if
204 Como siempre que usamos rand calculamos un par de nmeros aleatorios, hemos
modificado el programa de este modo:
struct Naufrago naufrago al azar void
struct Naufrago naufrago
naufrago fila rand
naufrago columna rand
naufrago encontrado 0
return naufrago
void pon naufragos struct GrupoNaufragos grupoNaufragos int cantidad
Situa aleatoriamente cantidad nufragos en la estructura grupoNaufrag
os.
int fila columna ya hay uno ahi i
struct Naufrago un naufrago
grupoNaufragos cantidad 0
while grupoNaufragos cantidad
cantidad
un naufrago naufrago al azar
ya hay uno ahi 0
for i 0 i grupoNaufragos cantidad i
if un naufrago fila
grupoNaufragos naufrago i fila
un naufrago columna
grupoNaufragos naufrago i columna
ya hay uno ahi 1
break
if
cantidad
n naufrago
grupoNaufragos cantidad
978-84-693-0143-2
208
208
factorial n 1
valor
return 0
Nada nuevo. Ya conoces el concepto de recursin de Python. En C es lo mismo. T
iene
inters, eso s, que estudiemos brevemente el aspecto de la memoria en un instante d
ado.
Por ejemplo, cuando llamamos a factorial 5 , que ha llamado a factorial 4 , que
a su vez
ha llamado a factorial 3 , la pila presentar esta configuracin:
factorial
n
3
llamada
desde lnea 8
factorial
n
4
llamada
desde lnea 8
factorial
n
5
llamada
desde lnea 17
main
valor
5
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
207 Disea una funcin que calcule recursivamente x n . La variable x ser de tipo
210 Disea un procedimiento recursivo llamado muestra bin que reciba un nmero
entero positivo y muestre por pantalla su codificacin en binario. Por ejemplo, si
llamamos
a muestra bin 5 , por pantalla aparecer el texto
.
...............................................................................
...
2.
7
0 1 3 11
12 21 82 98
3.
7
4 11 18
29 30 37 43 75
4.
denado:
0
5
10
11
12
13
1
14
15
0 1 3 4 11 11 12 18 21 29 30 37 43 75 82
98
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
210
210
programacin con C - UJI
Introduccin a la
c
UJI
3
12
4
13
14
15
11 21 3 1 98 0 12 82
29 30 11 18 43 4 75 37
resolver i
ndependientemente cada problema
0
6
7
8
10
11
12
13
14
15
0 1 3 11 12 21 82 98
4 11 18 29 30 37 43 75
y combinar ambas soluciones.
0
3
11
4
12
5
13
6
14
1
8
2
10
15
0 1 3 4 11 11 12 18 21 29 30 3
7 43 75 82 98
Est claro que hemos hecho trampa: las lneas de trazo discontinuo esconden un proceso
complejo, pues la ordenacin de cada uno de los vectores de 8 elementos supone la
ordenacin (recursiva) de dos vectores de 4 elementos, que a su vez. . . Cundo acaba
el proceso recursivo? Cuando llegamos a un caso trivial: la ordenacin de un vecto
r que
slo tenga 1 elemento.
He aqu el proceso completo:
0
1
2
3
4
5
6
7 8
9
10
11
12
13
14
15
11 21 3 1 98 0 12
82 29 30 11 18 43 4 75 37
0
6
7
9
10
11
12
13
14
15
11 21 3 1 98 0 12 82
29 30 11 18 43 4 75 37
0
4
10
Divisiones
11
12
13
14
15
11 21 3 1
98 0 12 82
29 30 11 18
43 4 75 37
0
6
10
11
8
12
11 21
13
14
15
3 1
98 0
12 82
29 30
11 18
43 4
75 37
10
11
4
8
12
13
14
5
11
21
12
18
11
0
1
82
43
2
6
10
75
4
8
12
11 21
37
11
13
14
15
1 3
98
12 82
29 30
11 18
4 43
0
4
10
30
98
29
1
5
2
6
37 75
11
12
13
14
15
1 3 11 21
0 12 82 98
11 18 29 30
4 37 43 75
Fusiones
0
6
8
7
9
10
11
12
13
14
15
0 1 3 11 12 21 82 98
4 11 18 29 30 37 43 75
0
2
11
3
12
4
13
5
14
1
9
10
15
0 1 3 4 11 11 12 1
8 21 29 30 37 43 75 82 98
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
211
211
Int
roduccin a la programacin con C -UJI
cUJI
Nos queda por estudiar con detalle el proceso de fusin. Desarrollemos primero
una
funcin que recoja la idea bsica de la ordenacin por fusin: se llamar mergesort
y recibir un vector v y, en principio, la talla del vector que deseamos ordenar.
Esta
funcin utilizar una funcin auxiliar merge encargada de efectuar la fusin de vectores
ya ordenados. Aqu tienes un borrador incompleto:
void mergesort int v
int talla
if talla
1
return
else
mergesort la primera mitad de v
mergesort la segunda mitad de v
merge la primera mitad de v la segunda mitad de v
int inicio
int final
if final inicio
0
return
else
mergesort v inicio
inicio final 2
mergesort v
inicio final
2 1 final
merge la primera mitad de v la segunda mitad de v
1 3 11 21
i
0 12 82 98
j
k
Inicialmente, los tres ndices valen 0. Ahora comparamos a i con b j , seleccionam
os
el menor y almacenamos el valor en c k . Es necesario incrementar i si escogimos
un
elemento de a y j si lo escogimos de b. En cualquier caso, hemos de incrementar
tambin
la variable k:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
212
212
amacin con C -UJI
cUJI
Introduccin a la progr
0
6
1 3 11 21
i
0 12 82 98
j
k
El proceso se repite hasta que alguno de los dos primeros ndices, i o j, se sale de
l
vector correspondiente, tal y como ilustra esta secuencia de imgenes:
0
1
2
3
0
1
2 3
0
1 2 3 4 5 6 7
1 3 11 21
i
0 12 82 98
j
0 1
k
1
0
6
1 3 11 21
0 12 82 98
j
0 1 3
k
1
0
6
1 3 11 21
0 12 82 98
0 1 3
11
i
k
1
0
6
1 3 11 21
0 12 82 98
0 1 3
11 12
i
k
1
0
6
1 3 11 21
0 12 82 98
0 1 3
11 12 21
i
k
Ahora, basta con copiar los ltimos elementos del otro vector al final de c:
0
1
2
3
0
1
2 3
0
1 2 3 4 5 6 7
1 3 11 21
0 12 82 98
0 1 3
11 12 21 82
i
k
1
0
6
1 3 11 21
0 12 82 98
0 1 3
11 12 21 82 98
i
k
Un ltimo paso del proceso de fusin debera copiar los elementos de c en a y b, que e
n
realidad son fragmentos contiguos de un mismo vector.
Vamos a por los detalles de implementacin. No trabajamos con dos vectores ind
ependientes, sino con un slo vector en el que se marcan subvectores con pares de
ndices.
void merge int v
2
int i j k
int c final2 inicio1 1
tiempo de ejecucin.
i inicio1
j inicio2
k 0
while i final1 j
if v i v j
c k
v i
else
c k
v j
final2
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
3
213
21
while i
c k
final1
v i
while j
c k
final2
v j
final2
while i
c k
final1
v i
while j
c k
final2
v j
100
int i j k
int c final2 inicio1 1
i inicio1
j final1 1
k 0
while i final1
if v i v j
final2
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
214
214
c k
else
c k
v i
v j
while i
c k
final1
v i
while j
c k
final2
v j
if final inicio 0
return
else
mergesort v inicio inicio final 2
mergesort v inicio final
2 1 final
merge v inicio inicio final 2 final
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
215
215
Mergesort y el estilo C
Los programadores C tienden a escribir los programas de una forma muy co
mpacta.
Estudia esta nueva versin de la funcin merge:
void merge int v
int i j k
int c final2 inicio1 1
for i inicio1 j final1 1
k 0 i final1
j fin
c k
v i
v j
while i final1 c k
while j final2 c k
for k 0 k final2 inicio1
v i
v j
v i
v j
1 k v inicio1 k
al2
c k
Observa que los bucles for aceptan ms de una inicializacin (separndolas por
comas)
y permiten que alguno de sus elementos est en blanco (en el primer for la
accin
de incremento del ndice est en blanco). No te sugerimos que hagas t lo mism
o: te
prevenimos para que ests preparado cuando te enfrentes a la lectura de pr
ogramas C
escritos por otros.
Tambin vale la pena apreciar el uso del operador ternario para evitar
una estructura
condicional if else que en sus dos bloques asigna un valor a la misma ce
lda del vector.
Es una prctica frecuente y da lugar, una vez acostumbrado, a programas ba
stante
legibles.
C debe conocer la cabecera de una funcin antes de que sea llamada, es decir, debe
conocer el tipo de retorno y el nmero y tipo de sus parmetros. Normalmente ello no
plantea ningn problema: basta con definir la funcin antes de su uso, pero no siemp
re
es posible. Imagina que una funcin f necesita llamar a una funcin g y que g, a su
vez, necesita llamar a f (recursin indirecta). Cul ponemos delante? La solucin es
fcil: da igual, la que quieras, pero debes hacer una declaracin anticipada de la f
uncin
que defines en segundo lugar. La declaracin anticipada no incluye el cuerpo de la
funcin: consiste en la declaracin del tipo de retorno, identificador de funcin y li
sta de
parmetros con su tipo, es decir, es un prototipo o perfil de la funcin en cuestin.
Estudia este ejemplo6 :
int impar int a
int par int a
if a 0
return 1
else
return impar a 1
El ejemplo es meramente ilustrativo: hay formas mucho ms eficientes de saber
si un nmero es par o
6
impar.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
216
216
100
int inicio1 int final1 int final2
Declaracin anticipada.
if final inicio 0
return
else
mergesort v inicio inicio final
2
mergesort v inicio final
2 1 final
merge v inicio inicio final
2 final
Podemos usarla: se ha declarado antes.
las macros. Una macro tiene parmetros y se usa como una funcin cualquiera, pero la
s
llamadas no se traducen en verdaderas llamadas a funcin. Ahora vers por qu.
Vamos con un ejemplo:
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
217
217
c
UJI
Introduccin a la prog
ramacin con C - UJI
g y
float g float x
return x x
En la lnea 3 se usa g y an no se ha definido. Por la forma de uso, el compi
lador
deduce que su perfile es int g int x . Pero, al ver la definicin, detecta
un conflicto.
El problema se soluciona alterando el orden de definicin de las funcion
es o, si se
prefiere, mediante una declaracin anticipada:
float g float x
int f int y
return 1
g y
float g float x
return x x
define
x x x
La directiva con la que se define una macro es define, la misma con la que decla
rbamos
constantes. La diferencia est en que la macro lleva uno o ms parmetros (separados
por comas) encerrados entre parntesis. Este programa define y usa la macro
:
include
define
x x x
. La razn es que el pr
cesador la sustituye por su cuerpo, consiguiendo que el compilador vea esta otra
versin
del programa:
include
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
218
218
2 2 2
x x x
3 3
3 3 por. . . 3 3
3 3!
El resultado es, efectivamente, 15, y no el que esperbamos. Puedes evi
tar este
problema usando parntesis:
include
define
main void
printf
return 0
3 3
7
No del todo cierto, pero no entraremos en detalles.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
219
219
Ahora el fragmento
3 3 se sustituye por 3 3
3 3 , que es lo que
esperamos. Otro problema resuelto.
No te fes. Ya te hemos dicho que las macros son peligrosas. Sigue estan
do
mal. Qu esperas que calcule 1.0
3 3 ?, el valor de 1/3
3 3 se c
include
define
3 3 se convierte en 1.0
3 3
3 3
, que
es 1/36. Pero todava hay un problema: si ejecutamos este fragmento de cdigo:
i
3
z
i
la variable se incrementa 2 veces, y no una sla. Ten en cuenta que el compilador
traduce
lo que ve, y ve esto:
i
3
z
i
i
Y este problema no se puede solucionar.
Recuerda! Si usas macros, toda precaucin es poca.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
212 Disea una macro que calcule la tangente de una cantidad de radianes. Puedes
usar las funciones sin y cos de
, pero
ninguna otra.
213 Disea una macro que devuelva el mnimo de dos nmeros, sin importar si son
enteros o flotantes.
214 Disea una macro que calcule el valor absoluto de un nmero, sin importar si
es entero o flotante.
215 Disea una macro que decremente una variable entera si y slo si es positiva.
La macro devolver el valor ya decrementado o inalterado, segn convenga.
...............................................................................
...
inline
Los inconvenientes de las macros desaconsejan su uso. Lenguajes como C dan sopor
te a
las macros slo por compatibilidad con C, pero ofrecen alternativas mejores. Por e
jemplo,
puedes definir funciones inline. Una funcin inline es como cualquier otra funcin,
slo
que las llamadas a ella se gestionan como las llamadas a macros: se sustituye la
llamada
por el cdigo que se ejecutara en ese caso, o sea, por el cuerpo de la funcin con lo
s
valores que se suministren para los parmetros. Las funciones inline presentan muc
has
ventajas frente a la macros. Entre ellas, la posibilidad de utilizar variables l
ocales o la
no necesidad de utilizar parntesis alrededor de toda aparicin de un parmetro.
Las funciones inline son tan tiles que compiladores como
las integr
an desde
hace aos como extensin propia del lenguaje C y han pasado a formar parte del lengu
aje
C99. Al compilar un programa C99 como ste:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
220
220
ramacin con C - UJI
Introduccin a la prog
c
UJI
include
inline int doble int a
return a
i 1
return 0
Hay ocasiones, no obstante, en las que el compilador no puede efectuar la su
stitucin
de la llamada a funcin por su cuerpo. Si la funcin es recursiva, por ejemplo, la s
ustitucin
es imposible. Pero aunque no sea recursiva, el compilador puede juzgar que una f
uncin
es excesivamente larga o compleja para que compense efectuar la sustitucin. Cuand
o se
declara una funcin como inline, slo se est sugiriendo al compilador que efecte la
sustitucin, pero ste tiene la ltima palabra sobre si habr o no una verdadera llamada
a funcin.
static
Hay un tipo especial de variable local: las variables static. Una variable stati
c es invisible
fuera de la funcin, como cualquier otra variable local, pero recuerda su valor en
tre
diferentes ejecuciones de la funcin en la que se declara.
Veamos un ejemplo:
include
int turno void
static int contador
return contador
221
221
int i
for i 0 i 10 i
printf
turno
return 0
Si ejecutas el programa aparecern por pantalla los nmeros del 0 al 9. Con cada lla
mada,
contador devuelve su valor y se incrementa en una unidad, sin olvidar su valor e
ntre
llamada y llamada.
La inicializacin de las variables static es opcional: el compilador asegura q
ue empiezan valiendo 0.
Vamos a volver a escribir el programa que presentamos en el ejercicio 169 pa
ra generar
nmeros primos consecutivos. Esta vez, vamos a hacerlo sin usar una variable globa
l que
recuerde el valor del ltimo primo generado. Usaremos en su lugar una variable loc
al
static:
include
int siguienteprimo void
static int ultimoprimo
int esprimo
int i
do
ultimoprimo
esprimo 1
for i 2 i ultimoprimo 2 i
if ultimoprimo i 0
esprimo 0
break
while esprimo
return ultimoprimo
int main void
int i
printf
for i 0 i 10 i
printf
siguienteprimo
return 0
Mucho mejor. Si puedes evitar el uso de variables globales, evtalo. Las variables
locales
static pueden ser la solucin en bastantes casos.
Hay un tipo de parmetro especial que puedes pasar a una funcin: otra funcin!
Veamos un ejemplo. En este fragmento de programa se definen sendas funciones
C
que aproximan numricamente la integral definida en un intervalo para las funcione
s
matemticas f (x) = x 2 y f (x) = x 3 , respectivamente:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
222
222
return s
float integra cubo float a float b int n
int i
float s x
s 0.0
x a
for i 0 i n i
s x x x b a
x
b a n
return s
Las dos funciones que hemos definido son bsicamente iguales. Slo difieren en su id
entificador y en la funcin matemtica que integran. No sera mejor disponer de una nica
funcin C, digamos integra, a la que suministremos como parmetro la funcin matemtica
que queremos integrar? C lo permite:
float integra float a float b int n float
f float
int i
float s x
s 0.0
x a
for i 0 i n i
s f x
b a
x
b a n
return s
Hemos declarado un cuarto parmetro que es de tipo puntero a funcin. Cuando llamamo
s
a integra, el cuarto parmetro puede ser el identificador de una funcin que reciba
un
float y devuelva un float:
include
float integra float a float b int n float
int i
float s x
float
0.0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
223
223
x a
for i 0 i n i
s f x
b a
x
b a n
return s
float cuadrado float x
return x x
float cubo float x
return x x x
int main void
printf
printf
uadrado
ubo
return 0
La forma en que se declara un parmetro del tipo puntero a funcin resulta un tant
o
complicada. En nuestro caso, lo hemos declarado as: float f float . El primer flo
at
indica que la funcin devuelve un valor de ese tipo. El f indica que el parmetro f
es un puntero a funcin. Y el float entre parntesis indica que la funcin trabaja con
un
parmetro de tipo float. Si hubisemos necesitado trabajar con una funcin que recibe
un
float y un int, hubisemos escrito float f float int en la declaracin del parmetro.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
216 Puedes usar la funcin integra para calcular la integral definida de la funcin
matemtica sin(x)? Cmo?
217 Disea una funcin C capaz de calcular
b
f (
i),
i=a
siendo f una funcin matemtica cualquiera que recibe un entero y devuelve un entero
.
218 Disea una funcin C capaz de calcular
b
d
f
(i, j),
i=a j=c
siendo f una funcin matemtica cualquiera que recibe dos enteros y devuelve un ente
ro.
...............................................................................
...
Introduccin a la progr
c
UJI
puede, por ejemplo, agrupar las funciones, registros y constantes propias de cie
rto tipo
de clculos.
Proceder as tiene varias ventajas:
Mejora la legibilidad del cdigo (cada fichero es relativamente breve y a
grupa
temticamente las funciones, registros y constantes).
La compilacin es ms rpida (cuando se modifica un fichero, slo es necesario
compilar ese fichero).
Y, quiz lo ms importante, permite reutilizar cdigo. Es un beneficio a medi
o y
largo plazo. Si, por ejemplo, te dedicas a programar videojuegos tridim
ensionales,
vers que todos ellos comparten ciertas constantes, registros y funciones
definidas
por t o por otros programadores: tipos de datos para modelar puntos, polg
onos,
texturas, etctera; funciones que los manipulan, visualizan, leen/escribe
n en disco,
etctera. Puedes definir estos elementos en un fichero y utilizarlo en cu
antos programas desees. Alternativamente, podras copiar-y-pegar las funciones, co
nstantes
y registros que uno necesita en cada programa, pero no es conveniente e
n absoluto:
corregir un error en una funcin obligara a editar todos los programas en
los que
se peg; por contra, si est en un solo fichero, basta con corregir la defi
nicin una
sola vez.
C permite escribir un programa como una coleccin de unidades de compilacin.
El concepto es similar al de los mdulos Python: cada unidad agrupa definiciones d
e
variables, tipos, constantes y funciones orientados a resolver cierto tipo de pr
oblemas.
Puedes compilar independientemente cada unidad de compilacin (de ah el nombre) de
modo que el compilador genere un fichero binario para cada una de ellas. El enla
zador
se encarga de unir en una ltima etapa todas las unidades compiladas para crear un
nico fichero ejecutable.
Lo mejor ser que aprendamos sobre unidades de compilacin escribiendo una muy
sencilla: un mdulo en el que se define una funcin que calcula el mximo de dos nmero
enteros. El fichero que corresponde a esta unidad de compilacin se llamar
.
He aqu su contenido:
int maximo int a int b
if a b
return a
else
return b
El programa principal se escribir en otro fichero llamado
ho programa
llamar a la funcin maximo:
. Dic
include
int main void
int x y
printf
scanf
printf
scanf
printf
x
y
maximo x y
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
225
225
return 0
Hemos marcado el programa como incorrecto. Por qu? Vers, estamos usando una funcin, maximo, que no est definida en el fichero
. Cmo sabe el compilador
cuntos parmetros recibe dicha funcin?, y el tipo de cada parmetro?, y el tipo del
valor de retorno? El compilador se ve obligado a generar cdigo de mquina para llam
ar
a una funcin de la que no sabe nada. Mala cosa.
Cmo se resuelve el problema? Puedes declarar la funcin sin definirla, es decir,
puedes declarar el aspecto de su cabecera (lo que denominamos su prototipo) e in
dicar
que es una funcin definida externamente:
include
extern int maximo int a int b
int main void
int x y
printf
scanf
printf
scanf
printf
return 0
x
y
maximo x y
La compilacin necesita tres pasos: uno por cada unidad de compilacin y otro para
enlazar.
1.
ichero
El primer paso (
o unidad de compilacin
indica al compil
ador que
es un mdulo y no define a la funcin main. El resultado de
la compilacin se deja en un fichero llamado
. La extensin abrevi
a el
trmino object code, es decir, cdigo objeto. Los ficheros con extensin
contienen el cdigo de mquina de nuestras funciones8 , pero no es direct
amente
ejecutable.
2.
El segundo paso (
) es
similar al primero y genera el fichero
a partir de
.
8
. . . pero no slo eso: tambin contienen otra informacin, como la denominada
tabla de smbolos.
Introduccin a la programacin
Andrs Marzal/Isabel
con- CISBN: 978-84-693-0143-2
Gracia
226
226
3.
El tercer paso (
) es
especial. El
compilador recibe dos ficheros con extensin y genera un nico fichero eje
cutable, llamado principal. Este ltimo paso se encarga de enlazar las dos
unidades
compiladas para generar el fichero ejecutable.
Por enlazar entendemos que las llamadas a funciones cuyo cdigo de mquina
era desconocido (estaba en otra unidad de compilacin) se traduzcan en sa
ltos
a las direcciones en las que se encuentran los subprogramas de cdigo mqu
ina
correspondientes (y que ahora se conocen).
Aqu tienes un diagrama que ilustra el proceso:
Paso 1
Compilador
Paso 3
Enla
zador
principal
Paso 2
Compilador
Puedes ahorrarte un paso fundiendo los dos ltimos en uno slo. As:
Este diagrama muestra todos los pasos del proceso a los que aludimos:
Paso 1
Compilador
Compilador
zador
Enla
principal
Paso 2
Para conseguir un programa ejecutable es necesario que uno de los mdulos (pero
slo uno de ellos!) defina una funcin main. Si ningn mdulo define main o si main se
define en ms de un mdulo, el enlazador protestar y no generar fichero ejecutable
alguno.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
227
227
Documentacin y cabeceras
Es importante que documentes bien los ficheros de cabecera, pues es frecue
nte que los
programadores que usen tu mdulo lo consulten para hacerse una idea de qu ofr
ece.
Nuestro mdulo podra haberse documentado as:
Mdulo: extremos
Propsito: funciones para clculo de valores mximos
y mnimos.
Autor: A. U. Thor.
Fecha: 12 de enero de 1997
Estado: Incompleto. Falta la funcin minimo.
extern int maximo int a int b
Calcula el mximo de dos nmero enteros a y b.
Y por qu los programadores no miran directamente el fichero
en
lugar del
cuando quieren consultar algo? Por varias razones. Una de ellas es que, po
siblemente,
el
no est accesible. Si el mdulo es un producto comercial, probablemente
slo les
hayan vendido el mdulo ya compilado (el fichero ) y el fichero de cabecera.
Pero
incluso si se tiene acceso al , puede ser preferible ver el . El fichero
puede
estar plagado de detalles de implementacin, funciones auxiliares, variables
para uso
interno, etc., que hacen engorrosa su lectura. El fichero de cabecera cont
iene una somera
declaracin de cada uno de los elementos del mdulo que se publican para su uso
en
otros mdulos o programas, as que es una especie de resumen del .
include
include
int main void
int x y
printf
scanf
printf
scanf
printf
return 0
x
y
maximo x y
La nica diferencia con respecto a otros include que ya hemos usado estriba en el
uso de
comillas dobles para encerrar el nombre del fichero, en lugar de los caracteres
y .
Con ello indicamos al preprocesador que el fichero
se encuent
ra en nuestro
directorio activo. El preprocesador se limita a sustituir la lnea en la que apare
ce include
por el contenido del fichero. En un ejemplo tan sencillo no hem
os ganado
mucho, pero si el mdulo
contuviera muchas funciones, con slo una
lnea
habramos conseguido importarlas todas.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
228
228
Aqu tienes una actualizacin del grfico que muestra el proceso completo de compi
lacin:
Compilador
Preprocesador
Enlazador
Compilador
principal
No slo puedes declarar funciones en los ficheros de cabecera. Tambin puedes defini
r
constantes, variables y registros.
Poco hay que decir sobre las constantes. Basta con que las definas con defi
ne en
el fichero de cabecera. Las variables, sin embargo, s plantean un problema. Este
mdulo,
por ejemplo, declara una variable entera en
:
int variable
Si deseamos que otras unidades de compilacin puedan acceder a esa variable, tendr
emos
que incluir su declaracin en la cabecera. Cmo? Una primera idea es poner, directame
nte,
la declaracin as:
E
E
int variable
Pero es incorrecta. El problema radica en que cuando incluyamos la cabecera
en nuestro programa, se insertar la lnea int variable , sin ms, as que se estar definiendo una nueva variable con el mismo identificador que otra. Y declarar dos va
riables
con el mismo identificador es un error.
Quien detecta el error es el enlazador: cuando vaya a generar el programa ej
ecutable,
encontrar que hay dos objetos que tienen el mismo identificador, y eso est prohibi
do.
La solucin es sencilla: preceder la declaracin de variable en la cabecera
con la palabra reservada extern:
extern int variable
De ese modo, cuando se compila un programa que incluye a
, el comp
ilador
sabe que variable es de tipo int y que est definida en alguna unidad de compilacin
,
por lo que no la crea por segunda vez.
Finalmente, puedes declarar tambin registros en las cabeceras. Como los programas
que construiremos son sencillos, no se plantear problema alguno con la definicin d
e
registros: basta con que pongas su declaracin en la cabecera, sin ms. Pero si tu p
rograma
incluye dos cabeceras que, a su vez, incluyen ambas a una tercera donde se defin
en
constantes o registros, puedes tener problemas. Un ejemplo ilustrar mejor el tipo
de
dificultades al que nos enfrentamos. Supongamos que un fichero
define u
n registro:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
229
229
Bibliotecas
Ya has usado funciones y datos predefinidos, como las funciones y las con
stantes matemticas. Hemos hablado entonces del uso de la biblioteca matemtica. Por qu bi
blioteca y no mdulo? Una biblioteca es ms que un mdulo: es un conjunto de
mdulos.
Cuando se tiene una plyade de ficheros con extensin , conviene empaqueta
rlos
en uno solo con extensin (por archive). Los ficheros con extensin son
similares a los ficheros con extensin
: meras colecciones de ficheros.
De hecho,
tar (tape archiver) es una evolucin de
(por archiver), el programa con el
ue
se manipulan los ficheros con extensin .
La biblioteca matemtica, por ejemplo, agrupa un montn de mdulos. En un s
istema
Linux se encuentra en el fichero
y puedes consulta
r su contenido
con esta orden:
Como puedes ver, hay varios ficheros con extensin en su interior. (Slo
te
mostramos el principio y el final del resultado de la llamada, pues hay u
n total de 395
ficheros!)
Cuando usas la biblioteca matemtica compilas as:
o, equivalentemente, as:
Cabecera
struct
int a
Fin de cabecera
Ahora, los ficheros
existencia de sendas funciones:
incluyen a
y declaran la
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
230
230
Cabecera
include
int funcion de b punto h int x
Fin de cabecera
Cabecera
include
int funcion de c punto h int x
Fin de cabecera
Y, finalmente, nuestro programa incluye tanto a
como a
include
include
include
int main void
El resultado es que el
acaba quedando incluido dos veces! Tras el paso de
por el preprocesador, el compilador se enfrenta, a este texto:
include
Cabecera
Cabecera
struct
int a
.
.
Fin de cabecera
.
.
Fin de cabecera
int funcion de c punto h int x
Fin de cabecera
.
int main void
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
231
231
.
.
Fin de cabecera
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
232
232
La segunda inclusin de
no ha supuesto el copiado del texto guardado entre dir
ectivas
ifndef endif. Ingenioso, no?
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
233
233
234
scanf
tall
a
a malloc talla sizeof int
for i 0 i talla i
a i i
free a
a
return 0
Fjate en cmo se ha definido el vector a (lnea 6): como int a, es decir, como punter
o
a entero. No te dejes engaar: no se trata de un puntero a un entero, sino de un p
untero
a una secuencia de enteros. Ambos conceptos son equivalentes en C, pues ambos so
n
meras direcciones de memoria. La variable a es un vector dinmico de enteros, pues
su
memoria se obtiene dinmicamente, esto es, en tiempo de ejecucin y segn convenga a
las necesidades. No sabemos an cuntos enteros sern apuntados por a, ya que el valor
de talla no se conocer hasta que se ejecute el programa y se lea por teclado.
Sigamos. La lnea 10 reserva memoria para talla enteros y guarda en a la direc
cin
de memoria en la que empiezan esos enteros. La funcin malloc presenta un prototip
o
similar a ste:
void
Es una funcin que devuelve un puntero especial, del tipo de datos void . Qu signifi
ca
void ? Significa puntero a cualquier tipo de datos, o sea, direccin de memoria,
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
235
235
sin ms. La funcin malloc no se usa slo para reservar vectores dinmicos de enteros:
puedes reservar con ella vectores dinmicos de cualquier tipo base. Analicemos aho
ra el
argumento que pasamos a malloc. La funcin espera recibir como argumento un nmero
entero: el nmero de bytes que queremos reservar. Si deseamos reservar talla valor
es
de tipo int, hemos de solicitar memoria para talla sizeof int bytes. Recuerda qu
e
sizeof int es la ocupacin en bytes de un dato de tipo int (y que estamos asumiend
o
que es de 4).
Si el usuario decide que talla valga, por ejemplo, 5, se reservar un total de
20 bytes
y la memoria quedar as tras ejecutar la lnea 10:
0 1 2 3
4
a
Es decir, se reserva suficiente memoria para albergar 5 enteros.
Como puedes ver, las lneas 1112 tratan a a como si fuera un vector de enteros
cualquiera. Una vez has reservado memoria para un vector dinmico, no hay diferenc
ia
alguna entre l y un vector esttico desde el punto de vista prctico. Ambos pueden
indexarse (lnea 12) o pasarse como argumento a funciones que admiten un vector de
l
mismo tipo base.
Aritmtica de punteros
Una curiosidad: el acceso indexado a 0 es equivalente a a. En general, a
i es
equivalente a a i , es decir, ambas son formas de expresar el concepto acc
ede al
contenido de la direccin a con un desplazamiento de i veces el tamao del ti
po base.
La sentencia de asignacin a i
i podra haberse escrito como a i
i. En C es
posible sumar o restar un valor entero a un puntero. El entero se interpr
eta como un
desplazamiento dado en unidades tamao del tipo base (en el ejemplo, 4 bytes,
que
es el tamao de un int). Es lo que se conoce por aritmtica de punteros.
La aritmtica de punteros es un punto fuerte de C, aunque tambin tiene s
us detractores: resulta sencillo provocar accesos incorrectos a memoria si se usa
mal.
Finalmente, la lnea 13 del programa libera la memoria reservada y la lnea 14 gu
arda
en a un valor especial:
. La funcin free tiene un prototipo similar a ste:
puntero
Como ves, free recibe un puntero a cualquier tipo de datos: la direccin de memori
a en la
que empieza un bloque previamente obtenido con una llamada a malloc. Lo que hace
free
es liberar ese bloque de memoria, es decir, considerar que pasa a estar disponib
le para
otras posibles llamadas a malloc. Es como cerrar un fichero: si no necesito un r
ecurso, lo
libero para que otros lo puedan aprovechar.2 Puedes aprovechar as la memoria de f
orma
ptima.
Recuerda: tu programa debe efectuar una llamada a free por cada llamada a ma
lloc.
Es muy importante.
Conviene que despus de hacer free asignes al puntero el valor
, espec
ialmente
si la variable sigue viva durante bastante tiempo.
es una constante definid
a en
. Si un puntero vale
, se entiende que no apunta a un bloque de m
emoria.
Grficamente, un puntero que apunta a
se representa as:
2
Y, como en el caso de un fichero, si no lo liberas t explcitamente, se liber
a automticamente al finalizar
la ejecucin del programa. An as, te exigimos disciplina: oblgate a liberarlo t mismo
tan pronto dejes de
necesitarlo.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
6
236
23
La funcin malloc puede fallar por diferentes motivos. Podemos saber cundo ha f
allado porque malloc lo notifica devolviendo el valor
. Imagina que solicitas 2
megabytes
de memoria en un ordenador que slo dispone de 1 megabyte. En tal caso, la funcin
malloc devolver el valor
para indicar que no pudo efectuar la reserva de
memoria
solicitada.
Los programas correctamente escritos deben comprobar si se pudo obtener la m
emoria
solicitada y, en caso contrario, tratar el error.
a malloc talla
sizeof int
if a
printf
else
a malloc talla
printf
sizeof int
237
237
Fragmentacin de la memoria
Ya hemos dicho que malloc puede fracasar si se solicita ms memoria de la d
isponible en
el ordenador. Parece lgico pensar que en un ordenador con 64 megabytes, de
los que el
sistema operativo y los programas en ejecucin han consumido, digamos, 16 m
egabytes,
podamos solicitar un bloque de hasta 48 megabytes. Pero eso no est garanti
zado.
Imagina que los 16 megabytes ya ocupados no estn dispuestos contiguamente
en la
memoria sino que, por ejemplo, se alternan con fragmentos de memoria libr
e de modo
que, de cada cuatro megabytes, uno est ocupado y tres estn libres, como mue
stra esta
figura:
En tal caso, el bloque de memoria ms grande que podemos obtener con malloc
es de
slo tres megabytes!
Decimos que la memoria est fragmentada para referirnos a la alternanci
a de bloques
libres y ocupados que limita su disponibilidad. La fragmentacin no slo limi
ta el mximo
tamao de bloque que puedes solicitar, adems, afecta a la eficiencia con la
que se
ejecutan las llamadas a malloc y free.
int talla i
printf
scanf
talla
a malloc talla sizeof int
for i 0 i talla i
a i i
free a
a
return 0
238
238
scanf
tall
a
a malloc talla sizeof int
for i 0 p a i talla i
p
p i
free a
a
return 0
El efecto del bucle es inicializar el vector con la secuencia 0, 1, 2. . .
El puntero p empieza
apuntando a donde a, o sea, al principio del vector. Con cada autoincremen
to, p , pasa
a apuntar a la siguiente celda. Y la sentencia p i asigna al lugar apuntad
o por p el
valor i.
0
a i
return pares
Observa que devolvemos un dato de tipo int , es decir, un puntero a entero; buen
o, en
realidad se trata de un puntero a una secuencia de enteros (recuerda que son con
ceptos
equivalentes en C). Es la forma que tenemos de devolver vectores desde una funcin
.
Este programa, por ejemplo, llama a selecciona pares:
include
include
include
define
10
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
239
239
int
seleccion
numpares
int i j
int pares
Contamos los elementos pares en el parmetro numpares, pasado por refe
rencia.
numpares 0
for i 0 i talla i
if a i
2
0
numpares
pares
malloc
numpares
sizeof int
j 0
for i 0 i talla i
if a i
2 0
pares j
a i
return pares
Ahora podemos resolver el problema:
include
include
include
define
int
10
selecciona pares int a
numpares
int i j
int pares
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
240
240
malloc
numpares
sizeof int
j 0
for i 0 i talla i
if a i
2 0
pares j
a i
return pares
int main void
int vector
i
int seleccion seleccionados
Llenamos el vector con valores aleatorios.
srand time 0
for i 0 i
i
vector i rand
Se efecta ahora la seleccin de pares.
seleccion selecciona pares vector
seleccionados
La variable seleccion apunta ahora a la zona de memoria con los elem
entos pares.
Adems, la variable seleccionados contiene el nmero de pares.
Ahora los mostramos en pantalla.
for i 0 i seleccionados i
printf
seleccion i
free seleccion
seleccion
return 0
Por cierto, el prototipo de la funcin, que es ste:
int
selecciona pares int a
int talla int
seleccionado
s
puede cambiarse por este otro:
int
selecciona pares int
s
seleccionado
241
241
primeros void
int i v 10
for i 0 i 10 i
v i i 1
return v
a primeros
printf
a i
No existe a i .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
219 Disea una funcin que seleccione todos los nmeros positivos de un vector de
enteros. La funcin recibir el vector original y un parmetro con su longitud y devol
ver
dos datos: un puntero al nuevo vector de enteros positivos y su longitud. El pun
tero
se devolver como valor de retorno de la funcin, y la longitud mediante un parmetro
adicional (un entero pasado por referencia).
220 Desarrolla una funcin que seleccione todos los nmeros de un vector de float
mayores que un valor dado. Disea un programa que llame correctamente a la funcin y
muestre por pantalla el resultado.
221 Escribe un programa que lea por teclado un vector de float cuyo tamao se
solicitar previamente al usuario. Una vez ledos los componentes del vector, el pro
grama
copiar sus valores en otro vector distinto que ordenar con el mtodo de la burbuja.
Recuerda liberar toda memoria dinmica solicitada antes de finalizar el programa.
222 Escribe una funcin que lea por teclado un vector de float cuyo tamao se
solicitar previamente al usuario. Escribe, adems, una funcin que reciba un vector c
omo
el ledo en la funcin anterior y devuelva una copia suya con los mismos valores, pe
ro
ordenados de menor a mayor (usa el mtodo de ordenacin de la burbuja o cualquier ot
ro
que conozcas).
Disea un programa que haga uso de ambas funciones. Recuerda que debes liberar
toda memoria dinmica solicitada antes de finalizar la ejecucin del programa.
223 Escribe una funcin que reciba un vector de enteros y devuelva otro con sus n
mayores valores, siendo n un nmero menor o igual que la talla del vector original
.
224 Escribe una funcin que reciba un vector de enteros y un valor n. Si n es meno
r
o igual que la talla del vector, la funcin devolver un vector con las n primeras c
eldas
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
242
242
ramacin con C - UJI
Introduccin a la prog
c
UJI
10
pares
int i j
numpares 0
for i 0 i talla i
if a i 2 0
numpares
pares
malloc
j 0
for i 0 i talla i
if a i 2 0
pares j
numpares
sizeof int
a i
seleccion
sele
for i 0 i seleccionados i
printf
seleccion i
free seleccion
seleccion
return 0
Fjate en la declaracin del parmetro pares en la lnea 7: es un puntero a un vecto
r
de enteros, o sea, un vector de enteros cuya direccin se suministra a la funcin. Po
r qu?
Porque a resultas de llamar a la funcin, la direccin apuntada por pares ser una nuev
a
direccin (la que obtengamos mediante una llamada a malloc). La lnea 16 asigna un v
alor
a pares. Resulta interesante que veas cmo se asigna valores al vector apuntado po
r
pares en la lnea 21 (los parntesis alrededor de pares son obligatorios). Finalmen
te,
observa que seleccion se declara en la lnea 27 como un puntero a entero y que se
pasa
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
3
243
24
include
include
include
struct VectorDinamicoEnteros
int elementos
Puntero a la zona de memoria con los elementos.
int talla
Nmero de enteros almacenados en esa zona de memoria.
struct VectorDinamicoEnteros selecciona pares struct VectorDinamicoEnteros
entrada
Recibe un vector dinmico y devuelve otro con una seleccin de los elemen
tos
pares del primero.
int i j
struct VectorDinamicoEnteros pares
pares talla 0
for i 0 i entrada talla i
if entrada elementos i
pares talla
pares elementos
2
malloc pares talla
0
sizeof int
j 0
for i 0 i entrada talla i
if entrada elementos i 2
0
pares elementos j entrada elementos i
return pares
int main void
int i
struct VectorDinamicoEnteros vector seleccionados
vector talla 10
vector elementos malloc vector talla
srand time 0
for i 0 i vector talla i
sizeof int
244
2
scanf
a
printf
else
Situacin normal.
pares
int i j
numpares 0
for i 0 i talla i
if a i
2 0
numpares
pares malloc numpares sizeof int
if pares
Algo fue mal: no conseguimos la memoria.
numpares 0
Informamos de que el vector tiene capacidad 0
...
return 0
un error.
j 0
for i 0 i talla i
if a i
2
0
pares j
a i
return 1
Si llegamos aqu, todo fue bien, as que avisamos de ello c
on el valor 1.
Aqu tienes un ejemplo de uso de la nueva funcin:
n
if
seleccionados
seleccio
Todo va bien.
else
Algo fue mal.
Hay que decir, no obstante, que esta forma de aviso de errores empieza a q
uedar
obsoleto. Los lenguajes de programacin ms modernos, como C o Python, suelen
basar la deteccin (y el tratamiento) de errores en las denominadas excepciones.
seleccionados
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
245
245
elementos
2
malloc pares
0
talla
sizeof i
nt
j 0
for i 0 i entrada talla i
if entrada elementos i
pares elementos j
0
entrada elementos i
sizeof int
seleccionados
seleccionados talla
return 0
Como ves, tienes muchas soluciones tcnicamente diferentes para realizar lo mis
mo.
Debers elegir en funcin de la elegancia de cada solucin y de su eficiencia.
Listas Python
Empieza a quedar claro que Python es un lenguaje mucho ms cmodo que C para
gestionar vectores dinmicos, que all denominbamos listas. No obstante, debes
tener
presente que el intrprete de Python est escrito en C, as que cuando manejas
listas
Python ests, indirectamente, usando memoria dinmica como malloc y free.
Cuando creas una lista Python con una orden como a
0
5 o
a
0 0 0 0 0 , ests reservando espacio en memoria para 5 elementos y a
signndole a cada elemento el valor 0. La variable a puede verse como un simpl
e puntero
a esa zona de memoria (en realidad es algo ms complejo).
Cuando se pierde la referencia a una lista (por ejemplo, cambiando el
valor asignado
a a), Python se encarga de detectar automticamente que la lista ya no es a
puntada por
nadie y de llamar a free para que la memoria que hasta ahora ocupaba pase
a quedar
libre.
247
247
0.0
x
0 x
0 y
0 y
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
248
248
printf
scanf
pol puntos
pol p malloc pol puntos sizeof struct Punto
for i 0 i pol puntos i
printf
i
printf
scanf
pol p i x
printf
scanf
pol p i y
return pol
void libera poligono struct Poligono
pol
free pol p
pol p
pol puntos 0
float perimetro poligono struct Poligono pol
int i
float perim
0.0
x
0 x
0 y
0 y
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
roduccin a la programacin con C -UJI
cUJI
249
249
Int
int puntos
void lee poligono struct Poligono
pol
int i
printf
puntos
pol p malloc pol puntos
for i 0 i pol puntos i
printf
i
printf
scanf
printf
scanf
scanf
pol
p i x
p i y
pol
free pol p
pol p
pol puntos 0
float perimetro poligono const struct Poligono
int i
float perim
pol
0.0
pia de su
contenido. Hay una razn para hacerlo: la eficiencia. Cada dato de tipo struct Pol
igono
esta formado por un puntero (4 bytes) y un entero (4 bytes), as que ocupa 8 bytes
.
Si pasamos o devolvemos una copia de un struct Poligono, estamos copiando 8 byte
s.
Si, por contra, pasamos su direccin de memoria, slo hay que pasar 4 bytes. En este
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
250
250
0.0
Introduccin a la prog
c
UJI
Con calloc, puedes pedir memoria para un vector de talla enteros as:
a
Por qu no usar siempre calloc, si parece mejor que malloc? Por eficienci
a. En
ocasiones no desears que se pierda tiempo de ejecucin inicializando la memo
ria a
cero, ya que t mismo querrs inicializarla a otros valores inmediatamente. R
ecuerda
que garantizar la mayor eficiencia de los programas es uno de los objetiv
os del lenguaje
de programacin C.
Las cadenas son un caso particular de vector. Podemos usar cadenas de cualquier
longitud
gracias a la gestin de memoria dinmica. Este programa, por ejemplo, lee dos cadena
s
80
cadena2
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
252
252
printf
printf
gets cadena1
gets cadena2
cadena3
cadena2
malloc
strlen cadena1
sizeof char
strlen
Pero, ojo!, la cadena apuntada por p es, en ese caso, inmutable: si intent
as asignar un
char a p i , el programa puede abortar su ejecucin. Por qu? Porque los liter
ales de
cadena residen en una zona de memoria especial (la denominada zona de texto)
que
est protegida contra escritura. Y hay una razn para ello: en esa zona resid
e, tambin,
el cdigo de mquina correspondiente al programa. Que un programa modifique s
u
propio cdigo de mquina es una psima prctica (que era relativamente frecuente
en
los tiempos en que predominaba la programacin en ensamblador), hasta el pu
nto de
que su zona de memoria se marca como de slo lectura.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
231 Implementa una funcin que reciba una cadena y devuelva una copia invertida.
(Ten en cuenta que la talla de la cadena puede conocerse con strlen, as que no es
necesario que suministres la talla explcitamente ni que devuelvas la talla de la
memoria
solicitada con un parmetro pasado por referencia.)
Escribe un programa que solicite varias palabras a un usuario y muestre e
l resultado
de invertir cada una de ellas.
...............................................................................
...
Podemos extender la idea de los vectores dinmicos a matrices dinmicas. Pero el asu
nto
se complica notablemente: no podemos gestionar la matriz como una sucesin de elem
entos contiguos, sino como un vector dinmico de vectores dinmicos.
scanf
scanf
filas
columnas
reserva de memoria
m malloc filas sizeof float
for i 0 i filas i
m i
malloc columnas sizeof float
trabajo con m i
liberacin de memoria
for i 0 i filas i
free m i
free m
m
return 0
Analicemos poco a poco el programa.
Declaracin del tipo
Empecemos por la declaracin de la matriz (lnea 6). Es un puntero un poco extrao: se
declara como float m. Dos asteriscos, no uno. Eso es porque se trata de un pun
tero a
un puntero de enteros o, equivalentemente, un vector dinmico de vectores dinmicos
de
enteros.
Reserva de memoria
Sigamos. Las lneas 9 y 10 solicitan al usuario los valores de filas y columnas. E
n la
lnea 13 encontramos una peticin de memoria. Se solicita espacio para un nmero filas
de punteros a float. Supongamos que filas vale 4. Tras esa peticin, tenemos la si
guiente
asignacin de memoria para m:
25
4
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
254
0
m
1
El vector m es un vector dinmico cuyos elementos son punteros (del tipo float
). De
momento, esos punteros no apuntan a ninguna zona de memoria reservada. De ello s
e
encarga la lnea 15. Dicha lnea est en un bucle, as que se ejecuta para m 0 , m 1 ,
m 2 , . . . El efecto es proporcionar un bloque de memoria para cada celda de m.
He aqu
el efecto final:
0 1 2
3 4
0
m
0 1 2
3 4
1
4
2
4
3
2 es de tipo float.
255
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
Introduccin a la programacin con C - UJI
c
UJI
255
m i 1
columnas: el contenido de m i
pasa a
ser la direccin de memoria columnas celdas ms a la derecha de la direccin m
i 1 .
9
0
1
2
3
4
Introduccin a la programacin
cUJI
!Mal!
int i j
for i 0 i
for j 0 j
printf
printf
i
j
m i
Supongamos ahora que nos piden una funcin que efecte la liberacin de la memoria
de una matriz:
void libera matriz int
m int filas int columnas
int i
for i 0 i filas i
free m i
free m
Ahora resulta innecesario el paso del nmero de columnas, pues no se usa en la fun
cin:
void libera matriz int
m int filas
int i
for i 0 i filas i
free m i
free m
Falta un detalle que hara mejor a esta funcin: la asignacin del valor
a m al fin
al
de todo. Para ello tenemos que pasar una referencia a la matriz, y no la propia
matriz:
void libera matriz int
m int filas
int i
257
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
oduccin a la programacin con C - UJI
c
UJI
257
Intr
for i 0 i filas i
free m i
free m
m
Qu horror! Tres asteriscos en la declaracin del parmetro m! C no es, precisamente,
el colmo de la elegancia.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
232 Disea una funcin que reciba un nmero de filas y un nmero de columnas y
devuelva una matriz dinmica de enteros con filascolumnas elementos.
233 Disea un procedimiento que reciba un puntero a una matriz dinmica (sin
memoria asignada), un nmero de filas y un nmero de columnas y devuelva, mediante e
l
primer parmetro, una matriz dinmica de enteros con filascolumnas elementos.
...............................................................................
...
La gestin de matrices dinmicas considerando por separado sus tres variables (p
untero a memoria, nmero de filas y nmero de columnas) resulta poco elegante y da lug
ar
a funciones con parmetros de difcil lectura. En el siguiente apartado aprenders a u
sar
matrices dinmicas que agrupan sus tres datos en un tipo registro definido por el
usuario.
filas filas
columnas columnas
m malloc filas sizeof float
i 0 i filas i
mat m i
return mat
Introduccin a la prog
c
UJI
crea matriz 3 4
columnas 0
mat columnas
else
mat
mat
mat
for
filas filas
columnas columnas
m malloc filas sizeof float
i 0 i filas i
mat m i
malloc columnas sizeof float
matriz
mat
int i
if mat m
for i 0 i mat filas i
free mat m i
free mat m
mat
mat
mat
m
filas 0
columnas
Para liberar la memoria de una matriz dinmica m, efectuaremos una llamada com
o
sta:
libera matriz
m
Como hemos de leer dos matrices por teclado, diseemos ahora una funcin capaz d
e
leer una matriz por teclado:
struct Matriz lee matriz void
int i j filas columnas
struct Matriz mat
printf
scanf
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
filas
259
259
printf
scanf
column
as
mat
for i 0 i filas i
for j 0 j columnas j
printf
scanf
mat m i
i j
j
return mat
Observa que hemos llamado a crea matriz tan pronto hemos sabido cul era el nme
ro
de filas y columnas de la matriz.
Y ahora, implementemos un procedimiento que muestre por pantalla una matriz:
void muestra matriz struct Matriz mat
int i j
for i 0 i mat filas i
for j 0 j mat columnas j
printf
mat m i j
printf
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
234 En muestra matriz hemos pasado la matriz mat por valor. Cuntos bytes se
copiarn en pila con cada llamada?
235 Disea una nueva versin de muestra matriz en la que mat se pase por referencia.
Cuntos bytes se copiarn en pila con cada llamada?
...............................................................................
...
Podemos proceder ya mismo a implementar una funcin que multiplique dos matr
ices:
struct Matriz multiplica matrices struct Matriz a struct Matriz b
int i j k
struct Matriz c
if a columnas
b filas
plicar
c filas c columnas 0
cm
return c
c crea matriz a filas b columnas
for i 0 i c filas i
for j 0 j c columnas j
cm i j
0.0
for k 0 k a columnas k
cm i j
am i k
bm k
j
return c
No se pueden multi
Introduccin a la prog
c
UJI
include
definicin de funciones
int main void
struct Matriz a b c
a lee matriz
b lee matriz
c multiplica matrices a b
if c m
printf
else
printf
muestra matriz c
libera matriz
libera matriz
libera matriz
a
b
c
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
236 Disea una funcin que sume dos matrices.
237 Pasar estructuras por valor puede ser ineficiente, pues se debe obtener una
copia en pila de la estructura completa (en el caso de las matrices, cada variab
le de
tipo struct Matriz ocupa 12 bytes un puntero y dos enteros, cuando una referencia
supone la copia de slo 4 bytes). Modifica la funcin que multiplica dos matrices pa
ra
que sus dos parmetros se pasen por referencia.
238 Disea una funcin que encuentre, si lo hay, un punto de silla en una matriz.
Un punto de silla es un elemento de la matriz que es o bien el mximo de su fila y
el
mnimo de su columna a la vez, o bien el mnimo de su fila y el mximo de su columna
a la vez. La funcin devolver cierto o falso dependiendo de si hay algn punto de sil
la.
Si lo hay, el valor del primer punto de silla encontrado se devolver como valor d
e un
parmetro pasado por referencia.
...............................................................................
...
Hemos aprendido a definir matrices dinmicas con un vector dinmico de vectores dinmicos. El primero contiene punteros que apuntan a cada columna. Una caracterstica
de
las matrices es que todas las filas tienen el mismo nmero de elementos (el nmero d
e
columnas). Hay estructuras similares a las matrices pero que no imponen esa rest
riccin.
Pensemos, por ejemplo, en una lista de palabras. Una forma de almacenarla en mem
oria
es la que se muestra en este grfico:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
261
261
ramacin con C - UJI
Introduccin a la prog
c
UJI
0
listapal
a n u a l
1
d a d i v o s
o
2
m a n o
3
t a c o
Ves? Es parecido a una matriz, pero no exactamente una matriz: cada palabra o
cupa
tanta memoria como necesita, pero no ms. Este programa solicita al usuario 4 pala
bras
y las almacena en una estructura como la dibujada:
include
include
include
define
define
4
80
sizeof char
define
define
4
80
262
char listapal
char linea
int i
sizeof
char
strcpy listapal i linea
Mostrar el contenido de la lista
for i 0 i
i
printf
i listapal i
Liberar memoria
for i 0 i
i
free listapal i
return 0
Fjate en cmo hemos definido listapal: como un vector esttico de 4 punteros a caract
eres
(char listapal
).
Vamos a ilustrar el uso de este tipo de estructuras de datos con la escritur
a de
una funcin que reciba una cadena y devuelva un vector de palabras, es decir, vamo
s
a implementar la funcionalidad que ofrece Python con el mtodo split. Empecemos po
r
considerar la cabecera de la funcin, a la que llamaremos extrae palabras. Est clar
o
que uno de los parmetros de entrada es una cadena, o sea, un vector de caracteres
:
extrae palabras char frase
No hace falta suministrar la longitud de la cadena, pues sta se puede calcular co
n la
funcin strlen. Cmo representamos la informacin de salida? Una posibilidad es devolve
r
un vector de cadenas:
char
extrae palabras char frase
O sea, devolvemos un puntero ( ) a una serie de datos de tipo char , o sea, cade
nas.
Pero an falta algo: hemos de indicar explcitamente cuntas palabras hemos encontrado
:
char
extrae palabras char frase
int
numpals
Hemos recurrido a un parmetro adicional para devolver el segundo valor. Dicho parm
etro
es la direccin de un entero, pues vamos a modificar su valor. Ya podemos codifica
r el
cuerpo de la funcin. Empezaremos por contar las palabras, que sern series de carac
teres
separadas por blancos (no entraremos en mayores complicaciones acerca de qu es un
a
palabra).
char
extrae palabras char frase
int
numpals
int i lonfrase
lonfrase strlen frase
numpals 1
for i 0 i lonfrase 1 i
if frase i
frase i 1
num
pals
if frase 0
numpals
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
3
263
26
argv
if argc
3
printf
else
if strcmp argv 1
printf
else
printf
0
argv 2
0
s a l u d a
1
- n
2
n o m b r e
Ya podemos reservar memoria para el vector de cadenas, pero an no para cada una d
e
ellas:
char
extrae palabras char frase
int numpals
int i lonfrase
char palabras
lonfrase strlen frase
numpals 1
for i 0 i lonfrase 1 i
if frase i
frase i 1
numpal
s
if frase 0
palabras
numpals
malloc
numpals
sizeof char
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
264
264
Ahora pasamos a reservar memoria para cada una de las palabras y, tan pronto hag
amos
cada reserva, escribirla en su porcin de memoria:
char
extrae palabras char frase
int numpals
int i j inicio pal longitud pal palabra actual lonfrase
char palabras
lonfrase strlen frase
numpals 1
for i 0 i lonfrase 1 i
if frase i
frase i 1
numpals
if frase 0
numpals
palabras
malloc
palabra actual
i 0
if frase 0
while frase
mos blancos iniciales.
numpals
sizeof char
0
i
while i lonfrase
inicio pal i
while frase
i
remos la palabra.
lonfrase
Salta
lonfrase
Recor
palabra actual
lonfrase
Salta
Y pasamos a la siguiente.
return palabras
Buf! Complicado, verdad? Veamos cmo se puede usar la funcin desde el programa
principal:
E
E
include
include
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
265
265
cUJI
numero palabras
return 0
Ya? An no. Aunque este programa compila correctamente y se ejecuta sin problemas,
hemos de considerarlo incorrecto: hemos solicitado memoria con malloc y no la he
mos
liberado con free.
include
include
numero palabras
int
e enteros.
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
266
266
int i j k
Reserva de memoria
a malloc 3 sizeof int
for i 0 i 3 i
a i
malloc 3 sizeof int
for j 0 j 3 j
a i j
malloc 3 sizeof int
Inicializacin
for i 0 i 3 i
for j 0 j 3 j
for k 0 k 3 k
a i j k i j k
Impresin
for i 0 i 3 i
for j 0 j 3 j
for k 0 k 3 k
printf
i j k a i
k
Liberacin de memoria.
for i 0 i 3 i
for j 0 j 3 j
free a i j
free a i
free a
a
return 0
En la siguiente figura se muestra el estado de la memoria tras la inicializac
in de la
matriz tridimensional:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
267
267
0 1 2
0
1 2 3
0
2 3 4
0
2
0
a
0
1 2 3
0
2
1
2 3 4
2
0
3 4 5
0
2 3 4
0
3 4 5
0
4 5 6
void
realloc void
a
malloc 10
o para 10 enteros.
a
realloc a 20
para que quepan 20.
a
realloc a 5
duce a slo 5 (los 5 primeros).
sizeof int
sizeof int
sizeof int
Se pide espaci
Ahora se ampla
Y ahora se re
free a
return 0
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
68
2
26
La funcin realloc recibe como primer argumento el puntero que indica la zona de m
emoria que deseamos redimensionar y como segundo argumento, el nmero de bytes que
deseamos asignar ahora. La funcin devuelve el puntero a la nueva zona de memoria.
Qu hace exactamente realloc? Depende de si se pide ms o menos memoria de la
que ya se tiene reservada:
Si se pide ms memoria, intenta primero ampliar la zona de memoria asigna
da. Si
las posiciones de memoria que siguen a las que ocupa a en ese instante
estn
libres, se las asigna a a, sin ms. En caso contrario, solicita una nueva
zona de
memoria, copia el contenido de la vieja zona de memoria en la nueva, li
bera la vieja
zona de memoria y nos devuelve el puntero a la nueva.
Si se pide menos memoria, libera la que sobra en el bloque reservado. U
n caso
extremo consiste en llamar a realloc con una peticin de 0 bytes. En tal
caso, la
llamada a realloc es equivalente a free.
Al igual que malloc, si realloc no puede atender una peticin, devuelve un puntero
a
. Una cosa ms: si se llama a realloc con el valor
como primer parmet
ro,
realloc se comporta como si se llamara a malloc.
Como puedes imaginar, un uso constante de realloc puede ser una fuente de in
eficiencia. Si tienes un vector que ocupa un 1 megabyte y usas realloc para que ocu
pe 1.1
megabyes, es probable que provoques una copia de 1 megabyte de datos de la zona
vieja
a la nueva. Es ms, puede que ni siquiera tengas memoria suficiente para efectuar
la
nueva reserva, pues durante un instante (mientras se efecta la copia) estars usand
o 2.1
megabytes.
Desarrollemos un ejemplo para ilustrar el concepto de reasignacin o redimensi
onamiento de memoria. Vamos a disear un mdulo que permita crear diccionarios de
palabras. Un diccionario es un vector de cadenas. Cuando creamos el diccionario,
no
sabemos cuntas palabras albergar ni qu longitud tiene cada una de las palabras.
Tendremos que usar, pues, memoria dinmica. Las palabras, una vez se introducen en
el
diccionario, no cambian de tamao, as que bastar con usar malloc para gestionar sus
reservas de memoria. Sin embargo, la talla de la lista de palabras s vara al aadir
palabras, as que deberemos gestionarla con malloc/realloc.
Empecemos por definir el tipo de datos para un diccionario.
struct Diccionario
char
palabra
int palabras
Aqu tienes un ejemplo de diccionario que contiene 4 palabras:
palabra
0
a n u a l
palabras
4
1
d a d i v o s o
2
m a n o
3
t a c o
269
269
d palabras
return d
pal
pal
int i
if d palabra
for i 0 i d palabras i
free d palabra i
free d palabra
d palabra
d palabras 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
239 Disea una funcin que devuelva cierto (valor 1) o falso (valor 0) en funcin de
Introduccin a la prog
c
UJI
vector 10
escalar
puntero
otro puntero
242 Disea una funcin que muestre por pantalla todas la palabras del diccionario
que acaban con un sufijo dado (una cadena).
...............................................................................
...
La funcin que determina si una palabra pertenece o no a un diccionario requie
re
tanto ms tiempo cuanto mayor es el nmero de palabras del diccionario. Es as porque
el diccionario est desordenado y, por tanto, la nica forma de estar seguros de que
una palabra no est en el diccionario es recorrer todas y cada una de las palabras
(si,
por contra, la palabra est en el diccionario, no siempre es necesario recorrer el
listado
completo).
Podemos mejorar el comportamiento de la rutina de bsqueda si mantenemos el
diccionario siempre ordenado. Para ello hemos de modificar la funcin de insercin d
e
palabras en el diccionario:
pal
pal
Si ya
Encont
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
271
2
71
.
for j d palabras j i j
d palabra j
malloc strlen d palabra j 1
strcpy d palabra j d palabra j 1
free d palabra j 1
1 sizeof char
for j d palabras j i i
d palabra j
d palabra j 1
Y copiamos en su celda la nueva palabra
d palabra i
malloc strlen pal 1
sizeof char
strcpy d palabra i pal
d palabras
No est mal, pero no hemos pedido ni liberado memoria dinmica! Ni siquiera hemos
usado strcpy, y eso que dijimos que haba que usar esa funcin para asignar una cade
na
a otra. Cmo es posible? Antes hemos de comentar qu significa una asignacin como
sta:
d
palabra j
d
palabra j 1
Significa que d palabra j apunta al mismo lugar al que apunta d palabra j 1 .
Por qu? Porque un puntero no es ms que una direccin de memoria y asignar a un
puntero el valor de otro hace que ambos contengan la misma direccin de memoria, e
s
decir, que ambos apunten al mismo lugar.
Veamos qu pasa estudiando un ejemplo. Imagina un diccionario en el que ya hem
os
insertado las palabras anual, dadivoso, mano y taco y que vamos a insertar ahora
la palabra feliz. Partimos, pues, de esta situacin:
palabra
0
a n u a l
palabras
4
1
d a d i v o s o
2
m a n o
3
t a c o
Una vez hemos redimensionado el vector de palabras, tenemos esto:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
272
272
palabra
0
a n u a l
palabras
4
1
d a d i v o s o
2
m a n o
3
t a c o
4
Podemos reordenar grficamente los elementos, para ver que, efectivamente, estamos
haciendo hueco para la nueva palabra:
palabra
0
a n u a l
palabras
4
1
d a d i v o s o
2
3
m a n o
4
t a c o
El bucle ha acabado. Ahora se pide memoria para el puntero d palabra i (siendo i
igual a 2). Se piden 6 bytes (feliz tiene 5 caracteres ms el terminador nulo):
273
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
roduccin a la programacin con C - UJI
c
UJI
273
Int
palabra
0
a n u a l
palabras
4
1
d a d i v o s o
2
3
m a n o
4
t a c o
Y, finalmente, se copia en d palabra i el contenido de pal con la
se
incrementa el valor de d palabras:
palabra
0
a n u
palabras
5
1
d a d
2
f e l
3
m a n
4
t a c
funcin strcpy y
a l
i v o s o
i z
o
o
274
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C
uccin a la programacin con C - UJI
c
UJI
274
Introd
return 0
Podemos hacer una pequea mejora para evitar el sobrecoste de llamar dos veces
a
la funcin strcmp:
int buscar en diccionario struct Diccionario d char pal
int izquierda centro derecha comparacion
izquierda 0
derecha d palabras
while izquierda derecha
centro
izquierda derecha 2
comparacion strcmp pal d palabra centro
if comparacion
0
return 1
else if comparacion 0
derecha centro
else
izquierda centro 1
return 0
Juntemos todas las piezas y aadamos una funcin main que nos pida primero las
palabras del diccionario y, a continuacin, nos pida palabras que buscar en l:
include
include
include
define
80
struct Diccionario
char
palabra
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
troduccin a la programacin con C -UJI
cUJI
275
275
In
int palabras
struct Diccionario crea diccionario void
struct Diccionario d
d palabra
d palabras 0
return d
void libera diccionario struct Diccionario
int i
if d palabra
for i 0 i d palabras i
free d palabra i
free d palabra
d palabra
d palabras 0
pal
pal
0
0
Si ya
Aqu he
r.
for j d palabras j i j
d palabra j
malloc strlen d palabra j 1
strcpy d palabra j d palabra j 1
free d palabra j 1
Y copiamos en su celda la nueva palabra
d palabra i
malloc strlen pal 1
sizeof char
strcpy d palabra i pal
d palabras
1 sizeof char
izquierda 0
derecha d palabras
while izquierda derecha
Introduccin
Andrs aMarzal/Isabel
la programacin con -C ISBN: 978-84-693-0143-2
Gracia
276
276
centro
izquierda derecha 2
comparacion strcmp pal d palabra centro
if comparacion
0
return 1
else if comparacion 0
derecha centro
else
izquierda centro 1
return 0
int main void
struct Diccionario mi diccionario
int num palabras
char linea
1
mi diccionario
crea diccionario
printf
gets linea sscanf linea
num palabras
while mi diccionario palabras
num palabras
printf
mi diccionario palabras 1
gets linea
inserta palabra en diccionario mi diccionario linea
do
printf
gets linea
if strlen linea 0
if buscar en diccionario mi diccionario linea
printf
else
printf
while strlen linea
libera diccionario
0
mi diccionario
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
243 Cuntas comparaciones se hacen en el peor de los casos en la bsqueda dicotmica de una palabra cualquiera en un diccionario con 8 palabras? Y en un dicciona
rio
con 16 palabras? Y en uno con 32? Y en uno con 1024? Y en uno con 1048576? (Nota:
el valor 1048576 es igual a 220 .)
244 Al insertar una nueva palabra en un diccionario hemos de comprobar si exista
previamente y, si es una palabra nueva, averiguar en qu posicin hay que insertarla
. En
la ltima versin presentada, esa bsqueda se efecta recorriendo el diccionario palabra
a palabra. Modifcala para que esa bsqueda sea dicotmica.
245 Disea una funcin que funda dos diccionarios ordenados en uno slo (tambin
ordenado) que se devolver como resultado. La fusin se har de modo que las palabras
que estn repetidas en ambos diccionarios aparezcan una sla vez en el diccionario f
inal.
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
277
277
ramacin con C - UJI
Introduccin a la prog
c
UJI
Para que te hagas una idea del montaje, te mostramos la representacin grfica d
e
las estructuras de datos con las que representamos la agenda del ejemplo:
0
1
2
3 4 5
6 7
8
9 10
P e p e
e r e z \0
0
4
10
A n a
G a r c a \0
2
8
J u a
G i l \0
0
M a r a
persona
P a z \0
nombre
nombre
personas
4
telefono
telefono
telefonos telefonos
2
3
2
3 4
5 6
7
nombre
nombre
telefono
telefono
telefonos
telefonos
0
1
8
9 10
0
10
0
10
1
10
2
9
0
9
1
Introduccin a la programacin c
Entradas
struct Entrada
char nombre
char
telefono
int telefonos
Nombre de la persona.
Vector dinmico de nmeros de telfono.
Nmero de elementos en el anterior vecto
r.
void crea entrada struct Entrada e char nombre
Inicializa una entrada con un nombre. La lista de telfonos se pone a
.
Reservamos memoria para el nombre y efectuamos la asignacin.
e nombre malloc strlen nombre 1 sizeof char
strcpy e nombre nombre
e
e
telefono
telefonos
nombre
telefonos i
i 1 e
int i
free e nombre
for i 0 i e telefonos i
free e telefono i
free e telefono
e
e
e
nombre
telefono
telefonos
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
...............................................................................
...
Ya tenemos resuelta la gestin de entradas. Ocupmonos ahora del tipo agenda y d
e
su gestin.
Agenda
struct Agenda
struct Entrada
int personas
persona
Vector de entradas
Nmero de entradas en el vector
a char
nombre
int i
Averiguar si ya tenemos una persona con ese nombre
for i 0 i a personas i
if strcmp a persona i nombre nombre
0
return
Si llegamos aqu, es porque no tenamos registrada a esa persona.
a persona realloc a persona a personas 1
sizeof struct Ent
rada
crea entrada a persona a personas nombre
a personas
void muestra agenda struct Agenda
Pasamos a as por eficiencia.
int i
for i 0 i a personas i
muestra entrada a persona i
struct Entrada
a char nombre
int i
for i 0 i a personas i
if strcmp a persona i nombre nombre
return a persona i
Si llegamos aqu, no lo hemos encontrado. Devolvemos
para indicar que no
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
280
280
int i
for i 0 i a personas i
libera entrada a persona i
free a persona
a persona
a personas 0
Fjate en el prototipo de buscar entrada por nombre: devuelve un puntero a un
dato
de tipo struct Entrada. Es un truco bastante utilizado. Si no existe una persona
con el
nombre indicado, se devuelve un puntero a
, y si existe, un puntero a es
a persona.
Ello nos permite, por ejemplo, mostrarla a continuacin llamando a la funcin que mu
estra
entradas, pues espera un puntero a un struct Entrada. Ahora vers cmo lo hacemos en
el programa principal.
Un lenguaje para cada tarea
Acabas de ver que el tratamiento de cadenas en C es bastante primitivo y
nos obliga
a estar pendientes de numerosos detalles. La memoria que ocupa cada caden
a ha de
ser explcitamente controlada por el programador y las operaciones que pode
mos hacer
con ellas son, en principio, primitivas. Python, por contra, libera al pr
ogramador de
innumerables preocupaciones cuando trabaja con objetos como las cadenas o
los vectores
dinmicos (sus listas). Adems, ofrece de fbrica numerosas utilidades para manip
ular
cadenas (cortes, funciones del mdulo string, etc.) y listas (mtodo append,
sentencia del,
cortes, ndices negativos, etc.). Por qu no usamos siempre Python? Por eficie
ncia. C
permite disear, por regla general, programas mucho ms eficientes que sus eq
uivalentes
en Python. La mayor flexibilidad de Python tiene un precio.
Antes de programar una aplicacin, hemos de preguntarnos si la eficienc
ia es un
factor crtico. Un programa con formularios (con un interfaz grfico) y/o acc
esos a una
base de datos funcionar probablemente igual de rpido en C que en Python, ya
que el
cuello de botella de la ejecucin lo introduce el propio usuario con su (le
nta) velocidad
de introduccin de datos y/o el sistema de base de datos al acceder a la in
formacin.
En tal caso, parece sensato escoger el lenguaje ms flexible, el que permit
a desarrollar
la aplicacin con mayor facilidad. Un programa de clculo matricial, un siste
ma de
adquisicin de imgenes para una cmara de vdeo digital, etc. han de ejecutarse
a una
velocidad que (probablemente) excluya a Python como lenguaje para la impl
ementacin.
200
40
80
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
281
281
enum
Programa principal
int main void
struct Agenda miagenda
struct Entrada encontrada
int opcion
char nombre
1
char telefono
1
char linea
1
miagenda
crea agenda
do
printf
printf
printf
printf
printf
printf
printf
gets linea
sscanf linea
opcion
switch opcion
case Ver
muestra agenda
break
case AltaPersona
printf
gets nombre
anyadir persona
break
miagenda
miagenda nombre
case AnyadirTelefono
printf
gets nombre
encontrada buscar entrada por nombre
nombre
if encontrada
printf
nombre
printf
nombre
else
printf
gets telefono
anyadir telefono a entrada encontrada telefono
break
case Buscar
miagenda
printf
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
282
282
gets nombre
encontrada buscar entrada por nombre
miagenda nombre
if encontrada
printf
nombre
else
muestra entrada encontrada
break
while opcion
libera agenda
Salir
miagenda
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
247 Disea una funcin que permita eliminar una entrada de la agenda a partir del
nombre de una persona.
248 La agenda, tal y como la hemos implementado, est desordenada. Modifica el
programa para que est siempre ordenada.
...............................................................................
...
lista
3 8 2
Pero slo conceptualmente. En la implementacin, el nivel de complejidad al que nos
enfrentamos es mucho mayor. Eso s, a cambio ganaremos en flexibilidad y podremos of
recer
versiones eficientes de ciertas operaciones sobre listas de valores que implemen
tadas con
vectores dinmicos seran muy costosas. Por otra parte, aprender estas tcnicas supone
una inversin a largo plazo: muchas estructuras dinmicas que estudiars en cursos de
Estructuras de Datos se basan en el uso de registros enlazados y, aunque mucho ms
complejas que las simples secuencias de valores, usan las mismas tcnicas bsicas de
gestin de memoria que aprenders ahora.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
283
283
ramacin con C - UJI
Introduccin a la prog
c
UJI
lla
struct
crea VDH void
struct
vdh
vdh dato malloc 1 sizeof int
0 vdh capacidad
1
return vdh
void anyade dato struct
capacidad
if vdh
vdh
vdh
sizeof
talla
vdh capacidad
capacidad 2
dato realloc vdh dato vdh
int
vdh ta
vdh
dato vdh
talla
undato
vdh
talla
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
284
284
Para ir aprendiendo a usar estas tcnicas, gestionaremos ahora una lista con r
egistros
enlazados. La manejaremos directamente, desde un programa principal, y nos centr
aremos
en la realizacin de una serie de acciones que parten de un estado de la lista y l
a dejan
en otro estado diferente. Ilustraremos cada una de las acciones mostrando con to
do detalle
qu ocurre paso a paso. En el siguiente apartado encapsularemos cada accin elemental
(aadir elemento, borrar elemento, etc.) en una funcin independiente.
Vamos a crear una lista de enteros. Empezamos por definir el tipo de los registr
os que
componen la lista:
struct Nodo
int info
struct Nodo
sig
Un registro de tipo struct Nodo consta de dos elementos:
un entero llamado info, que es la informacin que realmente nos importa,
y un puntero a un elemento que es. . . otro struct Nodo! (Observa que h
ay cierto
nivel de recursin o autoreferencia en la definicin: un struct Nodo conti
ene un
puntero a un struct Nodo. Por esta razn, las estructuras que vamos a es
tudiar se
denominan a veces estructuras recursivas.)
Si quisiramos manejar una lista de puntos en el plano, tendramos dos opcio
nes:
1.
sig
x
sig
sig
sig
1.1
0.2
7.1
0.1
3.7
lista
y
2.1
2.
struct Nodo
struct Punto info
struct Nodo sig
info
g
info
sig
info
si
sig
x
x
1.1
0.2
3.7
y
y
lista
7.1
0.1
2.1
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
285
285
lista
lista
lista
malloc sizeof struct Nodo
ste es el resultado:
info
sig
lista
Ya tenemos el primer nodo de la lista, pero sus campos an no tienen los valores q
ue
deben tener finalmente. Lo hemos representado grficamente dejando el campo info e
n
blanco y sin poner una flecha que salga del campo sig.
Por una parte, el campo info debera contener el valor 8, y por otra, el campo
sig
debera apuntar a
:
int main void
struct Nodo
lista
lista
lista
lista
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
6
286
28
sig
sig
lista
8
En primer lugar, hemos de crear un nuevo nodo al que deber apuntar lista. El
campo sig del nuevo nodo, por su parte, debera apuntar al nodo que contiene el va
lor 8.
Empecemos por la peticin de un nuevo nodo que, ya que debe ser apuntado por lista
,
podemos pedir y rellenar as:
int main void
struct Nodo
lista
lista
lista
lista
malloc sizeof struct Nodo
info 3
sig
No sabemos cmo expresar esta asignacin.
Algo ha ido mal! Cmo podemos asignar a lista sig la direccin del siguiente nodo
con valor 8? La situacin en la que nos encontramos se puede representar as:
info sig
3
lista
info
8
sig
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
287
287
lista
aux
aux lista
lista malloc sizeof struct Nodo
lista info 3
lista sig aux
La declaracin de la lnea 3 es curiosa. Cuando declaras dos o ms punteros en una
sola lnea, has de poner el asterisco delante del identificador de cada puntero. E
n una
lnea de declaracin que empieza por la palabra int puedes declarar punteros a enter
os y
enteros, segn precedas los respectivos identificadores con asterisco o no. Detengm
onos
un momento para considerar el estado de la memoria justo despus de ejecutarse la
lnea
6, que reza aux lista:
aux
info
8
lista
sig
El efecto de la lnea 6 es que tanto aux como lista apuntan al mismo registro
. La
asignacin de un puntero a otro hace que ambos apunten al mismo elemento. Recuerda
que un puntero no es ms que una direccin de memoria y que copiar un puntero a otro
hace que ambos contengan la misma direccin de memoria, es decir, que ambos apunte
n
al mismo lugar.
Sigamos con nuestra traza. Veamos cmo queda la memoria justo despus de ejecut
ar
la lnea 7, que dice lista malloc sizeof struct Nodo :
aux
info
nfo
sig
sig
lista
8
info
aux
info
nfo
sig
sig
lista
8
La lista an no est completa, pero observa que no hemos perdido la referencia a
l
ltimo fragmento de la lista. El puntero aux la mantiene. Nos queda por ejecutar l
a lnea
9, que efecta la asignacin lista sig aux y enlaza as el campo sig del primer nodo
con el segundo nodo, el apuntado por aux. Tras ejecutarla tenemos:
aux
info
nfo
sig
sig
lista
8
Perfecto! Seguro? Y qu hace aux apuntando an a la lista? La verdad, nos da
igual. Lo importante es que los nodos que hay enlazados desde lista formen la li
sta
que queramos construir. No importa cmo quedan los punteros auxiliares: una vez han
desempeado su funcin en la construccin de la lista, son suprfluos. Si te quedas ms
tranquilo, puedes aadir una lnea con aux
al final del programa para que aux
no
quede apuntando a un nodo de la lista, pero, repetimos, es innecesario.
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
288
288
lista
aux
sig
sig
lista
8
O sea, la lista que cuelga de lista sigue igual, pero ahora aux apunta a un nu
evo nodo. Pasemos a estudiar la lnea 7, que parece complicada porque contiene vari
as
aplicaciones del operador . Esa lnea reza as: lista sig sig aux. Vamos a ver qu
significa leyndola de izquierda a derecha. Si lista es un puntero, y lista sig es
el
campo sig del primer nodo, que es otro puntero, entonces lista sig sig es el cam
po sig
del segundo nodo, que es otro puntero. Si a ese puntero le asignamos aux, el cam
po sig
del segundo nodo apunta a donde apuntar aux. Aqu tienes el resultado:
info
sig
info
sig
aux
info
sig
lista
8
An no hemos acabado. Una vez hayamos ejecutado las lneas 8 y 9, el trabajo esta
r
completo:
aux
info
info
2
sig
info
sig
sig
lista
8
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
289
289
cUJI
Y es so lo que buscbamos? S. Reordenemos grficamente los diferentes componentes para que su disposicin en la imagen se asemeje ms a lo que esperbamos:
aux
sig
info
sig
3
info
lista
sig
info
8
2
Ahora queda ms claro que, efectivamente, hemos conseguido el objetivo. Esta f
igura
y la anterior son absolutamente equivalentes.
An hay algo en nuestro programa poco elegante: la asignacin lista sig sig aux
es complicada de entender y da pie a un mtodo de adicin por el final muy poco extensible. Qu queremos decir con esto ltimo? Que si ahora queremos aadir a la lista
de 3 nodos un cuarto nodo, tendremos que hacer lista sig sig sig aux. Y si quisiramos aadir un quinto, lista sig sig sig sig aux Imagina que la lista tiene
100 o 200 elementos. Menuda complicacin proceder as para aadir por el final! No
podemos expresar la idea aadir por el final de un modo ms elegante y general? S.
Podemos hacer lo siguiente:
1. buscar el ltimo elemento con un bucle y mantenerlo referenciado con un punt
ero
auxiliar, digamos aux;
aux
info
info
sig
sig
lista
8
2.
gamos
sig
sig
lista
8
info
sig
nuevo
3.
po sig
escribir en el nodo apuntado por nuevo el nuevo dato y hacer que su cam
apunte a
;
aux
info
info
sig
sig
lista
8
nuevo
4.
info
2
sig
hacer que el nodo apuntado por aux tenga como siguiente nodo al nodo ap
untado
por nuevo.
aux
info
sig
info
sig
lista
8
info
sig
nuevo
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
290
290
n C -UJI
cUJI
Introduccin a la programacin co
sig
info
lista
sig
sig
3
2
nuevo
Modifiquemos el ltimo programa para expresar esta idea:
int main void
struct Nodo
lista
aux
nuevo
aux lista
while aux sig
aux aux sig
nuevo malloc sizeof struct Nodo
nuevo info 2
nuevo sig
aux sig nuevo
return 0
La inicializacin y el bucle de las lneas 68 buscan al ltimo nodo de la lista y l
o
mantienen apuntado con aux. El ltimo nodo se distingue porque al llegar a l, aux s
ig
es
, de ah la condicin del bucle. No importa cun larga sea la lista: tanto si
tiene
1 elemento como si tiene 200, aux acaba apuntando al ltimo de ellos.5 Si partimos
de
una lista con dos elementos, ste es el resultado de ejecutar el bucle:
aux
info
info
sig
sig
lista
8
nuevo
Las lneas 911 dejan el estado de la memoria as:
aux
info
info
sig
sig
lista
8
nuevo
info
2
sig
aux
info
info
sig
sig
lista
8
info
2
nuevo
sig
5
Aunque falla en un caso: si la lista est inicialmente vaca. Estudiaremos este
problema y su solucin
ms adelante.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
291
291
-UJI
cUJI
lista
aux
nuevo
aux
return 0
Observa que el punto y coma que aparece al final del bucle for hace que no tenga
sentencia alguna en su bloque:
for aux
lista aux
sig
aux
aux
sig
El bucle se limita a desplazar el puntero aux hasta que apunte al ltimo elemento
de la lista. Esta expresin del bucle que busca el elemento final es ms propia de l
a
programacin C, ms idiomtica.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
249 Hemos diseado un mtodo (que mejoraremos en el siguiente apartado) que
permite insertar elementos por el final de una lista y hemos necesitado un bucle
. Har
falta un bucle para insertar un elemento por delante en una lista cualquiera? Cmo
haras para convertir la ltima lista en esta otra?:
info
sig
info sig
info
sig
info sig
lista
1
3
8
2
...............................................................................
...
Vamos a aprender ahora a borrar elementos de una lista. Empezaremos por ver cmo
eliminar el primer elemento de una lista. Nuestro objetivo es, partiendo de esta
lista:
info sig
i
nfo
sig
info sig
lista
3
8
2
llegar a esta otra:
info
info
sig
sig
lista
2
Como lo que deseamos es que lista pase a apuntar al segundo elemento de la li
sta,
podramos disear una aproximacin directa modificando el valor de lista:
lista
lista
lista
sig
la cabeza original de la lista.
aux
nuevo
return 0
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
292
292
ramacin con C - UJI
Introduccin a la prog
c
UJI
sig
info
sig
Efectivamente, hemos conseguido que la lista apuntada por lista sea lo que p
retendamos, pero hemos perdido la referencia a un nodo (el que hasta ahora era el prim
ero)
lista
aux
nuevo
!
free lista
lista lista
moria vlida.
sig
return 0
Pero, claro, no iba a resultar tan sencillo. La lnea 7, que dice lista lista sig,
no
puede ejecutarse! Tan pronto hemos ejecutado la lnea 6, tenemos otra fuga de memo
ria:
info
info
sig
sig
lista
2
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
293
293
O sea, hemos liberado correctamente el primer nodo, pero ahora hemos perdido
la
referencia al resto de nodos y el valor de lista sig est indefinido. Cmo podemos
arreglar esto? Si no liberamos memoria, hay una fuga, y si la liberamos perdemos
la
referencia al resto de la lista. La solucin es sencilla: guardamos una referencia
al resto
de la lista con un puntero auxiliar cuando an estamos a tiempo.
int main void
struct Nodo
lista
aux lista
free lista
lista aux
aux
nuevo
sig
return 0
Ahora s. Veamos paso a paso qu hacen las ltimas tres lneas del programa. La
asignacin aux lista sig introduce una referencia al segundo nodo:
aux
info
ig
info
sig
lista
sig
info
s
8
2
Al ejecutar free lista , pasamos a esta otra situacin:
aux
info
ig
info
sig
lista
s
8
2
No hay problema. Seguimos sabiendo dnde est el resto de la lista: cuelga de
aux. As pues, podemos llegar al resultado deseado con la asignacin lista aux:
aux
info
info
sig
sig
lista
2
Vas viendo ya el tipo de problemas al que nos enfrentamos con la gestin de lis
tas?
Los siguientes apartados te presentan funciones capaces de inicializar listas, d
e insertar,
borrar y encontrar elementos, de mantener listas ordenadas, etc. Cada apartado t
e presentar una variante de las listas enlazadas con diferentes prestaciones que permiten
elegir
soluciones de compromiso entre velocidad de ciertas operaciones, consumo de memo
ria y
complicacin de la implementacin.
sig
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
294
294
Como ya dijimos, este tipo de nodo slo alberga un nmero entero. Si necesitsemos una
lista de float deberamos cambiar el tipo del valor del campo info. Y si quisisemos
una
lista de personas, podramos aadir varios campos a struct Nodo (uno para el nombre,
otro para la edad, etc.) o declarar info como de un tipo struct Persona definido
previamente
por nosotros.
Una lista es un puntero a un struct Nodo, pero cuesta poco definir un nuevo
tipo para
referirnos con mayor brevedad al tipo lista:
TipoLista
Ahora, podemos declarar una lista como struct Nodo o como TipoLista, indistintam
ente.
Por claridad, nos referiremos al tipo de una lista con TipoLista y al de un punt
ero a un
nodo cualquiera con struct Nodo , pero no olvides que ambos tipos son equivalent
es.
Definicin de struct con typedef
Hay quienes, para evitar la escritura repetida de la palabra struct, recu
rren a la inmediata creacin de un nuevo tipo tan pronto se define el struct. Este cdigo,
por ejemplo,
hace eso:
typedef struct Nodo
int info
struct Nodo sig
TipoNodo
Como struct Nodo y TipoNodo son sinnimos, pronto se intenta definir la est
ructura
as:
typedef struct Nodo
!
int info
TipoNodo sig
TipoNodo
Mal!
Nuestra primera funcin crear una lista vaca. El prototipo de la funcin, que declaram
os
en la cabecera
, es ste:
, resulta
trivial:
include
include
TipoLista lista vacia void
return
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
295
295
lista vacia
return 0
Ciertamente podramos haber hecho lista
, sin ms, pero queda ms elegante
proporcionar funciones para cada una de las operaciones bsicas que ofrece una lis
ta, y
crear una lista vaca es una operacin bsica.
Nos vendr bien disponer de una funcin que devuelva cierto o falso en funcin de si l
a
lista est vaca o no. El prototipo de la funcin es:
Ahora vamos a crear una funcin que inserta un elemento en una lista por la cabeza
, es
decir, haciendo que el nuevo nodo sea el primero de la lista. Antes, veamos cul e
s el
prototipo de la funcin:
lista vacia
inserta por cabeza lista 2
inserta por cabeza lista 8
inserta por cabeza lista 3
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
ntroduccin a la programacin con C -UJI
cUJI
296
296
3
return 0
Vamos con la implementacin de la funcin. La funcin debe empezar pidiendo un
nuevo nodo para el nmero que queremos insertar.
TipoLista inserta por cabeza TipoLista lista int valor
struct Nodo
nuevo
nuevo
info
valor
Ahora hemos de pensar un poco. Si lista va a tener como primer elemento a nuevo,
podemos enlazar directamente lista con nuevo?
TipoLista inserta por cabeza TipoLista lista int valor
mal
struct Nodo
nuevo
sig
info
sig
lista
Hemos perdido la referencia a la vieja lista. Una solucin sencilla consiste en, ant
es
de modificar lista, asignar a nuevo sig el valor de lista:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
297
297
nuevo
sig
info
sig
nuevo
info
sig
lista
2
Las lneas 5 y 6 modifican los campos del nodo apuntado por nuevo:
info
sig
nuevo
3
info
info
sig
sig
lista
2
Finalmente, la lnea 7 hace que lista apunte a donde nuevo apunta. El resultad
o final
es ste:
info
sig
nuevo
3
info
info
sig
sig
lista
2
Slo resta redisponer grficamente la lista para que no quepa duda de la correccin de
la solucin:
nuevo
info
sig
info
lista
2
sig
info
sig
3
Hemos visto, pues, que el mtodo es correcto cuando la lista no est vaca. Lo ser
tambin si suministramos una lista vaca? La lista vaca es un caso especial para el q
ue
siempre deberemos considerar la validez de nuestros mtodos.
Hagamos una comprobacin grfica. Si partimos de esta lista:
lista
y ejecutamos la funcin (con valor igual a 10, por ejemplo), pasaremos momentneamen
te
por esta situacin:
info
sig
nuevo
10
lista
y llegaremos, al final, a esta otra:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
298
298
nuevo
info
10
lista
sig
Nos interesa conocer ahora la longitud de una lista. La funcin que disearemos reci
be
una lista y devuelve un entero:
aux
0
aux
aux
info
sig
sig
sig
info
sig
sig
lista
2
En la primera iteracin, contador se incrementa en una unidad y aux pasa a apuntar
al
segundo nodo:
aux
info
info
sig
info
sig
sig
lista
2
Acto seguido, en la segunda iteracin, contador pasa a valer 2 y aux pasa a apunta
r al
tercer nodo:
aux
info
info
sig
info
sig
sig
lista
2
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
99
Introduccin a la programacin con C - UJI
Introduccin a la programacin con C
c
UJI
2
299
info
lista
2
sig
info
sig
3
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
250 Funcionar correctamente longitud lista cuando le pasamos una lista vaca?
251 Disea una funcin que reciba una lista de enteros con enlace simple y devuelva
el valor de su elemento mximo. Si la lista est vaca, se devolver el valor 0.
252 Disea una funcin que reciba una lista de enteros con enlace simple y devuelva
su media. Si la lista est vaca, se devolver el valor 0.
...............................................................................
...
Ahora que sabemos recorrer una lista no resulta en absoluto difcil disear un proce
dimiento que muestre el contenido de una lista en pantalla. El prototipo es ste:
aux
lista aux
aux
aux
sig
printf
aux
info
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
253 Disea un procedimiento que muestre el contenido de una lista al estilo Python
.
Por ejemplo, la lista de la ltima figura se mostrar como 3 8 2 . Fjate en que la
coma slo aparece separando a los diferentes valores, no despus de todos los nmeros.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
300
300
ramacin con C - UJI
Introduccin a la prog
c
UJI
254 Disea un procedimiento que muestre el contenido de una lista como se indica
en el siguiente ejemplo. La lista formada por los valores 3, 8 y 2 se representa
r as:
Diseemos ahora una funcin que inserte un nodo al final de una lista. Su prototipo
ser:
aux
lista aux
sig
aux
aux
sig
Analicemos paso a paso el bucle con un ejemplo. Imagina que la lista que nos
suministran en lista ya tiene tres nodos:
info
sig
info
sig
info sig
lista
3
8
2
La primera iteracin del bucle hace que aux apunte al primer elemento de la lista:
aux
info
info
sig
lista
sig
info
sig
2
Habr una nueva iteracin si aux sig es distinto de
, es decir, si el nodo apu
ntado por aux no es el ltimo de la lista. Es nuestro caso, as que iteramos haciendo
aux aux sig, o sea, pasamos a esta nueva situacin:
aux
info
info
sig
lista
sig
info
sig
2
Sigue siendo cierto que aux
? S. Avanzamos aux un nodo ms
a la derecha:
aux
sig es distinto de
info
info
sig
lista
sig
info
sig
2
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
301
301
aux
nuevo
aux
El efecto de la ejecucin de las nuevas lneas, suponiendo que el valor es 10, es ste
:
info
sig
nuevo
10
aux
info
fo
sig
info
sig
lista
sig
in
2
Est claro que ha funcionado correctamente, no? Tal vez resulte de ayuda ver la mis
ma
estructura reordenada as:
aux
nuevo
info
sig
lista
info
sig
info
sig
3
10
info
sig
Bien, entonces, por qu hemos marcado la funcin como incorrecta? Veamos qu ocurre
si la lista que nos proporcionan est vaca. Si la lista est vaca, lista vale
. En
la
primera iteracin del bucle for asignaremos a aux el valor de lista, es decir,
. Para
ver si pasamos a efectuar la primera iteracin, hemos de comprobar antes si aux si
g es
distinto de
. Pero es un error preguntar por el valor de aux sig cuando aux
es
! Un puntero a
no apunta a nodo alguno, as que no podemos preguntar p
or el
valor del campo sig de un nodo que no existe. Ojo con este tipo de errores!: los
accesos
a memoria que no nos pertenece no son detectables por el compilador. Se manifiesta
n
en tiempo de ejecucin y, normalmente, con consecuencias desastrosas6 , especialme
nte
al efectuar escrituras de informacin. Cmo podemos corregir la funcin? Tratando a la
lista vaca como un caso especial:
TipoLista inserta por cola TipoLista lista int valor
struct Nodo
aux
nuevo
if lista
lista malloc sizeof struct Nodo
lista info valor
6
En Linux, por ejemplo, obtendrs un error (tpicamente Segmentation fault) y se a
bortar inmediatamente la ejecucin del programa. En Microsoft Windows es frecuente que el ordenado
r se cuelgue.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
302
302
cUJI
lista
sig
else
for aux lista aux sig
aux
nuevo malloc sizeof struct Nodo
nuevo info valor
nuevo sig
aux sig nuevo
aux
sig
return lista
Como puedes ver, el tratamiento de la lista vaca es
Ya te
lo advertimos antes: comprueba siempre si tu funcin
situaciones extremas. La lista vaca es un caso para
ar
la validez de tu aproximacin.
La funcin puede retocarse factorizando acciones
if else:
aux
nuevo
aux
aux
sig
return lista
Mejor as.
La funcin que vamos a disear ahora recibe una lista y devuelve esa misma lista sin
el
nodo que ocupaba inicialmente la posicin de cabeza. El prototipo ser ste:
2
Tampoco podemos empezar haciendo free lista para liberar el primer nodo, pue
s
entonces perderamos la referencia al resto de nodos. La memoria quedara as:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
303
303
info
info
sig
info
sig
sig
lista
2
Quin apuntara entonces al primer nodo de la lista?
La solucin requiere utilizar un puntero auxiliar:
E
aux
aux
sig
return lista
Tenlo siempre presente: si usas la expresin aux
sig para cualquier puntero aux,
has de estar completamente seguro de que aux no es
.
Vamos a disear ahora una funcin que elimine el ltimo elemento de una lista. He aqu
su prototipo:
.
La primera fase consistir bsicamente en esto:
E
aux
3
304
aux
for aux
sig
lista aux
sig
aux
Alto! Este mismo bucle ya nos di problemas cuando tratamos de insertar por la cola
: no
funciona correctamente con listas vacas. De todos modos, el problema tiene fcil so
lucin:
no tiene sentido borrar nada de una lista vaca.
E
E
TipoLista borra cola TipoLista lista
struct Nodo
aux
if lista
for aux
aux
sig
lista aux
sig
aux
return lista
Ahora el bucle solo se ejecuta con listas no vacas. Si partimos de esta lista:
aux
info
o
sig
info
lista
2
sig
inf
sig
3
sig
info
lista
2
sig
inf
sig
3
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
255 Seguro que el bucle de borra cola funciona correctamente siempre? Piensa si
hace lo correcto cuando se le pasa una lista formada por un solo elemento.
...............................................................................
...
Si hemos localizado ya el ltimo nodo de la lista, hemos de liberar su memor
ia:
E
E
TipoLista borra cola TipoLista lista
struct Nodo
if lista
for aux lista aux
aux
sig
aux
sig
aux
free aux
info
sig
lista
8
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
305
305
c
UJI
Introduccin a la prog
ramacin con C - UJI
Fjate: slo nos falta conseguir que el nuevo ltimo nodo (el de valor igual a 8) teng
a
como valor del campo sig a
. Problema: y cmo sabemos cul es el ltimo nodo?
No se puede saber. Ni siquiera utilizando un nuevo bucle de bsqueda del ltimo nodo
,
ya que dicho bucle se basaba en que el ltimo nodo es reconocible porque tiene a
como valor de sig, y ahora el ltimo no apunta con sig a
.
El truco consiste en usar otro puntero auxiliar y modificar el bucle de bsqueda
del ltimo para haga que el nuevo puntero auxiliar vaya siempre un paso por detrs
de aux. Observa:
E
E
TipoLista borra cola TipoLista lista
struct Nodo
aux
atras
if lista
for atras
g
atras
aux
aux
aux
lista aux
aux sig
si
free aux
Fjate en el nuevo aspecto del bucle for. Utilizamos una construccin sintctica que an
no conoces, as que nos detendremos brevemente para explicarla. Los bucles for per
miten
trabajar con ms de una inicializacin y con ms de una accin de paso a la siguiente
iteracin. Este bucle, por ejemplo, trabaja con dos variables enteras, una que tom
a valores
crecientes y otra que toma valores decrecientes:
for i 0
j 10 i 3 i j
printf
i j
Ojo! Es un nico bucle, no son dos bucles anidados. No te confundas! Las diferentes
inicializaciones y pasos de iteracin se separan con comas. Al ejecutarlo, por pan
talla
aparecer esto:
Sigamos con el problema que nos ocupa. Veamos, paso a paso, qu hace ahora el
bucle. En la primera iteracin tenemos:
atras
aux
info
info
sig
info
lista
2
Y en la segunda iteracin:
atras
aux
info
sig
info
lista
2
sig
sig
sig
inf
sig
3
Y en la tercera:
atras
aux
info
o
sig
info
lista
2
sig
inf
sig
3
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
306
306
amacin con C - U JI
UJI
c
Introduccin a la progr
Ves? No importa cun larga sea la lista; el puntero atras siempre va un paso por de
trs
del puntero aux. En nuestro ejemplo ya hemos llegado al final de la lista, as que
ahora
podemos liberar el nodo apuntado por aux:
atras
aux
info
info
sig
sig
lista
8
Ahora podemos continuar: ya hemos borrado el ltimo nodo, pero esta vez s que sabem
os
cul es el nuevo ltimo nodo.
E
E
TipoLista borra cola TipoLista lista
struct Nodo
if lista
for atras
atras
free aux
atras sig
aux
aux aux
atras
aux
sig
aux
lista aux
sig
sig
sig
lista
8
An no hemos acabado. La funcin borra cola trabaja correctamente con la lista v
aca,
pues no hace nada en ese caso (no hay nada que borrar), pero, funciona correctame
nte
cuando suministramos una lista con un nico elemento? Hagamos una traza.
Tras ejecutar el bucle que busca a los elementos ltimo y penltimo, los puntero
s
atras y aux quedan as:
atras
aux
info
sig
lista
3
Ahora se libera el nodo apuntado por aux:
atras
aux
lista
Y, finalmente, hacemos que atras sig sea igual
. Pero, eso es imposi
ble! El
puntero atras apunta a
, y hemos dicho ya que
no es un nodo y, por
tanto, no
tiene campo alguno.
Tratemos este caso como un caso especial. En primer lugar, cmo podemos detecta
rlo?
Viendo si atras vale
. Y qu hemos de hacer entonces? Hemos de hacer que list
a
pase a valer
, sin ms.
TipoLista borra cola TipoLista lista
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
307
307
cUJI
struct Nodo
if lista
for atras
atras
free aux
if atras
lista
else
atras sig
aux
atras
aux
aux aux
aux
lista aux
sig
sig
return lista
Ya est. Si aplicsemos este nuevo mtodo, nuestro ejemplo concluira as:
atras
aux
lista
Hemos aprendido una leccin: otro caso especial que conviene estudiar explcitam
ente
es el de la lista compuesta por un solo elemento.
Insistimos en que debes seguir una sencilla regla en el diseo de funciones co
n
punteros: si accedes a un campo de un puntero ptr, por ejemplo, ptr sig o ptr in
fo,
pregntate siempre si cabe alguna posibilidad de que ptr sea
; si es as, tie
nes un
problema que debes solucionar.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
256 Funcionan correctamente las funciones que hemos definido antes (clculo de la
longitud, insercin por cabeza y por cola y borrado de cabeza) cuando se suministr
a una
lista compuesta por un nico elemento?
...............................................................................
...
Vamos a disear ahora una funcin que no modifica la lista. Se trata de una funcin qu
e
nos indica si un valor entero pertenece a la lista o no. El prototipo de la func
in ser
ste:
aux
aux
aux
ig
if aux info
return 1
return 0
valor
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
308
308
ramacin con C - UJI
Introduccin a la prog
c
UJI
El problema que abordamos ahora es el diseo de una funcin que recibe una lista y u
n
valor y elimina el primer nodo de la lista cuyo campo info coincide con el valor
.
aux
aux
aux
sig
if aux info
valor
return lista
Veamos con un ejemplo en qu situacin estamos cuando llegamos a la lnea marcada con puntos suspensivos. En esta lista hemos buscado el valor 8, as que podem
os
representar la memoria as:
aux
info
sig
info
sig
lista
2
sig
info
8
Nuestro objetivo ahora es, por una parte, efectuar el siguiente empalme entr
e nodos:
aux
info
sig
info
sig
lista
2
sig
info
8
sig
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
309
309
ramacin con C - UJI
Introduccin a la prog
c
UJI
aux
atras
for atras
atras
aux
if aux info
atras sig
aux
return lista
El puntero atras empieza apuntando a
y siempre va un paso por detrs de aux.
atras
aux
info
info
sig
info
lista
sig
sig
3
info
lista
2
sig
info
sig
3
sig
info
lista
sig
sig
3
sig
info
sig
sig
lista
8
3
2
aux
atras
for atras
atras
aux aux
if aux info
if atras
aux
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
310
310
on C -UJI
cUJI
Introduccin a la programacin c
sig
return lista
Ahora podemos borrar el elemento apuntado por aux con tranquilidad y devolver la
lista
modificada:
TipoLista borra primera ocurrencia TipoLista lista int valor
struct Nodo
atras
for atras
aux aux
if aux info
if atras
lista aux
else
atras sig
free aux
return lista
aux
aux lista aux
aux
valor
atras
sig
sig
aux sig
return lista
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
258 Funciona borra primera ocurrencia cuando ningn nodo de la lista contiene el
valor buscado?
259 Funciona correctamente en los siguientes casos?
lista vaca;
lista con un slo elemento que no coincide en valor con el buscado;
lista con un slo elemento que coincide en valor con el buscado.
Si no es as, corrige la funcin.
................................................................................
..
Borrar todos los nodos con un valor dado y no slo el primero es bastante ms compli
cado,
aunque hay una idea que conduce a una solucin trivial: llamar tantas veces a la f
uncin
que hemos diseado en el apartado anterior como elementos haya originalmente en la
lista. Pero, como comprenders, se trata de una aproximacin muy ineficiente: si la
lista
tiene n nodos, llamaremos n veces a una funcin que, en el peor de los casos, reco
rre la
lista completa, es decir, da n pasos para completarse. Es ms eficiente borrar todos
los
elementos de una sola pasada, en tiempo directamente proporcional a n.
Supn que recibimos esta lista:
info
sig
info
sig
sig
info
sig
info
sig
lista
3
8
8
1
info
2
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
311
311
ramacin con C - UJI
Introduccin a la prog
c
UJI
y nos piden eliminar todos los nodos cuyo campo info vale 8.
Nuestro problema es localizar el primer 8 y borrarlo dejando los dos puntero
s auxiliares en un estado tal que podamos seguir iterando para encontrar y borrar el s
iguiente
8 en la lista (y as con todos los que haya). Ya sabemos cmo localizar el primer 8.
Si
usamos un bucle con dos punteros (aux y atras), llegamos a esta situacin:
atras
aux
info
sig
sig
info
lista
info
sig
info
sig
sig
info
Si eliminamos el nodo apuntado por aux, nos interesa que aux pase a apuntar
al
siguiente, pero que atras quede apuntando al mismo nodo al que apunta ahora (sie
mpre
ha de ir un paso por detrs de aux):
atras
aux
info
lista
2
sig
info
info
3
8
sig
sig
info
sig
aux
atras
valor
sig
aux sig
sig
else
atras aux
aux aux sig
return lista
Hemos optado por un bucle while en lugar de un bucle for porque necesitamos un m
ayor
control de los punteros auxiliares (con el for, en cada iteracin avanzamos ambos
punteros
y no siempre queremos que avancen).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
260 Funciona borra valor con listas vacas? Y con listas de un slo elemento? Y con
una lista en la que todos los elementos coinciden en valor con el entero que bus
camos?
Si falla en alguno de estos casos, corrige la funcin.
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
312
312
a programacin con C - UJI
Introduccin a l
c
UJI
while y for
Hemos dicho que el bucle for no resulta conveniente cuando queremos tener
un gran
control sobre los punteros auxiliares. No es cierto. El bucle for de C pe
rmite emular a
cualquier bucle while. Aqu tienes una versin de borra valor (eliminacin de t
odos los
nodos con un valor dado) que usa un bucle for:
TipoLista borra valor TipoLista lista int valor
struct Nodo
for atras
if aux info
if atras
lista aux
else
atras sig
free aux
if atras
aux lista
else
aux atras
aux
atras
sig
else
atras aux
aux aux sig
return lista
Observa que en el bucle for hemos dejado en blanco la zona que indica cmo
modificar
los punteros aux y atras. Puede hacerse. De hecho, puedes dejar en blanco
cualquiera de
los componentes de un bucle for. Una alternativa a while 1 , por ejemplo,
es for
.
Vamos a disear una funcin que permite insertar un nodo en una posicin dada de la
lista. Asumiremos la siguiente numeracin de posiciones en una lista:
info
sig
info sig
info sig
lista
3
8
2
0
1
2
3
O sea, insertar en la posicin 0 es insertar una nueva cabeza; en la posicin 1,
un
nuevo segundo nodo, etc. Qu pasa si se quiere insertar un nodo en una posicin mayor
que la longitud de la lista? Lo insertaremos en ltima posicin.
El prototipo de la funcin ser:
extern TipoLista inserta en posicion TipoLista lista int pos int valor
y aqu tienes su implementacin:
TipoLista inserta en posicion TipoLista lista int pos int valor
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
13
313
3
struct Nodo
int i
nuevo
nuevo
aux
aux
atras
nuevo
for i 0 atras
aux lista i pos
i
atras aux aux aux
nuevo sig aux
if atras
lista nuevo
else
atras sig nuevo
return lista
sig
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
261 Modifica la funcin para que, si nos pasan un nmero de posicin mayor que el
nmero de elementos de la lista, no se realice insercin alguna.
...............................................................................
...
Las listas que hemos manejado hasta el momento estn desordenadas, es decir, sus n
odos
estn dispuestos en un orden arbitrario. Es posible mantener listas ordenadas si l
as
inserciones se realizan utilizando siempre una funcin que respete el orden.
La funcin que vamos a desarrollar, por ejemplo, inserta un elemento en una li
sta ordenada de menor a mayor de modo que la lista resultante sea tambin una lista orde
nada
de menor a mayor.
TipoLista inserta en orden TipoLista lista int valor
struct Nodo
nuevo
nuevo
aux
atras
nuevo
for atras
lista nuevo
else
atras sig nuevo
return lista
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
314
314
c
UJI
Introduccin a la prog
ramacin con C - UJI
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
262 Haz una traza de la insercin del valor 7 con inserta en orden en cada una de
estas listas:
a)
info
o
sig
info
lista
8
sig
info
lista
23
sig
info
lista
9
sig
sig
inf
b)
info
o
sig
sig
inf
12
15
c)
info
o
sig
sig
inf
d)
lista
e)
inf
o
sig
lista
f)
inf
o
sig
lista
10
263 Disea una funcin de insercin ordenada en lista que inserte un nuevo nodo si
y slo si no haba ningn otro con el mismo valor.
264 Determinar la pertenencia de un valor a una lista ordenada no requiere que
recorras siempre toda la lista. Disea una funcin que determine la pertenencia a un
a
lista ordenada efectuando el menor nmero posible de comparaciones y desplazamient
os
sobre la lista.
265 Implementa una funcin que ordene una lista cualquiera mediante el mtodo de
la burbuja.
266 Disea una funcin que diga, devolviendo el valor 1 o el valor 0, si una lista e
st
ordenada o desordenada.
...............................................................................
...
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
315
315
ramacin con C - UJI
Introduccin a la prog
c
UJI
La funcin que disearemos ahora recibe dos listas y devuelve una nueva lista que re
sulta
de concatenar (una copia de) ambas.
TipoLista concatena listas TipoLista a TipoLista b
TipoLista c
struct Nodo
aux
nuevo
anterior
sig
sig
if anterior
anterior sig
return c
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
267 Disea una funcin que aada a una lista una copia de otra lista.
268 Disea una funcin que devuelva una lista con los elementos de otra lista que
sean mayores que un valor dado.
269 Disea una funcin que devuelva una lista con los elementos comunes a otras
dos listas.
270 Disea una funcin que devuelva una lista que es una copia invertida de otra
lista.
...............................................................................
...
Acabaremos este apartado con una rutina que recibe una lista y borra todos y cad
a uno
de sus nodos. A estas alturas no debera resultarte muy difcil de entender:
TipoLista libera lista TipoLista lista
struct Nodo aux
aux
lista
otroaux
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
316
316
ramacin con C - UJI
Introduccin a la prog
c
UJI
while aux
otroaux aux
free aux
aux otroaux
sig
return
Alternativamente podramos definir la rutina de liberacin como un procedimien
to:
void libera lista TipoLista
lista
otroaux
aux
lista
while aux
otroaux aux
free aux
aux otroaux
sig
lista
De este modo nos aseguramos de que el puntero lista fija su valor a
.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
271 Disea una funcin que devuelva un corte de la lista. Se proporcionarn como
parmetros dos enteros i y j y se devolver una lista con una copia de los nodos que
ocupan las posiciones i a j 1, ambas includas.
272 Disea una funcin que elimine un corte de la lista. Se proporcionarn como
parmetros dos enteros i y j y se eliminarn los nodos que ocupan las posiciones i a
j 1, ambas includas.
...............................................................................
...
sig
TipoLista
extern
extern
extern
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con ISBN:
C
9788469301432
317
317
ramacin con C UJI
Introduccin a la prog
c
UJI
extern
extern
extern
extern
extern
extern
extern
include
include
include
TipoLista lista vacia void
return
int es lista vacia TipoLista lista
return lista
TipoLista inserta por cabeza TipoLista lista int valor
struct Nodo
nuevo
aux
nuevo
aux
sig
aux
aux
sig
Introduccin
Andrs aMarzal/Isabel
la programacin con C ISBN: 9788469301432
Gracia
Introduccin a la programacin con C UJI
cUJI
318
318
return lista
TipoLista borra cola TipoLista lista
struct Nodo
if lista
for atras
atras aux aux
free aux
if atras
lista
else
atras sig
aux
atras
aux
aux
lista aux
sig
sig
return lista
int longitud lista TipoLista lista
struct Nodo
int contador
aux
0
aux
aux
sig
lista aux
aux
aux
aux
sig
info
aux
aux
aux
sig
aux
for atras
aux lista aux
aux aux
aux sig
if aux info
valor
if atras
lista aux sig
else
atras sig aux sig
atras
atras
free aux
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
319
319
return lista
return lista
TipoLista borra valor TipoLista lista int valor
struct Nodo
aux
atras
aux lista
while aux
if aux info
if atras
lista aux
else
atras sig
free aux
if atras
aux lista
else
aux atras
atras
valor
sig
aux sig
sig
else
atras aux
aux aux sig
return lista
TipoLista inserta en posicion TipoLista lista int pos int valor
struct Nodo
int i
nuevo
nuevo
aux
atras
nuevo
for i 0 atras
aux lista i pos
atras aux aux aux sig
nuevo sig aux
if atras
lista nuevo
else
atras sig nuevo
return lista
aux
aux
atras
nuevo
for atras
aux lista aux
atras
aux sig
if valor
aux info
Au insertamos el nodo entre atras y aux.
aux
320
320
if atras
lista nuevo
else
atras sig nuevo
Y como ya est insertado, acabamos.
return lista
Si llegamos au, es ue nuevo va al final de la lista.
nuevo sig
if atras
lista nuevo
else
atras sig nuevo
return lista
TipoLista concatena listas TipoLista a TipoLista b
TipoLista c
struct Nodo
aux
nuevo
anterior
sig
sig
if anterior
anterior sig
return c
TipoLista libera lista TipoLista lista
struct Nodo aux
aux lista
while aux
otroaux aux
free aux
aux otroaux
return
Andrs aMarzal/Isabel
otroaux
sig
Introduccin
Gracia
la programacin con CISBN: 9788469301432
321
321
In
include
include
int main void
TipoLista l l2 l3
printf
l lista vacia
muestra lista l
printf
printf
l inserta por
l inserta por
l inserta por
muestra lista
es lista vacia l
cabeza l 2
cabeza l 8
cabeza l 3
l
printf
longitud lis
ta l
printf
l inserta por
l inserta por
l inserta por
muestra lista
cola l 1
cola l 5
cola l 10
l
printf
l borra cabeza l
muestra lista l
printf
l borra cola l
muestra lista l
printf
pertenece
printf
pertenece
l 5
l 7
printf
l inserta por cola l 1
muestra lista l
printf
l borra primera ocurrencia l 1
muestra lista l
printf
l borra primera ocurrencia l 1
muestra lista l
printf
l borra primera ocurrencia l 1
muestra lista l
printf
l inserta por cola l 2
322
322
printf
l borra valor l 2
muestra lista l
printf
l borra valor l 8
muestra lista l
printf
l inserta en posicion l 0 1
muestra lista l
printf
l inserta en posicion l 2 10
muestra lista l
printf
l inserta en posicion l 1 3
muestra lista l
printf
l inserta en orden
l inserta en orden
l inserta en orden
l inserta en orden
muestra lista l
l
l
l
l
4
0
20
5
printf
l2 lista vacia
l2 inserta por cola l2 30
l2 inserta por cola l2 40
l2 inserta por cola l2 50
muestra lista l2
printf
l3 concatena listas l l2
muestra lista l3
printf
l libera lista l
l2 libera lista l2
l3 libera lista l3
muestra lista l
muestra lista l2
muestra lista l3
return 0
Recuerda ue debes compilar estos programas en al menos dos pasos:
323
323
c
duccin a la programacin con C UJI
UJI
Intro
Las listas ue hemos estudiado hasta el momento son muy rpidas para, por ejemplo,
la
insercin de elementos por la cabeza. Como la cabeza est permanentemente apuntada
por un puntero, basta con pedir memoria para un nuevo nodo y hacer un par de aju
stes
con punteros:
hacer ue el nodo ue sigue al nuevo nodo sea el ue era apuntado por
el puntero
a cabeza,
y hacer ue el puntero a cabeza apunte ahora al nuevo nodo.
No importa cun larga sea la lista: la insercin por cabeza es siempre igual de rpida
.
Reuiere una cantidad de tiempo constante. Pero la insercin por cola est seriament
e
Introduccin a la programacin
Andrs Marzal/Isabel
con CISBN: 9788469301432
Gracia
c
Introduccin a la programacin con C UJI
UJI
324
324
penalizada en comparacin con la insercin por cabeza. Como no sabemos dnde est el
ltimo elemento, hemos de recorrer la lista completa cada vez ue deseamos aadir po
r
la cola. Una forma de eliminar este problema consiste en mantener siempre dos pu
nteros:
uno al primer elemento de la lista y otro al ltimo.
La nueva estructura de datos ue representa una lista podra definirse as:
ig
struct Nodo
int info
struct Nodo
sig
struct Lista cc
struct Nodo
struct Nodo
cabeza
cola
Podemos representar grficamente una lista con punteros a cabeza y cola as:
info
sig
info s
info sig
lista
cabeza
cola
3
2
Los punteros lista cabeza y lista cola forman un nico objeto del tipo lista cc.
Vamos a presentar ahora unas funciones ue gestionan listas con punteros a c
abe
za y cola. Afortunadamente, todo lo aprendido con las listas del apartado anteri
or nos
vale. Eso s, algunas operaciones se simplificarn notablemente (aadir por la cola, p
or
ejemplo), pero otras se complicarn ligeramente (eliminar la cola, por ejemplo), y
a ue
ahora hemos de encargarnos de mantener siempre un nuevo puntero (lista cola) apu
ntando
correctamente al ltimo elemento de la lista.
La funcin ue crea una lista vaca es, nuevamente, muy sencilla. El prototipo es ste
:
extern struct Lista cc crea lista cc vacia void
y su implementacin:
struct Lista cc crea lista cc vacia void
struct Lista cc lista
lista cabeza lista cola
return lista
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
25
325
y deseamos insertar el valor 1 en cabeza, basta con modificar lista cabeza y aju
star
el campo sig del nuevo nodo para ue apunte a la antigua cabeza. Como puedes ver
,
lista cola sigue apuntando al mismo lugar al ue apuntaba inicialmente:
info
sig
info
si
g
info sig
info sig
lista
cabeza
cola
1
8
3
2
Ya est, no? No. Hay un caso en el ue tambin hemos de modificar lista cola adems
de lista cabeza: cuando la lista est inicialmente vaca. Por u? Porue el nuevo nodo
de la lista ser cabeza y cola a la vez.
Fjate, si partimos de esta lista:
lista
cabeza
cola
sig
lista
cabeza
cola
1
Si slo modificsemos el valor de lista cabeza, tendramos esta otra lista mal formada
en
la ue lista cola no apunta al ltimo elemento:
info
sig
lista
cabeza
cola
1
nuevo
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
326
326
acin con C UJI
cUJI
Introduccin a la program
sig
nuevo
info
info
lista
sig
sig
info
sig
cabeza
cola
3
8
2.
asignamos un valor a nuevo
acemos ue el nuevo
info y h
sig sea
info
1
nuevo
sig
info
info
lista
sig
sig
info
sig
cabeza
cola
3
8
3.
nuevo
sig
info
sig
info
lista
sig
info
sig
cabeza
cola
3
8
4.
info
1
sig
info
sig
info
lista
sig
info
sig
cabeza
cola
3
8
info
info
sig
sig
sig
lista
cabeza
cola
3
8
La nica precaucin ue hemos de tener es ue, cuando la lista est inicialmente
vaca, se modifiue tanto el puntero a la cabeza como el puntero a la cola para u
e
ambos apunten a nuevo.
struct Lista cc inserta por cola struct Lista cc lista int valor
struct Nodo
nuevo
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
327
327
Introduccin a la programacin con C UJI
cUJI
nuevo
nuevo
nuevo
if lista cola
lista cola sig nuevo
lista cola nuevo
else
lista cabeza
return lista
lista cola
nuevo
Fjate: la insercin por cola en este tipo de listas es tan eficiente como la in
sercin
por cabeza. No importa lo larga ue sea la lista: siempre cuesta lo mismo insert
ar por
cola, una cantidad constante de tiempo. Acaba de rendir su primer fruto el conta
r con
punteros a cabeza y cola.
2.
Si la lista tiene un slo elemento, lo eliminamos y ponemos lista cabeza
y lista cola
a
.
3. Y si la lista tiene ms de un elemento, como sta:
info
nfo
sig
info
lista
sig
sig
cabeza
cola
3
sig
info
lista
sig
cabeza
cola
3
sig
sig
info
lista
sig
sig
cabeza
cola
3
2
c) y liberamos la memoria ocupada por el primer nodo.
aux
info
sig
info
lista
sig
cabeza
cola
Introduccin a la programacin co
aux
a ser
El borrado del ltimo elemento de una lista con punteros a cabeza y cola plantea u
n pro
blema: cuando hayamos eliminado el nodo apuntado por lista cola, a uin debe apunt
ar
lista cola? Naturalmente, al ue hasta ahora era el penltimo nodo. Y cmo sabemos
cul era el penltimo? Slo hay una forma de saberlo: buscndolo con un recorrido de
los nodos de la lista.
Nuevamente distinguiremos tres casos distintos en funcin de la talla de la l
ista:
1.
Si la lista est vaca, no hacemos nada.
2.
Si la lista tiene un nico elemento, liberamos su memoria y hacemos ue l
os
punteros a cabeza y cola apunten a
.
3. En otro caso, actuaremos como en este ejemplo,
info
sig
info
sig
info
sig
lista
cabeza
cola
3
8
2
a) buscamos el penltimo elemento (sabremos cul es porue si se le apunt
con aux, entonces aux sig coincide con lista cola) y lo apuntamos
con una
variable auxiliar aux,
aux
info
info
sig
info
sig
lista
cabeza
sig
cola
3
8
2
b) hacemos ue el penltimo no tenga siguiente nodo (ponemos su campo s
ig a
sig
info
sig
sig
lista
cabeza
cola
3
8
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
329
329
on C UJI
Introduccin a la programacin c
cUJI
c) liberamos la memoria del ue hasta ahora era el ltimo nodo (el apu
ntado
info
sig
lista
cabeza
cola
3
8
d) y, finalmente, hacemos ue lista cola apunte a aux.
aux
info
sig
info
sig
lista
cabeza
cola
3
aux
Lista vaca.
if lista cabeza
return lista
Lista con un solo nodo.
if lista cabeza
lista cola
free lista cabeza
lista cabeza lista cola
return lista
a aux
lista col
Fjate en la condicin del bucle: detecta si hemos llegado o no al penltimo nodo pre
guntando si el ue sigue a aux es el ltimo (el apuntado por lista cola).
La operacin de borrado de la cola no es, pues, tan eficiente como la de borra
do de la
cabeza, pese a ue tenemos un puntero a la cola. El tiempo ue necesita es direc
tamente
Introduccin a la prog
c
UJI
Vamos a dotar a cada nodo de dos punteros: uno al siguiente nodo en la lista y o
tro al
anterior.
Los nodos sern variables de este tipo:
struct DNodo
int info
struct DNodo
struct DNodo
ant
sig
ant
ant
info
info
sig
sig
lista
2
Vamos paso a paso.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
331
331
1.
info
sig
ant
info
sig
nuevo
ant
info
sig
lista
2
2.
info
3
sig
ant
info
sig
nuevo
ant
info
sig
lista
2
3.
info
3
sig
ant
info
sig
nuevo
ant
info
sig
lista
2
4.
info
3
sig
ant
info
sig
nuevo
ant
info
sig
lista
2
5.
info
3
sig
ant
info
sig
lista
ant
info
sig
8
2
El caso de la insercin en la lista vaca es trivial: se pide memoria para un nu
evo
nodo cuyos punteros ant y sig se ponen a
y hacemos que la cabeza apunte
a dicho
nodo.
Aqu tienes la funcin que codifica el mtodo descrito. Hemos factorizado y dispue
sto
al principio los elementos comunes al caso general y al de la lista vaca:
TipoDLista inserta por cabeza TipoDLista lista int valor
struct DNodo
nuevo
nuevo
nuevo
nuevo
nuevo
malloc sizeof struct DNodo
info valor
ant
sig lista
if lista
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
332
332
lista
ant
nuevo
lista nuevo
return lista
Te proponemos como ejercicios algunas de las funciones bsicas para el manejo
de
listas doblemente enlazadas:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
282 Disea una funcin que inserte un nuevo nodo al final de una lista doblemente
enlazada.
283 Disea una funcin que borre la cabeza de una lista doblemente enlazada. Presta
especial atencin al caso en el que la lista consta de un slo elemento.
...............................................................................
...
Vamos a desarrollar la funcin de borrado del ltimo elemento de una lista doblement
e
enlazada, pues presenta algn aspecto interesante.
Desarrollemos nuevamente el caso general sobre una lista concreta para deduc
ir el
mtodo a seguir. Tomemos, por ejemplo, sta:
ant
info
sig
ant
info
sig
ant
info
sig
lista
3
8
2
1.
Empezamos localizando el ltimo elemento de la lista (con un bucle) y ap
untndolo
con un puntero:
aux
ant
ant
2.
info
lista
8
info
sig
sig
ant
info
sig
3
2
ant
3.
info
lista
8
sig
info
ant
sig
info
sig
atras
aux
ant
info
sig
ant
info
sig
lista
3
4.
Y se pone el campo sig del que hasta ahora era penltimo (el apuntado po
r atras)
a
.
atr
as
aux
ant
sig
ant
info
lista
8
info
sig
3
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
333
333
ntroduccin a la programacin con C - UJI
c
UJI
El caso de la lista vaca tiene fcil solucin: no hay nada que borrar. Es ms probl
emtica la lista con slo un nodo. El problema con ella estriba en que no hay element
o
penltimo (el anterior al ltimo es
). Tendremos, pues, que detectar esta sit
uacin y
tratarla adecuadamente.
TipoDLista borra por cola TipoDLista lista
struct DNodo
aux
atras
Lista vaca.
if lista
return lista
Lista con un nodo.
if lista sig
free lista
lista
return lista
Caso general.
for aux lista aux
atras aux ant
free aux
atras sig
return lista
sig
aux aux
sig
1.
Empezamos localizando el elemento que ocupa actualmente la posicin n. Un
simple
bucle efectuar esta labor:
a
ux
ant
sig
ant
info
lista
2
2.
info
sig
ant
info
sig
3
le
asignamos el valor:
a
ux
ant
sig
ant
ant
info
lista
2
info
info
sig
ant
info
sig
3
sig
nuevo
1
3.
ux
ant
sig
ant
ant
info
lista
2
info
info
sig
ant
info
sig
3
sig
nuevo
1
4.
ant:
a
ux
ant
sig
ant
ant
info
lista
2
info
info
sig
ant
info
sig
3
sig
nuevo
1
5.
Ojo con este paso, que es complicado. Hacemos que el anterior a aux ten
ga como
siguiente a nuevo, es decir, aux ant sig nuevo:
a
ux
ant
sig
ant
ant
info
lista
2
info
info
sig
ant
info
sig
3
sig
nuevo
1
6.
Y ya slo resta que el anterior a aux sea nuevo con la asignacin aux
ant
nuevo:
a
ux
ant
sig
ant
ant
info
lista
2
info
info
sig
ant
info
sig
3
sig
nuevo
1
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
35
335
3
Introduccin a la programacin con C -UJI
cUJ
aux
nuevo
Nuevamente hay un par de casos triviales: si la lista est vaca, no hay que hacer n
ada
y si la lista tiene un slo elemento, slo hemos de actuar si ese elemento tiene el
valor
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
336
336
ramacin con C - UJI
Introduccin a la prog
c
UJI
info
sig
lista
ant
info
sig
ant
sig
3
8
2.
gali-
info
2
Hacemos que el que sigue al anterior de aux sea el siguiente de aux (qu
matas!). O sea, hacemos aux ant sig aux sig:
aux
ant
info
sig
lista
ant
info
info
sig
ant
sig
3
3.
Ahora hacemos que el que antecede al siguiente de aux sea el anterior
a aux. Es
decir, aux sig ant aux ant:
aux
ant
info
sig
lista
ant
info
sig
ant
sig
3
4.
info
2
info
lista
info
sig
3
sig
Hemos de ser cautos. Hay un par de casos especiales que merecen ser tratados
aparte:
el borrado del primer nodo y el borrado del ltimo nodo. Veamos cmo proceder en el
primer caso: tratemos de borrar el nodo de valor 3 en la lista del ejemplo anter
ior.
1.
nta al
Una vez apuntado el nodo por aux, sabemos que es el primero porque apu
mismo nodo que lista:
aux
ant
info
sig
lista
ant
8
2.
puntero
info
info
sig
ant
sig
3
2
Hacemos que el segundo nodo deje de tener antecesor, es decir, que el
:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
337
337
on C -UJI
cUJI
Introduccin a la programacin c
aux
info
sig
ant
info
ant
sig
lista
info
sig
ant
3.
info
Ahora hacemos que lista pase a apuntar al segundo nodo (lista aux
sig):
aux
sig
ant
info
ant
sig
lista
info
sig
ant
4.
Y por fin, podemos liberar al nodo apuntado por aux (free aux ):
aux
ant
info
sig
ant
info
sig
lista
8
aux
info
sig
ant
info
ant
sig
lista
info
sig
ant
2
2.
aux
info
sig
ant
info
ant
sig
lista
8
3
2
3.
info
sig
ant
aux
ant
ant
info
info
sig
sig
lista
aux
aux aux
sig
if aux
return lista
No se encontr.
if aux ant
Es el primero de la lista
.
lista aux sig
else
aux ant sig aux
sig
if aux sig
No es el ltimo de la lista
.
aux sig ant
aux
ant
free aux
return lista
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
287 Disea una funcin que permita efectuar la insercin ordenada de un elemento
en una lista con enlace doble que est ordenada.
288 Disea una funcin que permita concatenar dos listas doblemente enlazadas. La
funcin recibir las dos listas y devolver una lista nueva con una copia de la primer
a
seguida de una copia de la segunda.
289 Disea una funcin que devuelva una copia invertida de una lista doblemente
enlazada.
...............................................................................
...
Ya sabemos manejar listas con puntero a cabeza y listas con punteros a cabeza y
cola.
Hemos visto que las listas con puntero a cabeza son ineficientes a la hora de aad
ir
elementos por la cola: se tarda tanto ms cuanto mayor es el nmero de elementos de
la
lista. Las listas con puntero a cabeza y cola permiten realizar operaciones de i
nsercin
por cola en un nmero constante de pasos. An as, hay operaciones de cola que tambin
son ineficientes en esta ltima estructura de datos: la eliminacin del nodo de cola
, por
ejemplo, sigue necesitando un tiempo proporcional a la longitud de la lista.
La estructura que presentamos en esta seccin, la lista doblemente enlazada c
on
puntero a cabeza y cola, corrige la ineficiencia en el borrado del nodo de cola.
Una lista
doblemente enlazada con puntero a cabeza y cola puede representarse grficamente a
s:
ant
info
sig
ant
info
sig
ant
info
sig
lista
cabeza
cola
3
La definicin del tipo es fcil ahora que ya hemos estudiado diferentes tipos
de listas:
struct DNodo
int info
struct DNodo
struct DNodo
ant
sig
struct DLista cc
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
339
339
ramacin con C - UJI
Introduccin a la prog
c
UJI
struct DNodo
struct DNodo
cabeza
cola
info
sig
info
ant
sig
info
si
g
lista
cabeza
cola
3
8
hacemos lo siguiente:
a) localizamos al penltimo elemento, que es lista cola
ant, y lo mantenemos
apuntado con un puntero auxiliar aux:
ant
sig
fo
ant
info
sig
aux
info
ant
in
sig
lista
cabeza
cola
3
8
ant
info
sig
ant
info
sig
lista
cabeza
cola
3
c) ponemos aux
sig a
aux
ant
info
sig
ant
info
sig
lista
cabeza
cola
3
d) y ajustamos lista cola para que apunte ahora donde apunta aux:
aux
ant
info
sig
ant
info
sig
lista
cabeza
cola
3
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
340
340
Introduccin a la programacin con C -UJI
cUJI
ant
Ha sido fcil, no? No ha hecho falta bucle alguno. La operacin se ejecuta en un nmero
de pasos que es independiente de lo larga que sea la lista.
Ahora te toca a t desarrollar cdigo. Practica con estos ejercicios:
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
290 Disea una funcin que calcule la longitud de una lista doblemente enlazada
con punteros a cabeza y cola.
291 Disea una funcin que permita insertar un nuevo nodo en cabeza.
292 Disea una funcin que permita insertar un nuevo nodo en cola.
293 Disea una funcin que permita borrar el nodo de cabeza.
294
Disea una funcin que elimine el primer elemento de la lista con un val
or dado.
295
Disea una funcin que elimine todos los elementos de la lista con un va
lor dado.
296 Disea una funcin que inserte un nodo en una posicin determinada que se
indica por su ndice.
297 Disea una funcin que inserte ordenadamente en una lista ordenada.
298 Disea una funcin que muestre por pantalla el contenido de una lista, mostrando
el valor de cada celda en una lnea. Los elementos se mostrarn en el mismo orden co
n
el que aparecen en la lista.
299 Disea una funcin que muestre por pantalla el contenido de una lista, mostrando
el valor de cada celda en un lnea. Los elementos se mostrarn en orden inverso.
300 Disea una funcin que devuelva una copia invertida de una lista doblemente
enlazada con puntero a cabeza y cola.
...............................................................................
...
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
341
341
ramacin con C - UJI
Introduccin a la prog
c
UJI
simple
simple cabeza/cola
constante
constante
constante
constante
lineal
constante
lineal
lineal
lineal
lineal
cuadrtico
cuadrtico
Hemos indicado con la palabra constante que se requiere una cantidad de tiemp
o
fija, independiente de la longitud de la lista; con la palabra lineal, que se requ
iere un
tiempo que es proporcional a la longitud de la lista; y con cuadrtico, que el coste
crece
con el cuadrado del nmero de elementos.
Para que te hagas una idea: insertar por cabeza un nodo en una lista cuesta
siempre
la misma cantidad de tiempo, tenga la lista 100 o 1000 nodos. Insertar por la co
la en una
lista simplemente enlazada con puntero a cabeza, sin embargo, es unas 10 veces ms
lento
si la lista es 10 veces ms larga. Esto no ocurre con una lista simplemente enlaza
da que
tenga puntero a cabeza y cola: insertar por la cola en ella siempre cuesta lo mi
smo. Ojo
con los costes cuadrticos! Invertir una lista simplemente enlazada de 1000 elemen
tos es
100 veces ms costoso que invertir una lista con 10 veces menos elementos.
En la tabla hemos marcado algunos costes con un asterisco. Son costes para
el peor
de los casos. Buscar un nodo concreto en una lista obliga a recorrer todos los n
odos
slo si el que buscamos no est o si ocupa la ltima posicin. En el mejor de los casos,
el coste temporal es constante: ello ocurre cuando el nodo buscado se encuentra
en la
lista y, adems, ocupa la primera posicin. De los anlisis de coste nos ocuparemos ms
adelante.
Un anlisis de la tabla de tiempos permite concluir que la lista doblemente e
nlazada
con punteros a cabeza y cola es siempre igual o mejor que las otras estructuras.
Debemos
escogerla siempre? No, por tres razones:
1.
Aunque la lista ms compleja requiere tiempo constante en muchas operacio
nes,
stas son algo ms lentas y sofisticadas que operaciones anlogas en las otra
s
estructuras ms sencillas. Son, por fuerza, algo ms lentas.
2.
El consumo de memoria es mayor en la lista ms compleja (8 bytes adiciona
les para
cada nodo y 8 bytes para los punteros a cabeza y cola, frente a 4 bytes
adicionales
para cada nodo y 4 bytes para un puntero a cabeza en la estructura ms se
ncilla),
as que puede no compensar la ganancia en velocidad o, sencillamente, es
posible
que no podamos permitirnos el lujo de gastar el doble de memoria extra.
3.
Puede que nuestra aplicacin slo efecte operaciones baratas sobre cualquier
lista. Imagina que necesitas una lista en la que siempre insertas y eli
minas nodos
por cabeza, jams por el final. Las cuatro estructuras ofrecen tiempo con
stante para
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
342
342
esas dos operaciones, slo que, adems, las dos primeras son mucho ms senci
llas
y consumen menos memoria que las dos ltimas.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
301 Rellena una tabla similar a la anterior para estas otras operaciones:
a) Insertar ordenadamente en una lista ordenada.
b) Insertar en una posicin concreta.
c) Buscar un elemento en una lista ordenada.
d) Buscar el elemento de valor mnimo en una lista ordenada.
e) Buscar el elemento de valor mximo en una lista ordenada.
f) Unir dos listas ordenadas de modo que el resultado est ordenado.
g) Mostrar el contenido de una lista por pantalla.
h) Mostrar el contenido de una lista en orden inverso por pantalla.
302 Vamos a montar una pila con listas. La pila es una estructura de datos en la
que
slo podemos efectuar las siguientes operaciones:
insertar un elemento en la cima,
eliminar el elemento de la cima,
consultar el valor del elemento de la cima.
Qu tipo de lista te parece ms adecuado para implementar una pila? Por qu?
303 Vamos a montar una cola con listas. La cola es una estructura de datos en la
que slo podemos efectuar las siguientes operaciones:
insertar un elemento al final de la cola,
eliminar el elemento que hay al principio de la cola,
consultar el valor del elemento que hay al principio de la cola.
Qu tipo de lista te parece ms adecuado para construir una cola? Por qu?
...............................................................................
...
En este apartado vamos a desarrollar una aplicacin prctica que usa listas: un prog
rama
para la gestin de una coleccin de discos compactos. Cada disco compacto contendr
un ttulo, un intrprete, un ao de edicin y una lista de canciones. De cada cancin nos
interesar nicamente el ttulo.
Las acciones del programa, que se presentarn al usuario con un men, son stas.
1.
Aadir un disco.
2.
3.
4.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
343
343
ramacin con C - UJI
Introduccin a la prog
c
UJI
5.
6.
prete.
7.
Finalizar.
A priori no sabemos cuntas canciones hay en un disco, ni cuntos discos hay que
almacenar en la base de datos, as que utilizaremos listas para ambas entidades. N
uestra
coleccin ser, pues, una lista de discos que, a su vez, contienen listas de cancion
es.
No slo eso: no queremos que nuestra aplicacin desperdicie memoria con cadenas que
consumen ms memoria que la necesaria, as que usaremos memoria dinmica tambin
para la reserva de memoria para cadenas.
Lo mejor es dividir el problema en estructuras de datos claramente diferenci
adas (una
para la lista de discos y otra para la lista de canciones) y disear funciones par
a manejar
cada una de ellas. Atencin al montaje que vamos a presentar, pues es el ms complic
ado
de cuantos hemos estudiado.
struct Cancion
char titulo
struct Cancion
sig
typedef struct Cancion
TipoListaCanciones
struct Disco
char titulo
char interprete
int anyo
TipoListaCanciones canciones
struct Disco sig
typedef struct Disco
TipoColeccion
10
14
11
15
T a n g e r i n e
D r e a m \0
0
6
12
7
13
L o g o s \0
I g n a c i o \0
0
V a n g e l i s \0
titulo
titulo
titulo
sig
sig
sig
interprete
interprete
nterprete
coleccion
anyo
anyo
anyo
1972
1982
1977
canciones
canciones
anciones
titulo
titulo
titulo
sig
titulo
sig
sig
sig
titulo
sig
0
0
5
titulo
sig
7
L o g o s \0
I g n a c i o \0
0
2
1
3
2
4
3
1
0
8
titulo
sig
O g u
D o m
n d e \0
i n i o n \0
0
2
4
3
5
8
titulo
sig
T o
b e \0
O f f e r i n g \0
0
2
6
3
0
5
1
7
2
3
9
10
4
10
E x p
r e s s i o n \0
N u m b e r
o n e \0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
344
344
Introduccin a la programacin con C -UJ
I
cUJI
nuevo
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
304 La verdad es que insertar las canciones por la cabeza es el mtodo menos
indicado, pues cuando se recorra la lista para mostrarlas por pantalla aparecern
en
orden inverso a aqul con el que fueron introducidas. Modifica anyade cancion para
que
las canciones se inserten por la cola.
305 Y ya que sugerimos que insertes canciones por cola, modifica las estructuras
necesarias para que la lista de canciones se gestione con una lista de registros
con
puntero a cabeza y cola.
...............................................................................
...
Mostrar la lista de canciones es muy sencillo:
void muestra canciones TipoListaCanciones lista
struct Cancion
for aux lista aux
printf
aux
aux
aux aux
titulo
sig
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
345
345
ramacin con C - UJI
Introduccin a la prog
c
UJI
aux
sig
Borrar todas las canciones de una lista debe liberar la memoria propia de ca
da nodo,
pero tambin debe liberar la cadena que almacena cada ttulo, pues tambin se solicit
con malloc:
TipoListaCanciones libera canciones TipoListaCanciones lista
struct Cancion
aux
siguiente
aux lista
while aux
siguiente aux sig
free aux titulo
free aux
aux siguiente
return
No ha sido tan difcil. Una vez sabemos manejar listas, las aplicaciones prctic
as
se disean reutilizando buena parte de las rutinas que hemos presentado en apartad
os
anteriores.
Pasamos a encargarnos de las funciones que gestionan la lista de discos. Com
o es
habitual, empezamos con una funcin que crea una coleccin (una lista) vaca:
TipoColeccion crea coleccion void
return
Aadir un disco obliga a solicitar memoria tanto para el registro en s como par
a
algunos de sus componentes: el ttulo y el intrprete:
TipoColeccion anyade disco TipoColeccion lista char titulo char interprete
int anyo TipoListaCanciones canciones
struct Disco
disco
lista disco
return lista
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
346
346
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
306 Modifica anyade disco para que los discos estn siempre ordenados alfabticamente por intrprete y, para cada intrprete, por valor creciente del ao de edicin.
................................................................................
..
Y la memoria solicitada debe liberarse ntegramente: si al reservar memoria pa
ra un
disco ejecutamos tres llamadas a malloc, habr que efectuar tres llamadas a free:
TipoColeccion libera coleccion TipoColeccion lista
struct Disco
aux
siguiente
aux lista
while aux
siguiente aux sig
free aux titulo
free aux interprete
aux canciones libera canciones aux
canciones
free aux
aux siguiente
return
Mostrar por pantalla el contenido de un disco es sencillo, especialmente si u
samos
muestra canciones para mostrar la lista de canciones.
void muestra disco struct Disco eldisco
printf
eldisco titulo
printf
eldisco interprete
printf
eldisco anyo
printf
muestra canciones eldisco canciones
Mostrar la coleccin completa es trivial si usamos la funcin que muestra un d
isco:
void muestra coleccion TipoColeccion lista
struct Disco
aux
aux aux
sig
aux
aux aux
sig
if strcmp aux titulo titulo
return aux
return
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
347
347
amacin con C - UJI
Introduccin a la progr
c
UJI
struct Disco
ar interprete
struct Disco
aux
sig
0
La funcin de bsqueda por ttulo de cancin es similar, slo que llama a la funcin que
busca una cancin en una lista de canciones:
struct Disco
busca disco por titulo cancion TipoColeccion coleccio
n char titulo
struct Disco
aux
atras
for atras
aux coleccion aux
atras aux aux aux sig
if strcmp aux titulo titulo
0 strcmp aux interprete interprete
0
if atras
coleccion aux sig
else
atras sig aux sig
free aux titulo
free aux interprete
aux canciones libera canciones aux canciones
free aux
return coleccion
return coleccion
Ya tenemos todas las herramientas para enfrentarnos al programa principal:
include
include
include
include
define
enum
1000
Anyadir 1 BuscarPorTituloDisco BuscarPorInterprete BuscarPorTit
uloCancion
Mostrar EliminarDisco Salir
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
348
348
crea coleccion
do
printf
printf
printf
printf
printf
printf
printf
printf
printf
gets linea
sscanf linea
opcion
switch opcion
case Anyadir
printf
gets titulo disco
printf
gets interprete
printf
gets linea sscanf linea
anyo
lista canciones crea lista canciones
do
printf
gets titulo cancion
if strlen titulo cancion
0
lista canciones anyade cancion lista canciones titulo c
ancion
while strlen titulo cancion
0
coleccion anyade disco coleccion titulo disco
interprete anyo lista canciones
break
case BuscarPorTituloDisco
printf
gets titulo disco
undisco busca disco por titulo disco coleccion titulo disco
if undisco
muestra disco undisco
else
printf
titulo disco
break
case BuscarPorInterprete
printf
gets interprete
undisco busca disco por interprete coleccion interprete
if undisco
muestra disco undisco
else
printf
interprete
break
case BuscarPorTituloCancion
Introduccin a la programacin con C
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
cUJI
Introduccin a la programacin con C -UJI
349
349
printf
gets titulo cancion
undisco busca disco por titulo cancion coleccion titulo can
cion
if undisco
muestra disco undisco
else
printf
titulo cancion
break
case Mostrar
muestra coleccion coleccion
break
case EliminarDisco
printf
gets titulo disco
printf
gets interprete
coleccion borra disco por titulo e interprete coleccion titul
o disco
interprete
break
while opcion
coleccion
Salir
libera coleccion coleccion
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
307 Modifica el programa para que se almacene la duracin de cada cancin (en
segundos) junto al ttulo de la misma.
308 La funcin de bsqueda de discos por intrprete se detiene al encontrar el primer
disco de un intrprete dado. Modifica la funcin para que devuelva una lista con una
copia
de todos los discos de un intrprete. Usa esa lista para mostrar su contenido por
pantalla
con muestra coleccion y elimnala una vez hayas mostrado su contenido.
309 Disea una aplicacin para la gestin de libros de una biblioteca. Debes mantener dos listas: una lista de libros y otra de socios. De cada socio recordamos e
l nombre,
el DNI y el telfono. De cada libro mantenemos los siguientes datos: ttulo, autor,
ISBN,
cdigo de la biblioteca (una cadena con 10 caracteres) y estado. El estado es un p
untero
que, cuando vale
, indica que el libro est disponible y, en caso contrario
, apunta al
socio al que se ha prestado el libro.
El programa debe permitir dar de alta y baja libros y socios, as como efectua
r el
prstamo de un libro a un socio y gestionar su devolucin. Ten en cuenta que no es
posible dar de baja a un socio que posee un libro en prstamo ni dar de baja un li
bro
prestado.
...............................................................................
...
Introduccin a la prog
c
UJI
info
lista
2
sig
info
sig
3
Este tipo de estructura de datos es til, por ejemplo, para mantener una
lista de
tareas a las que hay que ir dedicando atencin rotativamente: cuando hemo
s hecho
una ronda, queremos pasar nuevamente al primer elemento. El campo sig d
el ltimo
elemento permite pasar directamente al primero, con lo que resulta senc
illo codificar
un bucle que recorre rotativamente la lista.
En muchas aplicaciones es preciso trabajar con matrices dispersas. Una
matriz dispersa es una matriz en la que muy pocos componentes presentan un valor
diferente
de cero. Esta matriz, por ejemplo, es dispersa:
0 0 2.5 0 0 1.2 0 0 0 0
0 0
0 0 0 0 0 0 0 0
0 3.7 0 0 0 0 0 0 0 0
0 0
0 0 0 0 0 0 0 0
0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0
0 0 0 0 0 0 0 0
0 0
0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
De los 100 componentes de esta matriz de 10 10, tan slo hay 6 no nulos.
Las
matrices dispersas pueden representarse con listas de listas para ahorr
ar memoria.
Una lista mantiene las filas que, a su vez, son listas de valores no nu
los. En estas
ltimas listas, cada nodo almacena la columna del valor no nulo y el prop
io valor. La
matriz dispersa del ejemplo se representara as (suponiendo que filas y co
lumnas
empiezan numerndose en 1, como es habitual en matemticas):
matriz
columna
sig
columna
sig
sig
fila
1
sig
sig
fila
3
columna
cols
3
valor
2.5
6
valor
1.2
sig
cols
columna
2
valor
3.7
columna
sig
columna
sig
2
8
sig
fila
valor
6
0.2
cols
valor
1.3
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
1
351
valor
8.1
35
UJI
info
izq
10
der
der
info
info
izq
izq
3
15
der
der
info
12
izq
info
der
1
izq
info
der
info
izq
izq
6
23
Una ventaja de los rboles binarios de bsqueda es la rapidez con que puede
n
resolver la pregunta pertenece un valor determinado al conjunto de valore
s del
rbol?. Hay un mtodo recursivo que recibe un puntero a un nodo y dice:
si el puntero vale
; la respuesta es no;
si el valor coincide con el del nodo apuntado, la respuesta es s;
si el valor es menor que el valor del nodo apuntado, entonces la r
espuesta la
conoce el hijo izquierdo, por lo que se le pregunta a l (recursiv
amente);
y si el valor es mayor que el valor del nodo apuntado, entonces la
respuesta
la conoce el hijo derecho, por lo que se le pregunta a l (recursi
vamente).
Ingenioso, no? Observa que muy pocos nodos participan en el clculo de la
respuesta. Si deseas saber, por ejemplo, si el 6 pertenece al rbol de la fi
gura, slo
hay que preguntarle a los nodos que tienen el 10, el 3 y el 6. El resto
de nodos
no se consultan para nada. Siempre es posible responder a una pregunta
de pertenencia en un rbol con n nodos visitando un nmero de nodos que es, a lo
sumo,
igual a 1 + log2 n. Rapidsimo. Qu costar, a cambio, insertar o borrar un no
do
en el rbol? Cabe pensar que mucho ms que un tiempo proporcional al nmero
de nodos, pues la estructura de los enlaces es muy compleja. Pero no es
as. Existen procedimientos sofisticados que consiguen efectuar esas operaciones
en tiempo
proporcional al logaritmo en base 2 del nmero de nodos!
Hay muchas ms estructuras de datos que permiten acelerar sobremanera los programas que gestionan grandes conjuntos de datos. Apenas hemos empezado a conocer
y aprendido a manejar las herramientas con las que se construyen los programas:
las
estructuras de datos y los algoritmos.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
352
352
-UJI
cUJI
Me temo que s, seora dijo Alicia. No recuerdo las cosas como sola. . .
y no conservo el mismo tamao diez minutos seguidos!
LEWIS CARROLL, Alicia en el Pas de
las Maravillas.
Acabamos nuestra introduccin al lenguaje C con el mismo objeto de estudio con el
que finalizamos la presentacin del lenguaje Python: los ficheros. Los ficheros pe
rmiten
guardar informacin en un dispositivo de almacenamiento de modo que sta sobreviva
a la ejecucin de un programa. No te vendra mal repasar los conceptos introductorio
s a
ficheros antes de empezar.
Con Python estudiamos nicamente ficheros de texto. Con C estudiaremos dos tipos d
e
ficheros: ficheros de texto y ficheros binarios.
Ya conoces los ficheros de texto: contienen datos legibles por una persona y pue
des
generarlos o modificarlos desde tus propios programas o usando aplicaciones como
los
editores de texto. Los ficheros binarios, por contra, no estn pensados para facil
itar su
lectura por parte de seres humanos (al menos no directamente).
Pongamos que se desea guardar un valor de tipo entero en un fichero de text
o, por
ejemplo, el valor 12. En el fichero de texto se almacenar el dgito
(codi
ficado en
ASCII como el valor 49) y el dgito
(codificado en ASCII como el valor 50), e
s decir, dos
datos de tipo char. A la hora de leer el dato, podremos leerlo en cualquier vari
able de tipo
entero con capacidad suficiente para almacenar ese valor (un char, un unsigned c
har, un
int, un unsigned int, etc.). Esto es as porque la lectura de ese dato pasa por un
proceso
de interpretacin relativamente sofisticado: cuando se lee el carcter
, se
memoriza
el valor 1; y cuando se lee el carcter
, se multiplica por 10 el valor memor
izado y se
le suma el valor 2. As se llega al valor 12, que es lo que se almacena en la vari
able en
cuestin. Observa que, codificado como texto, 12 ocupa dos bytes, pero que si se a
lmacena
en una variable de tipo char ocupa 1 y en una variable de tipo int ocupa 4.
Un problema de los ficheros de texto es la necesidad de usar marcas de sepa
racin
entre sus diferentes elementos. Si, por ejemplo, al valor 12 ha de sucederle el
valor 100,
no podemos limitarnos a disponer uno a continuacin del otro sin ms, pues el ficher
o
contendra la siguiente secuencia de caracteres:
353
Andrs Marzal/Isabel Gracia - ISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
353
1
0
\t
\n
\n
\n
Las herramientas con las que leemos los datos de ficheros de texto saben lidiar
con las
complicaciones que introducen estos separadores blancos repetidos.
Los ficheros de texto cuentan con la ventaja de que se pueden inspeccionar c
on ayuda
de un editor de texto y permiten as, por lo general, deducir el tipo de los difer
entes datos
que lo componen, pues stos resultan legibles.
Pero si optamos por almacenarlo como un int, sern cuatro los bytes escritos:
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
354
354
d)
g)
15
32768
b) 0
e) 128
h) 2147483647
c) 12
f) 32767
i)
2147483648
Y cunto ocupa cada uno de ellos si los almacenamos en un fichero binario como
valores de tipo int?
311 Cmo se interpreta esta secuencia de bytes en cada uno de los siguientes
supuestos?
Introduccin a la prog
c
UJI
Portabilidad de ficheros
Los ficheros binarios presentan algunos problemas de portabilidad, pues
no todos los
ordenadores almacenan en memoria los valores numricos de la misma forma:
los ficheros
binarios escritos en un ordenador big-endian no son directamente legibles
en un
ordenador little-endian.
Los ficheros de texto son, en principio, ms portables, pues la tabla
ASCII es un
estndar ampliamente aceptado para el intercambio de ficheros de texto. No
obstante,
la tabla ASCII es un cdigo de 7 bits que slo da cobertura a los smbolos pro
pios de
la escritura del ingls y algunos caracteres especiales. Los caracteres ac
entuados, por
ejemplo, estn excluidos. En los ltimos aos se ha intentado implantar una fa
milia de
estndares que den cobertura a estos y otros caracteres. Como 8 bits resul
tan insuficientes para codificar todos los caracteres usados en la escritura de cualqu
ier lenguaje, hay
diferentes subconjuntos para cada una de las diferentes comunidades cult
urales. Las lenguas romnicas occidentales usan el estndar IsoLatin-1 (o ISO-8859-1), reci
entemente
ampliado con el smbolo del euro para dar lugar al IsoLatin-15 (o ISO-8859
-15). Los
problemas de portabilidad surgen cuando interpretamos un fichero de text
o codificado
con IsoLatin-1 como si estuviera codificado con otro estndar: no veremos
ms que un
galimatas de smbolos extraos all donde se usan caracteres no ASCII.
1.
Se abre el fichero en modo lectura, escritura, adicin, o cualquier otro
modo vlido.
2.
Se trabaja con l leyendo o escribiendo datos, segn el modo de apertura es
cogido.
Al abrir un fichero se dispone un cabezal de lectura o escritura en un pu
nto
definido del fichero (el principio o el final). Cada accin de lectura o
escritura
desplaza el cabezal de izquierda a derecha, es decir, de principio a fi
nal del fichero.
3.
Se cierra el fichero.
Bueno, lo cierto es que, como siempre en C, hay un paso adicional y previo a est
os
tres: la declaracin de una variable de tipo fichero. La cabecera
incluye
la
definicin de un tipo de datos llamado FILE y declara los prototipos de las funcio
nes de
manipulacin de ficheros. Nuestra variable de tipo fichero ha de ser un puntero a
FILE,
es decir, ha de ser de tipo FILE .
Las funciones bsicas con las que vamos a trabajar son:
char modo
Los modos de apertura para ficheros de texto con los que trabajaremos s
on stos:
o.
devuel
fichero
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
356
356
FILE
direccion
es
fprintf : escribe en un fichero. Recibe un fichero abierto con fopen (u
n FILE ), una
cadena de formato (donde puedes usar las marcas de formato que aprendis
te a
usar con printf ) y los valores que deseamos escribir. La funcin devuelv
e el nmero
de caracteres efectivamente escritos (valor que puedes usar para compro
bar si se
FILE
valores
FILE
fichero
Como puedes ver no va a resultar muy difcil trabajar con ficheros de texto en
C. A
fin de cuentas, las funciones de escritura y lectura son bsicamente idnticas a pri
ntf y
scanf , y ya hemos aprendido a usarlas. La nica novedad destacable es la nueva fo
rma
de detectar si hemos llegado al final de un fichero o no: ya no se devuelve la c
adena
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
357
357
vaca como consecuencia de una lectura al final del fichero, como ocurra en Python,
sino
que hemos de preguntar explcitamente por esa circunstancia usando una funcin (feof
).
Nada mejor que un ejemplo para aprender a utilizar ficheros de texto en C. V
amos a
generar los 1000 primeros nmeros primos y a guardarlos en un fichero de texto. Ca
da
nmero se escribir en una lnea.
include
int es primo int n
int i
primo
for j
if
j primo
1
2 j n 2 j
n j
0
primo 0
break
return primo
int main void
FILE fp
int i n
fp fopen
i 1
n 0
while n 1000
if es primo i
fprintf fp
n
i
fclose fp
return 0
Hemos llamado a la variable de fichero fp por ser abreviatura del trmino file
pointer
(puntero a fichero). Es frecuente utilizar ese nombre para las variables de tipo
FILE .
Una vez compilado y ejecutado el programa
obtenemos un f
ichero de
texto llamado
del que te mostramos sus primeras y ltimas lneas (pue
des
comprobar la correccin del programa abriendo el fichero
con un e
ditor de
texto):
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
358
358
Aunque en pantalla lo vemos como una secuencia de lneas, no es ms que una secuenci
a
de caracteres:
1
1
\n
\n 7
\n
3
\n
\n
\n
...
fp
fp fopen
fscanf fp
i
while
feof fp
printf
i
fscanf fp
i
fclose fp
return 0
Observa que la llamada a fscanf se encuentra en un bucle que se lee as mientra
s no
se haya acabado el fichero. . . , pues feof averigua si hemos llegado al final de
l fichero.
La lnea 9 contiene una lectura de datos para que la consulta a feof tenga sentido
: feof
slo actualiza su valor tras efectuar una operacin de lectura del fichero. Si no te
gusta
la aparicin de dos sentencias fscanf , puedes optar por esta alternativa:
include
int main void
FILE
int i
fp fopen
while 1
fscanf fp
fp
Introduccin a la programacin
Andrs Marzal/Isabel
con- C
Gracia
ISBN: 978-84-693-0143-2
359
359
c
UJI
if feof fp
printf
break
i
fclose fp
return 0
Y si deseas evitar el uso de break, considera esta otra:
include
int main void
FILE
int i
fp
fp fopen
do
fscanf fp
if feof fp
printf
while feof fp
fclose fp
i
i
return 0
Y si el fichero no existe?
Al abrir un fichero puede que detectes un error: fopen devuelve la direcc
in
. Hay
varias razones, pero una que te ocurrir al probar algunos de los programas
del texto es
que el fichero que se pretende leer no existe. Una solucin puede consistir
en crearlo
en ese mismo instante:
f fopen ruta
if f
f fopen ruta
fclose f
f fopen ruta
Si el problema era la inexistencia del fichero, este truco funcionar, pues
el modo
lo crea cuando no existe.
Es posible, no obstante, que incluso este mtodo falle. En tal caso, es
probable que
tengas un problema de permisos: tienes permiso para leer ese fichero?, tien
es permiso
para escribir en el directorio en el que reside o debe residir el fichero
? Ms adelante
prestaremos atencin a esta cuestin.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
312 Disea un programa que aada al fichero
los 100 siguientes nmeros
primos. El programa leer el contenido actual del fichero para averiguar cul es el l
timo
primo del fichero. A continuacin, abrir el fichero en modo adicin (
) y aadir 100
nuevos primos. Si ejecutsemos una vez
y, a continuacin, dos veces el
nuevo programa, el fichero acabara conteniendo los 1200 primeros primos.
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
360
360
ramacin con C - UJI
Introduccin a la prog
c
UJI
313 Disea un programa que lea de teclado una frase y escriba un fichero de texto
llamado
en el que cada palabra de la frase ocupa una lnea.
314 Disea un programa que lea de teclado una frase y escriba un fichero de texto
llamado
en el que cada lnea contenga un carcter de la frase.
315 Modifica el programa miniGalaxis para que gestione una lista de records. Un
fichero de texto, llamado
almacenar el nombre y nmero de
movimientos de los 5 mejores jugadores de todos los tiempos (los que completaron
el
juego usando el menor nmero de sondas).
316 Disponemos de dos ficheros: uno contiene un diccionario y el otro, un texto.
El
diccionario est ordenado alfabticamente y contiene una palabra en cada lnea. Disea
un programa que lea el diccionario en un vector de cadenas y lo utilice para det
ectar
errores en el texto. El programa mostrar por pantalla las palabras del texto que
no estn
en el diccionario, indicando los nmeros de lnea en que aparecen.
Supondremos que el diccionario contiene, a lo sumo, 1000 palabras y que la p
alabra
ms larga (tanto en el diccionario como en el texto) ocupa 30 caracteres.
(Si quieres usar un diccionario real como el descrito y trabajas en Unix, en
contrars
uno en ingls en
o
. Puedes averi
guar el
nmero de palabras que contiene con el comando
de Unix.)
317 Modifica el programa del ejercicio anterior para que el nmero de palabras del
vector que las almacena se ajuste automticamente al tamao del diccionario. Tendrs
que usar memoria dinmica.
Si usas un vector de palabras, puedes efectuar dos pasadas de lectura en el
fichero que
contiene el diccionario: una para contar el nmero de palabras y saber as cunta memo
ria
es necesaria y otra para cargar la lista de palabras en un vector dinmico. Natura
lmente,
antes de la segunda lectura debers haber reservado la memoria necesaria.
Una alternativa a leer dos veces el fichero consiste en usar realloc juicios
amente:
reserva inicialmente espacio para, digamos, 1000 palabras; si el diccionario con
tiene un
nmero de palabras mayor que el que cabe en el espacio de memoria reservada, dupli
ca
la capacidad del vector de palabras (cuantas veces sea preciso si el problema se
da ms
de una vez).
Otra posibilidad es usar una lista simplemente enlazada, pues puedes crearla
con
una primera lectura. Sin embargo, no es recomendable que sigas esta estrategia,
pues
no podrs efectuar una bsqueda dicotmica a la hora de determinar si una palabra est
incluida o no en el diccionario.
...............................................................................
...
Ya vimos en su momento que fscanf presenta un problema cuando leemos cadenas
:
slo lee una palabra, es decir, se detiene al llegar a un blanco. Aprendimos a usar
entonces una funcin, gets, que lea una lnea completa. Hay una funcin equivalente
para ficheros de texto:
char
fgets char cadena
int max tam FILE
f
ichero
Ojo con el prototipo de fgets! El parmetro de tipo FILE es el ltimo, no el primero!
361
361
100
f
f
f fopen
aux fgets s
printf
aux fgets s
printf
fclose f
f
aux s
f
aux s
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
362
362
ramacin con C - UJI
Introduccin a la prog
c
UJI
return 0
Primera cuestin: Cuntos bytes ocupa el fichero prueba txt?
Al ejecutarlo, obtenemos este resultado en pantalla:
Nuestro programa podr leer en memoria los datos de un fichero como ste y tambin
escribirlos en fichero desde memoria.
Las estructuras de datos que manejaremos en memoria se definen as:
struct Entrada
char nombre
char direccion
char telefono
struct NodoAgenda
struct Entrada datos
struct NodoAgenda sig
typedef struct NodoAgenda
TipoAgenda
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
ntroduccin a la programacin con C - UJI
363
363
UJI
c
aux
fclose fp
La lectura del fichero ser sencilla:
TipoAgenda lee agenda char nombre fichero
TipoAgenda agenda
struct Entrada entrada leida
FILE fp
char nombre
1 direccion
telefono 1
int longitud
agenda
crea agenda
fclose fp
return agenda
La nica cuestin reseable es la purga de saltos de lnea innecesarios.
He aqu el listado completo del programa:
include
include
define
200
enum
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
364
364
struct Entrada
char nombre
char direccion
char telefono
struct NodoAgenda
struct Entrada datos
struct NodoAgenda sig
typedef struct NodoAgenda
TipoAgenda
e
e
e
datos nombre
datos direccion
datos telefono
e
int i
free
free
free
free
e
e
e
e
datos nombre
datos direccion
datos telefono
aux
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
65
365
aux
aux
aux
sig
struct NodoAgenda
aux
sig
0
return
void libera agenda TipoAgenda agenda
struct NodoAgenda
aux
siguiente
aux agenda
while aux
siguiente aux sig
libera entrada aux
aux siguiente
aux
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
366
366
FILE fp
char nombre
telefono 1
int longitud
agenda
direccion
crea agenda
fclose fp
return agenda
Programa principal
int main void
TipoAgenda miagenda
struct NodoAgenda encontrada
int opcion
char nombre
1
char direccion
1
char telefono
1
char linea
1
miagenda
lee agenda
do
printf
printf
printf
printf
printf
printf
gets linea
sscanf linea
opcion
switch opcion
case Ver
muestra agenda miagenda
break
case Alta
printf
gets nombre
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
367
367
printf
printf
miagenda
gets direccion
gets telefono
anyadir entrada miagenda nombre direccion
telefono
break
case Buscar
printf
gets nombre
encontrada buscar entrada por nombre miagenda nombre
if encontrada
printf
nombre
else
muestra entrada encontrada
break
while opcion
Salir
Pero hay un serio problema: cmo sabe el programa dnde empieza y acaba cada disco?
El programa no puede distinguir entre el ttulo de una cancin, el de un disco o el
nombre
de un intrprete. Podramos marcar cada lnea con un par de caracteres que nos indique
n
qu tipo de informacin mantiene:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
Introduccin a la programacin con C -UJI
cUJI
368
368
Con
indicamos ttulo de disco; con , intrprete; con , ao; y con , ttulo
de cancin. Pero esta solucin complica las cosas en el programa: no sabemos de qu
tipo es una lnea hasta haber ledo sus dos primeros caracteres. O sea, sabemos que
un
disco ha acabado cuando ya hemos ledo una lnea del siguiente. No es que no se
pueda trabajar as, pero resulta complicado. Como podemos definir libremente el fo
rmato,
optaremos por uno que preceda los ttulos de las canciones por un nmero que indique
cuntas canciones hay:
Introduccin
Andrs aMarzal/Isabel
la programacin con -CISBN: 978-84-693-0143-2
Gracia
cUJI
Introduccin a la programacin con C -UJI
369
369
in
fclose f
return coleccion
Tan slo cabe resear dos cuestiones:
La deteccin del final de fichero se ha de hacer tras una lectura infruc
tuosa, por lo
que la hemos dispuesto tras el primer fgets del bucle.
La lectura de lneas con fgets hace que el salto de lnea est presente, as q
ue hay
que eliminarlo explcitamente.
Al guardar el fichero hemos de asegurarnos de que escribimos la informacin en
el
mismo formato:
void guarda coleccion TipoColeccion coleccion char nombre fichero
struct Disco disco
struct Cancion cancion
int numcanciones
FILE f
f fopen nombre fichero
for disco coleccion disco
disco
fprintf f
disco titulo
fprintf f
disco interprete
disco
sig
fprintf f
disco anyo
numcanciones 0
for cancion disco canciones cancion
cancion
cancion
sig
numcanciones
fprintf f
numcanciones
for cancion
cancion
fprintf f
disco
cancion
canciones cancion
sig
cancion titulo
fclose f
Observa que hemos recorrido dos veces la lista de canciones de cada disco: una p
ara
saber cuntas canciones contiene (y as poder escribir en el fichero esa cantidad) y
otra
para escribir los ttulos de las canciones.
Aqu tienes las modificaciones hechas al programa principal:
include
include
include
include
carga coleccion
do
printf
printf
printf
printf
printf
printf
printf
printf
printf
inea
gets linea
opcion
sscanf l
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
319 La gestin de ficheros mediante su carga previa en memoria puede resultar
problemtica al trabajar con grandes volmenes de informacin. Modifica el programa de
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
371
371
gramacin con C - UJI
Introduccin a la pro
c
UJI
la agenda para que no cargue los datos en memoria. Todas las operaciones (aadir d
atos
y consultar) se efectuarn gestionando directamente ficheros.
320 Modifica el programa propuesto en el ejercicio anterior para que sea posible
borrar entradas de la agenda. (Una posible solucin pasa por trabajar con dos fich
eros,
uno original y uno para copias, de modo que borrar una informacin sea equivalente
a
no escribirla en la copia.)
321 Modifica el programa de la agenda para que se pueda mantener ms de un
telfono asociado a una persona. El formato del fichero pasa a ser el siguiente:
Una lnea que empieza por la letra N contiene el nombre de una persona.
Una lnea que empieza por la letra D contiene la direccin de la persona c
uyo
nombre acaba de aparecer.
Una lnea que empieza por la letra T contiene un nmero de telfono asociado
a
la persona cuyo nombre apareci ms recientemente en el fichero.
Ten en cuenta que no se puede asociar ms de una direccin a una persona (y si eso
ocurre en el fichero, debes notificar la existencia de un error), pero s ms de un
telfono.
Adems, puede haber lneas en blanco (o formadas nicamente por espacios en blanco)
en el fichero. He aqu un ejemplo de fichero con el nuevo formato:
322 En un fichero matriz mat almacenamos los datos de una matriz de enteros con
el siguiente formato:
La primera lnea contiene el nmero de filas y columnas.
Cada una de las restantes lneas contiene tantos enteros (separados por
espacios)
como indica el nmero de columnas. Hay tantas lneas de este estilo como f
ilas
tiene la matriz.
Este ejemplo define una matriz de 3 4 con el formato indicado:
Escribe un programa que lea matriz mat efectuando las reservas de memoria dinm
ica
que corresponda y muestre por pantalla, una vez cerrado el fichero, el contenido
de la
matriz.
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
372
372
323 Modifica el programa del ejercicio anterior para que, si hay menos lneas con
valores de filas que filas declaradas en la primera lnea, se rellene el restante
nmero de
filas con valores nulos.
Aqu tienes un ejemplo de fichero con menos filas que las declaradas:
324 Disea un programa que facilite la gestin de una biblioteca. El programa permitir prestar libros. De cada libro se registrar al menos el ttulo y el autor. En c
ualquier
instante se podr volcar el estado de la biblioteca a un fichero y cargarlo de l.
Conviene que la biblioteca sea una lista de nodos, cada uno de los cuales re
presenta
un libro. Uno de los campos del libro podra ser una cadena con el nombre del pres
tatario.
Si dicho nombre es la cadena vaca, se entender que el libro est disponible.
...............................................................................
...
Permisos Unix
Los ficheros Unix llevan asociados unos permisos con los que es posible d
eterminar
qu usuarios pueden efectuar qu acciones sobre cada fichero. Las acciones so
n: leer,
escribir y ejecutar (esta ltima limitada a ficheros ejecutables, es decir,
resultantes de
una compilacin o que contienen cdigo fuente de un lenguaje interpretado y s
iguen
cierto convenio). Se puede fijar cada permiso para el usuario propietario d
el fichero,
para los usuarios de su mismo grupo o para todos los usuarios del sistema
.
Cuando ejecutamos el comando
con la opcin
, podemos ver l
os permisos
codificados con las letras
y el carcter :
El fichero
tiene permiso de lectura y escritura para el usuario
(caracteres 2 a
4), de slo lectura para los usuarios de su grupo (caracteres 5 a 7) y de sl
o lectura para
el resto de usuarios (caracteres 8 a 10). El fichero
puede ser ledo
, modificado y
ejecutado por el usuario. Los usuarios del mismo grupo pueden leerlo y ej
ecutarlo, pero
no modificar su contenido. El resto de usuarios no puede acceder al fiche
ro.
El comando Unix
permite modificar los permisos de un fiche
ro. Una forma
tradicional de hacerlo es con un nmero octal que codifica los permisos. Aq
u tienes un
ejemplo de uso:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
Introduccin a la programacin con C - UJI
c
UJI
373
373
include
int main void
FILE fp
char dedonde 80
int n
nombre 80
printf
gets dedonde
if dedonde 0
printf
gets nombre
fp fopen nombre
else
fp
stdin
fscanf fp
if fp
stdin
fclose fp
return 0
Existe otra forma de trabajar con fichero o teclado que es ms cmoda para el pr
ogramador: usando la capacidad de redireccin que facilita el intrprete de comandos Uni
x.
374
374
atoi a
printf
a b
return 0
Si deseas interpretar el texto como un float, puedes usar atof en lugar de
atoi. As de
fcil.
include
int main void
int i n
for i 0 i 10 i
scanf
n
if n 2 0
printf
return 0
Si lo compilas para generar un programa pares, lo ejecutas e introduces los sigu
ientes
10 nmeros enteros, obtendrs este resultado en pantalla:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
Introduccin a la programacin con C - UJI
UJI
c
375
375
Ahora el programa se ejecuta sin mostrar texto alguno por pantalla y el fiche
ro solopares txt acaba conteniendo lo que debiera haberse mostrado por pantalla.
376
376
0
Se reserva un carcter para el
while
c fgetc stdin
if c
break
if nc max lon
linea nc
c
if c
return
nc
linea nc
return nc
Para leer una cadena en un vector de caracteres con una capacidad mxima
de 100
caracteres, haremos:
lee linea cadena 100
El valor de cadena se modificar para contener la cadena leda. La cadena ms l
arga
leda tendr una longitud de 99 caracteres (recuerda que el
ocupa uno de
los 100).
Pero hay una posibilidad an ms sencilla: usar fgets sobre stdin:
fgets cadena 100 stdin
Una salvedad: fgets incorpora a la cadena leda el salto de lnea, cosa que g
ets no hace.
La primera versin, no obstante, sigue teniendo inters, pues te muestra
un esqueleto de funcin til para un control detallado de la lectura por teclado. Insp
irndote en
ella puedes escribir, por ejemplo, una funcin que slo lea dgitos, o letras,
o texto que
satisface alguna determinada restriccin.
argo,
cmo eliminar un fichero del sistema de ficheros ni cmo rebautizarlo. Hay dos funci
ones
de la librera estndar de C (accesibles al incluir
) que permiten efectuar e
stas
dos operaciones:
remove: elimina el fichero cuya ruta se proporciona.
int remove char ruta
La funcin devuelve 0 si se consigui eliminar el fichero y otro valor si s
e cometi
algn error. Ojo! No confundas borrar un fichero con borrar el contenido d
e un
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
377
377
La consulta de teclado
La funcin getc (o, para el caso, fgetc actuando sobre stdin) bloquea la e
jecucin del
programa hasta que el usuario teclea algo y pulsa la tecla de retorno. M
uchos programadores se preguntan cmo puedo saber si una tecla est pulsada o no sin qued
ar
bloqueado? Ciertas aplicaciones, como los videojuegos, necesitan efectua
r consultas al
estado del teclado no bloqueantes. Malas noticias: no es un asunto del l
enguaje C,
sino de bibliotecas especficas. El C estndar nada dice acerca de cmo efectu
ar esa
operacin.
En Unix, la biblioteca curses, por ejemplo, permite manipular los te
rminales y acceder
de diferentes modos al teclado. Pero no es una biblioteca fcil de (aprend
er a) usar. Y,
adems, presenta problemas de portabilidad, pues no necesariamente est disp
onible en
todos los sistemas operativos.
Cosa parecida podemos decir de otras cuestiones: sonido, grficos trid
imensionales,
interfaces grficas de usuario, etc. C, en tanto que lenguaje de programac
in estandarizado, no ofrece soporte. Eso s: hay bibliotecas para infinidad de campos
de aplicacin.
Tendrs que encontrar la que mejor se ajusta a tus necesidades y. . . estud
iar!
378
378
fread: recibe una direccin de memoria, el nmero de bytes que ocupa un dat
o, el
nmero de datos a leer y un fichero. He aqu su prototipo4 :
int fread void
fichero
Los bytes ledos se almacenan a partir de direccion. Devuelve el nmero de
datos
que ha conseguido leer (y si ese valor es menor que numdatos, es porque
hemos
llegado al final del fichero y no se ha podido efectuar la lectura comp
leta).
fwrite: recibe una direccin de memoria, el nmero de bytes que ocupa un da
to, el
nmero de datos a escribir y un fichero. Este es su prototipo:
int fwrite void
fichero
Escribe en el fichero los tam por numdatos bytes existentes desde direc
cion en
adelante. Devuelve el nmero de datos que ha conseguido escribir (si vale
menos
que numdatos, hubo algn error de escritura).
Empezaremos a comprender cmo trabajan estas funciones con un sencillo ejemplo.
Vamos a escribir los diez primeros nmeros enteros en un fichero:
include
int main void
FILE
int i
fp
fp fopen
for i 0 i 10 i
fwrite i sizeof int
fclose fp
1 fp
return 0
Analicemos la llamada a fwrite. Fjate: pasamos la direccin de memoria en la que em
pieza
un entero (con i) junto al tamao en bytes de un entero (sizeof int , que vale 4)
y el
valor 1. Estamos indicando que se van a escribir los 4 bytes (resultado de multi
plicar 1
por 4) que empiezan en la direccin i, es decir, se va a guardar en el fichero una
copia
exacta del contenido de i.
Quiz entiendas mejor qu ocurre con esta otra versin capaz de escribir un vector
completo en una sola llamada a fwrite:
include
int main void
FILE fp
int i v 10
for i 0 i 10 i
v i i
fp fopen
fwrite v sizeof int
10 fp
4
Bueno, casi. El prototipo no usa el tipo int, sino size_t, que est definido
como unsigned int. Preferimos
presentarte una versin modificada del prototipo para evitar introducir nuevos con
ceptos.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
379
379
fclose fp
return 0
Ahora estamos pasando la direccin en la que empieza un vector (v es una direccin,
as
que no hemos de poner un delante), el tamao de un elemento del vector (sizeof int
)
y el nmero de elementos del vector (10). El efecto es que se escriben en el fiche
ro los 40
bytes de memoria que empiezan donde empieza v. Resultado: todo el vector se alma
cena
en disco con una sola operacin de escritura. Cmodo, no?
Ya te dijimos que la informacin de todo fichero binario ocupa exactamente el
mismo
nmero de bytes que ocupara en memoria. Hagamos la prueba. Veamos con
, desde
el intrprete de comandos de Unix, cunto ocupa el fichero:
Efectivamente, ocupa exactamente 40 bytes (el nmero que aparece en quinto lugar).
Si
lo mostramos con
, no sale nada con sentido en pantalla.
(La opcin
de
hace que muestre la interpretacin como enteros de grupos de
4
bytes.) Ah estn los nmeros! La primera columna indica (en hexadecimal) el nmero de
byte del primer elemento de la fila.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
generado en este
include
int main void
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
380
380
ramacin con C - UJI
Introduccin a la prog
c
UJI
FILE fp
int i v 26
fp fopen
for i 97 i 123 i
v i 97 i
fwrite v sizeof int
fclose fp
26 fp
return 0
(Una pista: el valor ASCII del carcter
es 97.)
Y qu aparecer si lo visualizas con el comando
(la opcin indica que se
desea ver el fichero carcter a carcter e interpretado como secuencia de caracteres
).
...............................................................................
...
Ya puedes imaginar cmo se leen datos de un fichero binario: pasando la direcc
in
de memoria en la que queremos que se copie cierta cantidad de bytes del fichero.
Los
dos programas siguientes, por ejemplo, leen los diez valores escritos en los dos
ltimos
programas. El primero lee entero a entero (de 4 bytes en 4 bytes), y el segundo
con una
sola operacin de lectura (cargando los 40 bytes de golpe):
include
int main void
FILE fp
int i n
fp fopen
for i 0 i 10 i
fread n sizeof int
printf
n
fclose fp
return 0
include
int main void
FILE fd
int i v 10
fp fopen
fread v sizeof int 10 fp
for i 0 i 10 i
printf
v i
fclose fp
1 fp
return 0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
381
381
En los dos programas hemos indicado explcitamente que bamos a leer 10 enteros,
pues sabamos de antemano que haba exactamente 10 nmeros en el fichero. Es fcil
modificar el primer programa para que lea tantos enteros como haya, sin conocer
a priori
su nmero:
include
int main void
FILE
int n
fp
fp fopen
fread n sizeof int 1 fp
while feof fp
printf
n
fread n sizeof int 1 fp
fclose fp
return 0
Lo cierto es que hay una forma ms idiomtica, ms comn en C de expresar lo mismo
:
include
int main void
FILE
int n
f fopen
while fread
printf
fclose fp
fp
n sizeof int
n
1 fp
return 0
En esta ltima versin, la lectura de cada entero se efecta con una llamada a fread e
n
la condicin del while.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
326 Disea un programa que genere un fichero binario
con los 1000
primeros nmeros primos.
327 Disea un programa que aada al fichero binario
(ver ejerc
icio
anterior) los 100 siguientes nmeros primos. El programa leer el contenido actual d
el
fichero para averiguar cul es el ltimo primo conocido. A continuacin, abrir el fiche
ro
en modo adicin y aadir 100 nuevos primos. Si ejecutsemos dos veces el programa, el
Introduccin a la prog
c
UJI
include
include
struct Punto
float x
float y
int main void
FILE fp
struct Punto v 10
int i
Cargamos en memoria un vector de puntos.
fp fopen
fread v sizeof struct Punto 10 fp
fclose fp
Procesamos los puntos (calculamos el valor absoluto de cada coordena
da).
for i 0 i 10 i
v i x fabs v i x
v i y fabs v i y
Escribimos el resultado en otro fichero.
fp fopen
fwrite v sizeof struct Punto 10 fp
fclose fp
return 0
Esta otra versin no carga el contenido del primer fichero completamente en mem
oria
en una primera fase, sino que va leyendo, procesando y escribiendo punto a punto
:
include
include
struct Punto
float x
float y
int main void
FILE fp entrada
struct Punto p
int i
fp salida
fp entrada fopen
fp salida fopen
for i 0 i 10 i
fread p sizeof struct Punto
p x fabs p x
p y fabs p y
1 fp entrada
1 fp salida
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
383
383
fclose fp entrada
fclose fp salida
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCICI
OS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
328 Los dos programas anteriores suponen que hay diez puntos en
.
Modifcalos para que procesen tantos puntos como haya en el fichero.
329 Implementa un programa que genere un fichero llamado
con 10
elementos del tipo struct Punto. Las coordenadas de cada punto se generarn aleato
riamente en el rango [10, 10]. Usa el ltimo programa para generar el fichero
.
Comprueba ue contiene el valor absoluto de los valores de
. Si es
necesario,
disea un nuevo programa ue muestre por pantalla el contenido de un fichero de pu
ntos
cuyo nombre suministra por teclado el usuario.
...............................................................................
...
Los ficheros binarios pueden utilizarse como vectores en disco y acceder directa
mente a cualuier elemento del mismo. Es decir, podemos abrir un fichero binario
en
modo lecturaescritura y, gracias a la capacidad de desplazarnos libremente por l,
leer/escribir cualuier dato. Es como si dispusieras del control de avance rpido
hacia
adelante y hacia atrs de un reproductor/grabador de cintas magnetofnicas. Con l pue
des ubicar el cabezal de lectura/escritura en cualuier punto de la cinta y pulsar
el
botn play para escuchar (leer) o el botn record para grabar (escribir).
Adems de los modos de apertura de ficheros binarios ue ya conoces, puedes us
ar
tres modos de lectura/escritura adicionales:
: No se borra el contenido del fichero, ue debe existir previam
ente. El ca
bezal de lectura/escritura se sita al principio del fichero.
: Si el fichero no existe, se crea, y si existe, se trunca el c
ontenido a longitud
cero. El cabezal de lectura/escritura se sita al principio del fichero.
: Si el fichero no existe, se crea. El cabezal de lectura/escritu
ra se sita al
final del fichero.
Para poder leer/escribir a voluntad en cualuier posicin de un fichero abiert
o en
alguno de los modos binarios necesitars dos funciones auxiliares: una ue te perm
ita
desplazarte a un punto arbitrario del fichero y otra ue te permita preguntar en
u
posicin te encuentras en un instante dado. La primera de estas funciones es fseek
, ue
desplaza el cabezal de lectura/escritura al byte ue indiuemos.
int fseek FILE
fp int desplazamiento int desde donde
El valor desde donde se fija con una constante predefinida ue proporciona una i
nter
pretacin distinta a desplazamiento:
: el valor de desplazamiento es un valor absoluto a contar
desde el prin
cipio del fichero. Por ejemplo, fseek fp 3
desplaza al c
uarto byte del
fichero fp. (La posicin 0 corresponde al primer byte del fichero.)
Introduccin a la programacin
Andrs Marzal/Isabel
con ISBN:
Gracia
C
9788469301432
384
384
c
UJI
gramacin con C UJI
Introduccin a la pro
n ue nos
cuarto
byte del fichero fp, la llamada fseek fp 2
nos desplazar al
segundo
byte, y fseek fp 2
al sexto.
fp fopen
while fread n sizeof int 1 fp
0
if n 2
0
Si el ltimo valor ledo es par...
fseek fp sizeof int
... damos un paso atrs ...
fwrite cero sizeof int 1 fp
... y sobreescribimos su valo
r absoluto.
fclose fp
return 0
La segunda funcin ue te presentamos en este apartado es ftell. Este es su
prototipo:
int ftell FILE fp
El valor devuelto por la funcin es la posicin en la ue se encuentra el cabezal de
lectura/escritura en el instante de la llamada.
Veamos un ejemplo. Este programa, por ejemplo, crea un fichero y nos dice el
nmero
de bytes del fichero:
include
int main void
FILE fp
int i pos
fp fopen
for i 0 i 10 i
fwrite i sizeof int
1 fp
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
Introduccin a la programacin con C UJI
cUJI
385
385
fclose fp
fp fopen
fseek fp 0
pos ftell fp
printf
fclose fp
pos
return 0
Fjate bien en el truco ue permite conocer el tamao de un fichero: nos situamos al
final del fichero con fseek indicando ue ueremos ir al primer byte desde el fin
al
(byte 0 con el modo
) y averiguamos a continuacin la posicin en la ue
nos
encontramos (valor devuelto por ftell).
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
330 Disea una funcin de nombre rebobina que recibe un FILE y nos ubica al
inicio del mismo.
331 Disea una funcin que reciba un FILE (ya abierto) y nos diga el nmero de
bytes que ocupa. Al final, la funcin debe dejar el cursor de lectura/escritura en
el mismo
lugar en el que estaba cuando se la llam.
332 Disea un programa que calcule y muestre por pantalla el mximo y el mnimo
de los valores de un fichero binario de enteros.
333 Disea un programa que calcule el mximo de los enteros de un fichero binario
y lo intercambie por el que ocupa la ltima posicin.
334 Nos pasan un fichero binario
con una cantidad indeterminada
de
nmeros de tipo float. Sabemos, eso s, que los nmeros estn ordenados de menor a
mayor. Disea un programa que pida al usuario un nmero y determine si est o no est
en el fichero.
En una primera versin, implementa una bsqueda secuencial que se detenga tan
pronto ests seguro de que el nmero buscado est o no. El programa, en su versin final
,
deber efectuar la bsqueda dicotmicamente (en un captulo anterior se ha explicado
qu es una bsqueda dicotmica).
...............................................................................
...
Trabajar con ficheros binarios como si se tratara de vectores tiene ciertas
ventajas, pero
tambin inconvenientes. La ventaja ms obvia es la capacidad de trabajar con cantida
des
ingentes de datos sin tener que cargarlas completamente en memoria. El inconveni
ente
ms serio es la enorme lentitud con que se pueden ejecutar entonces los programas.
Ten en cuenta que desplazarse por un fichero con fseek obliga a ubicar el cabezal
de
lectura/escritura del disco duro, una operacin que es intrnsecamente lenta por com
portar
operaciones mecnicas, y no slo electrnicas.
Si en un fichero binario mezclas valores de varios tipos resultar difcil, cua
ndo no
imposible, utilizar sensatamente la funcin fseek para posicionarse en un punto ar
bitrario
del fichero. Tenemos un problema similar cuando la informacin que guardamos en un
fichero es de longitud intrnsecamente variable. Pongamos por caso que usamos un f
ichero
binario para almacenar una lista de palabras. Cada palabra es de una longitud, a
s que
no hay forma de saber a priori en qu byte del fichero empieza la n-sima palabra de
la
lista. Un truco consiste en guardar cada palabra ocupando tanto espacio como la
palabra
ms larga. Este programa, por ejemplo, pide palabras al usuario y las escribe en u
n fichero
binario en el que todas las cadenas miden exactamente lo mismo (aunque la longit
ud de
cada una de ellas sea diferente):
Introduccin a la programacin
Andrs Marzal/Isabel
con- ISBN:
Gracia
C
978-84-693-0143-2
386
386
ramacin con C - UJI
Introduccin a la prog
c
UJI
0 al 99:
from struct import pack
f open
for v in range 100
c pack
v
f write c
f close
Slo queda que aprendas a implementar acceso directo a los ficheros bina
rios con
Python. Tienes disponibles los modos de apertura
,
y
. Adems,
el mtodo
seek permite desplazarse a un byte cualquiera del fichero y el mtodo tell
indica en
qu posicin del fichero nos encontramos.
include
define
80
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
387
387
seguir
1
FILE fp
fp fopen
do
printf
gets palab
ra
fwrite palabra sizeof char
printf
fp
gets seguir
while strcmp seguir
fclose fp
return 0
Fjate en que cada palabra ocupa siempre lo mismo, independientemente de su lo
ngitud: 80 bytes. Este otro programa es capaz ahora de mostrar la lista de palabr
as en
orden inverso, gracias a la ocupacin fija de cada palabra:
include
define
80
p
printf
palabra
fclose fp
return 0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . EJERCIC
IOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
335 Los dos programas anteriores pueden plantear problemas cuando trabajan con
palabras que tienen 80 caracteres ms el terminador. Qu problemas? Cmo los solucionaras?
336 Disea un programa que lea una serie de valores enteros y los vaya escribiendo
Introduccin a la prog
c
UJI
2.
isodio.
3.
4.
rias.
Ver un listado por superhroe que muestre el ttulo de todas sus historias.
Ver un listado por ao que muestre el superhrore y ttulo de todas sus histo
Disea un programa que gestione la base de datos teniendo en cuenta que no queremo
s
cargarla en memoria cada vez que ejecutamos el programa, sino gestionarla direct
amente
sobre disco.
...............................................................................
...
Truncamiento de ficheros
Las funciones estndar de manejo de ficheros no permiten efectuar una oper
acin que
puede resultar necesaria en algunas aplicaciones: eliminar elementos de
un fichero. Una
forma de conseguir este efecto consiste en generar un nuevo fichero en e
l que escribimos
slo aquellos elementos que no deseamos eliminar. Una vez generado el nuev
o fichero,
borramos el original y renombramos el nuevo para que adopte el nombre de
l original.
Costoso.
En Unix puedes recurrir a la funcin truncate (disponible al incluir l
a cabecera
). El perfil de truncate es ste:
int truncate char nombre
int longitud
de un fichero
recibe el nombre de truncamiento.
389
389
: permiso denegado,
: el fichero no existe,
: demasiados ficheros abiertos,
...
Como manejarte con tantas constantes (algunas con significados un tanto difcil
de
comprender hasta ue curses asignaturas de sistemas operativos) resulta complica
do,
puedes usar una funcin especial:
void perror char s
Esta funcin muestra por pantalla el valor de la cadena s, dos puntos y un mensaje
de
error ue detalla la causa del error cometido. La cadena s, ue suministra el pr
ogramador,
suele indicar el nombre de la funcin en la ue se detect el error, ayudando as a la
depuracin del programa.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
Introduccin a la programacin con C UJI
cUJI
390
390
Esta tabla muestra el nombre de cada uno de los tipos de datos para valores ente
ros
(algunos tienen dos nombres vlidos), su rango de representacin y el nmero de bytes
(grupos de 8 bits) ue ocupan.
Tipo
Rango
Bytes
char
128 . . . 127
1
32768 . . . 32767
2
int
2147483648 . . . 2147483647
4
2147483648 . . . 2147483647
4
long long int (o long long)
9223372036854775808 . . . 922337203
6854775807
8
(Como ves, los tipos short int, long int y long long int pueden abreviarse, resp
ectivamente,
como short, long, y long long.)
Un par de curiosidades sobre la tabla de tipos enteros:
Los tipos int y long int ocupan lo mismo (4 bytes) y tienen el mismo ran
go. Esto
es as para el compilador
sobre un PC. En una muina distinta o con o
tro
compilador, podran ser diferentes: los int podran ocupar 4 bytes y los lon
g int, 8,
por ejemplo. En sistemas ms antiguos un int ocupaba 2 bytes y un long int
, 4.
El nombre del tipo char es abreviatura de carcter (character, en ingls) y, sin
embargo, hace referencia a los enteros de 8 bits, es decir, 1 byte. Los
valores de
tipo char son ambivalentes: son tanto nmeros enteros como caracteres.
Es posible trabajar con enteros sin signo en C, es decir, nmeros enteros posi
tivos. La
ventaja de trabajar con ellos es ue se puede aprovechar el bit de signo para au
mentar
el rango positivo y duplicarlo. Los tipos enteros sin signo tienen el mismo nomb
re ue
sus correspondientes tipos con signo, pero precedidos por la palabra unsigned,
ue acta
como un adjetivo:
Tipo
Rango
Bytes
unsigned char
0. . . 255
1
unsigned short int (o unsigned short)
2
unsigned int (o unsigned)
5
4
unsigned long int (o unsigned long)
5
4
unsigned long long int (o unsigned long long)
73709551615
8
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la programacin con C UJI
0. . . 65535
0. . . 429496729
0. . . 429496729
0. . . 184467440
391
391
Del mismo modo ue podemos marcar un tipo entero como sin signo con el adjetivo
unsigned, podemos hacer explcito ue tiene signo con el adjetivo signed. O sea, e
l tipo
int puede escribirse tambin como signed int: son exactamente el mismo tipo, slo u
e
en el segundo caso se pone nfasis en ue tiene signo, haciendo posible una mejora
en
la legibilidad de un programa donde este rasgo sea importante.
Puedes escribir nmeros enteros en notacin octal (base 8) o hexadecimal (base 16).
Un
nmero en notacin hexadecimal empieza por 0x. Por ejemplo, 0xff es 255 y 0x0 es 0.
Un
nmero en notacin octal debe empezar por un 0 y no ir seguido de una x. Por ejemplo
,
077 es 63 y 010 es 8.1
Puedes precisar ue un nmero entero es largo aadindole el sufijo (por Long).
Por ejemplo, 2L es el valor 2 codificado con 32 bits. El sufijo
(por long long)
indica
ue el nmero es un long long int. El literal 2L , por ejemplo, representa al nmero
entero 2 codificado con 64 bits (lo ue ocupa un long long int). El sufijo (comb
inado
opcionalmente con o ) precisa ue un nmero no tiene signo (la por unsigned).
Normalmente no necesitars usar esos sufijos, pues C hace conversiones automti
cas de tipo cuando conviene. S te har falta si uieres denotar un nmero mayor ue
2147483647 (o menor ue 2147483648), pues en tal caso el nmero no puede represen
tarse como un simple int. Por ejemplo, la forma correcta de referirse a 30000000
00 es con
el literal 3000000000L .
C resulta abrumador por la gran cantidad de posibilidades ue ofrece. Son m
uchas
formas diferentes de representar enteros, verdad? No te preocupes, slo en aplicaci
ones
muy concretas necesitars utilizar la notacin octal o hexadecimal o tendrs ue aadir
el sufijo a un literal para indicar su tipo.
Hay una marca de formato para la impresin o lectura de valores de cada tipo de en
tero:
Tipo
Marca
Tipo
Marca
char (nmero)
short
int
long
long long
unsigned
unsigned
unsigned
unsigned
unsigned
char
short
long
long long
Lo cierto es ue tambin puede usar notacin octal o hexadecimal en Python, aun
ue en su momento
no lo contamos.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
Introduccin a la programacin con C UJI
cUJI
392
392
es un entero
Tambin en el caso de los flotantes tenemos dnde elegir: hay tres tipos diferentes.
En
esta tabla te mostramos el nombre, mximo valor absoluto y nmero de bits de cada un
o
de ellos:
Tipo
Bytes
38
float
3.4028234710
4
double
1.797693134862315710308
8
long double
1.189731495357231765021263853031104932
12
Recuerda que los nmeros expresados en coma flotante presentan mayor resolucin
en la cercanas del 0, y que sta es tanto menor cuanto mayor es, en valor absoluto,
el
nmero representado. El nmero no nulo ms prximo a cero que puede representarse
con cada uno de los tipos se muestra en esta tabla:
Tipo
o no nulo
float
1.1754943
51038
double
2.225073858507201
410308
long double
3.362103143112093506262677817321
8104932
Ya conoces las reglas para formar literales para valores de tipo float. Puedes aa
dir el
sufijo para precisar ue el literal corresponde a un double y el sufijo para ind
icar
ue se trata de un long double. Por ejemplo, el literal 3.2 es el valor 3.2 codi
ficado
como double. Al igual ue con los enteros, normalmente no necesitars precisar el
tipo
del literal con el sufijo , a menos ue su valor exceda del rango propio de los
float.
Veamos ahora las principales marcas de formato para la impresin de datos de tipos
flotantes:
Tipo
Notacin convencional
Notacin cientfica
float
double
long double
Observa ue tanto float como double usan la misma marca de formato para impr
esin
(o sea, con la funcin printf y similares).
No pretendemos detallar todas las marcas de formato para flotantes. Tenemos,
adems,
otras como , , , ,
,
,
y . Cada marca introduce ciertos m
atices
ue, en segn u aplicaciones, pueden venir muy bien. Necesitars un buen manual
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
Introduccin a la programacin con C UJI
cUJI
393
393
de referencia a mano para controlar estos y otros muchos aspectos (no tiene sent
ido
memorizarlos) cuando ejerzas de programador en C durante tu vida profesional.2
Las marcas de formato para la lectura de datos de tipos flotantes presentan
alguna
diferencia:
Tipo
Notacin convenciona
l
float
double
long double
Observa ue la marca de impresin de un double es , pero la de lectura es
.
Es una incoherencia de C ue puede darte algn ue otro problema.
El tipo char, ue ya hemos presentado al estudiar los tipos enteros, es, a la ve
z el tipo
con el ue solemos representar caracteres y con el ue formamos las cadenas.
C99 define tres nuevos tipos bsicos: el tipo lgico (o booleano), el tipo complejo
y el
tipo imaginario.
Las variables de tipo Bool pueden almacenar los valores 0 (falso) o 1 (cierto). Si
se incluye la cabecera
es posible usar el identificador de tipo bo
ol y las
constantes
y
para referirse al tipo Bool y a los valores 1 y 0, respec
tivamente.
C99 ofrece soporte para la aritmtica compleja a travs de los tipos Complex e Imagi
nary.
En Unix puedes obtener ayuda acerca de las funciones estndar con el manual en
lnea. Ejecuta
2
, por ejemplo, y obtendrs una pgina de manual sobre la funcin printf , in
cluyendo informacin
sobre todas sus marcas de formato y modificadores.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con CISBN: 9788469301432
Introduccin a la programacin con C UJI
cUJI
394
394
Por u ofrece C tan gran variedad de tipos de datos para enteros y flotantes? Por
ue
C procura facilitar el diseo de programas eficientes proporcionando al programado
r
un juego de tipos ue le permita adoptar el compromiso adecuado entre ocupacin de
memoria y rango disponible. Por u iba un programador a uerer gastar 4 bytes en u
na
variable ue slo almacenar valores entre 0 y 255? Naturalmente, ofrecer ms control
no es gratis: a cambio hemos de tomar muchas ms decisiones. Ahorrar 3 bytes en un
a
variable puede no justificar el uebradero de cabeza, pero piensa en el ahorro
ue se
puede producir en un vector ue contiene miles o cientos de miles de elementos
ue
pueden representarse cada uno con un char en lugar de con un int.
Por otra parte, la aruitectura de tu ordenador est optimizada para realizar
clculos
con valores de ciertos tamaos. Por ejemplo, las operaciones con enteros suelen se
r
ms rpidas si trabajas con int (aunue ocupen ms bytes ue los char o short) y las
operaciones con flotantes ms eficientes trabajan con double.
Segn si valoras ms velocidad o consumo de memoria en una aplicacin, debers
escoger uno u otro tipo de datos para ciertas variables.
395
395
In
scanf
La funcin scanf (y fscanf ) se comporta de un modo un tanto especial y puede desc
on
certarte en ocasiones. Veamos u hace exactamente scanf :
Empieza saltndose los blancos ue encuentra (espacios en blanco, tabulador
es y
saltos de lnea).
A continuacin, consume los caracteres no blancos mientra le sirvan para leer
un valor del tipo ue se indica con la marca de formato (por ejemplo, dgit
os si la
marca es ).
La lectura se detiene cuando el siguiente carcter a leer no sirve (por ejemp
lo,
una letra si estamos leyendo un entero). Dicho carcter no es consumido. Los
caracteres consumidos hasta este punto se interpretan como la representacin
de
un valor del tipo ue se indica con la correspondiente marca de formato,
as ue se
crea dicho valor y se escribe en la zona de memoria ue empieza en la dir
eccin
ue se indiue.
Un ejemplo ayudar a entender el comportamiento de scanf :
include
int main void
int a c
float b
printf
printf
printf
printf
a b
printf
c
scanf
scanf
scanf
El entero a es d el flotante b
a
b
c
es f
y el entero c es d
return 0
Ejecutemos el programa e introduzcamos los valores 20, 3.0 y 4 pulsando el retor
no de
carro tras cada uno de ellos.
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la programacin con C UJI
396
396
\n
a
b
c
\n
20
b
c
Como no hay ms caracteres ue procesar, scanf ueda a la espera de ue el usuario
teclee algo con lo ue pueda formar un flotante y pulse retorno de carro. Cuando
el
usaurio teclea el 3.0 seguido del salto de lnea, pasamos a esta nueva situacin:
2
\n
\n
20
b
c
Ahora, scanf reanuda su ejecucin y consume el
, el
y el
. Como detec
ta ue
lo ue sigue no es vlido para formar un flotante, se detiene, interpreta los cara
cteres
ledos como el valor flotante 3.0 y lo almacena en la direccin de b:
397
397
c
UJI
Andrs Marzal/Isabel Gracia ISBN: 9788469301432
Introduccin a la programacin con C UJI
2
a
\n
\n
20
&b
b 3.0
c
Finalmente, el tercer scanf entra en ejecucin y empieza por saltarse el salto de
lnea.
2
a
\n
\n
20
b 3.0
c
Acto seguido se detiene, pues no es necesario ue el usuario introduzca nuevo te
xto ue
procesar. Entonces el usuario escribe el y pulsa retorno:
2
a
\n
\n
\n
20
b 3.0
c
Ahora scanf prosigue consumiendo el y detenindose nuevamente ante el salto de
lnea. El carcter ledo se interpreta entonces como el entero y se almacena en la
direccin de memoria de c:
2
0
\n
3
.
0 \n 4 \n
a 20
b 3.0
&c
c
Como puedes apreciar, el ltimo salto de lnea no llega a ser consumido, pero eso im
porta
poco, pues el programa finaliza correctamente su ejecucin.
Vamos a estudiar ahora el poru de un efecto curioso. Imagina ue, cuando el p
ro
grama pide al usuario el primer valor entero, ste introduce tanto dicho valor com
o los
dos siguientes, separando los tres valores con espacios en blanco. He au el resu
ltado
en pantalla:
El programa ha ledo correctamente los tres valores, sin esperar a ue el usua
rio
introduzca tres lneas con datos: cuando tena ue detenerse para leer el valor de b
, no
lo ha hecho, pues saba ue ese valor era 3.0; y tampoco se ha detenido al leer el v
alor
de c, ya ue de algn modo saba ue era 4. Veamos paso a paso lo ue ha sucedido,
pues la explicacin es bien sencilla.
Durante la ejecucin del primer scanf , el usuario ha escrito el siguiente tex
to:
2
\n
a
b
c
Como su objetivo es leer un entero, ha empezado a consumir caracteres. El
y
el
le son ltiles, as ue los ha consumido. Entonces se ha detenido frente al espacio
en
blanco. Los caracteres ledos se interpretan como el valor entero 20 y se almacena
n en
a:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con CISBN: 9788469301432
398
398
&a
2
\n
3
a
20
b
c
La ejecucin del siguiente scanf no ha detenido la ejecucin del programa, pues an ha
ba
caracteres pendientes de procesar en la entrada. Como siempre, scanf se ha salta
do el
primer blanco y ha ido encontrando caracteres vlidos para ir formando un valor de
l
tipo ue se le indica (en este caso, un flotante). La funcin scanf ha dejado de c
onsumir
caracteres al encontrar un nuevo blanco, se ha detenido y ha almacenado en b el
valor
flotante
. He au el nuevo estado:
2
\n
3
a
20
&b
b 3.0
c
Finalmente, el tercer scanf tampoco ha esperado nueva entrada de teclado: se ha
saltado
directamente el siguiente blanco, ha encontrado el carcter
y se ha detenido
porue
el carcter
ue le sigue es un blanco. El valor ledo (el entero 4) se almacena e
n c:
2
\n
3
a
20
b 3.0
&c
c
Tras almacenar en
\n
3
a
20
b 3.0
c
\n
\n
\n
0
x
2
\n
\n
(Prueba este ejercicio con el ordenador.)
...............................................................................
...
scanf
Vamos a estudiar ahora el comportamiento paso a paso de scanf cuando leemos una
cadena:
Se descartan los blancos iniciales (espacios en blanco, tabuladores o
saltos de
lnea).
Se leen los caracteres vlidos hasta el primer blanco y se almacenan en po
siciones de memoria consecutivas a partir de la que se suministra como argu
mento. Se
entiende por carcter vlido cualquier carcter no blanco (ni tabulador, ni
espacio
en blanco, ni salto de lnea. . . ).
Se aade al final un terminador de cadena.
Un ejemplo ayudar a entender qu ocurre ante ciertas entradas:
include
define
10
1
scanf
scanf
a
b
return 0
Si ejecutas el programa y escribes una primera cadena sin blancos, pulsas el ret
orno de
carro, escribes otra cadena sin blancos y vuelves a pulsar el retorno, la lectur
a se efecta
como cabe esperar:
\n
La funcin ha empezado a consumir los caracteres con los que ir formando la cadena
. Al
llegar al salto de lnea se ha detenido sin consumirlo. He aqu el nuevo estado de c
osas:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- ISBN:
C
978-84-693-0143-2
0
400
40
u
o
\n
0
5
u n o \0
(Fjate en que scanf termina correctamente la cadena almacenada en a.) Acto seguid
o se
ha ejecutado el segundo scanf . La funcin se salta entonces el blanco inicial, es
decir, el
salto de lnea que an no haba sido consumido.
u
o
\n
0
5
u n o \0
0
4
\n
1
u n o \0
0
4
u n o \0
0
1
a
4
\n
d o s \0
n
0
\n
9
a
4
8
b
u n o \0
0
1
9
d o s \0
Recuerda que scanf se salta siempre los blancos que encuentra al principio y
que se
detiene en el primer espacio que encuentra tras empezar a consumir caracteres vli
dos.
Vemoslo paso a paso. Empezamos con este estado de la entrada:
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- CISBN: 978-84-693-0143-2
401
401
macin con C - UJI
UJI
c
Introduccin a la progra
\n
\n
y se detiene al detectar
\n
0
u n o \0
Cuando se ejecuta, el segundo scanf empieza saltndose los blancos iniciales, que
son
todos los que hay hasta el salto de lnea (includo ste):
u
\n
0
u n o \0
De nuevo, como no hay ms que leer, la ejecucin se detiene. El usuario teclea enton
ces
nuevos caracteres:
u
o
o
\n
s
\n
0
u n o \0
\n
0
\n
u n o \0
n
o
\n
0
9
u n o \0
0
1
9
u
d
\n
d o s \0
Andrs Marzal/Isabel
Introduccin
Gracia
a la programacin con- C
ISBN: 978-84-693-0143-2
402
402
Introduccin a la programacin con C - UJI
c
UJI
Ya est.
Imagina ahora que nuestro usuario quiere introducir en a la cadena
y en
b la cadena
. Aqu tienes lo que ocurre al ejecutar el programa
\n
u
d
n
0
\n
1
9
a
u n o \0
n
0
9
a
\n
u n o \0
0
1
9
b
d o s \0
Ves? La consecuencia de este comportamiento es que con scanf slo podemos leer
palabras individuales. Para leer una lnea completa en una cadena, hemos de utiliz
ar una
funcin distinta: gets (por get string, que en ingls significa obtn cadena), disponible
incluyendo
en nuestro programa.
gets
scanf
Vamos a estudiar un caso concreto y analizaremos las causas del extrao comportami
ento
observado.
include
define
80
char a
int i
printf
printf
printf
printf
gets a
scanf
gets b
a i b
return 0
Observa que leemos cadenas con gets y un entero con scanf . Vamos a ejecutar el
programa
introduciendo la palabra
en la primera cadena, el valor en el entero y la pal
abra
en la segunda cadena.
\n
\n
0
4
9
a
u n o \0
n
0
\n
6
9
a
u n o \0
i 2
La funcin lee el
ueda el programa
es ste:
\n
\n
0
9
a
u n o \0
i 2
Introduccin a la programacin
Andrs Marzal/Isabel
con- CISBN: 978-84-693-0143-2
Gracia
404
404
Fjate bien en qu ha ocurrido: nos hemos quedado a las puertas de procesar el salto
de
lnea. Cuando el programa pasa a ejecutar el siguiente gets, lee una cadena vaca! Por
qu? Porque gets lee caracteres hasta el primer salto de lnea, y el primer carcter c
on
que nos encontramos ya es un salto de lnea. Pasamos, pues, a este nuevo estado:
u
\n
0
4
\n
u n o \0
i 2
0
\0
Cmo podemos evitar este problema? Una solucin posible consiste en consumir la
cadena vaca con un gets extra y una variable auxiliar. Fjate en este programa:
include
define
80
1
1
printf
printf
gets findelinea
printf
printf
gets a
scanf
gets b
a i b
return 0
Hemos introducido una variable extra, findelinea, cuyo nico objetivo es consumir
lo que
scanf no ha consumido. Gracias a ella, ste es el estado en que nos encontramos ju
sto
antes de empezar la lectura de b:
u
o
\n
\n
0
7
a
9
u n o \0
i 2
0
2
6
ndelinea
9
\0
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
405
405
ogramacin con C -UJI
cUJI
Introduccin a la pr
u
\n
\n
n
0
\n
1
9
a
u n o \0
i 2
0
ndelinea
\0
Ahora la lectura de
ado resultante:
\n
\n
0
4
\n
1
9
a
u n o \0
i 2
4
8
ndelinea
\0
0
9
b
d o s \0
80
b
1
1
Cadena auxiliar. Su contenido n
gets a
printf
scanf linea
printf
printf
gets linea
i
gets b
a i b
return 0
406
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -CISBN: 978-84-693-0143-2
406
Introduccin a la programacin co
n C -UJI
UJI
c
agilimosas
el larde.
burgovos,
rastas.
Andrs aMarzal/Isabel
Introduccin
Gracia
la programacin con -C ISBN: 978-84-693-0143-2
Introduccin a la programacin con C -UJI
cUJI
407
407