Sunteți pe pagina 1din 101

LENGUAJE C

INTRODUCCIN
El lenguaje C
El lenguaje C es uno de los ms rpidos y potentes que hay hoy en dia. Algunos dicen
que est desfasado (ja,ja) y que el futuro es Java. No se si tendr futuro pero est
claro que presente si tiene. No hay ms que decir que el sistema operativo Linux est
desarrollado en C en su prctica totalidad. As que creo que no slo no perdemos nada
aprendiendolo sino que ganamos mucho. Para empezar nos servir como base para
aprender C++ e introducirnos en el mundo de la programacin Windows. Si optamos
por Linux existe una biblioteca llamada gtk (o librera, como prefieras) que permite
desarrollas aplicaciones estilo windows con C.
No debemos confundir C con C++, que no son lo mismo. Se podra decir que C++ es
una extensin de C. Para empezar en C++ conviene tener una slida base de C.
Existen otros lenguajes como Visual Basic que son muy sencillos de aprender y de
utilizar. Nos dan casi todo hecho. Pero cuando queremos hacer algo complicado o que
sea rpido debemos recurrir a otros lenguajes (c++, delphi,...).
Peculiaridades de C
Una de las cosas importantes de C que debes recordar es que es Case Sensitive
(sensible a las maysculas o algo as). Es decir que para C no es lo mismo escribir
Printf que printf.
Conviene indicar tambin que las instrucciones se separan por ";".
Compiladores de C
Un compilador es un programa que convierte nuestro cdigo fuente en un programa
ejecutable (Me imagino que la mayora ya lo sabes, pero ms vale asegurar). El
ordenador trabaja con 0 y 1. Si escribiramos un programa en el lenguaje del
ordenador nos volveramos locos. Para eso estn lenguajes como el C. Nos permiten
escribir un programa de manera que sea fcil entenderlo por una persona. Luego es el
compilador el que se encarga de convertirlo al complicado idioma de un ordenador.
En la practica a la hora de crear un programa nosotros escribimos el cdigo fuente, en
nuestro caso en C, que normalmente ser un fichero de texto normal y corriente que
contiene las instrucciones de nuestro programa. Luego se lo pasamos al compilador y
este se encarga de convertirlo en un programa.
1
Siguiendo la filosofa de mi pgina los compiladores que usaremos sern gratutos. Yo
recomiendo usar el DJGPP para MS-Dos y el GNU C para Linux. Ambos son
compiladores de lnea de comando as que necesitaremos tambin un editor para crear
nuestros programas. La solucin ms simple en MS-Dos puede ser usar el edit, en
windows el notepad. Pero no son ms que editores sin ninguna otra funcionalidad. Otra
posibilidad es un entorno de desarrollo llamado RHIDE, un programa muy til que
automatiza muchas de las tareas del programador (del estilo del Turbo C y Turbo
Pascal). Si queremos una herramienta muy avanzada podemos usar Emacs, que es un
editor muy potente, aunque para algunos puede parecer muy complicado (valientes y
a por ello). Estos dos programas estn disponibles tanto en Linux como en MS-Dos.
El primer programa: Hola Mundo
En un alarde de originalidad vamos a hacer nuestro primer programa: hola mundo.
Nadie puede llegar muy lejos en el mundo de la programacin sin haber empezado su
carrera con este original y funcional programa. All va:
#include <stdio.h>
int main()
{
/* Aqu va el cuerpo del programa */
printf( !ola mundo"n )# /* $sto imprime hola mundo en
pantalla */
return %#
&
Comprobado con DJGPP
Qu fcil eh? Este programa lo nico que hace es sacar por pantalla el mensaje:
!ola mundo
Vamos ahora a comentar el programa lnea por lnea (Esto no va a ser ms que una
primera aproximacin).
#include <stdio.h>
#include es lo que se llama una directiva. Sirve para indicar al compilador que incluya
otro archivo. Cuando en compilador se encuentra con esta directiva la sustituye por el
archivo indicado. En este caso es el archivo stdio.h que es donde est definida la
funcin printf, que veremos luego.
int main()
Es la funcin principal del programa. Todos los programas de C deben tener una
funcin llamada main. Es la que primero se ejecuta. El int (entero) que tiene al
principio significa que cuando la funcin main acabe devolver un nmero entero. Este
valor se suele usar para saber cmo ha terminado el prorama. Normalmente este valor
ser 0 si todo ha ido bien, o un valor distinto si se ha producido algn error (pero esto
2
lo decidimos nosotros, ya lo veremos). De esta forma si nuestro programa se ejecuta
desde otro el programa 'padre' sabe como ha finalizado, si ha habido errores o no.
Se puede usar la definicin 'void main()', que no necesita devolver ningn valor, pero
se recomienda la forma con 'int' que es ms correcta. A lo largo de este curso vers
muchos ejemplos que uso 'void main' y falta el return 0; del final, el cdigo funciona
correctamente pero puede dar un 'warning' al compilar. En estos momentos estoy
intentando corregir esto, pido perdn por las molestias.
{
Son las llaves que indican el comienzo de una funcin, en este caso la funcin main.
/* Aqu va el cuerpo del programa */
Esto es un comentario, no se ejecuta. Sirve para describir el programa. Conviene
acostumbrarse a comentar los programas. Un comentario puede ocupar ms de una
lnea. Por ejemplo el comentario:
/* $ste es un comentario
que ocupa dos filas */
es perfectamente vlido.
printf( !ola mundo"n )#
Aqu es donde por fin el programa hace algo que podemos ver al ejecutarlo. La funcin
printf muestra un mensaje por la pantalla. Al final del mensaje "Hola mundo" aparece
el smbolo '\n'; este hace que despus de imprimir el mensaje se pase a la lnea
siguiente.
Fjate en el ";" del final. Es la forma que se usa en C para separar una instruccin de
otra. Se pueden poner varias en la misma lnea siempre que se separen por el punto y
coma.
return %#
Como he indicado antes el programa al finalizar develve un valor entero. Como en este
programa no se pueden producir errores (nunca digas nunca jams) la salida siempre
ser 0. La forma de hacer que el programa devuelva un 0 es usando return. Esta lnea
significa 'finaliza la funcin main haz que devuelva un 0.
&
...y cerramos llaves con lo que termina el programa. Todos los programas finalizan
cuando se llega al final de la funcin main.
3
Cmo se hace?
Primero debemos crear el cdigo fuente del programa. Para nuestro primer programa
el cdigo fuente es el del listado anterior. Arranca tu compilador de C, sea cual sea.
Crea un nuevo fichero y copia el cdigo anterior. Llmalo por ejemplo primero.c.
Ahora, tenemos que compilar el programa para crear el ejecutable. Si tu compilador es
de Windows, o con mens busca una opcin llamada "compile", o make, build o algo
as.
Si estamos usando DJGPP tenemos que llamarlo desde la lnea de comando:
gcc 'c primero.c 'o primero.e(e
Si usamos Rhide basta con hacer CTRL-F9.
Nota adicional sobre los comentarios
Los comentarios se pueden poner casi en cualquier parte. Excepto en medio de una
instruccin. Por ejemplo lo siguiente no es vlido:
pri/* $sto es un comentario */ntf( !ola mundo )# )A**A+A,
No podemos cortar a printf por en medio, tendramos un error al compilar. Lo siguiente
no dara error (al menos usando DJGPP), pero es una fea costumbre:
printf( /* $sto es un comentario */ !ola mundo )#
Y por ltimo tenemos:
printf( !ola/* $sto es un comentario */ mundo )#
Que no dara error, pero al ejecutar tendramos:
!ola /* $sto es un comentario */ mundo
porque /* Esto es un comentario */ queda dentro de las comillas y C lo interpreta
como texto, no como un comentario.
Qu sabemos hacer?
Pues la verdad es que todava no hemos aprendido mucho. Lo nico que podemos
hacer es compilar nuestros programas. Pero paciencia, en seguida avanzaremos.
Ejercicios
Busca los errores en este programa:
4
int main()
{
/* Aqu va el cuerpo del programa */
-rintf( !ola mundo"n )#
return %#
&
Solucin:
Si lo compilamos posiblemente obtendremos un error que nos indicar que no hemos
definido la funcin 'Printf'. Esto es porque no hemos includo la dichosa directiva
'#include <stdio.h>'. (En algunos compiladores no es necesario incluir esta directiva,
pero es una buena costumbre hacerlo).
Si lo corregimos y volvemos a compilar obtendremos un nuevo error. Otra vez nos dice
que desconoce 'Printf'. Esta vez el problema es el de las maysculas que hemos
indicado antes. Lo correcto es poner 'printf' con minsculas. Parece una tontera, pero
seguro que nos da ms de un problema.
MOSTRANDO INFORMACIN POR PANTALLA
Printf: Imprimir en pantalla
Siempre he credo que cuando empiezas con un nuevo lenguaje suele gustar el ver los
resultados, ver que nuestro programa hace 'algo'. Por eso creo que el curso debe
comenzar con la funcin printf, que sirve para sacar informacin por pantalla.
Para utilizar la funcin printf en nuestros programas debemos incluir la directiva:
#include <stdio.h>
al principio de programa. Como hemos visto en el programa hola mundo.
Si slo queremos imprimir una cadena basta con hacer (no olvides el ";" al final):
printf( .adena )#
Esto resultar por pantalla:
.adena
Lo que pongamos entre las comillas es lo que vamos a sacar por pantalla.
Si volvemos a usar otro printf, por ejemplo:
#include <stdio.h>
void main()
{
printf( .adena )#
5
printf( /egunda )#
&
Obtendremos:
.adena/egunda
Este ejemplo nos muestra cmo funciona printf. Para escribir en la pantalla se usa un
cursor que no vemos. Cuando escribimos algo el cursor va al final del texto. Cuando el
texto llega al final de la fila, lo siguiente que pongamos ir a la fila siguiente. Si lo que
queremos es sacar cada una en una lnea deberemos usar "\n". Es el indicador de
retorno de carro. Lo que hace es saltar el cursor de escritura a la lnea siguiente:
#include <stdio.h>
void main()
{
printf( .adena"n )#
printf( /egunda )#
&
y tendremos:
.adena
/egunda
Tambin podemos poner ms de una cadena dentro del printf:
printf( -rimera cadena /egunda cadena )#
Lo que no podemos hacer es meter cosas entre las cadenas:
printf( -rimera cadena te(to en medio /egunda cadena )#
esto no es vlido. Cuando el compilador intenta interpretar esta sentencia se encuentra
"Primera cadena" y luego texto en medio, no sabe qu hacer con ello y da un error.
Pero qu pasa si queremos imprimir el smbolo " en pantalla? Por ejemplo
imaginemos que queremos escribir:
$sto es e(tra0o
Si para ello hacemos:
printf( $sto es e(tra0o )#
obtendremos unos cuantos errores. El problema es que el smbolo " se usa para indicar
al compilador el comienzo o el final de una cadena. As que en realidad le estaramos
dando la cadena "Esto es", luego extrao y luego otra cadena vaca "". Pues resulta
que printf no admite esto y de nuevo tenemos errores.
6
La solucin es usar \". Veamos:
printf( $sto es "e(tra0o" )#
Esta vez todo ir como la seda. Como vemos la contrabarra '\' sirve para indicarle al
compilador que escriba caracteres que de otra forma no podramos.
Esta contrabarra se usa en C para indicar al compilador que queremos meter smbolos
especiales. Pero Y si lo que queremos es usar '\' como un carcter normal y poner por
ejemplo Hola\Adis? Pues muy fcil, volvemos a usar '\':
printf( !ola""Adi1s )#
y esta doble '\' indica a C que lo que queremos es mostrar una '\'.
Esto no ha sido mas que una introduccin a printf. Luego volveremos sobre ella.
oto!": Posicionando el cursor #$%&'
Esta funcin slo est disponible en compiladores de C que dispongan de la biblioteca
<conio.h>
Hemos visto que cuando usamos printf se escribe en la posicin actual del cursor y se
mueve el cursor al final de la cadena que hemos escrito.
Vale, pero qu pasa cuando queremos escribir en una posicin determinada de la
pantalla? La solucin est en la funcin gotoxy. Supongamos que queremos escribir
'Hola' en la fila 10, columna 20 de la pantalla:
#include <stdio.h>
#include <conio.h>
void main()
{
goto(2( 3%4 5% )#
printf( !ola )#
&
(Nota: para usar gotoxy hay que incluir la biblioteca conio.h).
Fjate que primero se pone la columna (x) y luego la fila (y). La esquina superior
izquierda es la posicin (1, 1).
Clrscr: (orrar la pantalla #$%&'
Ahora ya slo nos falta saber cmo se borra la pantalla. Pues es tan fcil como usar:
clrscr()
7
(clear screen, borrar pantalla).
Esta funcin n solo borra la pantalla, sino que adems sita el cursor en la posicin
(1, 1), en la esquina superior izquierda.
#include <stdio.h>
#include <conio.h>
void main()
{
clrscr()#
printf( !ola )#
&
Este mtodo slo vale para compiladores que incluyan el fichero stdio.h. Si tu sistema
no lo tiene puedes consultar la seccin siguiente.
(orrar la pantalla #otros mtodos'
Existen otras formas de borrar la pantalla aparte de usar stdio.h.
Si usas DOS:
s2stem (cls)# //-ara 67/
Si usas Linux:
s2stem (clear)# // -ara ,inu(
Otra forma vlida para ambos sistemas:
char a89:;{3<4=8=4=3=4=>=4%&# /* -ara am?os (en 67/ cargando antes
ansi.s2s) */
printf(@s4a)#

Qu sabemos hacer?
Bueno, ya hemos aprendido a sacar informacin por pantalla. Si quieres puedes
practicar con las instrucciones printf, gotoxy y clrscr. Lo que hemos visto hasta ahora
no tiene mucho secreto, pero ya veremos cmo la funcin printf tiene mayor
complejidad.
Ejercicios
Ejercicio 1: Busca los errores en el programa.
#include <stdio.h>
8
void main()
{
.lr/cr()#
goto(2( 5%4 5% )
printf( $sto2 en la fila 5% columna 5% )#
&
Solcin:
ClrScr est mal escrito, debe ponerse todo en minsculas, recordemos una vez
ms que el C diferencia las maysculas de las minsculas. Adems no hemos
includo la directiva !include "conio.h#, que necesitamos para usar clrscr() y
gotoxy().
Tampoco hemos puesto el punto y coma (;) despus del gotoxy( 10, 10 ).
Despus de cada instruccin debe ir un punto y coma.
El ltimo fallo es que el texto del printf no lo hemos puesto entre comillas. Lo
correcto sera: printf( "Estoy en la fila 10 columna 10" );
Ejercicio !. Escribe un programa que borre la pantalla y escriba en la primera lnea su
nombre y en la segunda su apellido:
Solcin:
#include <stdio.h>
#include <conio.h>
void main()
{
clrscr()#
printf( )orAa"n )#
printf( Brrutia )#
&
Tambin se poda haber hecho todo de golpe:
#include <stdio.h>
#include <conio.h>
void main()
{
clrscr()#
printf( )orAa"nBrrutia )#
&
Ejercicio ". Escriba un programa que borre la pantalla y muestre el texto "estoy aqui"
en la fila 10, columna 20 de la pantalla:
Solcin:
#include <stdio.h>
#include <conio.h>

void main()
{
clrscr()#
goto(2( 3%4 5% )#
printf( $sto2 aqui )#
&
TIPOS DE DATOS
Introducci)n
Cuando usamos un programa es muy importante manejar datos. En C podemos
almacenar los datos en variables. El contenido de las variables se puede ver o cambiar
en cualquier momento. Estas variables pueden ser de distintos tipos dependiendo del
tipo de dato que queramos meter. No es lo mismo guardar un nombre que un nmero.
Hay que recordar tambin que la memoria del ordenador es limitada, as que cuando
guardamos un dato, debemos usar slo la memoria necesaria. Por ejemplo si
queremos almacenar el nmero 400 usaremos una variable tipo int (la estudiamos ms
abajo) que ocupa slo 16 bits, y no una de tipo lon$ que ocupa 32 bits. Si tenemos un
ordenador con 32Mb de Ram parece una tontera ponernos a ahorrar bits
(1Mb=1024Kb, 1Kb=1024bytes, 1byte=8bits), pero si tenemos un programa que
maneja muchos datos puede no ser una cantidad despreciable. Adems ahorrar
memoria es una buena costumbre.
(Por si alguno tiene dudas: No hay que confundir la memoria con el espacio en el disco duro. Son dos cosas
distintas. La capacidad de ambos se mide en bytes, y la del disco duro suele ser mayor que la de la memoria Ram.
La informacin en la Ram se pierde al apagar el ordenador, la del disco duro permanece. Cuando queremos guardar
un fichero lo que necesitamos es espacio en el disco duro. Cuando queremos ejecutar un programa lo que
necesitamos es memoria Ram. La mayora me imagino que ya lo sabes, pero me he encontrado muchas veces con
gente que los confunde.)
*otas sobre los nombres de las +ariables
A las variables no se les puede dar cualquier nombre. No se pueden poner ms que
letras de la 'a' a la 'z' (la no vale), nmeros y el smbolo '_'. No se pueden poner
signos de admiracin, ni de interrogacin... El nombre de una variable puede contener
nmeros, pero su primer carcter no puede serlo.
Ejemplos de nombres vlidos:
camiones
numero
?uffer
a5
C5%hola3D
numEalumnos
!"emplos de nombres no #$l%dos&
5a?c
nom?reF
num/alumnos
1'
Tampoco valen como nombres de variable las #alabras reser$adas que usa el
compilador. Por ejemplo: for% main% do% &hile.
Por ltimo es interesante sealar que el C distingue entre maysculas y minsculas.
Por lo tanto:
Gom?re
nom?re
G7HI*$
seran tres variables distintas.
El tipo Int
En una variable de este tipo se almacenan nmeros enteros (sin decimales). El rango
de valores que admite es -32767 a 32767. Cuando definimos una variable lo que
estamos haciendo es decirle al compilador que nos reserve una zona de la memoria
para almacenar datos de tipo int. Para guardarla necesitaremos 16 bits de la memoria
del ordenador (2
16
=32767). Para poder usar una variable primero hay que declararla
(definirla). Hay que decirle al compilador que queremos crear una variable y hay que
indicarle de qu tipo. Por ejemplo:
int numero#
Esto hace que declaremos una variable llamada numero que va a contener un nmero
entero.
Pero dnde se declaran las variables?
Tenemos dos posibilidades, una es declararla como global y otra como local. Por ahora
vamos a decir que global es aquella variable que se declara fuera de la funcin main y
local la que se declara dentro:
Variable Global Variable Local
#include <stdio.h>
int (#
void main()
{
&
#include <stdio.h>
void main()
{
int (#
&
La diferencia prctica es que las variables globales se pueden usar en cualquier
procedimiento. Las variables locales slo pueden usarse en el procedimiento en el que
se declaran. Como por ahora slo tenemos el procedimiento (o funcin, o rutina, o
subrutina, como prefieras) main esto no debe preocuparnos mucho por ahora. Cuando
estudiemos cmo hacer un programa con ms funciones aparte de main volveremos
11
sobre el tema. Sin embargo debes saber que es buena costumbre usar variables
locales que globales. Ya veremos por qu.
Podemos declarar ms de una variable en una sola lnea:
int (4 2#
Mostrar variables por pantalla
Vamos a ir u poco ms all con la funcin printf. Supongamos que queremos mostrar
el contenido de la variable
(
por pantalla&
printf( @i4 ( )#
Suponiendo que x valga 10 (x=10) en la pantalla tendramos:
5%
Empieza a complicarse un poco no? Vamos poco a poco. Recuerdas el smbolo "\"
que usbamos para sacar ciertos caracteres? Bueno, pues el uso del "%" es parecido.
"%i" no se muestra por pantalla, se sustituye por el valor de la variable que va detrs
de las comillas. ( %i, de integer=entero en ingls).
Para ver el contenido de dos variables, por ejemplo x e y, podemos hacer:
printf( @i 4 ( )#
printf( @i4 2 )#
resultado (suponiendo x=10, y=20):
5% 3%
Pero hay otra forma mejor:
printf( @i @i4 (4 2 )#
... y as podemos poner el nmero de variables que queramos. Obtenemos el mismo
resultado con menos trabajo. No olvidemos que por cada variable hay que poner un %i
dentro de las comillas.
Tambin podemos mezclar texto con enteros:
printf( $l valor de ( es @i4 Jque ?ienK"n4 ( )#
que quedar como:
$l valor de ( es 5%4 Jque ?ienK
Como vemos %i al imprimir se sustituye por el valor de la variable.
12
Asignar valores a variables de tipo int
La asignacin de valores es tan sencilla como:
( ; 5%#
Tambin se puede dar un valor inicial a la variable cuando se define:
int ( ; 59#
Tambin se pueden inicializar varias variables en una sola lnea:
int ( ; 594 2 ; 3%#
Hay que tener cuidado con lo siguiente:
int (4 2 ; 3%#
Podramos pensar que x e y son igual a 20, pero no es as. La variable x est sin valor
inicial y la variable 'y' tiene el valor 20.
Veamos un ejemplo para resumir todo:
#include <stdio.h>
void main()
{
int ( ; 5%#
prinft( $l valor inicial de ( es @i"n4 ( )#
( ; 9%#
printf( Ahora el valor es @i"n4 ( )#
&
Cuya salida ser:
$l valor inicial de ( es 5%
Ahora el valor es 9%
Importante! Si imprimimos una variable a la que no hemos dado ningn valor no
obtendremos ningn error al compilar pero la variable tendr un valor cualquiera.
Prueba el ejemplo anterior quitando
int ( ; 5%#
. P(ede )(e te %mpr%ma el #alor 1' o p(ede )(e no.
El tipo Char
Las variables de tipo char sirven para almacenar caracteres. Los caracteres se
almacenan en realidad como nmeros del 0 al 255. Los 128 primeros (0 a 127) son el
13
ASCII estndar. El resto es el ASCII extendido y depende del idioma y del ordenador.
Consulta la tabla %SC&& en el anexo.
Para declarar una variable de tipo char hacemos:
char letra#
En una variable char slo #odemos almacenar solo na letra, no podemos
almacenar ni frases ni palabras. Eso lo veremos ms adelante (strings, cadenas). Para
almacenar un dato en una variable char tenemos dos posibilidades:
letra ; =A=#
o
letra ; L9#
En ambos casos se almacena la letra 'A' en la variable. Esto es as porque el cdigo
ASCII de la letra 'A' es el 65.
Para imprimir un char usamos el smbolo %c (c de character=caracter en ingls):
letra ; =A=#
printf( ,a letra esM @c.4 letra )#
resultado:
,a letra es A.
Tambin podemos imprimir el valor ASCII de la variable usando %i en vez de %c:
letra ; =A=#
printf( $l nNmero A/.OO de la letra @c esM @i.4 letra4 letra )#
resultado:
$l c1digo A/.OO de la letra A es L9.
Como vemos la nica diferencia para obtener uno u otro es el modificador (%c %i)
que usemos.
Las variables tipo char se pueden usar (y de hecho se usan mucho) para almacenar
enteros. Si necesitamos un nmero pequeo (entre -127 y 127) podemos usar una
variable char (8bits) en vez de una int (16bits), con el consiguiente ahorro de
memoria.
Todo lo dems dicho para los datos de tipo
int
se apl%ca tamb%*n a los de t%po
char
.
14
Una curiosidad:
letra ; =A=#
printf( ,a letra esM @c 2 su valor A/.OO esM @i"n4 letra4
letra )#
letra ; letra P 5#
printf( Ahora esM @c 2 su valor A/.OO esM @i"n4 letra4 letra )#
En este ejemplo letra comienza con el valor 'A', que es el cdigo ASCII 65. Al sumarle
1 pasa a tener el valor 66, que equivale a la letra 'B' (cdigo ASCII 66). La salida de
este ejemplo sera:
,a letra es A 2 su valor A/.OO es L9
Ahora es I 2 su valor A/.OO es LL
El modificador ,nsigned
Este modificador (que significa sin si$no) modifica el rango de valores que puede
contener una variable. Slo admite valores positivos. Si hacemos:
unsigned char varia?le#
Esta variable en vez de tener un rango de -128 a 128 pasa a tener un rango de 0 a
255.
El tipo -loat
En este tipo de variable podemos almacenar nmeros decimales, no slo enteros como
en los anteriores. El rango de posibles valores es del 3,4E-38 al 3,4E38.
Declaracin de una variable de tipo float:
float numero#
Para imprimir valores tipo float Usamos %f.
float num;Q%L%.R%#
printf( $l valor de num es M @f4 num )#
*esultadoM
$l valor de num esM Q%L%.R%
Si queremos escribirlo en notacin exponencial usamos %e:
float num ; Q%L%.R%#
printf( $l valor de num esM @e4 num )#
Sue da como resultadoM
$l valor de num esM Q.%L%R%e%%T
15
El tipo $ouble
En las variables tipo double se almacenan nmeros reales del 1,7E-307 al 1,7E308. Se
declaran como dou'le:
dou?le numero#
Para imprimir se usan los mismos modificadores que en float.
C)mo calcular el m.!imo +alor /ue admite un tipo de datos
Lo primero que tenemos que conocer es el tamao en bytes de ese tipo de dato.
Vamos a ver un ejemplo con el tipo INT. Hagamos el siguiente programa:
#include <stdio.h>
void main()
{
int num5#
printf( $l tipo int ocupa @i ?2tes"n4 siUeof(int) )#
&
Comprobado con DJGPP
!n m% ordenador el res(ltado es&
$l tipo int ocupa Q ?2tes.
Como sabemos 1byte = 8bits. Por lo tanto el tipo int ocupa 4*8=32 bits.
Ahora para calcular el mximo nmero debemos elevar 2 al nmero de bits obtenido.
En nuestro ejemplo: 2^32=4.294.967.296. Es decir en un int se podra almacenar un
nmero entre 0 y 4.294.967.296.
Sin embargo esto slo es cierto si usamos un tipo unsigned (sin signo, se hace
aadiendo la palabra unsigned antes de int). Para los tipos normales tenemos que
almacenar nmeros positivos y negativos. As que de los 4.294.967.296 posibles
nmeros la mitad sern positivos y la mitad negativos. Por lo tanto tenemos que dividir
el nmero anterior entre 2 = 2.147.483.648. Como el 0 se considera positivo el rango
de nmeros posibles que se pueden almacenar en un int sera: -2.147.483.648 a
2.147.483.647.
Resumen:
1) Obtenemos el nmero de bytes.
2) Multiplicamos por ocho (ya lo tenemos en bits).
3) Elevamos 2 al nmero de bits.
4) Dividimos entre 2.
16
%+erflo0: Qu pasa cuando nos saltamos el rango
El overflo& es lo que se produce cuando intentamos almacenar en una variable un
nmero mayor del mximo permitido. El comportamiento es distinto para variablesde
nmeros enteros y para variables de nmeros en coma flotante.
Con n'meros enteros
En mi ordenador y usando DJGPP bajo Dos el tamao del tipo int es de
4bytes(4*8=32bits). El nmero mximo que se puede almacenar en una variable tipo
int es por tanto 2.147.483.647 (ver apartado anterior). Si nos pasamos de este
nmero el que se guardar ser el siguiente pero empezando desde el otro extremo,
es decir, el -2.147.483.648. El compilador seguramente nos dar un aviso (warning)
de que nos hemos pasado.
#include <stdio.h>
void main()
{
int num5#
num5 ; 35Q<QRTLQR#
printf( $l valor de num5 esM @i"n4 num5 )#
&
Comprobado con DJGPP
El resultado que obtenemos es:
$l valor de num5 esM '35Q<QRTLQR
Comprueba si quieres que con el nmero anterior (2.147.483.647) no pasa nada.
Con n'meros en coma (lotante
El comportamiento con nmeros en coma flotante es distinto. Dependiendo del
ordenador si nos pasamos del rango al ejecutar un programa se puede producir un
error y detenerse la ejecucin.
Con estos nmeros tambin existe otro error que es el nder(lo). Este error se
produce cuando almacenamos un nmero demasiado pequeo (3,4E-38 en float).
1esumen de los tipos de datos en C
Esto es algo orientativo, depende del sistema.
Tipo Datos almacenados
N de
Bits
Valores posibles
(Rango)
Rango usando
unsigned
c+ar Caracteres 8 ,128 a 128 ' a 255
17
%nt enteros 16 ,32.767 a 32.767 ' a 65.535
lon- enteros lar-os 32
,2.147.483.647 a
2.147.483.647
' a 4.24.67.25
.loat
/(ms. reales (coma
.lotante)
32 3,4!,38 a 3,4!38
do(ble
/(ms. reales (coma
.lotante doble)
64 1,7!,3'7 a 1,7!3'8
Esto no siempre es cierto, depende del ordenador y del compilador. Para saber en
nuestro caso qu tamao tienen nuestros tipos de datos debemos hacer lo siguiente.
Ejemplo para int:
#include <stdio.h>
void main()
{
printf( Vama0o (en ?its) de int ; @i"n4 siUeof( int )
*R )#
&
Ya veremos ms tarde lo que significa sizeof. Por ahora basta con saber que nos dice
cual es el tamao de una variable o un tipo de dato.
Ejercicios
Ejercicio 1. Busque los errores:
#include <stdio.h>
void main()
{
int nNmero#
nNmero ; 3#
&
Solcin: Los nombres de variables no pueden llevar acentos, luego al compilar
nmero dar error.
#include <stdio.h>
void main()
{
int numero#
numero ; 3#
printf( $l valor es @i Gumero )#
&
18
Solcin: Falta la coma despus de "El valor es %i". Adems la segunda vez nmero
est escrito con maysculas.
CONSTANTES (USO DE #DEFINE)
&ntrodccin
Las constantes son aquellos datos que no pueden cambiar a lo largo de la ejecucin de
un programa.
#include <stdio.h>
void main()
{
in radio4 perimetro#
radio ; 3%#
perimetro ; 3 * T.5Q5L * radio#
printf( $l permetro esM @i4 perimetro )#
&
radio 0 perimetro son #ar%ables, s( #alor p(ede camb%ar a lo lar-o del pro-rama. 1%n
embar-o 2', 2 0 3.1416 son constantes, no +a0 manera de camb%arlas. !l #alor 3.1416 no
camb%a a lo lar-o del pro-rama , n% entre e"ec(c%2n 0 e"ec(c%2n. 12lo camb%ar$ c(ando
ed%tes el pro-rama 0 lo camb%es t( m%smo. !sto es ob#%o, 0 no t%ene n%n-3n m%ster%o as4 )(e
no le des #(eltas. 12lo )(er4a %nd%carlo por)(e en al-(nos l%bros le dan m(c+as #(eltas.
Constantes con nombre
Supongamos que tenemos que hacer un programa en el que haya que escribir unas
cuantas veces 3.1416 (como todos sabreis es PI). Es muy fcil que nos confundamos
alguna vez al teclearlo y al compilar el programa no tendremos ningn error, sin
embargo el programa no dar resultados correctos. Para evitar esto usamos las
constantes con nombre. Al definir una constante con nombre estamos dando un
nombre al valor, a 3.1416 le llamamos PI. Estas constantes se definen de la manera
siguiente:
#define nom?reEdeElaEconstante valorEdeElaEconstante
!"emplo&
#include <stdio.h>
#define PI 3.1416
void main()
{
int radio4 perimetro#
radio ; 3%#
perimetro ; 3 * PI * radio#
1
printf( $l permetro esM @i4 perimetro )#
&
De esta .orma cada #e5 )(e el comp%lador enc(entre el nombre P6 lo s(st%t(%r$ por 3.1416.
A una constante no se le puede dar un valor mientras se ejecuta, no se puede hacer PI
= 20;. Slo se le puede dar un valor con #define, y slo una vez. Tampoco podemos
usar el scanf para dar valores a una constante:
#define .7G/VAGV$ 5Q
void main()
{
...
scanf( @i4 .7G/VAGV$ )#
...
&
eso ser4a como +acer&
scanf( @i4 5Q )#
!sto es m(0 -ra#e, estamos d%c%endo )(e el #alor )(e escr%bamos en scan. se almacene en
la pos%c%2n 14 de la memor%a, lo )(e p(ede blo)(ear el ordenador.
Las constantes se suelen escribir en maysculas slo se puede definir una constante
por fila. No se #one "*" al (inal+
Las constantes nos proporcionan una mejor comprensin del cdigo fuente. Mira el
siguiente programa:
#include <stdio.h>
void main()
{
int precio#
precio ; ( Q * 39 * 5%% ) * ( 5.5L )#
printf( $l precio total esM @i4 precio )#
&
78(%en ent%ende lo )(e )(%ere dec%r este pro-rama9 !s d%.4c%l s% no %mpos%ble. :a-$moslo
de otra .orma.
#include <stdio.h>
#define .A>A/ Q
#define BGO6A6$/E-7*E.A>A 39
#define -*$.O7E-7*EBGO6A6 5%%
#define OH-B$/V7/ 5.5L
void main()
{
int precio#
precio ; ( .A>A/ * BGO6A6$/E-7*E.A>A * -*$.O7E-7*EBGO6A6 ) *
( OH-B$/V7/ )#
2'
printf( $l precio total esM @i4 precio )#
&
7; )(e a+ora se ent%ende me"or9 Claro )(e s4. <os n3meros no t%enen m(c+o s%-n%.%cado 0
s% re#%samos el pro-rama (n t%empo m$s tarde 0a no nos acordaremos )(* cosa era cada
n3mero. De la se-(nda .orma nos enteraremos al momento.
Tambin podemos definir una constante usando el valor de otras. Por supuesto las
otras tienen que estar definidas antes:
#include <stdio.h>
#define .A>A/ Q
#define BGO6A6$/E-7*E.A>A 39
#define -*$.O7E-7*EBGO6A6 5%%
#define PRECIO_POR_CAJA UNIDADES_POR_CAJA * PRECIO_POR_CAJA
#define OH-B$/V7/ 5.5L
void main()
{
int precio#
precio ; ( .A>A/ * PRECIO_POR_CAJA ) * ( OH-B$/V7/ )#
printf( $l precio total esM @i4 precio )#
&
#define tiene ms usos aparte de ste, ya los veremos en el captulo de directivas.
MANIPULANDO DATOS (OPERADORES)
,- es n o#erador?
Un operador sirve para manipular datos. Los hay de varios tipos: de asignacin, de
relacin, lgicos, aritmticos y de manipulacin de bits. En realidad los nombres
tampoco importan mucho; aqu lo que queremos es aprender a programar, no
aprender un montn de nombres.
.#erador de asi/nacin
Este es un operador que ya hemos visto en el captulo de Tipos de Datos. Sirve para
dar un valor a una variable. Este valor puede ser un nmero que tecleamos
directamente u otra variable:
a ; T# /* Hetemos un valor directamente */
o
a ; ?# /* ,e damos el valor de una varia?le */
Podemos dar #alores a #ar%as #ar%ables a la #e5&
a ; ? ; c ; 5%# /* 6amos a las varia?les a4?4c el valor 5%
*/
=amb%*n podemos as%-nar a #ar%as #ar%ables el #alor de otra de (n s2lo -olpe&
a ; ? ; c ; d# /* a4?4c toman el valor de d */
21
.#eradores aritm-ticos
Los operadores aritmticos son aquellos que sirven para realizar operaciones tales
como suma, resta, divisin y multiplicacin.
Si quieres saber cmo usar funciones matemticas ms complejas (exponentes, races,
trigonomtricas) vete al ca#0tlo corres#ondiente.
.#erador 123 : Sma
Este operador permite sumar variables:
#include <stdio.h>
void main()
{
int a ; 3#
int ? ; T#
int c#
c ; a P ?#
printf ( *esultado ; @i"n4 c )#
&
!l res(ltado ser$ 5 ob#%amente.
Por supuesto se pueden sumar varias variables o variables ms constantes:
#include <stdio.h>
void main()
{
int a ; 3#
int ? ; T#
int c ; 5#
int d#
d ; a P ? P c P Q#
printf ( *esultado ; @i"n4 c )#
&
!l res(ltado es 1'.
Podemos utilizar este operador para incrementar el valor de una variable:
( ; ( P 9#
Pero e>%ste (na .orma abre#%ada&
( += 9#
!sto s(ma el #alor 5 al #alor )(e ten4a la #ar%able >. ?eamos (n e"emplo&
#include <stdio.h>
void main()
{
int (#
22
( ; T#
2 ; 9#
( P; 3#
printf( ( ; @i"n4 ( )#
( P; 2# /* esto equivale a ( ; ( P 2 */
printf( ( ; @i"n4 ( )#
&
@es(ltado&
( ; 9
( ; 5%
.#erador 1223 : &ncremento
Este operador equivale a sumar uno a la variable:
#include <stdio.h>
void main()
{
int ( ; 9#
printf ( Walor de ( ; @i"n4 ( )#
x++#
printf ( Walor de ( ; @i"n4 ( )#
&
@es(ltado&
Walor de ( ; 9
Walor de ( ; L
1e p(ede poner antes o desp(*s de la #ar%able.
.#erador 143 : 5esta6Ne/ati$o
Este operador tiene dos usos, uno es la resta que funciona como el operador suma y el
otro es cambiar de signo.
Resta:
( ; ( ' 9#
Para la operac%2n resta se apl%ca todo lo d%c+o para la s(ma. 1e p(ede (sar tamb%*n como& x
-= !.
Pero tambin tiene el uso de cambiar de signo. Poniendolo delante de una variable o
constante equivale a multiplicarla por -1.
#include <stdio.h>
void main()
{
int a4 ?#
a ; 5#
? ; 'a#
23
printf( a ; @i4 ? ; @i"n4 a4 ? )#
&
@es(ltado& a A 1, b A ,1. /o t%ene m(c+o m%ster%o.
.#erador 1443 : Decremento
Es equivalente a ++ pero en vez de incrementar disminuye el valor de la variable.
Equivale a restar uno a la variable.
.#erador 173 : 8lti#licacin 9 #nteros
Este operador sirve para multiplicar y funciona de manera parecida a los anteriores.
Tambin sirve para definir y utilizar punteros, pero eso lo veremos ms tarde.
.#erador 163 : Di$isin
Este funciona tambin como los anteriores pero hay que tener cuidado. Si dividimos
dos nmero en coma flotante (tipo float) tenemos las divisin con sus correspondientes
decimales. Pero si dividimos dos enteros obtenemos un nmero entero. Es decir que si
dividimos 4/3 tenemos como resultado 1. El redondeo se hace por truncamiento,
simplemente se eliminan los decimales y se deja el entero.
Si dividimos dos enteros el resultado es un nmero entero, aunque luego lo saquemos
por pantalla usando %f o %d no obtendremos la parte decimal.
Cuando dividimos dos enteros, si queremos saber cual es el resto (o mdulo) usamos
el operador %, que vemos ms abajo.
.#erador 1:3 : 5esto
Si con el anterior operador obtenamos el mdulo o cociente de una divisin entera con
ste podemos tener el resto. No funciona ms que con enteros, no vale para nmeros
float o double.
Cmo se usa:
#include <stdio.h>
void main()
{
int a4 ?#
a ; 5R#
? ; 9#
printf( *esto de la divisi1nM @d "n4 a @ ? )#
&
.#eradores de com#aracin
24
Los operadores de condicin se utilizan para comprobar las condiciones de las
sentencias de control de flujo (las estudiaremos en el captulo sentencias).
Cuando se evala una condicin el resultado que se obtiene es 0 si no se cumple y un
nmero distinto de 0 si se cumple. Normalmente cuando se cumplen devuelven un 1.
Los operadores de comparacin son:
AA %-(al )(e se c(mple s% son %-(ales
BA d%st%nto )(e se c(mple 1 s% son d%.erentes
C ma0or )(e se c(mple s% el pr%mero es ma0or )(e el se-(ndo
D menor )(e se c(mple s% el pr%mero es menor )(e el se-(ndo
CA ma0or o %-(al )(e se c(mple s% el pr%mero es ma0or o %-(al )(e el se-(ndo
DA menor o %-(al )(e se c(mple s% el pr%mero es menor o %-(al )(e el se-(ndo
Veremos la aplicacin de estos operadores en el captulo Sentencias. Pero ahora
vamos a ver unos ejemplos:
#include <stdio.h>
void main()
{
printf( 5% > 9 da como resultado @i"n4 5%>9 )#
printf( 5% > 9 da como resultado @i"n4 5%>9 )#
printf( 9;; 9 da como resultado @i"n4 9;;9 )#
printf( 5%;;9 da como resultado @i"n4 5%;;9 )#
&
/o s2lo se p(eden comparar constantes, tamb%*n se p(eden comparar #ar%ables.
.#eradores l/icos
Estos son los que nos permiten unir varias comparaciones: 10>5 9 6==6. Los
operadores lgicos son: AND (&&), OR (||), NOT(!).
Operador && (AND, en castellano Y): Devuelve un 1 si se cumplen dos condiciones.
printf( *esultadoM @i4 (5%;;5% XX 9>3 )#
Eperador FF (E@, en castellano E)& De#(el#e (n 1 s% se c(mple (na de las dos cond%c%ones.
Operador ! (NOT, negacin): Si la condicin se cumple NOT hace que no se cumpla y
viceversa.
Ver el captulo Sentencias, seccin Notas sobre las condiciones para ms
informacin.
&ntrodccin a los bits 9 b9tes
25
En esta seccin voy a describir lo que son los bytes y los bits. No voy a descubrir nada
nuevo, as que la mayora se podrn saltar esta seccin.
Supongo que todo el mundo sabe lo que son los bytes y los bits, pero por si acaso all
va. Los bits son la unidad de informacin ms pequea, digamos que son la base para
almacenar la informacin. Son como los tomos a las molculas. Los valores que
puede tomar un bit son 0 1. Si juntamos ocho bits tenemos un byte.
Un byte puede tomar 256 valores diferentes (de 0 a 255). Cmo se consigue esto?
Imaginemos nuestro flamante byte con sus ocho bits. Supongamos que los ocho bits
valen cero. Ya tenemos el valor 0 en el byte. Ahora vamos a darle al ltimo byte el
valor 1.
00000001 -> 1
Este es el uno para el byte. Ahora vamos a por el dos y el tres: 00000010 -> 2
00000011 -> 3
y as hasta 255. Como vemos con ocho bits podemos tener 256 valores diferentes, que
en byte corresponden a los valores entre 0 y 255.
.#eradores de bits
Ya hemos visto que un byte son ocho bits. Pues bien, con los operadores de bits
podemos manipular las variables por dentro. Los diferentes operadores de bits son:
"perador Descripci#n
F E@ (E)
G ;/D (H)
I JE@ (E e>cl(s%#o)
K Complemento a (no o ne-ac%2n
CC Despla5am%ento a la derec+a
DD Despla5am%ento a la %5)(%erda
.#erador ; 1.53:
Toma los valores y hace con ellos la operacin OR. Vamos a ver un ejemplo:
#include <stdio.h>
void main()
{
printf( $l resultado de la operaci1n 3T9 Y 5QT esM @i"n4
235 143 )#
&
1e obt%ene&
26
$l resultado de la operaci1n 3T9 Y 5QT esM 3TD
?eamos la operac%2n a n%#el de b%ts&
3T9 '> 555%5%55
5QT '> 5%%%5555
3TD '> 555%5555
<a operac%2n E@ .(nc%ona de la s%-(%ente manera& =omamos los b%ts dos a dos 0 los
comparamos s% al-(no de ellos es (no, se obt%ene (n (no. 1% ambos son cero el res(ltado es
cero. Pr%mero se compara los dos pr%meros (el pr%mero de cada (no de los n3meros, 1 0 1 ,C
1), l(e-o la se-(nda pare"a (1 0 ' ,C 1) 0 as4 s(ces%#amente.
.#erador < 1%ND3:
Este operador compara los bits tambin dos a dos. Si ambos son 1 el resultado es 1. Si
no, el resultado es cero.
#include <stdio.h>
void main()
{
printf( $l resultado de la operaci1n 5<% X 599 esM @i"n4
1!" # 155 )#
&
=enemos&
$l resultado de la operaci1n 5<% X 599 esM 5TR
; n%#el de b%ts&
5<% '> 5%5%5%5%
599 '> 5%%55%55 #
5TR '> 5%%%5%5%
"perador $ (%"R)&
Compara los bits y los pone a unos si son distintos.
3T9 '> 555%5%55
5QT '> 5%%%5555 $
5%% '> 55%%5%%
"perador ' ((omplemento a uno)&
Este operador acepta un slo dato (operando) y pone a 0 los 1 y a 1 los 0, es decir los
invierte. Se pone delante del operando.
#include <stdio.h>
void main()
{
printf( $l resultado de la operaci1n Z593 esM @i"n4 %152
)#
&
$l resultado de la operaci1n Z593 esM 5%T
593 '> 5%%55%%% %
27
5%% '> %55%%555
"perador )) (Despla*amiento a la derec+a)&
Este operador mueve cada bit a la derecha. El bit de la izquierda se pone a cero, el de
la derecha se pierde. Si realizamos la operacin inversa no recuperamos el nmero
original. El formato es:
variable o dato && nmero de posiciones a desplazar
!l nmero de posiciones a desplazar %nd%ca c(antas #eces +a0 )(e mo#er los b%ts +ac%a la
derec+a. !"emplo&
#include <stdio.h>
void main()
{
printf( $l resultado de la operaci1n 59% >> 3 esM @i"n4
%152 )#
&
$l resultado de la operaci1n 59% >> 3 esM T<
?eamos la operac%2n paso a paso. !sta operac%2n e)(%#ale a +acer dos despla5am%entos a la
derec+a&
59% '> 5%%5%55% GNmero original
<9 '> %5%%5%55 -rimer desplaUamiento. $ntra un cero por la
iUquierda4
el cero de la derecha se pierde 2 los dem[s se
mueven a la derecha.
T< '> %%5%%5%5 /egundo desplaUamiento.
/E=;& Ln despla5am%ento a la %5)(%erda e)(%#ale a d%#%d%r por dos. !sto es m(0
%nteresante por)(e el despla5am%ento es m$s r$p%do )(e la d%#%s%2n. 1% )(eremos opt%m%5ar
(n pro-rama esta es (na b(ena %dea. 12lo s%r#e para d%#%d%r entre dos. 1% +acemos dos
despla5am%entos ser4a d%#%d%r por dos dos #eces, no por tres.
.#erador == 1Des#la>amiento a la i>?ierda3:
Funciona igual que la anterior pero los bits se desplazan a la izquierda. Esta operacin
equivale a multiplicar por 2.
.#erador Si>eo(
Este es un operador muy til. Nos permite conocer el tamao en b9tes de una
variable. De esta manera no tenemos que preocuparnos en recordar o calcular cuanto
ocupa. Adems el tamao de una variable cambia de un compilador a otro, es la mejor
forma de asegurarse. Se usa poniendo el nombre de la variable despus de sizeof y
separado de un espacio:
#include <stdio.h>
void main()
{
int varia?le#
printf( Vama0o de la varia?leM @i"n4 'i(e)f *+,i+-.e )#
28
&
=amb4en se p(ede (sar con los espec%.%cadores de t%pos de datos (c+ar, %nt, .loat, do(ble...).
Pero en *stos se (sa de manera d%.erente, +a0 )(e poner el espec%.%cador entre par*ntes%s&
#include <stdio.h>
void main()
{
printf( ,as varia?les tipo int ocupanM @i"n4 'i(e)f/in01
)#
&
.tros o#eradores
Existen adems de los que hemos visto otros operadores. Sin embargo ya veremos en
sucesivos captulos lo que significa cada uno.
.rden de e$alacin de .#eradores
Debemos tener cuidado al usar operadores pues a veces podemos tener resultados no
esperados si no tenemos en cuenta su orden de evaluacin. Vamos a ver la lista de
precedencias, cuanto ms arriba se evala antes:
,recedencia
() MN ,C .
B K OO ,, (molde) P G s%5eo. (!l P es el de p(ntero)
P Q R (!l P de a)(4 es el de m(lt%pl%cac%2n)
O ,
DD CC
D DA C CA
AA BA
G
I
F
GG
FF
9&
A OA ,A PA QA
,
2
Por ejemplo imaginemos que tenemos la siguiente operacin:
5% * 3 P 9
1% #amos a la tabla de precedenc%as #emos )(e el P t%ene (n orden s(per%or al O, por lo
tanto pr%mero se +ace el prod(cto 1'P2A2' 0 l(e-o la s(ma 2'O5A25. ?eamos otra&
5% * ( 3 P 9 )
;+ora con el par*ntes%s camb%a el orden de e#al(ac%2n. !l )(e t%ene ma0or precedenc%a
a+ora es el par*ntes%s, se e"ec(ta pr%mero. Como dentro del par*ntes%s s2lo +a0 (na s(ma se
e#al3a s%n m$s, 2O5A7. Ha solo )(eda la m(lt%pl%cac%2n 1'P7A7'. Etro caso&
5% * ( 9 * 3 PT )
Como antes, el )(e ma0or precedenc%a t%ene es el par*ntes%s, se e#al3a pr%mero. Dentro del
par*ntes%s tenemos prod(cto 0 s(ma. Como sabemos 0a se e#al3a pr%mero el prod(cto,
5P2A1'. 1e-(%mos en el par*ntes%s, nos )(eda la s(ma 1'O3A13. :emos acabado con el
par*ntes%s, a+ora al resto de la e>pres%2n. Co-emos la m(lt%pl%cac%2n )(e )(eda&
1'P13A13'.
Otro detalle que debemos cuidar son los operadores ++ y --. Estos tienen mayor
precedencia que las dems operaciones aritmticas (+, -, *, /, %). Por ejemplo:
5% * 9 PP
P(ede parecer )(e pr%mero se e"ec(tar$ la m(lt%pl%cac%2n 0 l(e-o el OO. Pero s% #amos a la
tabla de precedenc%as #emos )(e el OO est$ por enc%ma de P (de m(lt%pl%cac%2n), por lo
tanto se e#al(ar$ pr%mero 5OOA6. 1'P6A6'.
Es mejor no usar los operadores ++ y -- mezclados con otros, pues a veces
obtenemos resultados inesperados. Por ejemplo:
#include <stdio.h>
void main()
{
int a4 ?#
a ; 9#
? ; aPP#
printf( a ; @i4 ? ; @i"n4 a4 ? )#
&
!ste e"emplo en (nos comp%ladores dar$ a A 6, b A 5 0 en otros a A 6 0 b A 6. Por .a#or s%
pode%s, escr%b%dme d%c%endo )(* #alores os salen a #osotros 0 )(* comp%lador (sa%s.
Para asegurarse lo mejor sera separar la lnea donde se usa el ++ y el =:
#include <stdio.h>
void main()
{
int a4 ?#
a ; 9#
aPP#
? ; a#
printf( a ; @i4 ? ; @i"n4 a4 ? )#
3'
&
Ejercicios
Ejercicio 1: En este programa hay un fallo muy gordo y muy habitual en
programacin. A ver si lo encuentras:
#include <stdio.h>
void main()
{
int a4 c#
a ; 9#
c P; a P9#
&
-oluci#n&
Cuando calculamos el valor de 'c' sumamos a+5 ( =10 ) al valor de 'c'. Pero resulta
que 'c' no tena ningn valor indicado por nosotros. Estamos usando la variable 'c' sin
haberle dado valor. En algunos compiladores el resultado ser inesperado. Este es un
fallo bastante habitual, usar variables a las que no hemos dado ningn valor.
Ejercicio !: Cual ser el resultado del siguiente programa?
#include
#include
void main()
{
int a4 ?4 c#
a ; 9#
? ; PPa#
c ; ( a P 9 * 3 ) * ( ? P L / 3 ) P ( a * 3 )#
printf( @i4 @i4 @i4 a4 ?4 c )#
&
-oluci#n&
El resultado es 156. En la primera a vale 5. Pero en la segunda se ejecuta b = ++a =
++5 = 6. Tenemos a = b = 6.
INTRODUCIR DATOS POR TECLADO
&ntrodccin
Algo muy usual en un programa es esperar que el usuario introduzca datos por el
teclado. Para ello contamos con varias posibilidades: Usar las funciones de la biblioteca
estndar, crear nuestras propias interrupciones de teclado (MS-Dos) o usar funciones
de alguna biblioteca diferente (como por ejemplo Allegro).
31
Nosotros en este captulo vamos a estudiar la primera, usando las funciones de la
biblioteca estndar. Pero veamos por encima las otras posibilidades.
Las funciones estndar estn bien para un programa sencillito. Pero cuando queremos
hacer juegos por ejemplo, no suelen ser suficiente. Demasiado lentas o no nos dan
todas las posibilidades que buscamos, como comprobar si hay varias teclas pulsadas.
Para solucionar esto tenemos dos posibilidades:
La ms coplicada es crear nuestras propias interrupciones de teclado. Qu es una
interrupcin de teclado? Es un pequeo programa en memoria que se ejecuta
contnuamente y comprueba el estado del teclado. Podemos crear uno nuestro y hacer
que el ordenador use el que hemos creado en vez del suyo. Este tema no lo vamos a
tratar ahora, quizs en algn captulo posteror.
Otra posibilidad ms sencilla es usar una biblioteca que tenga funciones para controlar
el teclado. Por ejemplo si usamos la biblioteca Allegro, ella misma hace todo el trabajo
y nosotros no tenemos ms que recoger sus frutos con un par de sencillas
instrucciones. Esto soluciona mucho el trabajo y nos libra de tener que aprender cmo
funcionan los aspectos ms oscuros del control del teclado.
Vamos ahora con las funciones de la biblioteca estndar.
Scan(
El uso de scanf es muy similar al de printf con una diferencia, nos da la posibilidad de
que el usuario introduzca datos en vez de mostrarlos. No nos permite mostrar texto en
la pantalla, por eso si queremos mostrar un mensaje usamos un printf delante. Un
ejemplo:
#include <stdio.h>
void main()
{
int num#
printf( Ontroduce un nNmero )#
scanf( @i4 Xnum )#
printf( !as tecleado el nNmero @i"n4 num )#
&
Pr%mero #amos a #er (nas nota de est*t%ca, para +acer los pro-ramas (n poco m$s
ele-antes. Parece (na tonter4a, pero los pe)(eSos detalles +acen )(e (n pro-rama -ane
m(c+o. !l scan. no m(e#e el c(rsor de s( pos%c%2n act(al, as4 )(e en n(estro e"emplo
)(eda&
Ontroduce un nNmero E /* ,a ?arra horiUontal indica d1nde esta el
cursor */
!sto es por)(e en el pr%nt. no +emos p(esto al .%nal el s4mbolo de salto de l4nea TUnT.
;dem$s +emos de"ado (n espac%o al .%nal de Introduce un nmero: para )(e as4 c(ando
tecleemos el n3mero no sal-a pe-ado al mensa"e. 1% no +(b%*semos de"ado el espac%o
)(edar4a as4 al %ntrod(c%r el n3mero 12' (es (n e"emplo)&
Ontroduce un nNmero53%
32
V(eno, esto es m(0 %nteresante pero #amos a de"arlo 0 #amos al -rano. ?eamos c2mo
.(nc%ona el scan.. <o pr%mero nos .%"amos )(e +a0 (na cadena entre com%llas. !sta es
s%m%lar a la de pr%nt., nos s%r#e para %nd%carle al comp%lador )(* t%po de datos estamos
p%d%endo. Como en este caso es (n %nte-er (samos R%. Desp(*s de la coma tenemos la
#ar%able donde almacenamos el dato, en este caso Tn(mT.
Fjate que en el scanf la variable 'num' lleva delante el smbolo <, este es muy
importante, sirve para indicar al compilador cual es la direccin (o posicin en la
memoria) de la variable. Por ahora no te preocupes por eso, ya volveremos ms
adelante sobre el tema.
Podemos preguntar por ms de una variable a la vez en un slo scanf, hay que poner
un %i por cada variable:
#include <stdio.h>
void main()
{
int a4 ?4 c#
printf( Ontroduce tres nNmerosM )#
scanf( @i @i @i4 Xa4 X?4 Xc )#
printf( !as tecleado los nNmeros @i @i @i"n4 a4 ?4 c )#
&
De esta .orma c(ando el (s(ar%o e"ec(ta el pro-rama debe %ntrod(c%r los tres datos
separados por (n espac%o.
Tambin podemos pedir en un mismo scanf variables de distinto tipo:
#include <stdio.h>
void main()
{
int a#
float ?#
printf( Ontroduce dos nNmerosM )#
scanf( @i @f4 Xa4 X? )#
printf( !as tecleado los nNmeros @i @f"n4 a4 ? )#
&
; cada mod%.%cador (R%, R.) le debe corresponder (na #ar%able de s( m%smo t%po. !s dec%r,
al poner (n R% el comp%lador espera )(e s( #ar%able correspond%ente sea de t%po %nt. 1%
ponemos R. espera (na #ar%able t%po .loat.
Getch 9 /etche
Si lo que queremos es que el usuario introduzca un caracter por el teclado usamos las
funciones getch y getche. Estas esperan a que el usuario introduzca un carcter por el
teclado. La diferencia entre getche y getch es que la primera saca por pantalla la tecla
que hemos pulsado y la segunda no (la e del final se refiere a echo=eco). Ejemplos:
33
#include <stdio.h>
void main()
{
char letra#
printf( Ontroduce una letraM )#
fflush( stdout )#
letra ; getche()#
printf( "n!as introducido la letraM @c4 letra )#
&
@es(ltado&
Ontroduce una letraM +
!as introducido la letraM a
8(%5$s te est*s pre-(ntando )(* es eso de ..l(s+( stdo(t ). P(es b%en, c(ando (samos la
.(nc%2n pr%nt., no escr%b%mos d%rectamente en la pantalla, s%no en (na memor%a %ntermed%a
(lo )(e llaman (n b(.er). C(ando este b(.er se llena o c(ando metemos (n car$cter TUnT es
c(ando se en#%a el te>to a la pantalla. !n este e"emplo 0o )(er4a )(e aparec%era el mensa"e
Introduce una letra: 0 el c(rsor se )(edara "(sto desp(*s, es dec%r, s%n (sar TUnT. 1% se +ace
esto, en al-(nos comp%ladores el mensa"e no se m(estra en pantalla +asta )(e se p(lsa (na
tecla (pr(*balo en el t(0o). H la .(nc%2n ..l(s+( stdo(t ) lo )(e +ace es en#%ar a la pantalla
lo )(e +a0 en ese b(.er.
Y ahora un ejemplo con getch:
#include <stdio.h>
void main()
{
char letra#
printf( Ontroduce una letraM )#
fflush( stdout )#
letra ; getch()#
printf("n has introducido la letra M@c4 letra )#
&
@es(ltado&
Ontroduce una letraM
!as introducido la letraM a
Como vemos la nica diferencia es que en el primer ejemplo se muestra en pantalla lo
que escribimos y en el segundo no.
SENTENCIAS DE CONTROL DE FLUJO
&ntrodccin
Hasta ahora los programas que hemos visto eran lineales. Comenzaban por la primera
instruccin y acababan por la ltima, ejecutndose todas una sola vez. . Lgico no?.
Pero resulta que muchas veces no es esto lo que queremos que ocurra. Lo que nos
suele interesar es que dependiendo de los valores de los datos se ejecuten unas
34
instrucciones y no otras. O tambin puede que queramos repetir unas instrucciones un
nmero determinado de veces. Para esto estn las sentencia de control de flujo.
@cles
Los bucles nos ofrecen la solucin cuando queremos repetir una tarea un nmero
determinado de veces. Supongamos que queremos escribir 100 veces la palabra hola.
Con lo que sabemos hasta ahora haramos:
#include <stdio.h>
void main()
{
printf( !ola"n)#
printf( !ola"n)#
printf( !ola"n)#
printf( !ola"n)#
printf( !ola"n)#
... (2 as hasta 5%% veces)
&
WXen(da loc(raB H s% )(eremos repet%rlo m$s #eces nos )(edar4a (n pro-rama de lo m$s
lar-o.
Sin embargo usando un bucle for el programa quedara:
#include <stdio.h>
void main()
{
int i#
for ( i;% # i<5%% # iPP )
{
printf( !ola"n )#
&
&
Con lo )(e tenemos (n pro-rama m$s corto.
El bcle Aor
El formato del bucle for es el siguiente:
f),/ dar valores iniciales 2 condiciones 2 incrementos 1
3
conCunto de intrucciones a eCecutar en el ?ucle
4
?amos a #erlo con el e"emplo anter%or&
...
for ( i;% # i<5%% # iPP )
...
!n este caso as%-namos (n #alor %n%c%al a la #ar%able
i
. !se #alor es
cero
. !sa es la parte de dar valores iniciales. <(e-o tenemos
35
i<5%%
. !sa es la parte condiciones. !n ella ponemos la cond%c%2n es )(e % sea menor )(e 1'', de
modo )(e el b(cle se e"ec(tar$ m%entras % sea menor )(e 1''. !s dec%r, m%entras se c(mpla
la cond%c%2n. <(e-o tenemos la parte de incrementos, donde %nd%camos c($nto se
%ncrementa la #ar%able.
Como vemos, el for va delante del grupo de instrucciones a ejecutar, de manera que si
la condicin es falsa, esas instrucciones no se ejecutan ni una sola vez.
Cuidado: No se debe poner un ";" justo despus de la sentencia for, pues entonces
sera un bucle vaco y las instrucciones siguientes slo se ejecutaran una vez.
Veamoslo con un ejemplo:
#include <stdio.h>
void main()
{
int i#
for ( i;% # i<5%% # iPP )# /* .uidado con este
punto 2 coma */
{
printf( !ola"n )#
&
&
!ste pro-rama s2lo escr%b%r$ en pantalla
!ola
(na sola #e5.
Tamben puede suceder que quieras ejecutar un cierto nmero de veces una sla
instruccin (como sucede en nuestro ejemplo). Entonces no necesitas las llaves "{}":
#include <stdio.h>
void main()
{
int i#
for ( i;% # i<5%% # iPP ) printf( !ola"n )#
&
o tamb%*n&
for ( i;% # i<5%% # iPP )
printf( !ola"n )#
1%n embar-o, 0o me +e encontrado m(c+as #eces )(e es me"or poner las lla#es a(n)(e s2lo
+a0a (na %nstr(cc%2nY a #eces al aSad%r (na se-(nda %nstr(cc%2n m$s tarde se te ol#%dan las
com%llas 0 no te das c(enta. Parece (na tonter4a, pero m(c+as #eces, c(ando pro-ramas, son
estos los pe)(eSos .allos los )(e te #(el#en loco.
En otros lenguajes, como Basic, la sentencia for es muy rgida. En cambio en C es muy
flexible. Se puede omitir cualquiera de las secciones (inicializacin, condiciones o
incrementos). Tambin se pueden poner ms de una variable a inicializar, ms de una
condicin y ms de un incremento. Por ejemplo, el siguiente programa sera
perfectamente correcto:
#include <stdio.h>
36
void main()
{
int i4 C#
for( i;%4 C;9 # i<5% # iPP4 C;CP9 )
{
printf( !ola )#
printf( $sta es la lnea @i4 i )#
printf( C vale ; @i4 C )#
&
&
Como #emos en el e"emplo tenemos m$s de (na #ar%able en la secc%2n de %n%c%al%5ac%2n 0
en la de %ncrementos. =amb%*n pod4amos +aber p(esto m$s de (na cond%c%2n. <os
elementos de cada secc%2n se separan por comas. Cada secc%2n se separa por p(nto 0 coma.
Ms tarde veremos cmo usar el for con cadenas.
Bhile
El formato del bucle while es es siguiente:
56i.e / condici1n 1
3
?loque de instrucciones a eCecutar
4
Z+%le )(%ere dec%r mientras. ;)(4 se e"ec(ta el blo)(e de %ntr(cc%ones m%entras se c(mpla
la cond%c%2n %mp(esta en [+%le. ?amos a #er (n e"emplo&
#include <stdio.h>
void main()
{
int contador ; %#
\hile ( contador<5%% )
{
contadorPP#
printf( ]a vo2 por el @i4 parar^ enseguida."n4
contador )#
&
&
!ste pro-rama %mpr%me en pantalla los #alores del 1 al 1''. C(ando %A1'' 0a no se c(mple
la cond%c%2n. Lna cosa %mportante, s% +(b4esemos camb%ado el orden de las %nstr(cc%ones a
e"ec(tar&
...
printf( ]a vo2 por el @i4 parar^ enseguida."n4 contador )#
contadorPP#
...
!n esta ocas%2n se %mpr%men los #alores del ' al . C(%dado con esto, )(e a #eces prod(ce
errores d%.4c%les de encontrar.
Do Bhile
37
El formato del bucle do-while es:
d)
3
instrucciones a eCecutar
4 56i.e / condicin 12
<a d%.erenc%a entre
\hile
0
do'\hile
es )(e en este 3lt%mo, la cond%c%2n #a desp(es del con"(nto de %nstr(cc%ones a e"ec(tar. De
esta .orma, esas %nstr(cc%ones se e"ec(tan al menos (na #e5.
Su uso es similar al de while.
Sentencias de condicin
Hasta aqu hemos visto cmo podemos repetir un conjunto de instrucciones las veces
que deseemos. Pero ahora vamos a ver cmo podemos controlar totalmente el flujo de
un programa. Dependiendo de los valores de alguna variable se tomarn unas acciones
u otras. Empecemos con la sentencia if.
&(
La palabra if significa si (condicional), pero supongo que esto ya lo sabas. Su formato
es el siguiente:
if / condicin 1
3
instrucciones a ejecutar
4
C(ando se c(mple la cond%c%2n entre par*ntes%s se e"ec(ta el blo)(e %nmed%atamente
s%-(%ente al %. (blo)(e instrucciones a ejecutar).
En el siguiente ejemplo tenemos un programa que nos pide un nmero, si ese nmero
es 10 se muestra un mensaje. Si no es 10 no se muestra ningn mensaje:
#include <stdio.h>
void main()
{
int num#
printf( Ontroduce un nNmero )#
scanf( @i4 Xnum )#
if (num;;5%)
{
printf( $l nNmero es correcto"n )#
&
&
Como s%empre la cond%c%2n es .alsa s% es %-(al a cero. 1% es d%st%nta de cero ser$ #erdadera.
38
&( 4 Else
El formato es el siguiente:
if / condici1n 1
3
?loque que se eCecuta si se cumple la condici1n
4
e.'e
3
?loque que se eCecuta si no se cumple la condici1n
4
!n el %. s% no se c(mpl4a la cond%c%2n no se e"ec(taba el blo)(e s%-(%ente 0 el pro-rama
se-(4a s( c(rso normal. Con el %. else tenemos (n blo)(e ad%c%onal )(e s2lo se e"ec(ta s% no
se c(mple la cond%c%2n. ?eamos (n e"emplo&
#include <stdio.h>
void main()
{
int a#
printf( Ontroduce un nNmero )#
scanf( @i4 Xa )#
if ( a;;R )
{
printf ( $l nNmero introducido era un ocho."n )#
&
else
{
printf ( -ero si no has escrito un ochoKKK"n )#
&
&
;l e"ec(tar el pro-rama s% %ntrod(c%mos (n 8 se e"ec(ta el blo)(e s%-(%ente al %. 0 se
m(estra el mensa"e
$l nNmero introducido era un ocho.
1% escr%b%mos c(al)(%er otro n3mero se e"ec(ta el blo)(e s%-(%ente al else mostr$ndose el
mensa"e
-ero si no has escrito un ochoKKK
&( else i(
Se pueden poner if else anidados si se desea:
#include <stdio.h>
void main()
{
int a#
printf( Ontroduce un nNmero )#
scanf( @i4 Xa )#
if ( a<5% )
{
printf ( $l nNmero introducido era menor de
5%."n )#
3
&
else if ( a>5% XX a<5%% )
{
printf ( $l nNmero est[ entre 5% 2 5%%"n )#
&
else if ( a>5%% )
{
printf( $l nNmero es ma2or que 5%%"n )#
&
printf( +in del programa"n )#
&
!l s4mbolo .. de la cond%c%2n del se-(ndo %. es (n ;/D (H). De esta .orma la cond%c%2n
)(eda& Si a es mayor que 10 Y a es menor que 100. Cos(lta la secc%2n Notas sobre
condiciones para saber m$s.
Y as todos los if else que queramos. Si la condicin del primer if es verdadera se
muestra el mensaje El n(mero introducido era menor de )* y se saltan todos los if-
else siguientes (se muestra +in del pro$rama). Si la condicin es falsa se ejecuta el
siguiente else-if y se comprueba si a est entre 10 y 100. Si es cierto se muestra El
n(mero est, entre )* - )**. Si no es cierto se evala el ltimo else-if.
? 1el otro i(4else3
El uso de la interrogacin es una forma de condensar un if-else. Su formato es el
siguiente:
( condicion ) F ( instrucci1n 5 ) M ( instrucci1n 3 )
1% se c(mple la cond%c%2n se e"ec(ta la instruccin 1 0 s% no se e"ec(ta la instruccin 2.
?eamos (n e"emplo con el %.,else 0 l(e-o lo reescr%b%mos con \9\&
#include <stdio.h>
void main()
{
int a#
int ?#
printf( Ontroduce un nNmero )#
scanf( @i4 Xa )#
if ( a<5% )
{
? ; 5#
&
else
{
? ; Q#
&
printf ( ,a varia?le =?= toma el valorM @i"n4 ? )#
&
1% el #alor )(e tecleamos al e"ec(tar es menor )(e 1' entonces bA1, en camb%o s% tecleamos
(n n3mero ma0or )(e 1' TbT ser$ %-(al a 2. ;+ora #amos a reescr%b%r el pro-rama (sando T9T&
#include <stdio.h>
void main()
{
4'
int a#
int ?#
printf( Ontroduce un nNmero )#
scanf( @i4 Xa )#
? ; ( a<5% ) F 5 M Q #
printf ( ,a varia?le =?= toma el valorM @i"n4 ? )#
&
78(* es lo )(e s(cede a+ora9 1e e#al3a la cond%c%2n aD1'. 1% es #erdadera (a menor )(e
1') se e"ec(ta la %nstr(cc%2n 1, es dec%r, )(e b toma el #alor T1T. 1% es .alsa se e"ec(ta la
%nstr(cc%2n 2, es dec%r, b toma el #alor T4T.
Esta es una sentencia muy curiosa pero sinceramente creo que no la he usado casi
nunca en mis programas y tampoco la he visto mucho en programas ajenos.
S)itch
El formato de la sentencia switch es:
'5i076 / varia?le 1
3
7+'e opcin 18
c1digo a eCecutar si la varia?le tiene el
valor de la opci1n 5
-,e+92
7+'e opcin 28
c1digo a eCecutar si la varia?le tiene el
valor de la opci1n 3
-,e+92
def+:.08
c1digo que se eCecuta si la varia?le tiene
un valor distinto a los anteriores
-,e+92
4
?amos a #er c2mo .(nc%ona. <a sentenc%a s[%tc+ s%r#e par ele-%r (na opc%2n entre #ar%as
d%spon%bles. ;)(4 no tenemos (na cond%c%2n )(e se debe c(mpl%r s%no el #alor de (na
#ar%able. Depend%endo del #alor se c(mpl%r$ (n caso ( otro.
Vamos a ver un ejemplo de mltiples casos con if-else y luego con switch:
#include <stdio.h>
void main()
{
int num#
printf( Ontroduce un nNmero )#
scanf( @i4 Xnum )#
if ( num;;5 )
printf ( $s un 5"n )#
else if ( num;;3 )
printf ( $s un 3"n )#
else if ( num;;T )
printf ( $s un T"n )#
41
else
printf ( Go era ni 54 ni 34 ni T"n )#
&
;+ora con s[%tc+&
#include <stdio.h>
void main()
{
int num#
printf( Ontroduce un nNmero )#
scanf( @i4 Xnum )#
s\itch( num )
{
case 5M
printf( $s un 5"n )#
?reaA#
case 3M
printf( $s un 3"n )#
?reaA#
case TM
printf( $s un T"n )#
?reaA#
defaultM
printf( Go es ni 54 ni 34 ni T"n )#
&
&
Como #emos el c2d%-o con s[%tc+ es m$s c2modo de leer.
Vamos a ver qu pasa si nos olvidamos algn break:
#include <stdio.h>
void main()
{
int num#
printf( Ontroduce un nNmero )#
scanf( @i4 Xnum )#
s\itch( num )
{
case 5M
printf( $s un 5"n )#
/* Gos olvidamos el ?reaA que de?era ha?er
aqu */
case 3M
printf( $s un 3"n )#
?reaA#
defaultM
printf( Go es ni 54 ni 34 ni T"n )#
&
&
1% al e"ec(tar el pro-rama escr%b%mos (n dos tenemos el mensa"e Es un dos. =odo correcto.
Pero s% escr%b%mos (n (no lo )(e nos sale en pantalla es&
$s un 5
42
$s un 3
7Por )(*9 P(es por)(e cada caso emp%e5a con (n case 0 acaba donde +a0 (n brea]. 1% no
ponemos brea] a(n)(e +a0a (n case el pro-rama s%-(e +ac%a adelante. Por eso se e"ec(ta el
c2d%-o del case 1 0 del case 2.
Puede parecer una desventaja pero a veces es conveniente. Por ejemplo cuando dos
case deben tener el mismo cdigo. Si no tuviramos esta posibilidad tendramos que
escribir dos veces el mismo cdigo. (Vale, vale, tambin podramos usar funciones,
pero si el cdigo es corto puede ser ms conveniente no usar funciones. Ya hablaremos
de eso ms tarde.).
Sin embargo switch tiene algunas limitaciones, por ejemplo no podemos usar
condiciones en los case. El ejemplo que hemos visto en el apartado if-else-if no
podramos hacerlo con switch.
Sentecias de salto
Goto
La sentencia goto (ir a) nos permite hacer un salto a la parte del programa que
deseemos. En el programa podemos poner etiquetas, estas etiquetas no se ejecutan.
Es como poner un nombre a una parte del programa. Estas etiquetas son las que nos
sirven para indicar a la sentencia goto dnde tiene que saltar.
#include <stdio.h>
void main()
{
printf( ,nea 5"n )#
goto lineaT# /* ,e decimos al goto que ?usque la
etiqueta linea3 */
printf( ,nea 3"n )#
lineaTM /* $sta es la etiqueta */
printf( ,nea T"n )#
&
@es(ltado&
,nea 5
,nea T
Como #emos no se e"ec(ta el pr%nt. de <4nea 2 por)(e nos lo +emos saltado con el -oto.
El goto slo se puede usar dentro de funciones, y no se puede saltar desde una funcin
a otra. (Las funciones las estudiamos en el siguiente captulo).
Un apunte adicional del goto: Cuando yo comenc a programar siempre oa que no era
correcto usar el goto, que era una mala costumbre de programacin. Decan que haca
los programas ilegibles, difciles de entender. Ahora en cambio se dice que no est tan
mal. Yo personalmente me he encontrado alguna ocasin en la que usar el goto no
slo no lo haca ilegible sino que lo haca ms claro. En Internet se pueden encontrar
pginas que discuten sobre el tema. Pero como conclusin yo dira que cada uno la use
si quiere, el caso es no abusar de ella y tener cuidado.
Notas sobre las condiciones
43
Las condiciones de las sentencias se evalan al ejecutarse. De esta evaluacin
obtenemos un nmero. Las condiciones son falsas si este nmero es igual a cero. Son
verdaderas si es distinto de cero (los nmeros negativos son verdaderos).
Ah van unos ejemplos:
a ; 3#
? ; T#
if ( a ;; ? ) ...
;)(4 a==b ser4a %-(al a ', l(e-o .also.
if ( % ) ...
Como la cond%c%2n es %-(al a cero, es .alsa.
if ( 5 ) ...
Como la cond%c%2n es d%st%nta de cero, es #erdadera.
if ( '5%% ) ...
Como la cond%c%2n es d%st%nta de cero, es #erdadera.
Supongamos que queremos mostrar un mensaje si una variable es distinta de cero:
if ( aK;% ) printf( !ola"n )#
!sto ser4a red(ndante, bastar4a con poner&
if ( a ) printf( !ola"n )#
!sto s2lo #ale s% )(eremos comprobar )(e es d%st%nto de cero. 1% )(eremos comprobar )(e
es %-(al a 3&
if ( a ;; T ) printf( $s tres"n )#
Como #emos las cond%c%ones no s2lo est$n l%m%tadas a comparac%ones, se p(ede poner
c(al)(%er .(nc%2n )(e de#(el#a (n #alor. Depend%endo de s% este #alor es cero o no, la
cond%c%2n ser$ .alsa o #erdadera.
Tambin podemos probar varias condicines en una sola usando && (AND), || (OR).
Ejemplos de && (AND):
if ( a;;T XX ?;;3 ) printf( !ola"n )# /* /e cumple si a es
T ; ? es dos */
if ( a>5% XX a<5%% ) printf( !ola"n )# /* /e cumple si a es
ma2or que 5% ; menor que 5%% */
if ( a;;5% XX ?<T%% ) printf( !ola"n )# /* /e cumple si a es
igual a 5% ; ? es menor que T%% */
!"emplos de FF (E@)&
if ( a<5%% YY ?>3%% ) printf( !ola"n )# /* /e cumple si a menor
que 5%% O ? ma2or que 3%% */
if ( a<5% YY a>5%% ) printf( !ola"n )# /* /e cumple si a menor
que 5% O a ma2or que 5%% */
1e p(eden poner m$s de dos cond%c%ones&
if ( a>5% XX a<5%% XX ?>3%% XX ?<9%% )/* /e de?en cumplir las
cuatro condiciones */
!sto se c(mple s% a est$ entre 1' 0 1'' 0 b est$ entre 2'' 0 5''.
Tambin se pueden agrupar mediante parntesis varias condiciones:
44
if ( ( a>5% XX a<5%% ) YY ( ?>3%% XX ?<9%% ) )
!sta cond%c%2n se leer4a como s%-(e&
si a es ma2or que 5% 2 menor que 5%%
o
si ? es ma2or que 3%% 2 menor que 9%%
Es decir que si se cumple el primer parntesis o si se cumple el segundo la condicin
es cierta.
INTRODUCCIN A LAS FUNCIONES
&ntrodccin
Vamos a dar un paso ms en la complejidad de nuestros programas. Vamos a empezar
a usar funciones creadas por nosotros. Las funciones son de una gran utilidad en los
programas. Nos ayudan a que sean ms legibles y ms cortos. Con ellos estructuramos
mejor los programas.
Una funcin sirve para realizar tareas concretas y simplificar el programa. Nos sirve
para evitar tener que escribir el mismo cdigo varias veces. Ya hemos visto en el curso
algunas funciones como printf, gotoxy, scanf y clrscr. Estas funciones estn definidas
en una biblioteca (la biblioteca estndar de C). No tenemos que preocuparnos de ella
porque el compilador se encarga de ella automticamente.
Sin embargo nosotros tambin podemos definir nuestras propias funciones. Pocas
veces se ve un programa un poco complejo que no use funciones. Una de ellas, que
usamos siempre, es la funcin main.
De(inicin de na (ncin
Una funcin tiene el siguiente formato:
tipoEdeEvaria?le nom?reEdeElaEfunci1n( argumentos )
{
definici1n de varia?les#
cuerpo de la funci1n#
return %#
&
Poco a poco, empecemos por el nombre^de^la^.(nc%2n. Para el nombre no se p(eden (sar
mas )(e letras, n3meros 0 el s4mbolo T^T. /o se p(eden (sar n% acentos n% espac%os. ;dem$s
el nombre de la .(nc%2n debe empe5ar por (na letra, no p(ede empe5ar con (n n3mero. !l
nombre de la .(nc%2n se (sa para llamarla dentro del pro-rama.
El tipo_de_variable: Cuando una funcin se ejecuta y termina debe devolver un valor.
Este valor puede ser cualquiera de los tipos de variables que hemos visto en el captulo
de Tipos de datos (int, char, float, double) o un tipo de dato definido por nosotros
(esto lo veremos ms tarde). El valor que devuelve la funcin suele ser el resultado de
las operaciones que se realizan en la funcin, o si han tenido xito o no.
45
Tambin podemos usar el tipo void. Este nos permite que podamos devolver cualquier
tipo de variable o ninguna.
Definicin de variables: Dentro de la funcin podemos definir variables que slo
tendrn validez dentro de la propia funcin. Si declaramos una variable en una funcin
no podemos usarla en otra.
Cuerpo de la funcin: Aqu es donde va el cdigo de la funcin.
Return: Antes hemos indicado que la funcin devuelve un valor. La sentencia return se
usa para esto. El dato que se pone despues de return es el dato que se devuelve.
Puede ser una constante o una variable. Debe ser del mismo tipo que
tipo.de.varia'le.
Argumentos: Estos son variables que se pasan como datos a una funcin. Deben ir
separados por una coma. Cada variable debe ir con su tipo de variable. Las funciones
deben definirse antes de ser llamadas. En los ejemplos a continuacin se llama a la
funcin desde main, as que tenemos que definirlas antes que main. Esto lo veremos
en el apartado siguiente.
Ejem#lo 1. Funcin sin argumentos que no devuelve nada:
Este programa llama a la funcin prepara pantalla que borra la pantalla y muestra el
mensaje "la pantalla est limpia". Por supuesto es de nula utilidad pero nos sirve para
empezar.
#include <stdio.h>
#include <conio.h>
void preparaEpantalla() /* Go se de?e poner punto 2
coma aqu */
{
clrscr()#
printf( ,a pantalla est[ limpia"n )#
return# /* Go hace falta devolver ningNn valor4
mucha gente ni siquiera pone este return */
&
void main()
{
preparaEpantalla()# /* ,lamamos a la funci1n */
&
/0emplo 1. _(nc%2n con ar-(mentos, no de#(el#e n%n-3n #alor&
En este ejemplo la funcin com#ara toma dos nmeros, los compara y nos dice cual
es mayor.
#include <stdio.h>
#include <conio.h>
void compara( int a4 int ? ) /* Hetemos los par[metros a
2 ? a la funci1n */
{
46
if ( a>? ) printf( @i es ma2or que @i"n 4 a4 ? )#
else printf( @i es ma2or que @i"n4 ?4 a )#
return#
&
void main()
{
int num54 num3#
printf( OntroduUca dos nNmerosM )#
scanf( @i @i4 num54 num3 )#
compara( num54 num3 )# /* ,lamamos a la funci1n con sus
dos argumentos */
&
/0emplo 2. _(nc%2n con ar-(mentos )(e de#(el#e (n #alor.
Este ejemplo es como el anterior pero devuelve como resultado el mayor de los dos
nmeros.
#include <stdio.h>
#include <conio.h>
int compara( int a4 int ? ) /* Hetemos los par[metros a
2 ? a la funci1n */
{
int ma2or# /* $sta funci1n define su propia
varia?le4 esta varia?le s1lo se puede usar aqu */
if ( a>? )
ma2or ; a#
else ma2or ; ?#
return ma2or#
&
void main()
{
int num54 num3#
int resultado#
printf( OntroduUca dos nNmerosM )#
scanf( @i @i4 num54 num3 )#
resultado ; compara( num54 num3 )# /* *ecogemos el
valor que devuelve la funci1n en ,e':.0+d) */
printf( $l ma2or de los dos es @i"n4 resultado )#
&
!n este e"emplo pod4amos +aber +ec+o tamb%*n&
printf( $l ma2or de los dos es @i"n4 compara( num54 num3
) )#
De esta .orma nos a+orramos tener )(e de.%n%r la #ar%able Tres(ltadoT.
Dnde se de(inen las (nciones
47
Las funciones deben definirse siempre antes de donde se usan. Lo habitual en un
programa es:
-ecci#n Descripci#n
6ncl(des ;)(4 se %nd%can )(* .%c+eros e>ternos se (san
De.%n%c%ones ;)(4 se de.%nen las constantes )(e se (san en el pro-rama
De.%n%c%2n de
#ar%ables
;)(4 se de.%nen las #ar%ables -lobales (las )(e se p(eden (sar en
=ED;1 las .(nc%ones)
De.%n%c%2n de
.(nc%ones
;)(4 es donde se de.%nen las .(nc%ones
_(nc%2n ma%n ;)(4 se de.%ne la .(nc%2n ma%n.
Esta es una forma muy habitual de estructurar un programa. Sin embargo esto no es
algo rgido, no tiene por qu hacerse as, pero es recomendable.
Se puede hacer de otra forma, tambin aconsejable. Consiste en definir despus de las
variables las cabeceras de las funciones, sin escribir su cdigo. Esto nos permite luego
poner las fucniones en cualquier orden. Ejemplos:
#include <stdio.h>
#include <conio.h>
*)id 7)<=+,+/ in0 +> in0 - 12 ?* Defini<)' .+ 7+-e7e,+ de
.+ f:n7i@n *?
void main()
{
int num54 num3#
int resultado#
printf( OntroduUca dos nNmerosM )#
scanf( @i @i4 num54 num3 )#
resultado ; compara( num54 num3 )#
printf( $l ma2or de los dos es @i"n4 resultado )#
&
int compara( int a4 int ? ) /* Ahora podemos poner el
cuerpo de la funci1n donde queramos. */
/* Oncluso despu^s de donde
la llamamos (main) */
{
int ma2or#
if ( a>? )
ma2or ; a#
else ma2or ; ?#
return ma2or#
48
&
C(ando se de.%ne la cabecera de la .(nc%2n s%n s( c(erpo (o c2d%-o) debemos poner (n TYT al
.%nal. C(ando de.%namos el c(erpo m$s tarde no debemos poner el TYT, se +ace como (na
.(nc%2n normal.
La definicin debe ser igual cuando definimos slo la cabecera y cuando definimos el
cuerpo. Mismo nombre, mismo nmero y tipo de parmetros y mismo tipo de valor
devuelto.
Cida de na $ariable
Cuando definimos una variable dentro de una funcin, esa variable slo es vlida
dentro de la funcin. Si definimos una variable dentro de main slo podremos usarla
dentro de main. Si por el contrario la definimos como variable global, antes de las
funciones, se puede usar en todas las funciones.
Podemos crear una variable global y en una funcin una variable local con el mismo
nombre. Dentro de la funcin cuando llamamos a esa variable llamamos a la local, no a
la global. Esto no da errores pero puede crear confusin al programar y al analizar el
cdigo.
Ejercicios
Ejercicio 1: Descubre los errores:
#include <stdio.h>
#include <conio.h>
void main()
{
int num54 num3#
int resultado4
printf( OntroduUca dos nNmerosM )#
scanf( @i @i4 num54 num3 )#
resultado ; compara( num54 num3 )#
printf( $l ma2or de los dos es @i"n4 resultado )#
&
int compara( int a4 int ? )#
{
int ma2or#
if ( a>? ) ma2or ; a#
else ma2or ; ?#
return ma2or#
&
-oluci#n&
Hay una coma despus de int resultado en vez de un punto y coma.
Llamamos a la funcin compara dentro de main antes de definirla.
Cuando definimos la funcin compara con su cuerpo hemos puesto un punto y
coma al final, eso es un error.
4
/0ercicio 1& V(sca los errores.
#include <stdio.h>
int resultado( int parametro )
void main()
{
int a4 ?#
a ; 3# ? ; T#
printf( @i4 resultado( a )#
&
char resultado( int parametro )
{
return par[metroP?#
&
-oluci#n&
Hemos definido la cabecera de resultado sin punto y coma.
Cuando definimos el cuerpo de resultado en su cabecera hemos puesto char,
que no coincide con la definicin que hemos hecho al principio.
En la funcin resultado estamos usando la variable 'b' que est definida slo en
main. Por lo tanto es como si no existiera para resultado.
En printf nos hemos dejado un parntesis al final.
PUNTEROS
Punteros!!! uff. Si hasta ahora te haba parecido complicado preprate. Este es uno de
los temas que ms suele costar a la gente al aprender C. Los punteros son una de las
ms potentes caractersticas de C, pero a la vez uno de sus mayores peligros. Los
punteros nos permites acceder directamente a cualquier parte de la memoria. Esto da
a los programas C una gran pontencia. Sin embargo son una fuente ilimitada de
errores. Un error usando un puntero puede bloquear el sistema (si usamos ms-dos o
win95, no en Linux) y a veces puede ser difcil detectarlo.
Otros lenguajes no nos dejan usar punteros para evitar estos problemas, pero a la vez
nos quitan parte del control que tenemos en C. No voy a entrar a discutir si es mejor o
no poder usar punteros, aunque pienso que es mejor. Yo me voy a limitar a explicar
cmo funcionan.
A pesar de todo esto no hay que tenerles miedo. Casi todos los programas C usan
punteros. Si aprendemos a usarlos bien no tendremos mas que algn problema
espordico. As que atencin, valor y al toro.
Da memoria del ordenador
Si tienes bien claro lo que es la memoria del ordenador puedes saltarte esta seccin.
Pero si confundes la memoria con el disco duro o no tienes claro lo que es no te la
pierdas.
5'
A lo largo de mi experiencia con ordenadores me he encontrado con mucha gente que
no tiene claro cmo funciona un ordenador. Cuando hablamos de memoria nos
estamos refiriendo a la memoria RAM del ordenador. Son unas pastillas que se
conectan a la placa base y nada tienen que ver con el disco duro. El disco duro guarda
los datos permanentemente (hasta que se rompe) y la informacin se almacena como
ficheros. Nosotros podemos decirle al ordenador cundo grabar, borrar, abrir un
documento, etc. La memoria Ram en cambio, se borra al apagar el ordenador. La
memoria Ram la usan los programas sin que el usuario de stos se de cuenta.
Para hacernos una idea, hoy en dia la memoria se mide en MegaBytes (suelen ser 16,
32, 64, 128Mb) y los discos duros en GigaBytes (entre 3,4 y 70Gb, o mucho ms). Hay
otras memorias en el ordenador aparte de la mencionada. La memoria de video (que
est en la tarjeta grfica), las memorias cach (del procesador, de la placa...) y quizs
alguna ms que ahora se me olvida.
Direcciones de $ariables
Vamos a ir como siempre por partes. Primero vamos a ver qu pasa cuando
declaramos una variable.
Al declarar una variable estamos diciendo al ordenador que nos reserve una parte de la
memoria para almacenarla. Cada vez que ejecutemos el programa la variable se
almacenar en un sitio diferente, eso no lo podemos controlar, depende de la memoria
disponible y otros factores misteriosos. Puede que se almacene en el mismo sitio, pero
es mejor no fiarse. Dependiendo del tipo de variable que declaremos el ordenador nos
reservar ms o menos memoria. Como vimos en el captulo de tipos de datos cada
tipo de variable ocupa ms o menos bytes. Por ejemplo si declaramos un char, el
ordenador nos reserva 1 byte (8 bits). Cuando finaliza el programa todo el espacio
reservado queda libre.
Existe una forma de saber qu direcciones nos ha reservado el ordenador. Se trata de
usar el operador < (operador de direccin). Vamos a ver un ejemplo: Declaramos la
variable 'a' y obtenemos su valor y direccin.
#include <stdio.h>
void main()
{
int a#
a ; 5%#
printf( 6irecci1n de a ; @p4 valor de a ; @i"n4 Xa4 a )#
&
Para mostrar la d%recc%2n de la #ar%able (samos Rp en l(-ar de R%, s%r#e para escr%b%r
d%recc%ones de p(nteros 0 #ar%ables. !l #alor se m(estra en +e>adec%mal.
No hay que confundir el valor de la variable con la direccin donde est almacenada la
variable. La variable 'a' est almacenada en un lugar determinado de la memoria, ese
lugar no cambia mientras se ejecuta el programa. El valor de la variable puede
cambiar a lo largo del programa, lo cambiamos nosotros. Ese valor est almacenado
en la direccin de la variable. El nombre de la variable es equivalente a poner un
nombre a una zona de la memoria. Cuando en el programa escribimos 'a', en realidad
51
estamos diciendo, "el valor que est almacenado en la direccin de memoria a la que
llamamos 'a'".
,e son los #nteros
Ahora ya estamos en condiciones de ver lo que es un puntero. Un puntero es una
variable un tanto especial. Con un puntero podemos almacenar direcciones de
memoria. En un puntero podemos tener guardada la direccin de una variable.
Vamos a ver si cogemos bien el concepto de puntero y la diferencia entre stos y las
variables normales.
En el dibujo anterior tenemos una representacin de lo que sera la memoria del
ordenador. Cada casilla representa un byte de la memoria. Y cada nmero es su
direccin de memoria. La primera casilla es la posicin 00001 de la memoria. La
segunda casilla la posicin 00002 y as sucesivamente.
Supongamos que ahora declaramos una variable char: char numero = 43. El
ordenador nos guardara por ejemplo la posicin 00003 para esta variable. Esta
posicin de la memoria queda reservada y ya no la puede usar nadie ms. Adems
esta posicin a partir de ahora se le llama numero. Como le hemos dado el valor 43 a
numero, el valor 43 se almacena en numero, es decir, en la posicin 00003.
Si ahora usramos el programa anterior:
#include <stdio.h>
void main()
{
int numero#
52
numero ; QT#
printf( 6irecci1n de numero ; @p4 valor de numero ;
@i"n4 Xnumero4 numero )#
&
!l res(ltado ser4a&
6irecci1n de numero ; """"34 valor de numero ; 43
Creo )(e as4 0a est$ clara la d%.erenc%a entre el #alor de (na #ar%able (43) 0 s( d%recc%2n
(''''3). ;+ora #amos (n poco m$s all$, #amos a declarar (n p(ntero. :emos d%c+o )(e (n
p(ntero s%r#e para almacenar la d%recc%ones de memor%a. X(c+as #eces los p(nteros se (san
para -(ardar las d%recc%ones de #ar%ables. ?%mos en el cap4t(lo =%pos de Datos )(e cada
t%po de #ar%able oc(paba (n espac%o d%st%nto. Por eso c(ando declaramos (n p(ntero
debemos espec%.%car el t%po de datos c(0a d%recc%2n almacenar$. !n n(estro e"emplo
)(eremos )(e almacene la d%recc%2n de (na #ar%able c+ar. ;s4 )(e para declarar el p(ntero
punt debemos +acer&
char *punt#
!l P (aster%sco) s%r#e para %nd%car )(e se trata de (n p(ntero, debe %r "(sto antes del nombre
de la #ar%able, s%n espac%os. !n la #ar%able p(nt s2lo se p(eden -(ardar d%recc%ones de
memor%a, no se p(eden -(ardar datos. ?amos a #ol#er sobre el e"emplo anter%or (n poco
ampl%ado para #er c2mo .(nc%ona (n p(ntero&
#include <stdio.h>
void main()
{
int numero#
int *punt#
numero ; QT#
punt ; Xnumero#
printf( 6irecci1n de numero ; @p4 valor de numero ;
@i"n4 Xnumero4 numero )#
&
?amos a %r l4nea a l4nea.
En la primera int numero reservamos memoria para numero (supongamos que
queda como antes, posicin 00003). Por ahora numero no tiene ningn valor.
Siguiente lnea: int *punt/. Reservamos una posicin de memoria para
almacenar el puntero. Lo normal es que segn se declaran variables se guarden
en posiciones contiguas. De modo que quedara en la posicin 00004. Por ahora
punt no tiene ningn valor, es decir, no apunta a ninguna variable. Esto es lo
que tenemos por ahora:
Tercera lnea: numero 0 12/. Aqu ya estamos dando el valor 43 a numero. Se
almacena 43 en la direccin 00003, que es la de numero.
53
Cuarta lnea: punt 0 3numero/. Por fin damos un valor a punt. El valor que le
damos es la direccin de numero (ya hemos visto que & devuelve la direccin
de una variable). As que punt tendr como valor la direccin de numero,
00003. Por lo tanto ya tenemos:
Cuando un puntero tiene la direccin de una variable se dice que ese puntero a#nta
a esa variable.
N.E%: La declaracin de un puntero depende del tipo de dato al que queramos
apuntar. En general la declaracin es:
tipoEdeEdato *nom?reEdelEpuntero#
1% en #e5 de )(erer ap(ntar a (na #ar%able t%po c+ar como en el e"emplo +(b%ese s%do de
t%po %nt&
int *punt#
Para ?- sir$e n #ntero 9 cmo se sa
Los punteros tienen muchas utilidades, por ejemplo nos permiten pasar argumentos (o
parmetros) a una funcin y modificarlos. Tambin permiten el manejo de cadenas y
de arrays. Otro uso importante es que nos permiten acceder directamente a la
pantalla, al teclado y a todos los componenetes del ordenador. Pero esto ya lo veremos
ms adelante.
Pero si slo sirvieran para almacenar direcciones de memoria no serviran para mucho.
Nos deben dejar tambin la posibilidad de acceder a esas posiciones de memoria. Para
acceder a ellas se usa el operador *, que no hay que confundir con el de la
multiplicacin.
#include <stdio.h>
void main()
{
int numero#
int *punt#
numero ; QT#
punt ; Xnumero#
printf( 6irecci1n de numero ; @p4 valor de numero ;
@i"n4 Xnumero4 *=:n0 )#
&
54
1% nos .%"amos en lo )(e +a camb%ado con respecto al e"emplo anter%or, #emos )(e para
acceder al #alor de n3mero (samos Pp(nt en #e5 de n(mero. !sto es as4 por)(e p(nt ap(nta
a n(mero 0 Pp(nt nos perm%te acceder al #alor al )(e ap(nta p(nt.
#include <stdio.h>
void main()
{
int numero#
int *punt#
numero ; QT#
punt ; Xnumero#
*punt ; T%#
printf( 6irecci1n de numero ; @p4 valor de numero ;
@i"n4 Xnumero4 numero )#
&
;+ora +emos camb%ado el #alor de n(mero a tra#*s de Pp(nt.
En resumen, usando punt podemos apuntar a una variable y con *punt vemos o
cambiamos el contenido de esa variable.
Un puntero no slo sirve para apunta a una variable, tambin sirve para apuntar una
direccin de memoria determinada. Esto tiene muchas aplicaciones, por ejemplo nos
permite controlar el hardware directamente (en MS-Dos y Windows, no en Linux).
Podemos escribir directamente sobre la memoria de video y as escribir directamente
en la pantalla sin usar printf.
Fsando #nteros en na com#aracin
Veamos el siguiente ejemplo. Queremos comprobar si dos variables son iguales usando
punteros:
#include <stdio.h>
void main()
{
int a4 ?#
int *punt54 *punt3#
a ; 9# ? ; 9#
punt5 ; Xa# punt3 ; X?#
if ( punt5 ;; punt3 )
printf( /on iguales"n )#
&
;l-(%en podr4a pensar )(e el if se c(mple 0 se mostrar4a el mensa"e Son iuales en pantalla.
P(es no es as4, el pro-rama es err2neo. !s c%erto )(e a 0 b son %-(ales. =amb%*n es c%erto
)(e p(nt1 ap(nta a TaT 0 p(nt2 a TbT. <o )(e )(er4amos comprobar era s% a 0 b son %-(ales.
1%n embar-o con la cond%c%2n estamos comprobando s% p(nt1 ap(nta al m%smo s%t%o )(e
p(nt2, estamos comparando las d%recc%ones donde ap(ntan. Por s(p(esto a 0 b est$n en
d%st%nto s%t%o en la memor%a as4 )(e la cond%c%2n es .alsa. Para )(e el pro-rama .(nc%onara
deber4amos (sar los aster%scos&
55
#include <stdio.h>
void main()
{
int a4 ?#
int *punt54 *punt3#
a ; 9# ? ; 9#
punt5 ; Xa# punt3 ; X?#
if ( *=:n01 == *=:n02 )
printf( /on iguales"n )#
&
;+ora s4. !stamos comparando el conten%do de las #ar%ables a las )(e ap(ntan p(nt1 0
p(nt2. Debemos tener m(c+o c(%dado con esto por)(e es (n error )(e se c(ela con m(c+a
.ac%l%dad.
Vamos a cambiar un poco el ejemplo. Ahora 'b' no existe y punt1 y punt2 apuntan a
'a'. La condicin se cumplir porque apuntan al mismo sitio.
#include <stdio.h>
void main()
{
int a#
int *punt54 *punt3#
a ; 9#
=:n01 = #+2 =:n02 = #+2
if ( punt5 ;; punt3 )
printf( punt5 2 punt3 apuntan al mismo
sitio"n )#
&
Pnteros como ar/mentos de (nciones
Hemos visto en el captulo de funciones cmo pasar parmetros y cmo obtener
resultados de las funciones (con los valores devueltos con return). Pero tiene un
inconveniente, slo podemos tener un valor devuelto. Ahora vamos a ver cmo los
punteros nos permites modificar varias variables en una funcin.
Hasta ahora para pasar una variable a una funcin hacamos lo siguiente:
#include <stdio.h>
int suma( int a4 int ? )
{
return aP?M
&
void main()
{
int var54 var3#
56
var5 ; 9# var3 ; R#
printf( ,a suma es M @i"n4 suma(var54 var3) )#
&
V%en a)(4 +emos pasado a la .(nc%2n los par$metros TaT 0 TbT ()(e no podemos mod%.%car) 0
nos de#(el#e la s(ma de ambos. 1(pon-amos a+ora )(e )(eremos tener la s(ma pero
adem$s )(eremos )(e #ar1 se +a-a cero dentro de la .(nc%2n. Para eso +ar4amos lo
s%-(%ente&
#include <stdio.h>
int suma( in0 *+4 int ? )
{
*a ; %#
return aP?M
&
void main()
{
int var54 var3#
var5 ; 9# var3 ; R#
printf( ,a suma esM @i 2 a valeM @i"n4 suma(#*+,14
var3)4 var5 )#
&
_%"*monos en lo )(e +a camb%ado (con letra en ne-r%ta)& !n la .(nc%2n s(ma +emos
declarado TaT como p(ntero. !n la llamada a la .(nc%2n (dentro de ma%n) +emos p(esto G
para pasar la d%recc%2n de la #ar%able #ar1. Ha s2lo )(eda +acer cero a #ar1 a tra#*s de
PaA'.
Es importante no olvidar el operador & en la llamada a la funcin ya que sin el no
estaramos pasando la direccin de la variable sino cualquier otra cosa.
Podemos usar tantos punteros como queramos en la definicin de la funcin.
Ejercicios
Ejercicio 1: Encuentra un fallo muy grave:
#include <stdio.h>
void main()
{
int *a#
*a ; 9#
&
-oluci#n& /o +emos dado n%n-(na d%recc%2n al p(ntero. /o sabemos a d2nde ap(nta.
P(ede ap(ntar a c(al)(%er s%t%o, al darle (n #alor estamos encr%b%endo en (n l(-ar
desconoc%do de la memor%a. !sto p(ede dar problemas e %ncl(so blo)(ear el ordenador.
@ecordemos )(e al e"ec(tar (n pro-rama *ste se cop%a en la memor%a, al escr%b%r en
c(al)(%er parte p(ede )(e estemos camb%ando el pro-rama (en la memor%a, no en el d%sco
d(ro).
57
Ejercicio !: Escribe un programa que de un valor a una variable. Esta sea apuntada
por un puntero y sumarle 3 a travs del puntero. Luego imprimir el resultado.
Solcin: Esta es una posible solucin:
#include <stdio.h>
void main()
{
int a#
int *?#
a ; 9#
? ; Xa#
*? P; T#
printf( $l valor de a es ; @i"n4 a )#
&
Tambin se poda haber hecho: printf( "El valor de a es = %i\n", *b );
ARRAYS (MATRICES)
,- es n arra9?
La definicin sera algo as:
Ln arra0 es (n con"(nto de #ar%ables del m%smo t%po )(e t%enen el m%smo
nombre 0 se d%.erenc%an en el 4nd%ce.
Pero 7)(* )(%ere dec%r esto 0 para )(* lo )(eremos9. P(es b%en, s(pon-amos )(e somos (n
metere2lo-o 0 )(eremos -(ardar en el ordenador la temperat(ra )(e +a +ec+o cada +ora del
d%a. Para darle c%erta (t%l%dad al .%nal calc(laremos la med%a de las temperat(ras. Con lo )(e
sabemos +asta a+ora ser4a al-o as4 ()(e nad%e se moleste n% en probarlo)&
#include <stdio.h>
void main()
{
/* 6eclaramos 3Q varia?les4 una para cada hora del dia */
int temp54 temp34 tempT4 tempQ4 temp94 tempL4 temp<4 tempR#
int tempD4 temp5%4 temp554 temp534 temp5T4 temp5Q4 temp594 temp5L#
int temp5<4 temp5R4 temp5D4 temp3%4 temp354 temp334 temp3T4 temp%#
int media#
/* Ahora tenemos que dar el valor de cada una */
printf( Vemperatura de las %M )#
scanf( @i4 Xtemp% )#
printf( Vemperatura de las 5M )#
scanf( @i4 Xtemp5 )#
printf( Vemperatura de las 3M )#
scanf( @i4 Xtemp3 )#
...
printf( Vemperatura de las 3TM )#
scanf( @i4 Xtemp3T )#
58
media ; ( temp% P temp5 P temp3 P tempT P tempQ P ... P temp3T ) /
3Q#
printf( "n,a temperatura media es @i"n4 media )#
&
NOTA: Los puntos suspensivos los he puesto para no tener que escribir todo y que no
ocupe tanto, no se pueden usar en un programa.
Para acortar un poco el programa podramos hacer algo as:
#include <stdio.h>
void main()
{
/* 6eclaramos 3Q varia?les4 una para cada hora del dia */
int temp54 temp34 tempT4 tempQ4 temp94 tempL4 temp<4 tempR#
int tempD4 temp5%4 temp554 temp534 temp5T4 temp5Q4 temp594 temp5L#
int temp5<4 temp5R4 temp5D4 temp3%4 temp354 temp334 temp3T4 temp%#
int media#
/* Ahora tenemos que dar el valor de cada una */
printf( OntroduUca las temperaturas desde las % hasta las 3T
separadas por un espacionM )#
scanf( @i @i @i ... @i4 Xtemp%4 Xtemp54 Xtemp34 ... Xtemp3T )#
media ; ( temp% P temp5 P temp3 P tempT P tempQ P ... P temp3T ) /
3Q#
printf( "n,a temperatura media es @i"n4 media )#
&
Lo que no deja de ser un peazo. Y esto con un ejemplo que tiene tan slo 24
variables, imagnate si son ms!!
Y precisamente aqu es donde nos vienen de perlas los arrays. Vamos a hacer el
programa con un array. Usaremos nuestros conocimientos de bucles for y de scanf.
#include <stdio.h>
void main()
{
int temp83Q:# /* .on esto 2a tenemos declaradas las 3Q varia?les */
float media#
int hora#
/* Ahora tenemos que dar el valor de cada una */
for( hora;%# hora<3Q# horaPP )
{
printf( Vemperatura de las @iM 4 hora )#
scanf( @i4 Xtemp8hora: )#
media P; temp8hora:#
&
media ; media / 3Q#
printf( "n,a temperatura media es @f"n4 media )#
&
5
Como ves es un programa ms rpido de escribir (y es menos aburrido hacerlo), y ms
cmodo para el usuario que el anterior.
Como ya hemos comentado cuando declaramos una variable lo que estamos haciendo
es reservar una zona de la memoria para ella. Cuando declaramos un array lo que
hacemos (en este ejemplo) es reservar espacio en memoria para 24 variables de tipo
int. El tamao del array (24) lo indicamos entre corchetes al definirlo. Esta es la parte
de la definicin que dice: 4n arra- es un con5unto de varia'les del mismo tipo 6ue
tienen el mismo nom're.
La parte final de la definicin dice: - se diferencian en el 7ndice. En ejemplo recorremos
la matriz mediante un bucle for y vamos dando valores a los distintos elementos de la
matriz. Para indicar a qu elemento nos referimos usamos un nmero entre corchetes
(en este caso la variable hora), este nmero es lo que se llama &ndice. El primer
elemento de la matriz en C tiene el ndice 0, El segundo tiene el 1 y as
sucesivamente. De modo que si queremos dar un valor al elemento 4 (ndice 3)
haremos:
temp8 T : ; 3%#
/E=;& /o +a0 )(e con.(nd%rse. !n la declarac%2n del arra0 el n3mero entre corc+etes es el
n3mero de elementos, en camb%o c(ando 0a (samos la matr%5 el n3mero entre corc+etes es
el 4nd%ce.
Declaracin de n %rra9
La forma general de declarar un array es la siguiente:
tipo_de_dato nom?reEdelEarra28 dimensi1n :#
!l tipo!de!dato es (no de los t%pos de datos conoc%dos (%nt, c+ar, .loat...) o de los de.%n%dos
por el (s(ar%o con t0pde.. !n el e"emplo era %nt.
El nombre_del_array es el nombre que damos al array, en el ejemplo era temp.
La dimensin es el nmero de elementos que tiene el array.
Como he indicado antes, al declarar un array reservamos en memoria tantas variables
del tipo.de.dato como las indicada en dimensin.
Sobre la dimensin de n %rra9
Hemos visto en el ejemplo que tenemos que indicar en varios sitios el tamao del
array: en la declaracin, en el bucle for y al calcular la media. Este es un programa
pequeo, en un programa mayor probablemente habr que escribirlo muchas ms
veces. Si en un momento dado queremos cambiar la dimensin del array tendremos
que cambiar todos. Si nos equivocamos al escribir el tamao (ponemos 25 en vez de
24) cometeremos un error y puede que no nos demos cuenta. Por eso es mejor usar
una constante con nombre, por ejemplo ELEMENTOS.
#include <stdio.h>
6'
#define $,$H$GV7/ 3Q
void main()
{
int temp8EAEBENCOS:# /* .on esto 2a tenemos declaradas las 3Q
varia?les */
float media#
int hora#
/* Ahora tenemos que dar el valor de cada una */
for( hora;%# hora<EAEBENCOS# horaPP )
{
printf( Vemperatura de las @iM 4 hora )#
scanf( @i4 Xtemp8hora: )#
media P; temp8hora:#
&
media ; media / EAEBENCOS#
printf( "n,a temperatura media es @f"n4 media )#
&
.ompro?ado con 6>)--
;+ora con s2lo camb%ar el #alor de elementos (na #e5 lo estaremos +ac%endo en todo el
pro-rama.
&niciali>ar n arra9
En C se pueden inicializar los arrays al declararlos igual que hacamos con las
variables. Recordemos que se poda hacer:
int hoCas ; TQ#
P(es con arra0s se p(ede +acer&
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394
3Q4 334
354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q4 5Q4 5T4
53 &#
;+ora el elemento ' ()(e ser$ el pr%mero), es dec%r temperat(rasM'N #aldr$ 15. !l elemento
1 (el se-(ndo) #aldr$ 18 0 as4 con todos. ?amos a #er (n e"emplo&
#include <stdio.h>
void main()
{
int hora#
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394 3Q4
334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q4
5Q4 5T4 53 &#
for (hora;% # hora<3Q # horaPP )
{
printf( ,a temperatura a las @i era de @i grados."n4 hora4
temperaturas8hora: )#
&
&
.ompro?ado con 6>)--
61
Pero a ver quin es el habilidoso que no se equivoca al meter los datos, no es difcil
olvidarse alguno. Hemos indicado al compilador que nos reserve memoria para un
array de 24 elementos de tipo int. Qu ocurre si metemos menos de los reservados?
Pues no pasa nada, slo que los elementos que falten valdrn cero.
#include <stdio.h>
void main()
{
int hora#
/* +altan los tres Nltimos elementos */
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394
3Q4 334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q &#
for (hora;% # hora<3Q # horaPP )
{
printf( ,a temperatura a las @i era de @i grados."n4 hora4
temperaturas8hora: )#
&
&
.ompro?ado con 6>)--
El resultado ser:
,a temperatura a las % era de 59 grados.
,a temperatura a las 5 era de 5R grados.
,a temperatura a las 3 era de 3% grados.
,a temperatura a las T era de 3T grados.
...
,a temperatura a las 5< era de 5< grados.
,a temperatura a las 5R era de 59 grados.
,a temperatura a las 5D era de 5Q grados.
,a temperatura a las 3% era de 5Q grados.
A+ 0e<=e,+0:,+ + .+' 21 e,+ de " D,+d)'.
A+ 0e<=e,+0:,+ + .+' 22 e,+ de " D,+d)'.
A+ 0e<=e,+0:,+ + .+' 23 e,+ de " D,+d)'.
?emos )(e los 3lt%mos 3 elementos son n(los, )(e son a)(ellos a los )(e no +emos dado
#alores. !l comp%lador no nos a#%sa )(e +emos met%do m$s datos de los reser#ados.
NOTA: Fjate que para recorrer del elemento 0 al 23 (24 elementos) hacemos:
for(hora=0; hora<24; hora++). La condicin es que hora sea menos de 24. Tambin
podamos haber hecho que hora!=24.
Ahora vamos a ver el caso contrario, metemos ms datos de los reservados. Vamos a
meter 25 en vez de 24. Si hacemos esto dependiendo del compilador obtendremos un
error o al menos un warning (aviso). En unos compiladores el programa se crear y en
otros no, pero an as nos avisa del fallo. Debe indicarse que estamos intentando
guardar un dato de ms, no hemos reservado memoria para l.
Si la matriz debe tener una longitud determinada usamos este mtodo de definir el
nmero de elementos. En nuestro caso era conveniente, porque los dias siempre
tienen 24 horas. Es conveniente definir el tamao de la matriz para que nos avise si
metemos ms elementos de los necesarios.
62
En los dems casos podemos usar un mtodo alternativo, dejar al ordenador que
cuente los elementos que hemos metido y nos reserve espacio para ellos:
#include <stdio.h>
void main()
{
int hora#
/* +altan los tres Nltimos elementos */
int 0e<=e,+0:,+'EF ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394 3Q4
334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q &#
for ( hora;% # hora<3Q # horaPP )
{
printf( ,a temperatura a las @i era de @i grados."n4 hora4
temperaturas8hora: )#
&
&
.ompro?ado con 6>)--
Vemos que no hemos especificado la dimensin del array temperaturas. Hemos dejado
los corchetes en blanco. El ordenador contar los elementos que hemos puesto entre
llaves y reservar espacio para ellos. De esta forma siempre habr el espacio
necesario, ni ms ni menos. La pega es que si ponemos ms de los que queramos no
nos daremos cuenta.
Para saber en este caso cuantos elementos tiene la matriz podemos usar el operador
sizeof. Dividimos el tamao de la matriz entre el tamao de sus elementos y tenemos
el nmero de elementos.
#include <stdio.h>
void main()
{
int hora#
int elementos#
int temperaturas8: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394
3Q4 334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q &#
e.e<en0)' = 'i(e)f 0e<=e,+0:,+' ? 'i(e)f/in012
for ( hora;% # hora<elementos # horaPP )
{
printf( ,a temperatura a las @i era de @i grados."n4 hora4
temperas8hora: )#
&
printf( !an sido @i elementos."n 4 elementos )#
&
.ompro?ado con 6>)--
5ecorrer n arra9
En el apartado anterior veamos un ejemplo que mostraba todos los datos de un array.
Veamos tambin lo que pasaba si metamos ms o menos elementos al inicializar la
matriz. Ahora vamos a ver qu pasa si intentamos imprimir ms elementos de los que
hay en la matriz, en este caso intentamos imprimir 28 elementos cuando slo hay 24:
63
#include <stdio.h>
void main()
{
int hora#
/* +altan los tres Nltimos elementos */
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394 3Q4
334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q4 5T4
5T4 53 &#
for (hora;% # 6),+G2H # horaPP )
{
printf( ,a temperatura a las @i era de @i grados."n4 hora4
temperaturas8hora: )#
&
&
Comprobado con DJGPP
Lo que se obtiene en mi ordenador es:
,a temperatura a las 33 era de 59 grados.
...
,a temperatura a las 3T era de 53 grados.
A+ 0e<=e,+0:,+ + .+' 24 e,+ de 24 D,+d)'.
A+ 0e<=e,+0:,+ + .+' 25 e,+ de 342424H D,+d)'.
A+ 0e<=e,+0:,+ + .+' 26 e,+ de !"42 D,+d)'.
A+ 0e<=e,+0:,+ + .+' 2! e,+ de 1 D,+d)'.
?emos )(e a part%r del elemento 24 (%ncl(4do) tenemos res(ltados e>traSos. !sto es por)(e
nos +emos sal%do de los l4m%tes del arra0 e %ntenta acceder al elemento temperat(rasM25N 0
s(ces%#os )(e no e>%sten. ;s4 )(e nos m(estra el conten%do de la memor%a )(e est$ "(sto
detr$s de temperat(rasM23N (esto es lo m$s probable) )(e p(ede ser c(al)(%era. ;l contrar%o
)(e otros len-(a"es C no compr(eba los l4m%tes de los arra0, nos de"a salt$rnoslos a la
torera. !ste pro-rama no da error al comp%lar n% al e"ec(tar, tan s2lo de#(el#e res(ltados
e>traSos. =ampoco blo)(ear$ el s%stema por)(e no estamos escr%b%endo en la memor%a s%no
le0endo de ella.
Otra cosa muy diferente es meter datos en elementos que no existen. Veamos un
ejemplo 1ni se te ocrra ejectarlo3:
#include <stdio.h>
void main()
{
int temp83Q:#
float media#
int hora#
for( hora;%# 6),+G2H# horaPP )
{
printf( Vemperatura de las @iM 4 hora )#
scanf( @i4 Xtemp8hora: )#
media P; temp8hora:#
&
64
media ; media / 3Q#
printf( "n,a temperatura media es @f"n4 media )#
&
Lo que sospechaba, lo he probado en mi ordenador y se ha bloqueado. He tenido que
apagarlo. El problema ahora es que estamos intentando escribir en el elemento
temp[24] que no existe y puede ser un lugar cualquiera de la memoria. Como
consecuencia de esto podemos estar cambiando algn programa o dato de la memoria
que no debemos y el sistema hace pluf. As que mucho cuidado con esto.
Pnteros a arra9s
Aqu tenemos otro de los importantes usos de los punteros, los punteros a arrays.
Estos estn ntimamente relacionados.
Para que un puntero apunte a un array se puede hacer de dos formas, una es
apuntando al primer elemento del array:
int *puntero#
int temperaturas83Q:#
puntero ; Xtemperaturas8%:#
!l p(ntero ap(nta a la d%recc%2n del pr%mer elemento. Etra .orma e)(%#alente, pero m(c+o
m$s (sada es&
puntero ; temperaturas#
Con esto tamb%*n ap(ntamos al pr%mer elemento del arra0. _%"aos )(e el p(ntero t%ene )(e
ser del m%smo t%po )(e el arra0 (en este caso %nt).
Ahora vamos a ver cmo acceder al resto de los elementos. Para ello empezamos por
cmo funciona un array: Un array se guarda en posiciones seguidas en memoria, de
tal forma que el segundo elemento va inmediatamente despus del primero en la
memoria. En un ordenador en el que el tamao del tipo int es de 32 bits (4 bytes) cada
elemento del array ocupar 4 bytes. Veamos un ejemplo:
#include <stdio.h>
void main()
{
int i#
int temp83Q:#
for( i;%# i<3Q# iPP )
{
printf( ,a direcci1n del elemento @i es @p."n4 i4 Xtemp8i: )#
&
&
/E=;& @ec(erda )(e Rp s%r#e para %mpr%m%r en pantalla la d%recc%2n de (na #ar%able en
+e>adec%mal.
El resultado es (en mi ordenador):
65
,a direcci1n del elemento % es QcQT%.
,a direcci1n del elemento 5 es QcQTQ.
,a direcci1n del elemento 3 es QcQTR.
,a direcci1n del elemento T es QcQTc.
...
,a direcci1n del elemento 35 es QcQRQ.
,a direcci1n del elemento 33 es QcQRR.
,a direcci1n del elemento 3T es QcQRc.
(<as d%recc%ones est$n en +e>adec%mal). ?emos a)(4 )(e e.ect%#amente oc(pan pos%c%ones
consec(t%#as 0 )(e cada (na oc(pa 4 b0tes. 1% lo representamos en (na tabla&
4C43' 4C434 4C438 4C43C
tempM'N tempM1N tempM2N tempM3N
Ya hemos visto cmo funcionas los arrays por dentro, ahora vamos a verlo con
punteros. Voy a poner un ejemplo:
#include <stdio.h>
void main()
{
int i#
int temp83Q:#
int *punt#
=:n0 = 0e<=2
for( i;%# i<3Q# iPP )
{
printf( ,a direcci1n de temp8@i: es @p 2 la de punt es @p."n4
i4 Xtemp8i:4 =:n0 )#
=:n0++2
&
&
.ompro?ado con 6>)--
C(0o res(ltado es&
,a direcci1n de temp8%: es QcQT% 2 la de punt es QcQT%.
,a direcci1n de temp85: es QcQTQ 2 la de punt es QcQTQ.
,a direcci1n de temp83: es QcQTR 2 la de punt es QcQTR.
...
,a direcci1n de temp835: es QcQRQ 2 la de punt es QcQRQ.
,a direcci1n de temp833: es QcQRR 2 la de punt es QcQRR.
,a direcci1n de temp83T: es QcQRc 2 la de punt es QcQRc.
!n este e"emplo +a0 dos l4neas %mportantes (en ne-r%ta). <a pr%mera es punt " temp. Con
esta +acemos )(e el p(nt ap(nte al pr%mer elemento de la matr%5. 1% no +acemos esto p(nt
ap(nta a (n s%t%o c(al)(%era de la memor%a 0 debemos recordar )(e no es con#en%ente de"ar
los p(nteros as4, p(ede ser desastroso.
La segunda lnea importante es punt88. Con esto incrementamos el valor de punt,
pero curiosamente aunque incrementamos una unidad (punt++ equivale a
punt=punt+1) el valor aumenta en 4. Aqu se muestra una de las caractersticas
especiales de los punteros. Recordemos que en un puntero se guarda una direccin.
Tambin sabemos que un puntero apunta a un tipo de datos determinado (en este
66
caso int). Cuando sumamos 1 a un puntero sumamos el tamao del tipo al que apunta.
En el ejemplo el puntero apunta a una variable de tipo int que es de 4 bytes, entonces
al sumar 1 lo que hacemos es sumar 4 bytes. Con esto lo que se consigue es apuntar a
la siguiente posicin int de la memoria, en este caso es el siguiente elemento de la
matriz.
"peraci#n /3ui4alente Valor de punt
p(nt A tempY p(nt A GtempM'NY 4c43'
p(ntOOY s(mar 4 al conten%do de p(nt (4c43'O4) 4c434
p(ntOOY s(mar 4 al conten%do de p(nt (4c434O4) 4c438
Cuando hemos acabado estamos en temp[24] que no existe. Si queremos recuperar el
elemento 1 podemos hacer punt 0 temp otra vez o restar 24 a punt:
punt '; 3Q#
con esto +emos restado 24 pos%c%ones a p(nt (24 pos%c%ones %ntP4 b0tes por cada %ntA 6
pos%c%ones).
Vamos a ver ahora un ejemplo de cmo recorrer la matriz entera con punteros y cmo
imprimirla:
#include <stdio.h>
void main(int argc4char *argv8:)
{
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394 3Q4
334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q4
5T4 534 53 &#
int *punt#
int i#
punt ; temperaturas#
for( i;% # i<3Q# iPP )
{
printf( $lemento @iM @i"n4 i4 *punt )#
puntPP#
&
&
.ompro?ado con 6>)--
C(ando acabamos p(nt ap(nta a temperat(rasM24N, 0 no al pr%mer elemento, s% )(eremos
#ol#er a recorrer la matr%5 debemos #ol#er como antes al com%en5o. Para e#%tar perder la
re.erenc%a al pr%mer elemento de la matr%5 (temperat(rasM'N) se p(ede (sar otra .orma de
recorrer la matr%5 con p(nteros&
#include <stdio.h>
void main(int argc4char *argv8:)
{
int temperaturas83Q: ; { 594 5R4 3%4 3T4 334 3Q4 334 394 3L4 394 3Q4
67
334 354 3%4 5R4 5<4 5L4 5<4 594 5Q4 5Q4
5T4 534 53 &#
int *punt#
int i#
punt ; temperaturas#
for( i;% # i<3Q# iPP )
{
printf( $lemento @iM @i"n4 i4 */=:n0+i1 )#
&
&
.ompro?ado con 6>)--
Con *(puntPi) lo )(e +acemos es tomar la d%recc%2n a la )(e ap(nta punt (la d%recc%2n del
pr%mer elemento de la matr%5) 0 le s(mamos i pos%c%ones. De esta .orma tenemos la
d%recc%2n del elemento i. /o estamos s(mando (n #alor a punt, para s(marle (n #alor
+abr4a )(e +acer puntPP o puntP;algo, as4 )(e punt s%empre ap(nta al pr%nc%p%o de la
matr%5.
Se podra hacer este programa sin usar punt. Sustituyendolo por temperaturas y dejar
*(temperaturasPi). Lo que no se puede hacer es: temperaturasPP#.
&m#ortante: Como final debo comentar que el uso de ndices es una forma de
maquillar el uso de punteros. El ordenador convierte los ndices a punteros. Cuando al
ordenador le decimos temp[5] en realidad le estamos diciendo *(temp+5). As que
usar ndices es equivalente a usar punteros de una forma ms cmoda.
Paso de n arra9 a na (ncin
En C no podemos pasar un array entero a una funcin. Lo que tenemos que hacer es
pasar un puntero al array. Con este puntero podemos recorrer el array:
#include <stdio.h>
int sumar( int *m )
{
int suma4 i#
suma ; %#
for( i;%# i<5%# iPP )
{
suma P; m8i:#
&
return suma#
&
void main()
{
int contador#
int matriU85%: ; { 5%4 554 5T4 5%4 5Q4 D4 5%4 5R4 5%4 5% &#
68
for( contador;%# contador<5%# contadorPP )
printf( @Ti"n4 matriU8contador: )#
printf( P '''''"n )#
printf( @Ti4 sumar( matriU ) )#
&
.ompro?ado con 6>)--
!ste pro-rama t%ene al-(na cos%lla ad%c%onal como )(e m(estra toda la matr%5 en (na
col(mna. ;dem$s se (sa para %mpr%m%r los n3meros 52i. !l 3 %nd%ca )(e se t%enen )(e
al%near los n3meros a la derec+a, as4 )(eda m$s ele-ante.
Como he indicado no se pasa el array, sino un puntero a ese array. Si probamos el
truquillo de ms arriba de usar sizeof para calcular el nmero de elementos no
funcionar aqu. Dentro de la funcin suma aadimos la lnea:
printf( Vama0o del arra2M @i _?4 @i ?its"n4 siUeof m4 (siUeof
m)*R )#
De#ol#er$ 4 (depende del comp%lador) )(e ser$n los b0tes )(e oc(pa el %nt (m es (n
p(ntero a %nt). 7C2mo sabemos entonces c(al es el tamaSo del arra0 dentro de la .(nc%2n9
!n este caso lo +emos p(esto nosotros m%smos, 1'. Pero creo )(e lo me"or es (t%l%5ar
constantes como en el apartado Sobre la dimensin de n arra9.
He dicho que el array no se puede pasar a una funcin, que se debe usar un puntero.
Pero vemos que luego estamos usando m[i], esto lo podemos hacer porque como se
ha mencionado antes el uso de ndices en una forma que nos ofrece C de manejar
punteros con matrices. Ya se ha visto que m[i] es equivalente a *(m+i).
Otras declaraciones equivalentes seran:
int sumar( int m8: )
1
int sumar( int m85%: )
!n real%dad esta 3lt%ma no se s(ele (sar, no es necesar%o %nd%car el n3mero de elementos a la
matr%5.
int m[] e int *m son equivalentes.
STRINGS (CADENAS DE TEXTO)
Introducci)n
Vamos a ver por fin cmo manejar texto con C, hasta ahora slo sabamos cmo
mostrarlo por pantalla.
Para empezar dir que en C no existe un tipo string como en otros lenguajes. No
existe un tipo de datos para almacenar texto, se utilizan arrays de chars. Funcionan
igual que los dems arrays con la diferencia que ahora jugamos con letras en vez de
con nmeros.
6
Se les llama cadenas, strings o tiras de caracteres. A partir de ahora les llamaremos
cadenas.
Para declarar una cadena se hace como un array:
char te(to83%:#
Al igual que en los arrays no podemos meter ms de 20 elementos en la cadena.
Vamos a ver un ejemplo para mostrar el nombre del usuario en pantalla:
#include <stdio.h>
void main()
{
char nom?re83%:#
printf( OntroduUca su nom?re (3% letras m[(imo)M )#
scanf( @s4 nom?re )#
printf( "n$l nom?re que ha escrito esM @s"n4 nom?re )#
&
Comprobado con DJGPP
Vemos cosas curiosas como por ejemplo que en el scanf no se usa el smbolo <. No
hace falta porque es un array, y ya sabemos que escribir el nombre del array es
equivalente a poner &nombre[0].
Tambin puede llamar la atencin la forma de imprimir el array. Con slo usar %s ya
se imprime todo el array. Ya veremos esto ms adelante.
Si alguno viene de algn otro lenguaje esto es importante: en C no se puede hacer
esto:
#include <stdio.h>
#include <strings.h>
void main()
{
char te(to83%:#
0ex0) = IJ).+I2
&
Comprobado con DJGPP
2as cadenas por dentro
Es interesante saber cmo funciona una cadena por dentro, por eso vamos a ver
primero cmo se inicializa una cadena.
#include <stdio.h>
void main()
7'
{
char nom?re8: ; )orAa#
printf( Ve(toM @s"n4 nom?re )#
printf( Vama0o de la cadenaM @i ?2tes"n4 siUeof nom?re )#
&
Comprobado con DJGPP
Resultado al ejecutar:
Ve(toM )orAa
Vama0o de la cadenaM L ?2tes
Qu curioso! La cadena es "Gorka", sin embargo nos dice que ocupa 6 bytes. Como
cada elemento (char) ocupa un byte eso quiere decir que la cadena tiene 6 elementos.
Pero si "Gorka" slo tiene 5! Por qu? Muy sencillo, porque al final de una cadena se
pone un smbolo G\HG que significa "Fin de cadena". De esta forma cuando queremos
escribir la cadena basta con usar %s y el programa ya sabe cuntos elementos tiene
que imprimir, hasta que encuentre '\0'.
El programa anterior sera equivalente a:
#include <stdio.h>
void main(int argc4char *argv8:)
{
char nom?re8: ; { =)=4 =o=4 =r=4 =A=4 =a=4 ="%= &#
printf( Ve(toM @s"n4 nom?re )#
&
Comprobado con DJGPP
Aqu ya se ve que tenemos 6 elementos. Pero, Qu pasara si no pusiramos '\0' al
final?
#include <stdio.h>
void main()
{
char nom?re8: ; { =)=4 =o=4 =r=4 =A=4 =a= &#
printf( Ve(toM @s"n4 nom?re )#
&
Comprobado con DJGPP
En mi ordenador sala:
Ve(toM )orAa'
Vama0o de la cadenaM 9 ?2tes
Pero en el tuyo despus de "Gorka" puede aparecer cualquier cosa. Lo que aqu sucede
es que no encuentra el smbolo '\0' y no sabe cundo dejar de imprimir.
71
Afortunadamente, cuando metemos una cadena se hace de la primera forma y el C se
encarga de poner el dichoso smbolo al final.
Es importante no olvidar que la longitud de una cadena es la longitud del texto ms el
smbolo de fin de cadena. Por eso cuando definamos una cadena tenemos que
reservarle un espacio adicional. Por ejemplo:
char nom?re8L: ; )orAa#
Si olvidamos esto podemos tener problemas.
-unciones de manejo de cadenas
Existen unas cuantas funciones el la biblioteca estndar de C para el manejo de
cadenas:
strlen
strc#9
strcat
s#rint(
strcm#
Para usar estas funciones hay que aadir la directiva:
#include <strings.h>
Strlen
Esta funcin nos devuelve el nmero de caracteres que tiene la cadena (sin contar el
'\0').
#include <stdio.h>
#include <strings.h>
void main()
{
char te(to8:;)orAa#
int longitud#
longitud ; strlen(te(to)#
printf( ,a cadena "@s" tiene @i caracteres."n4 te(to4
longitud )#
&
Comprobado con DJGPP
Crea tus propias funciones: Vamos a ver cmo se hara esta funcin si no
dispusiramos de ella. Si no te enteras cmo funciona consulta 5ecorrer cadenas
con #nteros.
72
#include <stdio.h>
#include <strings.h>
void main()
{
char te(to8:;)orAa#
char *p#
int longitud;%#
p ; te(to#
\hile (*pK;="%=)
{
longitudPP#
printf( @c"n4 *p )# /* Hostramos la letra actual */
pPP# /* Wamos a la siguiente letra */
&
printf( ,a cadena "@s" tiene @i caracteres."n4 te(to4
longitud )#
&
Comprobado con DJGPP
Para medir la longitud de la cadena usamos un puntero para recorrerla (el puntero p).
Hacemos que p apunte a texto. Luego entramos en un bucle while. La condicin del
bucle comprueba si se ha llegado al fin de cadena ('\0'). Si no es as suma 1 a
lon$itud, muestra la letra por pantalla e incrementa el puntero en 1 (con esto pasamos
a la siguiente letra).
Strc#9
#include <string.h>
char *strcp2(char *cadena54 const char *cadena3)#
Copia el contenido de cadena9 en cadena). cadena9 puede ser una variable o una
cadena directa (por ejemplo "hola"). Debemos tener cuidado de que la cadena destino
(cadena1) tenga espacio suficiente para albergar a la cadena origen (cadena2).
#include <stdio.h>
#include <strings.h>
void main()
{
char te(tocurso8: ; $ste es un curso de ..#
char destino89%:#
strcp2( destino4 te(tocurso )#
printf( Walor finalM @s"n4 destino )#
&
Comprobado con DJGPP
73
Vamos a ver otro ejemplo en el que la cadena destino es una cadena constante ("Este
es un curso de C") y no una variable. Adems en este ejemplo vemos que la cadena
origen es sustituida por la cadena destino totalmete. Si la cadena origen es ms larga
que la destino, se eliminan las letras adicionales.
#include <stdio.h>
#include <strings.h>
void main()
{
char destino89%: ; $sto no es un curso de !VH, sino un curso de
..#
printf( @s"n4 destino )#
strcp2( destino4 $ste es un curso de .. )#
printf( @s"n4 destino )#
&
Comprobado con DJGPP
Strcat
#include <string.h>
char *strcat(char *cadena54 const char *cadena3)#
Copia la cadena9 al final de la cadena).
#include <stdio.h>
#include <strings.h>
void main()
{
char nom?reEcompleto89%:#
char nom?re8:;)orAa#
char apellido8:;Brrutia#
strcp2( nom?reEcompleto4 nom?re )#
strcat( nom?reEcompleto4 )#
strcat( nom?reEcompleto4 apellido )#
printf( $l nom?re completo esM @s."n4 nom?reEcompleto )#
&
Comprobado con DJGPP
Como siempre tenemos que asegurarnos que la variable en la que metemos las dems
cadenas tenga el tamao suficiente. Con la primera lnea metemos el nombre en
nom're.completo. Usamos strcpy para asegurarnos de que queda borrado cualquier
dato anterior. Luego usamos un strcat para aadir un espacio y finalmente metemos el
apellido.
S#rint(
74
#include <stdio.h>
int sprintf(char *destino4 const char *format4 ...)#
Funciona de manera similar a printf, pero en vez de mostrar el texto en la pantalla lo
guarda en una variable (destino). El valor que devuelve (int) es el nmero de
caracteres guardados en la variable destino.
Con sprintf podemos repetir el ejemplo de strcat de manera ms sencilla:
#include <stdio.h>
#include <strings.h>
void main()
{
char nom?reEcompleto89%:#
char nom?re8:;)orAa#
char apellido8:;Brrutia#
sprintf( nom?reEcompleto4 @s @s4 nom?re4 apellido )#
printf( $l nom?re completo esM @s."n4 nom?reEcompleto )#
&
Comprobado con DJGPP
Se puede aplicar a sprintf todo lo que vala para printf.
Strcm#
#include <string.h>
int strcmp(const char *cadena54 const char *cadena3)#
Compara cadena) y cadena9. Si son iguales devuelve 0. Un nmero negativo si
cadena) va antes que cadena9 y un nmero positivo si es al revs:
cadena1 == cadena2 -> 0
cadena1 > cadena2 -> nmero negativo
cadena1 < cadena2 -> nmero positivo
#include <stdio.h>
#include <strings.h>
void main()
{
char nom?re58:;)orAa#
char nom?re38:;-edro#
printf( @i4 strcmp(nom?re54nom?re3))#
&
75
Comprobado con DJGPP
Entrada de cadenas por teclado #scanf " gets'
scan(
/ets
,- son los b((er 9 cmo (ncionan
/etchar
scan(
Hemos visto en captulos anteriores el uso de scanf para nmeros, ahora es el
momento de ver su uso con cadenas.
Scanf almacena en memoria (en un buffer) lo que vamos escribiendo. Cuando
pulsamos ENTER (o Intro o Return, como se llame en cada teclado) lo analiza,
comprueba si el formato es correcto y por ltimo lo mete en la variable que le
indicamos.
#include <stdio.h>
#include <strings.h>
void main()
{
char cadena8T%:#
printf( $scri?e una pala?raM )#
fflush( stdout )#
scanf( @s4 cadena )#
printf( !e guardadoM "@s" "n4 cadena )#
&
Comprobado con DJGPP
Ejecutamos el programa e introducimos la palabra "hola". Esto es lo que tenemos:
$scri?e una pala?raM hola
!e guardadoM hola
Si ahora introducimos "hola amigos" esto es lo que tenemos:
$scri?e una pala?raM hola amigos
!e guardadoM hola
Slo nos ha cogido la palabra "hola" y se ha olvidado de amigos. Por qu? pues
porque scanf toma una palabra como cadena. Usa los espacios para separar variables.
Es importante siempre asegurarse de que no vamos a almacenar en cadena ms letras
de las que caben. Para ello debemos limitar el nmero de letras que le va a introducir
scanf. Si por ejemplo queremos un mximo de 5 caracteres usaremos %5s:
#include <stdio.h>
76
#include <strings.h>
void main()
{
char cadena8L:#
printf( $scri?e una pala?raM )#
fflush( stdout )#
scanf( @9s4 cadena )#
printf( !e guardadoM "@s" "n4 cadena )#
&
Comprobado con DJGPP
Si metemos una palabra de 5 letras (no se cuenta '\0') o menos la recoge sin
problemas y la guarda en cadena.
$scri?e una pala?raM )orAa
!e guardadoM )orAa
Si metemos ms de 5 letras nos cortar la palabra y nos dejar slo 5.
$scri?e una pala?raM >uanCo
!e guardadoM >uanC
Scanf tiene ms posibilidades (consulta la ayuda de tu compilador), entre otras
permite controlar qu caracteres entramos. Supongamos que slo queremos coger las
letras maysculas:
#include <stdio.h>
#include <strings.h>
void main()
{
char cadena8T%:#
printf( $scri?e una pala?raM )#
fflush( stdout )#
scanf( @EAKLFs4 cadena )#
printf( !e guardadoM "@s" "n4 cadena )#
&
Comprobado con DJGPP
Guarda las letras maysculas en la variable hasta que encuentra una minscula:
$scri?e una pala?raM !ola
!e guardadoM !
$scri?e una pala?raM !7,A
!e guardadoM !7,A
$scri?e una pala?raM AHig7/
!e guardadoM AH
/ets
77
Esta funcin nos permite introducir frases enteras, incluyendo espacios.
#include <stdio.h>
char *gets(char *?uffer)#
Almacena lo que vamos tecleando en la variable 'uffer hasta que pulsamos ENTER. Si
se ha almacenado algn caracter en 'uffer le aade un '\0' al final y devuelve un
puntero a su direccin. Si no se ha almacenado ninguno devuelve un puntero NULL.
#include <stdio.h>
#include <strings.h>
void main()
{
char cadena8T%:#
char *p#
printf( $scri?e una pala?raM )#
fflush( stdout )#
p ; gets( cadena )#
if (p) printf( !e guardadoM "@s" "n4 cadena )#
else printf( Go he guardado nadaK"n )#
&
Comprobado con DJGPP
Esta funcin es un poco peligrosa porque no comprueba si nos hemos pasado del
espacio reservado (de 29 caracteres en este ejemplo: 29letras+'\0').
,- son los b((er 9 cmo (ncionan?
#include <stdio.h>
void main()
{
char ch#
char nom?re83%:4 apellido83%:4 telefono85%:#
printf( $scri?e tu nom?reM )#
scanf( @8A'`:s4 nom?re )#
printf( ,o que recogemos del scanf esM @s"n4 nom?re )#
printf( ,o que ha?a quedado en el ?ufferM )#
\hile( (ch;getchar())K;="n= )
printf( @c4 ch )#
&
Comprobado con DJGPP
$scri?e tu nom?reM )7*Aa
,o que recogemos del scanf esM )7*
,o que ha?a quedado en el ?ufferM Aa
Cuidado con scanf!!!
#include <stdio.h>
void main()
{
78
char nom?re83%:4 apellido83%:4 telefono85%:#
printf( $scri?e tu nom?reM )#
scanf( @s4 nom?re )#
printf( $scri?e tu apellidoM )#
gets( apellido )#
&
Comprobado con DJGPP
/etchar
1ecorrer cadenas con punteros
Las cadenas se pueden recorrer de igual forma que hacamos con las matrices, usando
punteros.
Vamos a ver un ejemplo: Este sencillo programa cuenta los espacios y las letras 'e' que
hay en una cadena.
#include <stdio.h>
#include <strings.h>
void main()
{
char cadena8:;)orAa es un tipo estupendo#
char *p#
int espacios;%4 letrasEe;%#
p ; cadena#
\hile (*pK;="%=)
{
if (*p;;= =) espaciosPP#
if (*p;;=e=) letrasEePP#
pPP#
&
printf( $n la cadena "@s" ha2M"n4 cadena )#
printf( @i espacios"n4 espacios )#
printf( @i letras e"n4 letrasEe )#
&
Comprobado con DJGPP
Para recorrer la cadena necesitamos un puntero p que sea de tipo char. Debemos
hacer que p apunte a la cadena (p=cadena). As p apunta a la direccin del primer
elemento de la cadena. El valor de *p sera por tanto 'G'. Comenzamos el bucle. La
condicin comprueba que no se ha llegado al final de la cadena (*p!='\0'), recordemos
que '\0' es quien marca el final de sta. Entonces comprobamos si en la direccin a la
que apunta p hay un espacio o una letra e. Si es as incrementamos las variables
correspondientes. Una vez comprobado esto pasamos a la siguiente letra (p88).
Dos cosas muy importantes: primero no debemos olvidarnos nunca de inicializar un
puntero, en este caso hacer que apunte a cadena. Segundo no debemos olvidarnos de
incrementar el puntero dentro del bucle (p++), sino estaramos en un bucle infinito
siempre comprobando el primer elemento.
7
En la condicin del bucle podamos usar smplemente: while (!*p), que es equivalente
a (*p!='\0').
En este otro ejemplo sustitumos los espacios por guiones:
#include <stdio.h>
#include <strings.h>
void main()
{
char cadena8:;)orAa es un tipo estupendo#
char *p#
p ; cadena#
\hile (*pK;="%=)
{
if (*p;;= =) *p ; ='=#
pPP#
&
printf( ,a cadena quedaM "@s" "n4 cadena )#
&
Comprobado con DJGPP
y se obtiene:
,a cadena quedaM )orAa'es'un'tipo'estupendo
3rra"s de cadenas
Un array de cadenas puede servirnos para agrupar una serie de mensajes. Por ejemplo
todos los mensajes de error de un programa. Luego para acceder a cada mensaje
basta con usar su nmero.
#include <stdio.h>
#include <strings.h>
int error( int errnum )
{
char *errores8: ; {
Go se ha producido ningNn error4
Go ha2 suficiente memoria4
Go ha2 espacio en disco4
He he cansado de tra?aCar
&#
printf( $rror nNmero @iM @s."n4 errnum4 errores8errnum: )#
e(it( '5 )#
&
void main()
{
error( 3 )#
&
Comprobado con DJGPP
8'
El resultado ser:
$rror nNmero 3M Go ha2 espacio en disco.
Un array de cadenas es en realidad un array de punteros a cadenas. El primer
elemento de la cadena ("No se ha producido ningn error") tiene un espacio reservado
en memoria y errores:*; apunta a ese espacio.
%rdenar un arra" de cadenas
Vamos a ver un sencillo ejemplo de ordenacin de cadenas. En el ejemplo tenemos
que ordenar una serie de dichos populares:
#include <stdio.h>
#include <strings.h>
#define $,$H$GV7/ 9
void main()
{
char *dichos8$,$H$GV7/: ; {
,a avaricia rompe el saco4
H[s Wale p[Caro en mano que ciento volando4
Go por mucho madrugar amanece m[s temprano4
A0o de nieves4 a0o de ?ienes4
A ca?allo regalado no le mires el diente
&#
char *temp#
int i4 C#
printf( ,ista desordenadaM"n )#
for( i;%# i<$,$H$GV7/# iPP )
printf( @s."n4 dichos8i: )#
for( i;%# i<$,$H$GV7/'5# iPP )
for( C;%# C<$,$H$GV7/# CPP )
if (strcmp(dichos8i:4 dichos8C:)>%)
{
temp ; dichos8i:#
dichos8i: ; dichos8C:#
dichos8C: ; temp#
&
printf( ,ista ordenadaM"n )#
for( i;%# i<$,$H$GV7/# iPP )
printf( @s."n4 dichos8i: )#
&
Comprobado con DJGPP
Cmo funciona el programa:
1.- Tomamos el primer elemento de la matriz. Lo comparamos con todos los
siguientes. Si algunos es anterior los intercambiamos. Cuando acabe esta primera
vuelta tendremos "A caballo regalado no le mires el diente" en primera posicin.
81
2.- Tomamos el segundo elemento. Lo comparamos con el tercero y siguientes. Si
alguno es anterior los intercambiamos. Al final de esta vuelta quedar "A caballo
regalado no le mires el diente" en segunda posicin.
FUNCIONES (AVANZADO)
Pasar argumentos a un programa
Ya sabemos cmo pasar argumentos a una funcin. La funcin main tambin acepta
argumentos. Sin embargo slo se le pueden pasar dos argumentos. Veamos cules son
y cmo se declaran:
void main( int argc4 char *argv8: )
El primer argumento es ar/c (ar/ument count). Es de tipo int e indica el nmero de
argumentos que se le han pasado al programa.
El segundo es ar/$ (ar/ument $alues). Es un array de strings (o puntero a puntero a
char). En el se almacenan los parmetros. Normalmente (como siempre depende del
compilador) el primer elemento (argv[0]) es el nombre del programa con su ruta. El
segundo (argv[1]) es el primer parmetro, el tercero (argv[2]) el segundo parmetro y
as hasta el final.
A los argumentos de main se les suele llamar siempre as, no es necesario pero es
costumbre.
Veamos un ejemplo para mostrar todos los parmetros de un programa:
#include<stdio.h>
void main(int argc4char *argv8:)
{
int i#
for( i;% # i<argc # iPP )
printf( Argumento @iM @s"n4 i4 argv8i: )#
&
Comprobado con DJGPP
Si por ejemplo llamamos al programa argumentos.c y lo compilamos
(argumentos.e(e) podramos teclear (lo que est en negrita es lo que tecleamos):
cM"programas> +,D:<en0)' 6).+ +<iD)'
Tendramos como salida:
Argumento %M cM"programas"argumentos.e(e
Argumento 5M hola
Argumento 3M amigos
Pero si en vez de eso tecleamos:
82
cM"programas> +,D:<en0)' I6).+ +<iD)'I
Lo que tendremos ser:
Argumento %M cM"programas"argumentos.e(e
Argumento 5M hola amigos
Ejemplos en+iados
#include <stdio.h>
void main(int argc4char *argv8:)
{
int par54 par3#
par5; *argv85: ' =%=#
par3; *argv83: ' =%=#
printf(@d P @d ; @d"n4par54par34par5Ppar3)#
&
Comprobado con DJGPP
Este programa espera dos argumentos. Ambos deben ser nmeros del 0 al 9. El
programa los coge y los suma.
ESTRUCTURAS
Estructuras
Supongamos que queremos hacer una agenda con los nmeros de telfono de
nuestros amigos. Necesitaramos un array de Cadenas para almacenar sus nombres,
otro para sus apellidos y otro para sus nmeros de telfono. Esto puede hacer que el
programa quede desordenado y difcil de seguir. Y aqu es donde vienen en nuestro
auxilio las estructuras.
Para definir una estructura usamos el siguiente formato:
'0,:70 nom?reEdeElaEestructura 3
campos de estructura;
42
NOTA: Es importante no olvidar el ';' del final, si no a veces se obtienen errores
extraos.
Para nuestro ejemplo podemos crear una estructura en la que almacenaremos los
datos de cada persona. Vamos a crear una declaracin de estructura llamada ami$o:
struct estructuraEamigo {
char nom?re8T%:#
83
char apellido8Q%:#
char telefono85%:#
char edad#
&#
A cada elemento de esta estructura (nombre, apellido, telfono) se le llama campo o
miembro. (NOTA: He declarado edad como char porque no conozco a nadie con ms
de 127 aos.
Ahora ya tenemos definida la estructura, pero aun no podemos usarla. Necesitamos
declarar una variable con esa estructura.
struct estructuraEamigo amigo#
Ahora la variable ami$o es de tipo estructura.ami$o. Para acceder al nombre de ami$o
usamos: ami/o+nombre. Vamos a ver un ejemplo de aplicacin de esta estructura.
(NOTA: En el siguiente ejemplo los datos no se guardan en disco as que cuanda acaba
la ejecucin del programa se pierden).
#include <stdio.h>
struct estructuraEamigo { /* 6efinimos la estructura
estructura_amigo */
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
char edad#
&#
struct estructuraEamigo amigo#
void main()
{
printf( $scri?e el nom?re del amigoM )#
fflush( stdout )#
scanf( @s4 Xamigo.nom?re )#
printf( $scri?e el apellido del amigoM )#
fflush( stdout )#
scanf( @s4 Xamigo.apellido )#
printf( $scri?e el nNmero de tel^fono del amigoM )#
fflush( stdout )#
scanf( @s4 Xamigo.telefono )#
printf( $l amigo @s @s tiene el nNmeroM @s."n4 amigo.nom?re4
amigo.apellido4 amigo.telefono )#
&
Comprobado con DJGPP
Este ejemplo estara mejor usando gets que scanf, ya que puede haber nombres
compuestos que scanf no cogera por los espacios.
Se podra haber declarado directamente la variable ami$o:
struct estructuraEamigo {
char nom?re8T%:#
84
char apellido8Q%:#
char telefono85%:#
& amigo#
3rra"s de estructuras
Supongamos ahora que queremos guardar la informacin de varios amigos. Con una
variable de estructura slo podemos guardar los datos de uno. Para manejar los datos
de ms gente (al conjunto de todos los datos de cada persona se les llama REGISTRO)
necesitamos declarar arrays de estructuras.
Cmo se hace esto? Siguiendo nuestro ejemplo vamos a crear un array de
E<E=E>?@S elementos:
struct estructuraEamigo amigo8$,$H$GV7/:#
Ahora necesitamos saber cmo acceder a cada elemento del array. La variable definida
es ami$o, por lo tanto para acceder al primer elemento usaremos ami$o:*; y a su
miembro nom're: ami$o:*;.nom're. Veamoslo en un ejemplo en el que se supone que
tenemos que meter los datos de tres amigos:
#include <stdio.h>
#define $,$H$GV7/ T
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
int edad#
&#
struct estructuraEamigo amigo8$,$H$GV7/:#
void main()
{
int numEamigo#
for( numEamigo;%# numEamigo<$,$H$GV7/# numEamigoPP )
{
printf( "n6atos del amigo nNmero @iM"n4 numEamigoP5 )#
printf( Gom?reM )# fflush( stdout )#
gets(amigo8numEamigo:.nom?re)#
printf( ApellidoM )# fflush( stdout )#
gets(amigo8numEamigo:.apellido)#
printf( Vel^fonoM )# fflush( stdout )#
gets(amigo8numEamigo:.telefono)#
printf( $dadM )# fflush( stdout )#
scanf( @i4 Xamigo8numEamigo:.edad )#
\hile(getchar()K;="n=)#
&
/* Ahora imprimimos sus datos */
for( numEamigo;%# numEamigo<$,$H$GV7/# numEamigoPP )
{
85
printf( $l amigo @s 4 amigo8numEamigo:.nom?re )#
printf( @s tiene 4 amigo8numEamigo:.apellido )#
printf( @i a0os 4 amigo8numEamigo:.edad )#
printf( 2 su tel^fono es el @s."n 4 amigo8numEamigo:.telefono
)#
&
&
Comprobado con DJGPP
IMPORTANTE: Quizs alguien se pregunte qu pinta la lnea esa de &hileA$etcharABC
0D\nDB/. Esta lnea se usa para vaciar el buffer de entrada. Para ms informacin
consulta ,- son los b((er 9 cmo (ncionan.
Iniciali4ar una estructura
A las estructuras se les pueden dar valores iniciales de manera anloga a como
hacamos con los arrays. Primero tenemos que definir la estructura y luego cuando
declaramos una variable como estructura le damos el valor inicial que queramos.
Recordemos que esto no es en absoluto necesario. Para la estructura que hemos
definido antes sera por ejemplo:
struct estructuraEamigo amigo ; {
>uanCo4
,opeU4
9D3'%QRT4
T%
&#
NOTA: En algunos compiladores es posible que se exiga poner antes de struct la palabra static.
Por supuesto hemos de meter en cada campo el tipo de datos correcto. La definicin
de la estructura es:
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
int edad#
&#
por lo tanto el nombre ("Juanjo") debe ser una cadena de no ms de 29 letras
(recordemos que hay que reservar un espacio para el smbolo '\0'), el apellido
("Lopez") una cadena de menos de 39, el telfono una de 9 y la edad debe ser de tipo
char.
Vamos a ver la inicializacin de estructuras en accin:
#include <stdio.h>
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
86
char telefono85%:#
int edad#
&#
struct estructuraEamigo amigo ; {
>uanCo4
,opeU4
9D3'%QRT4
T%
&#
int main()
{
printf( @s tiene 4 amigo.apellido )#
printf( @i a0os 4 amigo.edad )#
printf( 2 su tel^fono es el @s."n 4 amigo.telefono )#
&
Comprobado con DJGPP
Tambin se puede inicializar un array de estructuras de la forma siguiente:
struct estructuraEamigo amigo8: ;
{
>uanCo4 ,opeU4 9%Q'QTQ34 T%4
Harcos4 )amindeU4 Q%9'QR3T4 Q34
Ana4 HartineU4 9TT'9LDQ4 3%
&#
En este ejemplo cada lnea es un registro. Como suceda en los arrays si damos valores
iniciales al array de estructuras no hace falta indicar cuntos elementos va a tener. En
este caso la matriz tiene 3 elementos, que son los que le hemos pasado.
Punteros a estructuras
Cmo no, tambin se pueden usar punteros con estructuras. Vamos a ver como
funciona esto de los punteros con estructuras. Primero de todo hay que definir la
estructura de igual forma que hacamos antes. La diferencia est en que al declara la
variable de tipo estructura debemos ponerle el operador '7' para indicarle que es un
puntero.
Creo que es importante recordar que un puntero no debe apuntar a un lugar
cualquiera, debemos darle una direccin vlida donde apuntar. No podemos por
ejemplo crear un puntero a estructura y meter los datos directamente mediante ese
puntero, no sabemos dnde apunta el puntero y los datos se almacenaran en un lugar
cualquiera.
Y para comprender cmo funcionan nada mejor que un ejemplo. Este programa utiliza
un puntero para acceder a la informacin de la estructura:
#include <stdio.h>
struct estructuraEamigo {
87
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
int edad#
&#
struct estructuraEamigo amigo ; {
>uanCo4
,opeU4
9D3'%QRT4
T%
&#
struct estructuraEamigo *pEamigo#
int main()
{
pEamigo ; Xamigo#
printf( @s tiene 4 pEamigo'>apellido )#
printf( @i a0os 4 pEamigo'>edad )#
printf( 2 su tel^fono es el @s."n 4 pEamigo'>telefono )#
&
Comprobado con DJGPP
Has la definicin del puntero p.ami$o vemos que todo era igual que antes. p.ami$o es
un puntero a la estructura estructura.ami$o. Dado que es un puntero tenemos que
indicarle dnde debe apuntar, en este caso vamos a hacer que apunte a la variable
ami$o:
pEamigo ; #amigo#
No debemos olvidar el operador < que significa 'dame la direccin donde est
almacenado...'.
Ahora queremos acceder a cada campo de la estructura. Antes lo hacamos usando el
operador '.', pero, como muestra el ejemplo, si se trabaja con punteros se debe usar el
operador '4I'. Este operador viene a significar algo as como: "dame acceso al
miembro ... del puntero ...".
Ya slo nos queda saber cmo podemos utilizar los punteros para introducir datos en
las estructuras. Lo vamos a ver un ejemplo:
#include <stdio.h>
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
int edad#
&#
struct estructuraEamigo amigo4 *pEamigo#
void main()
{
pEamigo ; Xamigo#
88
/* Ontroducimos los datos mediante punteros */
printf(Gom?reM )#fflush(stdout)#
gets(pEamigo'>nom?re)#
printf(ApellidoM )#fflush(stdout)#
gets(pEamigo'>apellido)#
printf($dadM )#fflush(stdout)#
scanf( @i4 XpEamigo'>edad )#
/* Hostramos los datos */
printf( $l amigo @s 4 pEamigo'>nom?re )#
printf( @s tiene 4 pEamigo'>apellido )#
printf( @i a0os."n4 pEamigo'>edad )#
&
Comprobado con DJGPP
NOTA: p.ami$o es un puntero que apunta a la estructura ami$o. Sin embargo
p.ami$oE#edad es una variable de tipo int. Por eso al usar el scanf tenemos que poner
el &.
Punteros a arra"s de estructuras
Por supuesto tambin podemos usar punteros con arrays de estructuras. La forma de
trabajar es la misma, lo nico que tenemos que hacer es asegurarnos que el puntero
inicialmente apunte al primer elemento, luego saltar al siguiente hasta llegar al ltimo.
#include <stdio.h>
#define $,$H$GV7/ T
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
int edad#
&#
struct estructuraEamigo amigo8: ;
{
>uanCo4 ,opeU4 9%Q'QTQ34 T%4
Harcos4 )amindeU4 Q%9'QR3T4 Q34
Ana4 HartineU4 9TT'9LDQ4 3%
&#
struct estructuraEamigo *pEamigo#
void main()
{
int numEamigo#
pEamigo ; amigo# /* apuntamos al primer elemento del arra2 */
/* Ahora imprimimos sus datos */
for( numEamigo;%# numEamigo<$,$H$GV7/# numEamigoPP )
{
8
printf( $l amigo @s 4 pEamigo'>nom?re )#
printf( @s tiene 4 pEamigo'>apellido )#
printf( @i a0os 4 pEamigo'>edad )#
printf( 2 su tel^fono es el @s."n 4 pEamigo'>telefono )#
/* 2 ahora saltamos al siguiente elemento */
pEamigoPP#
&
&
Comprobado con DJGPP
En vez de p.ami$o 0 ami$o/ se poda usar la forma p.ami$o 0 3ami$o:*;/, es decir
que apunte al primer elemento (el elemento 0) del array. La primera forma creo que
es ms usada pero la segunda quizs indica ms claramente al lector principiante lo
que se pretende.
Ahora veamos el ejemplo anterior de cmo introducir datos en un array de estructuras
mediante punteros:
#include <stdio.h>
#define $,$H$GV7/ T
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
int edad#
&#
struct estructuraEamigo amigo8$,$H$GV7/:4 *pEamigo#
void main()
{
int numEamigo#
/* apuntamos al primer elemento */
pEamigo ; amigo#
/* Ontroducimos los datos mediante punteros */
for ( numEamigo;%# numEamigo<$,$H$GV7/# numEamigoPP )
{
printf(6atos amigo @i"n4numEamigo)#
printf(Gom?reM )#fflush(stdout)#
gets(pEamigo'>nom?re)#
printf(ApellidoM )#fflush(stdout)#
gets(pEamigo'>apellido)#
printf($dadM )#fflush(stdout)#
scanf( @i4 XpEamigo'>edad )#
/* vaciamos el ?uffer de entrada */
\hile(getchar()K;="n=)#
/* saltamos al siguiente elemento */
pEamigoPP#
&
/* Ahora imprimimos sus datos */
=_+<iD) = +<iD)2
for( numEamigo;%# numEamigo<$,$H$GV7/# numEamigoPP )
{
printf( $l amigo @s 4 pEamigo'>nom?re )#
'
printf( @s tiene 4 pEamigo'>apellido )#
printf( @i a0os."n4 pEamigo'>edad )#
pEamigoPP#
&
&
Comprobado con DJGPP
Es importante no olvidar que al terminar el primer bucle for el puntero p_amigo apunta
al ltimo elemento del array de estructuras. Para mostrar los datos tenemos que hacer
que vuelva a apuntar al primer elemento y por eso usamos de nuevo p_amigo=amigo;
(en negrita).
Paso de estructuras a funciones
Las estructuras se pueden pasar directamente a una funcin igual que hacamos con
las variables. Por supuesto en la definicin de la funcin debemos indicar el tipo de
argumento que usamos:
int nom?reEfunci1n ( struct nombre_de_la_estructura nombre_de_la
variable_estructura )
En el ejemplo siguiente se usa una funcin llamada suma que calcula cual ser la edad
20 aos ms tarde (simplemente suma 20 a la edad). Esta funcin toma como
argumento la variable estructura ar$.ami$o. Cuando se ejecuta el programa llamamos
a suma desde main y en esta variable se copia el contenido de la variable ami$o.
Esta funcin devuelve un valor entero (porque est declarada como int) y el valor que
devuelve (mediante return) es la suma.
#include <stdio.h>
struct estructuraEamigo {
char nom?re8T%:#
char apellido8Q%:#
char telefono85%:#
int edad#
&#
struct estructuraEamigo amigo ; {
>uanCo4
,opeU4
9D3'%QRT4
T%
&#
int suma( struct estructuraEamigo argEamigo )
{
return argEamigo.edadP3%#
&
int main()
{
printf( @s tiene 4 amigo.apellido )#
printf( @i a0os 4 amigo.edad )#
1
printf( 2 dentro de 3% a0os tendr[ @i."n4 suma(amigo) )#
&
Comprobado con DJGPP
Si dentro de la funcin suma hubisemos cambiado algn valor de la estructura, dado
que es una copia no hubiera afectado a la variable ami$o de main. Es decir, si dentro
de 'suma' hacemos arg_amigo.edad = 20; el valor de arg_amigo cambiar, pero el de
ami$o seguir siendo 30.
Tambin se pueden pasar estructuras mediante punteros o se puede pasar
simplemente un miembro (o campo) de la estructura.
Si usamos punteros para pasar estructuras como argumentos habr que hacer unos
cambios al cdigo anterior (en negrita y subrrayado):
int suma( struct estructuraEamigo *argEamigo )
{
return argEamigoK&edadP3%#
&
int main()
{
printf( @s tiene 4 amigo.apellido )#
printf( @i a0os 4 amigo.edad )#
printf( 2 dentro de 3% a0os tendr[ @i."n4 suma(#amigo) )#
&
Comprobado con DJGPP
Lo primero ser indicar a la funcin suma que lo que va a recibir es un puntero, para
eso ponemos el * (asterisco). Segundo, como dentro de la funcin suma usamos un
puntero a estructura y no una variable estructura debemos cambiar el '.' (punto) por el
'->'. Tercero, dentro de main cuando llamamos a suma debemos pasar la direccin de
ami$o, no su valor, por lo tanto debemos poner '&' delante de ami$o.
Si usamos punteros a estructuras corremos el riesgo (o tenemos la ventaja, segn
cmo se mire) de poder cambiar los datos de la estructura de la variable ami$o de
main.
Pasar slo miembros de la estrctra
Otra posibilidad es no pasar toda la estructura a la funcin sino tan slo los miembros
que sean necesarios. Los ejemplos anteriores seran ms correctos usando esta tercera
opcin, ya que slo usamos el miembro 'edad':
int suma( char edad )
{
return edadP3%#
&
int main()
{
printf( @s tiene 4 amigo.apellido )#
printf( @i a0os 4 amigo.edad )#
2
printf( 2 dentro de 3% a0os tendr[ @i."n4 suma(amigo.edad) )#
&
Comprobado con DJGPP
Por supuesto a la funcin suma hay que indicarle que va a recibir una variable tipo
char (amigo->edad es de tipo char).
Estructuras dentro de estructuras #3nidadas'
Es posible crear estructuras que tengan como miembros otras estructuras. Esto tiene
diversas utilidades, por ejemplo tener la estructura de datos ms ordenada.
Imaginemos la siguiente situacin: una tienda de msica quiere hacer un programa
para el inventario de los discos, cintas y cd's que tienen. Para cada ttulo quiere
conocer las existencias en cada soporte (cinta, disco, cd), y los datos del proveedor (el
que le vende ese disco). Podra pensar en una estructura as:
struct inventario {
char titulo8T%:#
char autor8Q%:#
int e(istenciasEdiscos#
int e(istenciasEcintas#
int e(istenciasEcd#
char nom?reEproveedor8Q%:#
char telefonoEproveedor85%:#
char direccionEproveedor85%%:#
&#
Sin embargo utilizando estructuras anidadas se podra hacer de esta otra forma ms
ordenada:
struct estrucEe(istencias {
int discos#
int cintas#
int cd#
&#
struct estrucEproveedor {
char proveedor8Q%:#
char proveedor85%:#
char proveedor85%%:#
&#
struct estrucEinventario {
char titulo8T%:#
char autor8Q%:#
struct estrucEe(istencias e(istencias#
struct estrucEproveedor proveedor#
& inventario#
Ahora para acceder al nmero de cd de cierto ttulo usaramos lo siguiente:
inventario.e(istencias.cd
3
y para acceder al nombre del proveedor:
inventario.proveedor.nom?re
FUNCIONES MATEMTICAS
&ntrodccin
Las funciones matemticas se encuentran en <math.h>, algunas estn en <stdlib.h>,
ya se indicar en cada una. Con ellas podemos calcular races, exponentes, potencias,
senos, cosenos, redondeos, valores absolutos y logaritmos.
Tenemos que fijarnos en el tipo de dato que hay que pasar y en el que devuelven las
funciones, si es int o double.
En algunas funciones se necesitan conocimientos sobre estructuras. As que sera
mejor mirar el captulo correspondiente antes, aunque har una pequea explicacin.
Anciones matemJticas
Aqui va la lista de las funciones ordenadas por categoras.
Eri/onom-tricas
acos
Devuelve el arcoseno de un nmero.
#include <math.h>
dou?le acos(dou?le ()#
PotenciasK ra0cesK eL#onentes 9 lo/aritmos
s?rt
Devuelve la raiz cuadrada de un nmero.
#include <math.h>
dou?le sqrt(dou?le ()#
Calor absolto 9 redondeo
abs
Devuelve el valor absoluto de un nmero entero. Si el nmero es positivo devuelve el
nmero tal cual, si es negativo devuelve el nmero cambiado de signo.
#include <stdli?.h>
int a?s( int valor )#
!"emplo de (so&
#include <stdio.h>
4
void main()
{
int a ; 'T%#
int ?#
? ; a?s( '99 )#
printf( Walores a ; @i4 a?s(a) ; @i4 ? ; @i"n4 a4 a?s( a )4 ? )#
&
/o +ace .alta poner la d%rect%#a #include$stdli%&'(.
Como podemos apreciar slo vale para nmeros enteros. Si queremos usar nmeros
en coma flotante (o con decimales) debemos usar (abs. Tambin debemos tener
cuidado y usar esta ltima si nuestros nmeros int slo llegan hasta 35000.
INSTALACIN Y USO DE DJGPP
C)mo instalar $5PP #Instalaci)n b.sica'
Suponiendo que ya te has bajado todos los ficheros necesarios ahora vamos a
proceder a la instalacin.
1+4 Lo primero que se debe hacer es crear el directorio (o carpeta) donde instalaremos
DJGPP. Podemos instalar DJGPP en cualquier unidad de nuestro disco duro. Vamos a
suponer que lo instalamos en C:\DJGPP (quiz sea lo mejor para no complicarse).
&m#ortante: No lo instales en ninguna carpeta llamada DEV, ni c:\dev, ni d:\dev, en
ninguna unidad.
Si tienes la versin 1.x instalada, es mejor borrarla entera (mezclar programas v1.x
con v2 puede dar muchos problemas). El nico programa v1.x que se debera
conservar es el /o"!+eLe.
!+4 Copia los ficheros que te has bajado al directorio (o carpeta) de instalacin. En
nuestro caso C:\DJGPP.
"+4 Ahora tienes que descomprimir los ficheros que te has bajado. Para descomprimir
puedes usar el winzip (si tienes win95/98/2000), puedes usar el programa pkunzip, o
si no usar el unzip386 que viene con el djgpp.
Si usas winzip marca la casilla 'use folder name' para que conserve la estructura de
directorios. Descomprime los zip en el directorio que has creado para la instalacin
(por ejemplo C.\DJGPP).
Si usas pkunzip o unzip386 debes entrar en el directorio de instalacin. Ahora
descomprime los ficheros zip en ese directorio, conservando la estructura de directorio.
Por ejemplo para el fichero djdev203.zip:
pAunUip 'd dCdev3%T
oM
unUipTRL dCdev3%T
5
Los ficheros comprimidos ya no son necesarios, han cumplido su misin, si quieres
ahorrar espacio puedes borrarlos. Recuerda siempre guardar una copia en disquetes o
cdrom, nunca se sabe si tendrs que volver a instalar.
M+4 Ahora tenemos que hacer cambios en el fichero AUTOEXEC.BAT. Este fichero se
encuentra en C:\. Edtalo y aade las lneas siguientes (esto vale si lo instalas en
c:\djgpp, si no tienes que poner el directorio que has elegido):
set 6>)--;.M"6>)--"6>)--.$GW
set -AV!;.M"6>)--"IOG#@-AV!@
N+4 Ahora deberas reiniciar el ordenador para que se activen los cambios hechos en el
autoexec.bat (es lo mejor) o teclear estas dos instrucciones en la lnea de comandos
en una sesin DOS (si eres impaciente).
O+4 Una vez reiniciado ejecuta el programa /o"!4$!+eLe sin argumentos (si estas en
windows ejecutalo en una ventana DOS):
goT3'v3
Debera decirte cunta memoria DPMI y espacio de s&ap puede usar DJGPP en tu
sistema. Saldra algo parecido a esto:
6-HO memor2 availa?leM R%3% _?
6-HO s\ap space availa?leM TDQ5T _?
(Los nmeros que saldrn variarn dependiendo de la cantidad de memoria RAM
instalada en tu sistema, el espacio en disco disponible y el sevidor DPMI). Si go32-v2
indica menos de 4 MBytes de memoria DPMI, lee la seccin 3.8 de la FAQ. Si tienes
ms que esa memoria pero quieres el mximo rendimiento de tu sistema, lee esa
seccin igualmente).
Pues ya est. Ya podemos meternos de lleno en el mundo de la programacin en C con
nuestro nuevo compilador.
Creaci)n de programas #Compilaci)n'
Vamos a ver primero cmo compilar un sencillo programa ('hola mundo' estar bien).
Es interesante que entiendas esto primero para saber cmo funciona el compilador.
Luego puedes usar un &DE, que es un entorno de desarrollo (como por ejemplo Turbo
Pascal o Borland C) para hacer el trabajo ms cmodo. Existe uno muy bueno para
principiante (y para profesionales) llamado 5hide.
Para este ejemplo necesitas un editor de texto: el edit del DOS o el block de notas del
Windows estaran bien. Crea un fichero con el editor y copia el siguiente cdigo:
#include <stdio.h>
int main()
{
printf( !ola mundo"n )#
6
return %#
&
Llmalo myfile.c. Puedes guardarlo en el directorio que quieras, no tiene por qu estar
dentro del directorio DJGPP. Puedes guardarlo por ejemplo en un directorio llamado
misprog (c:\misprog).
Ya tenemos el fichero fuente, ahora slo hace falta compilarlo. Para compilar nuestro
programa usamos el GCC. Este es un compilador de l7nea de comando, al que tienes
que llamar desde la lnea de comandos del DOS. Se encuentra en el subdirectorio 'bin'
del directorio donde hemos instalado djgpp.
Com#ilacin de n #ro/rama C con n 'nico (ichero (ente
Vamos a compilar nuestro fichero fuente. Desde el directorio donde tenemos guardado
myfile.c (por ejemplo c:\misprog) tecleamos:
gcc m2file.c 'o m2file.e(e
Si todo va bien (debera hacerlo) aparecer en ese directorio el programa myfile.c listo
para ser ejecutado. Perfecto, ya tenemos nuestro primer programa.
Com#ilacin de n #ro/rama C ?e sa al/na biblioteca 1o librer0a3
Ahora vamos a suponer que nuestro programa usa la biblioteca (librera) 'lib/libm.a' (el
anterior no la usa). Para compilarlo haramos:
gcc m2file.c 'o m2file.e(e 'lm
La opcin 4lm enlaza con la librera 'lib/libm.a'. La opcin -lm se pone al final.
Com#ilar n #ro/rama con $arios (icheros (ente
1+4 Crear Aicheros objeto:
Para compilar un fichero de cdigo fuente de C o C++ y dejarlo como fichero objeto,
usa la siguiente lnea de comando:
gcc 'c 'aall m2file.c (para c1digo fuente .)
o
gcc 'c 'aall m2file.cc (para c1digo fuente .PP)
Con esto obtienes el fichero m2file.o. La opcin '-Wall' activa muchos mensajes de
aviso (warnings) que son especialmente tiles para nuevos usuarios de GCC. Tambin
se pueden usar otras extensiones, comp .cpp (ver seccin 8.4 de la FAQ).
!+4 Enla>ar (icheros objeto
Para enlazar varios ficheros objeto en un programa ejecutable, usa algo parecido a
esto:
7
gcc 'o m2prog.e(e m2main.o m2su?5.o m2su?3.o
Con esto obtienes m2prog.e(e que ya puede ser ejecutado.
Si en vez de un programa C, tenemos un programa C++, usa algo como esto:
g(( 'o m2prog.e(e m2main.o m2su?5.o m2su?3.o
g(( buscar automticamente las bibliotecas C++, de manera que no tienes que
incluirlas en la lnea de comando.
"+4 Com#ilacin 9 enla>ado en n solo #aso
Puedes combinar los pasos de compilacin y enlazado de la siguiente manera:
gcc 'aall 'o m2prog.e(e m2main.c m2su?5.c m2su?3.c
Puedes encontrar ms informacin de las opciones de GCC en la documentacin on-
line. Para leerla instala el paquete EeLin(o (txi390b.zip,ver la seccin docmentacin
on4line) y ejecuta:
info gcc invoAing
$ebugging #$epuraci)n'
Para depurar un programa antes hay que compilar su cdigo fuente usando la opcin '-
g':
gcc 'c 'aall 'g m2main.c
gcc 'c 'aall 'g m2su?5.c
gcc 'c 'aall 'g m2su?3.c
y luego enlazarlo tambin con la opcin '-g':
gcc 'g 'o m2prog.e(e m2main.o m2su?5.o m2su?3.o
Luego ejecuta tu programa con el debugger (depurador):
fsd? m2prog.e(e
o
gd? m2prog.e(e
o
ede?ugT3 m2prog.e(e
(Tendrs que conseguir el fichero /dbM1Ob+>i# si quieres depurar con GDB). FSDB
funciona a pantalla completa y tiene pantalla de ayuda a la que se accede pulsando F1.
GDB viene con documentos 'Info' (ver ms abajo) que se pueden leer con info.exe.
Edebug32 es un depurador poco usado; escribe h para obtener ayuda.
[Arriba]
8
$ocumentaci)n %n62ine
La documentacin OnLine es aquella que se consulta directamente sobre el ordenador,
que no est en papel.
La mayor parte de la documentacin On-line est organizada en un formato hipertexto
especial usado por el proyecto GNU. Cada paquete viene con su documentacin que
son los ficheros con extensin +in( (el Readme.1st dice .iNN) y que se descomprimen
en el subdirectorio in(o6 de tu directorio DJGPP. Para hojear estos documentos,
consigue el fichero t(iTD%?.Uip, y ejecuta info.exe. Si no sabes cmo usar Info, pulsa
'?'.
Compatibilidad con la +ersi)n 7899
Si te actualizas desde la versin 2.00 de DJGPP a la versin 2.01, deberas reinstalar
todos los paquetes que utilices. Debido a los diferentes mtodos que usa V2.01 para
manejar lneas de comando largas (y nombres de fichero largos bajo Win95), mezclar
programas V2.00 con programas V2.01 puede hacer muy difcil depurar problemas.
Ver la seccin 16.6 de la FAQ para ms informacin.
Compatibilidad con la +ersi)n :8!
Los binarios compilados bajo V1.x pueden usarse para aplicaciones para las cuales no
hay versin V2. Los programas V1 no pueden ejecutar programas V2 (pero s al revs),
as que no intentes, por ejemplo, usar Make v1.x para ejecutar el compilador v2.0.
Entorno de desarrollo #I$E'
Un entorno de desarrollo es un programa que se encarga de todas las fases del
desarrollo: edicin, generacin de makefiles, compilacin y depuracin.
Edicin: el programa debe servir como editor de textos y estar orientado a la
programacin.
Generacin de makefiles: cuando tenemos varios ficheros fuente es conveniente que el
ide genere automticamente el makefile (el fichero que da instrucciones al compilador
sobre lo que debe hacer).
Compilacin: el ide debe permitir compilar y ejecutar el programa que estamos
desarrollando sin necesidad de salir al DOS.
Depuracin: es conveniente que depure los programas y nos muestre los errores y
warnings que se produzcan.

El IDE no funciona slo, necesita a los programas DJGPP, es tan slo una herramienta
que nos hace ms facil trabajar con todos los programas que hacen falta. Si te bajas
slo el IDE no funcionar nada.
No es estrictamente necesario trabajar con un entorno de desarrollo, pero es muy
conveniente (cmodo sobre todo).
Actualmente, DJGPP no viene con un entorno de desarrollo integrado propio. Puedes
usar cualquier editor que pueda ejecutar programas DOS y examinar su salida, para
que haga las veces de IDE. Mucha gente que trabaja con DJGPP usa una versin DOS
de GNU Emacs (est disponible en el mismo sitio donde conseguiste DJGPP). Emacs es
un editor muy potente (tiene incorporado un lector Info, de forma que puedes leer la
documentacin DJGPP sin salir del editor), pero muchos otros editores libres pueden
servir como IDE. La nica tarea que estos editores (includo Emacs) no pueden realizar
es ejecutar un debugger a ventana completa.
Entorno 1HI$E
Si ests acostumbrado a entornos del estilo Borland C (para DOS) o Turbo C existe un
IDE especfico para DJGPP llamado RHIDE. Tiene auto-indentacin, llama
automticamente al compilador DJGPP, genera los Makefiles automticamente (lo que
facilita la vida a los principiantes) y facilita el acceso a la documentacin OnLine de
DJGPP. Tambin incorpora la posibilidad de depurar (debug) programas. Dado que
RHIDE es muy reciente se estn haciendo revisiones y arreglos todava. Sin embargo
yo lo estoy usando y no me ha dado ningn problema.
Para ejecutarlo simplemente entra en tu directorio de programas (por ejemplo
c:\misprog) y teclea rhide desde DOS. Se abrir el entorno de desarrollo. Para crear un
programa entra en el menu 'File' -> 'New'. Escribe el cdigo fuente de tu programa.
Una vez que hayas acabado entra en el menu 'Run' -> 'Run' o pulsa directamente
CTRL-F9 para compilar el programa.

1''
1'1

S-ar putea să vă placă și