Sunteți pe pagina 1din 153

Tabla de Contenido

Prlogo....................................................................................................................4
Prlogo del 2003.................................................................................................4
Prlogo del 2014.................................................................................................4
PARTE I: LO MS BSICO....................................................................................5
Introduccin........................................................................................................5
Conceptos generales..........................................................................................5
Variables y constantes........................................................................................5
Instrucciones de entrada / salida........................................................................6
Secuencias de escape.....................................................................................11
Aritmtica en C.................................................................................................11
Archivos de inclusin........................................................................................17
Variables globales y locales y reglas de alcance.............................................18
PARTE II: PROGRAMACIN ESTRUCTURADA................................................20
Condicin if-else...............................................................................................20
Condicin switch-case......................................................................................23
Ciclo for.............................................................................................................25
Ciclo while.........................................................................................................29
Ciclo do-while...................................................................................................30
PARTE III: FUNCIONES.......................................................................................33
PARTE IV:ARREGLOS Y ESTRUCTURAS.........................................................44
Arreglos unidimensionales...............................................................................44
Arreglos de tamao no predefinido..............................................................57
Arreglos de dos o ms dimensiones................................................................59
Arreglos con tamao no predefinido............................................................65
Arreglos de ms dimensiones..........................................................................67
Estructuras........................................................................................................69
Cadenas de caracteres....................................................................................72
PARTE V: APUNTADORES..................................................................................76
Apuntadores a variables...................................................................................77
Apuntadores y funciones..................................................................................79
Apuntadores y arreglos....................................................................................82
Arreglos de apuntadores..................................................................................87
Argumentos de la lnea de comandos..............................................................92
PARTE VI: ARCHIVOS.........................................................................................93
PARTE VII: MEMORIA DINMICA.....................................................................108
Listas enlazadas simples................................................................................108
rboles binarios..............................................................................................116
APNDICE A: LA BIBLIOTECA ESTANDAR.....................................................136
stdio.h.............................................................................................................136
Archivos......................................................................................................136
Salida con formato.....................................................................................137

Entrada con formato...................................................................................138


Entrada y salida de caracteres...................................................................139
Funciones de entrada y salida directa.......................................................140
Funciones de colocacin en archivos........................................................140
Funciones de error.....................................................................................141
string.h............................................................................................................141
math.h.............................................................................................................142
stdlib.h.............................................................................................................144
assert.h...........................................................................................................145
time.h..............................................................................................................145
APNDICE B: ALGUNOS PROBLEMAS PROPUESTOS.................................146
APNDICE C: RECOMENDACIONES Y TEMAS NO VISTOS.........................149

Prlogo

Prlogo
Prlogo del 2003
Antes que nada quiero agradecer a las personas que me apoyaron para publicar
este libro, la Lic. Ana Gabriela Hernandez, la Ing. Karina Requena, el Ing Juan
Carlos Saenz, el Ing. Claudio Arzola, el Ing. Jess Valles y a Luz Cristina
Hernandez, que hizo la portada.
El libro lo escrib pensando en algo que fuera lo ms claro posible y que fuera
directo al punto, en lo personal no me gustan los libros donde lo que abunda es
la paja, y segn yo, creo y deseo que este prlogo sea la nica paja que tenga
este libro. Hablando de la claridad y de ir directo al grano, este libro no abunda
en ejemplos, presenta un ejemplo que pretenda ilustrar lo ms posible el tema y
lo explica a profundidad. Por eso a lo largo del libro recomiendo crear
situaciones distintas y practicar lo ms que se pueda, porque el libro te dice
cmo hacer las cosas, pero no tiene un ejemplo para cada cosa distinta. El libro
no es para nada largo, al contrario, por lo mismo recomiendo que se lea
completo no es la gran biblia de C, pero si lo lees completo tendrs una
plataforma bastante slida en los aspectos fundamentales (o sea los ms
importantes) del lenguaje. Es preferible que tengas algo de experiencia con
algn otro lenguaje de programacin o con el diseo de algoritmos, pero no es
del todo necesario, solo necesitaras poner ms atencin.
Es tiempo de terminar con este prologo, si bajaste el libro desde Internet o lo
copiaste en un disco lo puedes regalar a quien t quieras, pero no lo modifiques
ni lo plagies. Aunque no es un sper libro ni te cost mucho adquirirlo implic un
trabajo para m, un trabajo que espero que valores y que te sea muy til. Disfruta
entonces el libro y salo bien, mucha suerte :) ...

Prlogo del 2014


Hace muchos aos que escrib este pequeo libro, era estudiante de quinto
semestre de ingeniera y no tena la formacin que tengo ahora, aunque quiz
tena ideas ms frescas. Este libro, a pesar de sus errores, pareci gustarle a la
gente. Hace tiempo que doy clases de programacin y pens que podra
utilizarlo como material de texto, igual el trabajo ya estaba hecho. Se le hicieron
un algunas correcciones, sobre todo en ortografa, adems se incluyeron
algunas notas y actualizaciones a la versin C11, lo dems qued prcticamente
igual, este librito ya tena su esencia. Espero que te sirva, es gratis!

PARTE I: LO MS BSICO

PARTE I: LO MS BSICO
Introduccin.
El lenguaje C lo escribieron Dennis Ritchie y Brian Kernigham cuando trabajaban
con Ken Tompsom en el desarrollo de una nueva versin del sistema UNIX, en
los laboratorios Bell, o sea que C se escribi para escribir UNIX. C es un
lenguaje muy compacto porque el ejecutable que resulta es muy pequeo, es
muy potente y flexible, aparte de que tiene gran portabilidad porque un cdigo
que se escribe para un sistema en particular puede correr sin modificaciones, o
con muy pocas, sobre otro sistema diferente, aparte de que es independiente del
hardware. C es un lenguaje muy pequeo en el sentido de que no tiene muchas
instrucciones, lo que lo hace amplio es su biblioteca de funciones, que es algo
que lo hace muy flexible porque uno puede escribir su propia librera de
funciones y usarla en los programas.
Algo que quiero aclarar es que mucha gente trata a C y a C++ como la misma
cosa, esa gente se equivoca. El lenguaje C es un lenguaje imperativo, se basa
en una secuencia de rdenes para simular o para resolver problemas, pero el
lenguaje C++ es un lenguaje orientado a objetos, nada que ver. La forma de
manejar varios aspectos en C es diferente a la de C++. C++ ofrece varias
cualidades que C no tiene y viceversa, as que hazte a la idea de que este libro
es de C y no de C++.

Conceptos generales.
Las fases para hacer un programa son teclear un cdigo fuente, compilarlo a
cdigo objeto, y finalmente se enlaza para obtener un ejecutable. 1
El cdigo fuente es un archivo de texto que contiene todas las instrucciones que
han de ejecutarse para alcanzar la meta que el programador desea. El compilar
un programa es transformar todas las instrucciones del programa a un lenguaje
que la computadora pueda entender. Este cdigo objeto tiene referencias a otras
funciones y datos externos, o sea que no estn incluidos en el mismo cdigo
fuente, que al momento de enlazarlo se junta con los otros programas objetos de
los archivos que se necesiten para que el programa funcione. Y por ltimo
tenemos el programa ejecutable que es un cdigo que la mquina puede
entender y ejecutar.

Variables y constantes.
En un programa, y supongo que ya sabes ms o menos qu es un programa y
cmo se compone, hay valores que pueden cambiar o que deben admitir datos
1

Hablando en trminos muy generales para un lenguaje de tipo compilado.

PARTE I: LO MS BSICO
que el usuario necesite, como la temperatura, el promedio de una persona o lo
que sea, estos datos son variables y pues siempre estn sujetos a sufrir
cambios, por otro lado tenemos valores que no deben cambiar como la
constante o algn parmetro importante para que el programa funcione bien.
Tanto las variables como las constantes tienen tipos de datos, C tiene varios
tipos de datos, pero los ms bsicos son:

int: valor entero, pondra el rango admitido, pero depende en mucho del
sistema.
float: flotantes, nmeros como 3.14159 o 1.23 E16.
char: caracter, han de saber que en C no hay tipo de datos "cadena",
cmo guardar una palabra o algo as? Eso lo vemos despus.

Al declarar una variable as se le permite cambiar en cualquier momento, en el


caso de las que queremos que sean constantes se pueden declarar de la
siguiente manera.
const char letra = 'a';
As el valor de letra no puede ser modificado por accidente.
Las variables pueden tomar cualquier nombre, excepto los que sean palabras
reservadas como switch o for, adems de que no deben contener smbolos
como comillas o parntesis. La longitud no es limitante, pero por lo regular no
deben de tener mas de treinta y dos letras, ms o menos, porque hay algunos
compiladores que no soportan nombres ms largos. Otra condicin es que no
empiecen con un nmero, aparte C es caso sensitivo, o sea que las minsculas
no son iguales a las maysculas, por lo que Var es diferente que var, lo mismo
aplica para todas las instrucciones, que deben estar en minsculas, y para los
datos de entrada, as que no es lo mismo teclear e y E, son datos diferentes.
Vamos a trabajar con esos datos por lo pronto para pasar a otros temas ms
avanzados con ms facilidad.

Instrucciones de entrada / salida.


Hasta ahora podemos escribir, bueno no, no podemos, sabemos que hay tipos
de datos y todo eso, pero cmo podemos pedir un valor o desplegarlo en la
pantalla? Personalmente no pondra ese tema en este punto de mi "curso", pero
voy a tratar de apegarme al mximo a lo que piden en la escuela.
Se va a trabajar con dos funciones de entrada y salida, la funcin de salida va a
6

PARTE I: LO MS BSICO
ser printf y la de entrada va a ser scanf, por ejemplo una lnea como la que
sigue.
printf("Que demonios hace esto?");
Imprime "Que demonios hace esto?" pero sin las comillas. Esas se ponen en la
funcin para delimitar la cadena que se va a imprimir. Pero bueno vamos a juntar
lo que hemos visto hasta ahora para escribir el primer programa en C. Por cierto,
no pienso poner detalles acerca de cmo crear el cdigo fuente y de cmo
compilarlo, eso tiene que ver de qu clase de compilador ests usando y pues
no puedo adivinar.
/*Esto es un comentario*/
#include <stdio.h> /*este es un archivo de cabecera, luego te explico, por ahora
solo ponlo*/
/*si este archivo el programa no hace nada*/
int main(int argc, char **args)/*inicia la funcin principal, me choca decirlo, pero
no preguntes solo ponlo as*/
{
printf("Hola mundo!!!");
return 0;
}
Que bonito! Pero y eso de qu sirve? Por lo pronto teclea el programa y
gurdalo con la extensin .c, ponle el nombre que quieras y consrvalo.
/*esto es un comentario*/
Es importante poner comentarios en los programas, para acordarnos que
hicimos y que otras gentes puedan entender qu pasaba por nuestra mente
cuando escribimos esas lneas de cdigo. Hay otra forma de poner comentarios,
que es ms propia de C++, que no es lo mismo que C, pero a veces funciona.
Esta forma es poner dos diagonales (//) al principio de cada lnea de comentario.

PARTE I: LO MS BSICO
#include<stdio.h>
Esta parte es muy importante, aqu incluimos un archivo, que es el que tiene las
funciones de entrada y salida, su nombre significa STandardD Input Output, la
extensin .h se refiere a head o cabecera, se les llama as porque siempre se
ponen a la cabeza del cdigo fuente. Hay muchos archivos de cabecera y ojal
que los veamos despus.
int main(int argc, char **args)
{
...
...
return 0;
}
En C todo se divide en funciones o segmentos de cdigo que hacen alguna
tarea en especial, regresan algn tipo de valor o no regresan nada y tambin
reciben datos o no, la funcin main es la directora de todas las dems, nada
funciona sin ella, en este caso main regresa un valor entero (int) mediante la
lnea return 0; esta vez solo regresa un 0, los valores que recibe se os pasa el
sistema operativo.
De vuelta con las funciones de entrada / salida, sabemos que un programa no
sirve de nada si no regresa un resultado til, la cosa es cmo poner los nmeros
y datos importantes en la salida o la entrada de nuestros programas, veamos el
siguiente cdigo.
#include<stdio.h>
int main(int argc, char **args)
{
int numero = 10; /*el nmero vale 10*/
printf("el numero vale %d", numero);
return 0;
}

PARTE I: LO MS BSICO
Podrs ver la notacin "%d" al final de la cadena, esto le dice a la funcin que en
ese punto inserte el valor entero decimal que viene a continuacin, luego
despus de la coma viene la variable que tiene que imprimir, no es tan
complicado despus de todo. Ese mismo programa pudo haberse escrito as.
...
int main(int argc, char **args)
{
printf("el numero vale %d", 10);
return 0;
}
El resultado es el mismo, lo que a la funcin printf le interesa es recibir un valor
entero, no le importa de donde venga. Hay ms formatos para imprimir diversos
tipos de valores, son los que siguen.

%d Imprimen un valor entero en notacin decimal.

%x Valor entero de forma exadecimal.

%o Pone un entero en notacin octal.

%f Valor flotante.

%c Imprime un caracter.

No son todos los formatos de entrada / salida, pero s nos sacan de casi
cualquier apuro.
Vamos a ver como poner varias cosas dentro del mismo printf, mira el siguiente
programa.
#include<stdio.h>
int main(int argc, char **args)
{
int val = 5;
char letra = 'o'; /*nota que el caracter se delimita por ' '*/

PARTE I: LO MS BSICO
printf("El numer%c es %d", letra, val);
return 0;
}
Como ves, se pueden poner varios argumentos en la funcin printf y se van
recogiendo en orden, o sea que si pongo printf("%d %f", 10.56, 4) es un error, se
debe poner printf("%d %f", 4, 10.56) en este programa vimos cmo colocar
caracteres y cmo se inicializan, no pierdas el detalle de poner las comillas
simples para encerrar el caracter que se va a poner.
Los formatos de salida son los mismos que los formatos de entrada, con unas
pocas diferencias, revisa el siguiente cdigo para aceptar valores desde el
teclado.
#include<stdio.h>
int main(int argc, char **args)
{
int num1, num2, num3; /*si, se pueden poner varias variables dentro de la
misma declaracin*/
printf(" Introduce un numero: ");
scanf("%d", &num1);
printf(" Introduce otros dos numeros: ")
scanf("%d %d", &num2, &num3);
printf("numero 1 = %d, numero 2 = %d, numero 3 = %d", num1, num2, num3);
return 0;
}
Como podrs adivinar el primer scanf lee un numero entero y lo pone en "num1",
el detalle de poner un & delante del nombre de la variable es muy importante, si
no lo pones pues el nmero que metiste no se va a guardar en la variable, luego
vemos qu significa ese smbolo. La parte ms complicada viene en el segundo
scanf, vienen dos variables, aqu lo que pasa es que hay que poner el numero 2,
un espacio y luego el numero 3, en lo personal no me gusta usar esa clase de
10

PARTE I: LO MS BSICO
formato, pero cada quien...

Secuencias de escape.
Si estuviste compilando y corriendo los programas, cosa que espero que hagas
y que experimentes un poco, te aseguro que es la mejor forma de aprender. Si lo
hiciste te habrs fijado en lo feo que imprime las cosas, para eso tenemos unas
secuencias de escape que nos permiten dar cierto formato a la impresin (tanto
como una terminal lo permite), hay secuencias de escape para poner una nueva
lnea, para retroceder sobre la lnea, etc., son los siguientes.

\n Nueva lnea

\t Tabulador horizontal

\\ Diagonal inversa

\v Tabulador vertical

\' Apostrofe

\r Regreso de carro

\" Comillas

\b Regresar un espacio

\o Nmero octal, p Ej. \o25

\f Avance de forma

\x Nmero exadecimal, p Ej. \x16.

Por ejemplo para escribir "hola mundo!!!" sin que el prompt empiece en la misma
lnea y el programa quede medio difcil de entender se pone as.
printf("\n\t\"Hola mundo!!!\"\n\n");
Escribe algunos programas o lo que quieras donde puedas revisar las
secuencias de escape para que veas cmo funcionan, ya que domines bien las
funciones de entrada y salida vamos a ver cmo hacer operaciones con
nmeros.

Aritmtica en C.
Se supone que ya sabes (muy poco) de cmo introducir variables y como
11

PARTE I: LO MS BSICO
imprimirlas, resulta ms o menos claro que la mayora de los programas deben
realizar clculos numricos para que sirvan de algo, las operaciones que
podemos hacer son las siguientes.

Suma: su smbolo es "+", si queremos sumar dos nmeros la sintaxis es a


+b

Resta: "-", a - b

Multiplicacin: "*", igual que las dems, a * b

Divisin: su smbolo es "/"

Mdulo: Supongo que es algo ms o menos nuevo, su smbolo es "%", si


queremos saber cual es el residuo de 5/2 pues escribimos 5 % 2, en este
caso 5/2 = 2 y el residuo es 1

Asignacin: El famossimo signo "igual" o "=", la asignacin se hace de


derecha a izquierda. c = a + b est bien escrito, pero a + b = c no va a
hacer exactamente lo que queremos

Incrementos: Son operadores unitarios, o sea que solo se usan con una
variable, no como la suma que requiere dos variables, escribir ++var es
como decir "incrementa la variable var en uno antes de usarla", en cambio
escribir var++ sera decir "usa la variable var y luego la incrementas en
uno". Tambin se puede usar la resta poniendo --var o var--, pero as no
se puede mostrar bien su funcionamiento, ser ms claro cuando veamos
ciclos.

Tambin hay operadores para comparar, como el > (mayor que), o el <= (menor
o igual), pero los veremos cuando leguemos al tema de las decisiones, por lo
pronto vamos a escribir unos programas para hacer operaciones con unos
nmeros.
El primer programa que vamos a hacer es uno muy sencillo que sume dos
nmeros enteros y muestre el resultado.
#include<stdio.h>
int main(int argc, char **args)
{
int a, b, c; /*ya habamos visto que se pueden poner varias declaraciones en la
misma lnea*/
12

PARTE I: LO MS BSICO

printf("

Escribe el numero a: ");

scanf("%d", &a); /*como repaso: recuerda que %d significa entero decimal y no


olvides el (&)*/
printf("

Bien, ahora escribe el numero b: ");

scanf("%d", &b);
c = a + b; /*suma, puedes poner los smbolos pegados si quieres*/
printf("\n\tLa suma de %d + %d es %d!!!\n\tQu padre\n", a, b, c);
return 0;
}
Por favor haz el programa, escrbelo bien y crrelo para que veas cmo trabaja.
Un consejo, no s que compilador ests usando, estoy seguro de que ya habrs
cometido algunos errores y pues no has de haber batallado mucho para
corregirlos porque son programas muy chicos, pero procura leer muy bien los
mensajes que tu compilador te d, generalmente el error est una lnea antes de
donde te dice el compilador. Te recomiendo que hagas este programa y una vez
que funcione bien le quites un punto y coma por ah y veas el mensaje de tu
compilador, o quita el return, quita un &, alguna coma o lo que sea para que te
acostumbres al funcionamiento de tu compilador y agarres prctica para resolver
errores, la vas a necesitar.
Espero que ya hayas hecho lo que te ped, revisa las lneas siguientes.
int a, b, c; /*ya habamos visto que se pueden poner varias declaraciones en la
misma lnea*/
c = a + b; /*suma, puedes poner los smbolos pegados si quieres*/
printf("\n\tLa suma de %d + %d es %d!!!\n\tQu padre\n", a, b, c);
Fjate que estamos trabajando con tres variables distintas, ese programa se
13

PARTE I: LO MS BSICO
puede hacer con solo dos variables. Cambia las lneas anteriores por estas
lneas.
int a, b;
printf("\n\tLa suma de %d + %d es %d!!!\n\tQue padre\n", a, b, a+b);
Por ltimo borra la lnea donde haces la suma de c = a + b y compila el
programa otra vez, crrelo y ve el resultado que te arroja.
El siguiente programa muestra la cmo se usa la operacin de mdulo (residuo).
#include<stdio.h>
int main(int argc, char **args)
{
int num, mod;
printf(" Escribe un numero: ");
scanf("%d", &num);
mod = num % 3;/*mod = residuo de num / 3*/
printf("

El residuo de %d entre 3 es %d\n", num, mod);

return 0;
}
Este programa tambin se puede escribir as.
#include<stdio.h>

14

PARTE I: LO MS BSICO
int main(int argc, char **args)
{
int num;
printf(" Escribe un numero: ");
scanf("%d", &num);
printf("

El residuo de %d entre 3 es %d\n", num, num % 3);

return 0;
}
En C se pueden transformar los tipos de datos en caso de que se necesite. Por
ejemplo si escribimos esto.
float a, b;
int c;
...
...
c = a+b;
El compilador regresa un error porque no podemos guardar un flotante en un
entero, pero si la suma la escribimos as.
c = (int)(a+b);
A esto se le llama conversin explcita de tipo, o sea que estamos obligando a lo
que resulte de sumar a+b a convertirse en un valor entero. Las versiones ms
nuevas de ANSI C no marcan error, quiz una advertencia, lo que ocurre en este
caso es que al querer guardar un valor flotante dentro de una variable entera el
valor se redondea y se pierden datos. Por ejemplo, si a = 2.3 y b = 1.4, a + b =
3.7, si c es entera al hacer c = a+b, entonces c = 2.3 + 1.4 o sea c = 3.7 el
resultado final sera que c = 3.
Por otro lado C convierte los tipos de datos segn las operaciones realizadas
con ellos utilizando algo llamado promocin de tipos, siempre el resultado de
una operacin ser del tipo ms grande involucrado
15

PARTE I: LO MS BSICO

int * float = float porque float es ms grande que int.

float / int = float

int / int = int, por ejemplo 7/2 = 3.

Otro detalle importante que hay que conocer es la precedencia de los


operadores en C.
La siguiente tabla muestra la lista de operadores ordenados por precedencia.
Los que estn en la misma celta tienen la misma precedencia.
Operador
++ -()
[]
.
->

Descripcin
Post- incremento y decremento
Llamada a funcin
Elemento de vector
Seleccin de elemento por referencia
Seleccin de elemento con puntero

Asociatividad

Izquierda a
derecha

++ -+!~
(type)
*
&
sizeof

Pre- incremento y decremento


Suma y resta unitaria
NOT lgico y NOT binario
Conversin de tipo
Indireccin
Direccin de
Tamao de

*/%
+<< >>

Multiplicacin, divisin y mdulo


Suma y resta
Operaciones binarias de desplazamiento
Operadores relaciones "menor que", "menor
o igual que", "mayor que" y "mayor o igual
que"
Izquierda a
Operadores relaciones "igual a" y "distinto
derecha
de"
AND binario
XOR binario
OR binario
AND lgico
OR lgico
Operador ternario
Derecha a

< <=
> >=
== !=
&
^
|
&&
||
c?t:f

16

Derecha a
izquierda

PARTE I: LO MS BSICO
=
+= -=
*= /= %=
<<= >>=
&= ^= |=

Asignaciones

izquierda

Este es el operador de menor prioridad en


C. Sirve para separar una coleccin de
,
Izquierda a
expresiones, que se irn evaluando de
izquierda a derecha
Fuente: http://es.wikipedia.org/wiki/Anexo:Operadores_de_C_y_C%2B
%2B#Precedencia_de_operadores

Archivos de inclusin.
Como viste, en todos los programas que hemos hecho viene la inclusin del
archivo del archivo stdio.h, este es parte de la biblioteca estndar. Y ya te haba
explicado que ah vienen todas las instrucciones de entrada y salida. Trata de
quitar esa lnea y vers que el compilador de manda errores en las funciones de
entrada y salida. Esos archivos son para guardar funciones que se usan mucho
y pues es pesado estarlas escribiendo, a veces yo los uso para dividir mis
programas en archivos ms chicos y fciles de leer. Al poner el nombre del
archivo entre "<>" le decimos al compilador que lo busque en su propio directorio
include, en cambio si le ponemos comillas le damos la ruta completa, por
ejemplo #include "c:\\mio.h" busca precisamente ese archivo y no lo busca en
otro lado. Si ponemos #include "lynx.h" lo busca en el mismo directorio donde
est el cdigo fuente. Vamos a hacer un experimento, teclea el siguiente cdigo.
/*contenido de cabeza.h*/
#include<stdio.h>
int a;
int b;
int c;
/*fin de cabeza.h*/
Guarda este archivo como cabeza.h, dentro del mismo directorio escribe el
siguiente cdigo.

17

PARTE I: LO MS BSICO
/*contenido de cabeza.c*/
#include"cabeza.h"
int main(int argc, char **args)
{
a = 5;
b = 6;
c = a+b;
printf("La suma es %d\n", c);
return 0;
}
Ahora guarda este cdigo como cabeza.c en el mismo directorio donde
guardaste cabeza.h y compila el programa, bsicamente el resultado es el
mismo que si hubieras escrito todo en el mismo archivo.

Variables globales y locales y reglas de alcance.


Como habrs notado en el ejemplo anterior, las variables a, b y c quedaron
afuera de la funcin main, eso se conoce como variable global, ya que en C todo
se divide en funciones cada funcin tiene sus propias variables, cuando las
variables se declaran fuera de todas las funciones, como en el ejemplo que
acabamos de ver, estas variables son accesibles desde todas partes del
programa, por eso se les llama variables globales. Cuando las variables se
declaran dentro de una funcin, como main por ejemplo, solo son visibles para
esa funcin y no para las dems, por eso se llaman variables locales.
En C tambin existen bloques que pueden tener sus propias variables, veamos
el siguiente cdigo y checa el resultado que da.
#include<stdio.h>
int main(int argc, char **args)
{/*inicio de un bloque, bloque 1*/

18

PARTE I: LO MS BSICO
int a = 5;/*variable local*/
{/*inicio de un bloque, bloque 2*/
int a = 13;/*otra variable, pero propia del bloque dos, la variable
a del bloque 1 es invisible aqu*/
printf("Dentro del bloque 2 a vale %d\n", a);
}
/*a la salida del bloque 2 a vale 5, nunca cambi su valor*/
printf("Dentro del bloque 1 a vale %d\n", a);
return 0;
}
En este caso hay dos variables a en el programa, pero la a del bloque 2 "oculta"
a la a del bloque 1.
#include<stdio.h>
int main(int argc, char **args)
{/*bloque 1*/
int a = 5;
{/*bloque 2*/
int b = 10;
/*dentro del bloque 2 a (del bloque 1) es visible*/
printf("Dentro del bloque 2 a vale %d y b vale %d\n", a, b);
}
/*fuera del bloque 2 b es invisible, no existe*/
return 0;
}

19

PARTE I: LO MS BSICO
Aqu vemos que la variable a es visible en el bloque 2, pero b no es visible en el
bloque 1, lo que pasa es que una variable es visible "hacia adentro" de los
bloques, pero no hacia afuera, por eso de las variables globales, ya que se
declaran muy afuera son visibles en todos los bloques. El truco es que si dentro
de un bloque o funcin declaramos una variable con el mismo nombre que una
de "ms afuera", la de ms afuera no ser visible, solo se podr trabajar con la
de ms adentro. Tienes que trabajar un poco poniendo a prueba lo que vimos
aqu, ya que es la manera en que puedas aprender, este libro es solo la lnea de
salida para que sepas qu es C y cmo usarlo.
Ahora que ya vimos las primicias de C, ms o menos como son los errores,
variables, etc., podemos pasar a la siguiente parte, que es la programacin
estructurada.

PARTE II: PROGRAMACIN ESTRUCTURADA


En un programa que se puede considerar ms o menos til deben existir
estructuras de control que permitan tomar decisiones o hacer una tarea
repetidas veces. C tiene varias estructuras de control para trabajar con ellas,
vamos a empezar por la ms sencilla: la decisin.

Condicin if-else.
A veces es necesario decidir entre dos caminos o dos acciones distintas si una
condicin, o varias se cumplen, la primer estructura para tomar decisiones que
vamos a ver es el if, que a veces es acompaado por else. Para tomar
decisiones hay que evaluar condiciones, en C tenemos operadores lgicos y
relacionales, son los siguientes.
Relacionales:

Mayor que

>

Menor que

<

Mayor o igual que

>=

Menor o igual que

<=

Igual

==

Diferente

!=

Lgicos:

And lgico

&&

Or lgico

||

20

PARTE II: PROGRAMACIN ESTRUCTURADA

Negacin

Vamos a suponer que queremos decir que "si a es mayor o igual que 3 y b es
diferente de c", esto se puede escribir as: "(a >= 3) && (b != c)". As es, puedes
poner parntesis para separar expresiones o para agruparlas.
Pero es mejor que hagamos unos programas para poder ver cmo funcionan los
operadores y la decisin if. El siguiente programa toma un nmero, lo compara
con diez y te dice si es mayor o menor.
#include<stdio.h>
int main(int argc, char **args)
{
int num;
printf("\tIntroduce un numero: ");
scanf("%d", &num);
/* if(num > 10) equivale a decir "si num es mayor que 10"*/
if(num > 10)
{
/*en caso de que num sea mayor que diez imprime el mensaje*/
printf("

Tu numero es mayor que diez\n");

}
/*se sale del if y se continua con el programa*/
return 0;
}
El programa es muy sencillo, simplemente compara el nmero con diez e
imprime un mensaje si es mayor, el funcionamiento de la instruccin if es muy
simple, ahora vamos a cambiar un poco el programa, si el nmero es mayor que
diez va a imprimir el mensaje, pero de lo contrario va a imprimir otro mensaje.
Cambia el siguiente cdigo...
21

PARTE II: PROGRAMACIN ESTRUCTURADA

If(num > 10)


{
printf(...
}
/*justo aqu se agrega un else, que quiere decir "de lo contrario"*/
else
{
printf("

Tu numero es menor o igual a diez\n");

}
Al poner esas ltimas lneas es como decir "si es mayor que diez haz esto, de lo
contrario haz esto otro". Se pueden poner sentencias ms complicadas para
tomar decisiones, por ejemplo para evaluar rangos de valores. El siguiente
pedazo de programa ve que el nmero est entre 100 y 200, que seran los
lmites permitidos, en cazo de que el nmero este fuera de ese rango ser
rechazado.
If( (num >= 100) && (num <= 200) )
{
/* si el nmero est entre 100 y 200*/
printf("

El numero fue aceptado!!!\n");

}
else
{
/*si el nmero es menor que 100 o mayor que 200*/
printf("

El numero no es vlido\n");

}
Tambin se pueden poner un if dentro de otro if, vamos a suponer que una
calificacin sirve si est entre 60 y 100, si est entre 60 y 75 vamos a poner un
mensaje de que la calificacin est regular, si est entre 76 y 100 vamos a decir
22

PARTE II: PROGRAMACIN ESTRUCTURADA


que est muy bien.
If( (cal >= 60) && (cal <= 100) )
{
/*si la calificacin est entre 60 y 100 el chavo ya pas*/
printf("

Tu calificacin esta ");

/*en este punto sabemos que la calificacin no puede ser menor que 60*/
if( cal < 76)
{
/*si la calificacin es menor que 76 es regular*/
printf("regular\n");
}
/*de lo contrario la calificacin debe ser mayor o igual que 76, sirve
de mucho la teora de conjuntos para estas cosas*/
else
{
printf("muy bien\n");
}
}

Condicin switch-case.
Existe otra estructura para tomar decisiones, vamos a suponer que tenemos un
men de tres letras, de la a a la c. Podramos hacer una estructura de if's
anidados o algo por el estilo, pero a veces resulta ms sencillo, como en este
caso, utilizar una estructura de casos para el men. La estructura switch-case
funciona as.
#include<stdio.h>
int main(int argc, char **args)
{
23

PARTE II: PROGRAMACIN ESTRUCTURADA


char op;
printf("

Elige una letra de la a a la e: ");

scanf("%c", &op);
/*en esta lnea se le dice a switch que va a trabajar con la variable op*/
switch(op)
{
case 'a': /*en caso de que op sea a*/
printf("

Elegiste la letra a\n");

break; /*esta instruccin es importante*/


case 'b':
printf("

La segunda letra del alfabeto\n");

break;
case 'c':
printf("

Esta no es una vocal\n");

break;
default: /*default actua cuando no se eligi una letra correcta
no es necesario ponerla, pero puede ser muy util*/
printf("

No elegiste las letras que te dije!!! :-(");

}
return 0;
}
Creo que no necesita mucha explicacin el cmo funciona el switch, puede
funcionar con enteros, flotantes o caracteres. Dentro de los casos hay una
instruccin nueva, break es una instruccin que rompe todas las estructuras, en
este caso break hace que el programa se salga de la estructura switch, a veces
es importante ponerla. Escribe el mismo programa, pero qutale todos los
break's, juega con l y ve que es lo que pasa si eliges una b por ejemplo. Creo
que no te va a gustar qu es lo que pasa. Ahora regresa los break's a donde
deben de ir y quita la instruccin default (y tambin todo lo que est dentro del
24

PARTE II: PROGRAMACIN ESTRUCTURADA


default), es mejor que veas ms o menos cmo funciona si le quitas el default, si
funciona, pero ya no te dice si te equivocaste de letra.

Ciclo for.
En C hay tres tipos de ciclos, el ciclo do-while, while y el for, la verdad es que no
encuentro cmo explicarte como funciona el ciclo for sin usar ejemplos, as que
vamos a hacer un programa.
El siguiente programa va a presentar un men, se van a tener dos opciones,
imprimir todos los nmeros pares del 0 al 100 o imprimir los nones del 0 al 100.
Claro que necesitamos ciclos para hacer todo eso, seran muchas lneas de
cdigo. Para hacer el programa vamos a usar un switch, dos if's y dos ciclos for.
#include<stdio.h>
#define N 100 /*algo nuevo, definir valores previos*/
int main(int argc, char **args)
{
int i;
int op;
/*imprimiendo el men*/
printf("\n\t\tMenu\n\n");
printf("\t1) Imprimir los nones\n");
printf("\t2) Imprimir los pares\n");
printf("

Cual es tu opcion? ");

scanf("%d", &op);
switch(op)
{
case 1:
/*el ciclo for es como decir "desde i = 0; mientras que sea menor que N;
incrementa i de uno por uno*/
for(i=0; i<N; i++)
25

PARTE II: PROGRAMACIN ESTRUCTURADA


{
if( (i % 2) == 1 ) /*si el numero entre dos da 1 como residuo*/
/*parece que falta algo? quit las llaves a propsito*/
printf(" %d ", i);
}
break;
case 2:
for(i=0; i<N; i++)
if( ( i % 2) == 0 )
{
printf(" %d ", i);
}
}
printf("\nHasta luego\n");
return 0;
}
En este programa vimos tres cosas nuevas, el define, que quit las llaves y el
ciclo for. Aprovech poner el define porque se usa mucho donde hay ciclos,
define crea un valor constante para ser usado dentro del programa, a veces es
muy til si existe un valor que no cambia y debe ser usado en todo el programa,
si necesitamos cambiarlo solo cambiamos el define y listo. Las llaves las quit
porque no son del todo necesarias, en los ciclos y las decisiones las llaves se
usan solo cuando son dos o ms instrucciones dentro de la decisin o el ciclo.
Cuando es solo una instruccin no hay problema si se ponen o no. En el caso
del segundo for el if equivale a una sola instruccin, por eso quit las llaves
tambin.
El ciclo for, que es el que nos interesa, tiene tres parmetros para trabajar, el
primero es decirle al for desde donde va a empezar, el segundo le dice cuando
debe seguir, como "mientras i sea menor que N sigue trabajando", el ltimo le
dice a for en qu medida debe incrementar o decrementar su variable, en este
caso i.
Este programa va a recoger un nmero del teclado (o sea que lo va a preguntar),
luego mientras el nmero sea menor que 100 va a elevarlo a la potencia que tu
26

PARTE II: PROGRAMACIN ESTRUCTURADA


quieras y va a incrementarlo el la medida que tu quieras, y va a hacer eso
mientras a potencia sea menor o igual 1000.
#include<stdio.h>
#include<math.h> /*una librera nueva, puras matemticas*/
#define N 100
#define MAX 1000
int main(int argc, char **args)
{
int num, exp, pot, inc;
printf(" Desde que numero quieres empezar? ");
scanf("%d", &num);
printf(" Cual quieres que sea el exponente? ");
scanf("%d", &exp);
printf(" Cual quieres que sea el incremento? ");
scanf("%d", &inc);
for(num; num<=N && (pot = (int)pow(num, exp)) <= MAX; num+=inc)
{
printf(" %d ", pot);
}
printf("\nHasta luego\n");
return 0;
}
Aqu agregamos otra comparacin, se pueden evaluar expresiones ms
complejas, incluso aqu hacemos asignaciones dentro de la misma evaluacin
de la expresin, es por eso que hay que distinguir bien entre == y =, ya que
dentro de los if y cosas por el estilo donde se evalen expresiones se pueden
27

PARTE II: PROGRAMACIN ESTRUCTURADA


hacer asignaciones. Por otro lado tenemos la expresin num+=inc, es que en C
hay dos formas de asignar valores, eso equivale a escribir num = num+inc, se
puede poner en todos lados, no solo en el for.
En este programa incluimos el archivo math.h este archivo de cabecera tiene
funciones matemticas como senos y cosenos, nmeros absolutos o la potencia,
que fue la que utilizamos aqu. Este for est muy raro, primero nada ms est el
nombre de una variable en el primer parmetro, pero esa variable ya tiene un
valor, as que no hay problema. En el segundo parmetro hay una expresin
ms compleja, es como decir "mientras num sea menor o igual a N y que pot,
que es el resultado de num elevado a la exp, sea menor o igual a MAX", es
complicado pero ahorra lneas de cdigo, por ltimo el parmetro que queda
incrementa a num segn el valor de inc. Fjate en la parte que dice "pot =
(int)pow(num,exp)", en la parte donde est int entre parntesis, recuerda que es
una conversin de tipo, lo que pasa es que pow siempre regresa un valor doble,
que es un flotante pero ms largo, as que tuvimos que convertirlo a entero para
poder usarlo, en las versiones ms recientes de C se puede omitir la conversin
explcita, solamente se obtienen algunas advertencias a la hora de compilar.
Vamos a escribir otro programa que haga lo mismo, pero con la instruccin
break.
#include<stdio.h>
#include<math.h>
#define N 100
#define MAX 1000
int main(int argc, char **args)
{
int num, exp, pot, inc;
printf(" Desde que numero quieres empezar? ");
scanf("%d", &num);
printf(" Cual quieres que sea el exponente? ");
scanf("%d", &exp);
printf(" Cual quieres que sea el increnmento? ");
scanf("%d", &inc);
28

PARTE II: PROGRAMACIN ESTRUCTURADA

for(num; num<=N; num+=inc)


{
if( ( pot = (int)pow(num,exp) ) <= MAX )
printf(" %d ", pot);
else
break;
}
printf("\nHasta luego\n");
return 0;
}
Hace lo mismo, solo que en ms lneas, fjate en el else, est la instruccin
break, lo que hace esa instruccin es que en el momento en que pot sea mayor
que MAX interrumpe el ciclo, esa es la caracterstica de break, como lo
habamos visto en el switch.

Ciclo while.
Existe otro ciclo diferente, el ciclo while realiza la misma tarea mientras una
condicin se cumpla, es ms sencillo que el ciclo for, vamos a hacer un
programa que capture una letra varias veces y la vaya imprimiendo hasta que la
letra sea z.
#include<stdio.h>
int main(int argc, char **args)
{
char letra;
printf("Introduce las letras que quieras, para salir introduce una z\n");
while((letra = getchar()) != 'z')
{
29

PARTE II: PROGRAMACIN ESTRUCTURADA


if(letra != '\n')
{
getchar();
printf("%c\n", letra);
}
}
printf("Hasta luego\n");
return 0;
}
Este programita muestra dos cosas nuevas, la funcin getchar y el ciclo while.
En la condicin del ciclo while aparece la sentencia (letra = getchar()) != 'z',
getchar es una funcin que toma caracteres desde la entrada estndar, que es el
teclado, cuando pulsamos una tecla esta se mete en un buffer, y en el momento
que prensamos enter la empujamos hacia el programa, lo mismo pasa con
scanf. En la condicin del ciclo pues, se introduce un caracter y ah mismo se
evala, si no es z se entra al ciclo, sencillo. Dentro del ciclo nos topamos con el
if, que pregunta si la letra es diferente a '\n', pero ese es una secuencia de
escape, lo que pasa es que esa secuencia de escape significa "enter" as que lo
que pregunta en realidad es que si el usuario tecle enter en vez de un caracter,
recuerdas el enter que empujaba el caracter hacia adentro? Lo que pasa es
que si se meti una letra y luego un enter este se queda en el buffer, y causa
muchos problemas a las funciones que agarran datos desde el teclado, lo que
hago con ese if es ver si no se meti un enter solo, en cuyo caso no hay
problema, pero si se meti una letra habr problemas, as que sacamos ese
enter con otro getchar y listo, nuestro teclado est limpio. Y, aprovechando el if,
si el usuario meti un caracter que no sea enter pues imprime el valor, eso para
no imprimir lneas en blanco. Prueba a quitar ese control del if para el enter que
se queda atorado, vers que te imprime lneas que no deberan de aparecer,
otra cosa que pasa es que al pedir un valor ms adelante, si no limpiamos ese
enter, simplemente no toma el caracter siguiente sino el enter atorado y pues
causa problemas, no sabes cuantos topes me di a causa de eso.

Ciclo do-while.
El programa que acabamos de hacer (el del while), tiene una pequea falla, yo
quera que imprimiera la z otra vez, pero no la pone! Pero el seor Ritchie
30

PARTE II: PROGRAMACIN ESTRUCTURADA


tambin tuvo el mismo problema un da y pues hizo el ciclo do-while. Lo que
pasa con el ciclo while es que evala primero la condicin y luego entra al ciclo,
pues el ciclo do-while entra primero y evala la condicin al ltimo, vamos a
cambiar el programa para que ponga la z que quiero.
#include<stdio.h>
int main(int argc, char **args)
{
char letra;
printf("Introduce las letras que quieras, para salir introduce una z\n");
do
{
letra = getchar();/*puedes poner un scanf si quieres*/
if(letra != '\n')
{
getchar();/*aqu no pongas un scanf*/
putchar(letra);
putchar('\n');
}
}while(letra != 'z');
printf("Hasta luego\n");
return 0;
}
Listo, el programita ya imprime la z que quera, la diferencia entre el ciclo dowhile y el ciclo while es muy pequea, pero puede significar mucho. Aparte
vimos una funcin nueva, putchar, seguro has de saber ingls y has de saber lo
que significa, "pon un caracter". Esta funcin pone un caracter, pero solo uno,
nos puede ser de mucha utilidad en algunas ocasiones, esta funcin pone el
31

PARTE II: PROGRAMACIN ESTRUCTURADA


caracter que le digas, como putchar(letra), pone el contenido de letra en la
pantalla. En el caso de putchar('\n') pues es como poner un enter, una lnea
nueva. Puedes poner putchar('a') para escribir una a en la pantalla.
Ahora vamos a suponer que el usuario tiene su teclado con las maysculas
activadas y nunca va a poder salir del ciclo porque no va a poner una z
minscula. En los ciclos while y do-while tambin se pueden poner cosas ms
complicadas. Mira el siguiente cdigo del programa que acabamos de hacer.
#include<stdio.h>
int main(int argc, char **args)
{
char letra;
printf("Introduce las letras que quieras, para salir introduce una z\n");
do
{
letra = getchar();/*puedes poner un scanf si quieres*/
if(letra != '\n')
{
getchar();/*aqu no pongas un scanf*/
putchar(letra);
putchar('\n');
}
}while( (letra != 'z') && (letra != 'Z') );
printf("Hasta luego\n");
return 0;
}
Listo, ahora el pobre usuario podr terminar de ver el programa sin asustarse en
el do-while le ponemos un and lgico para que vea si la letra es z o Z. Por cierto,
32

PARTE II: PROGRAMACIN ESTRUCTURADA


no olvides el punto y coma que va al ltimo del do-while.
Se supone que tambin debo ensearte a hacer ciclos anidados, pero te lo dejo
de tarea, son como los if's, solo pones uno dentro de otro. Ya mero terminamos
la parte dos del libro y aunque no lo creas ya sabes mucho de cmo programar
en C, en realidad C es muy sencillo y pequeo, se podra decir que ya sabes
ms o menos la mitad de lo que debes de saber, dominando estas bases te
aseguro que a casi cualquier problema que encontrars una buena solucin. Lo
ltimo de este tema es una referencia a los ciclos infinitos, es simple,
supongamos que haces un ciclo, pero la condicin siempre se cumple, eso
quiere decir que nunca saldrs del ciclo, que grave, no? Debes tener cuidado
con esos detalles para que tus programas no se ciclen.

PARTE III: FUNCIONES


Te acuerdas que el principio del libro te expliqu que C se divide en funciones?
Esa es la parte bella de la programacin, dividir en pedazos pequeos el
problema para resolverlo con ms facilidad. Digamos que necesitamos un
programa que calcule la temperatura promedio de cada da y luego haga una
grfica para ver la variacin de la temperatura en un mes. Pues se puede hacer
una funcin que calcule la temperatura promedio en un da y otra que imprima la
grfica de barras. As si una parte tiene algn error es ms fcil de localizar
porque tenemos los componentes aislados.
Vamos a hacer un programa que calcule el rea de un cuadrado y que calcule el
ngulo de un vector, vamos a ponerle un men para usar casos y llamar a dos
funciones que vamos a crear.
#include<stdio.h>
#include<math.h>
void area(void); /*prototipos de funcin*/
void angulo(void);
int main(int argc, char **args)
{
int op;
printf("Pulsa 1 para sacar el rea de un cuadrado o pulsa 2 para sacar el
ngulo de un vector. ");
33

PARTE III: FUNCIONES


op = getchar();
if(op != '\n')
{
getchar();
}
switch(op)
{
case '1':
area();/*mandamos llamar a nuestra funcin*/
break;
case '2':
angulo();
break;
}
return 0;
}
void area(void)/*nuestras funciones*/
{
float lado;
printf(" Cuanto mide el lado del cuadrado? ");
scanf("%f", &lado);
/*imprimir el cuadrado de lado*/
printf("El area del cuadrado es %.2f\n", pow(lado,2));
}
void angulo(void)
{
float x, y, tan, ang;
34

PARTE III: FUNCIONES


const float pi = 3.14159265;
printf("Introduce la cordenada x: ");
scanf("%f", &x);
printf("Introduce la cordenada y: ");
scanf("%f", &y);
tan = x/y; /*la tangente es el cateto opuesto sobre el adyacente*/
ang = 180 * atan(tan) / pi;/*el ngulo viene en radianes, hay que convertirlo a
grados*/
printf("El angulo es %.2f\n", ang);
}
Te voy a explicar el programa parte por parte.
void area(void);
Las funciones regresan y admiten valores por lo general, la primer palabra en la
declaracin de una funcin es el tipo de dato que regresa. En este caso la primer
palabra es void, que quiere decir que no va a regresar ningn valor, por ejemplo
int main(int argc, char **args) indica que la funcin main debe regresar un valor
entero. Lo siguiente en la declaracin de una funcin es el nombre de la
funcin, su declaracin tiene las mismas reglas que para las variables, y claro,
no debe tener el nombre de alguna otra variable que se llame igual. Por ltimo
entre los parntesis van los valores que va a recibir la funcin, pueden ser de
cualquier tipo, al igual que el valor de retorno (casi), en este caso se puso void,
que indica que no va a recibir nada. En el caso de la funcin main(), se supone
que no va a recibir valores, pero se debera escribir int main(int argc, char
**args). Ocurre algo, en C el no poner nada entre los parntesis de la
declaracin de la funcin supone que esta puede recibir cualquier parmetro, a
diferencia de C++, que significa que no va a recibir nada, como si funcion() fuera
igual a funcion(void). En lo personal no uso esa caracterstica de C, prefiero
poner void cuando no pienso recibir nada en la funcin y declarar los parmetros
cuando s quiero que la funcin los reciba.
Pero no te he explicado por qu la funcin se debe declarar al principio, es como
una variable, se debe declarar antes de usarla, este es ms o menos el mismo
35

PARTE III: FUNCIONES


caso. Podras escribir todas las funciones antes de main, porque todas las
funciones deben ser declaradas antes de quien las invoca, pero es ms lo,
mejor escribes todos los prototipos de las funciones que vayas a usar antes de
main y ya los puedes escribir donde se te d la gana despus de main. Entonces
ya sabes por qu puse los nombres de las funciones antes de main y las
funciones las escrib despus de main, es buena costumbre hacer eso.
Recuerda adems que si en el prototipo (arriba de main) escribiste int
funcion(void), abajo de main la funcin debe llevar el mismo nombre.
int op;
...
op = getchar();
...
case '1':
Por qu puse op como int y en el resto del programa lo trato como caracter? Es
un truco que aprend, resulta que un char es un int disfrazado de letra, cuando
escribes int numero = 'a' en realidad no le pones la letra a sino su cdigo ASCII,
entonces puedes engaar al programa hacindole creer que est trabajando con
un char. Si pusiramos algo como int truco = '@'; y luego printf("%d", truco);
escribira un 64 en la pantalla, y "alt + 64 = @". Si ves la tabla ASCII vers que la
clave de @ es 64.
area()
Al escribir el nombre de la funcin con sus parntesis la estamos mandando
llamar para que haga lo que tiene que hacer. Es como cuando escribimos char
letra = getchar(); mandamos llamar la funcin getchar y el valor que regresa, un
int que se puede hacer pasar por caracter, lo guardamos en letra.
void area(void)
{
...
...
...
}
En esta parte est la funcin con todo lo que tiene que hacer. Pareca ms
36

PARTE III: FUNCIONES


complicado, no crees?
Ya vimos cmo trabajan las funciones sencillitas, ahora vamos a hacer una
funcin que saque el factorial de un nmero, pero lo nuevo es que el factorial va
a ser un valor de retorno y el nmero lo va a recibir como parmetro.
#include<stdio.h>
long fact(int);/*el prototipo de la funcin*/
void main(void)/*vamos a cambiar un poco*/
{
int num;
printf(" Escribe el numero para sacar su factorial: ");
scanf("%d", &num);
printf(" El factorial de %d es %d\n", num, fact(num));
}
long fact(int a)
{
int i;
long fact = 1;
for(i=1; i<=a; i++)
{
fact *= i;
}
return fact;
}
Vamos a ver saltamontes, la cosa es sencilla, te lo voy a explicar igual, parte por
parte.
37

PARTE III: FUNCIONES

long fact(int);
En este prototipo de funcin vemos un tipo de dato nuevo, long es un entero y se
trata como tal, pero es ms largo, si un entero normalmente trabaja con 16 bits,
que le da un rango de -2+1 a 2-1 el long trabaja con 32 bits, el doble, as que
puede almacenar nmeros desde -2+1 a 2-1 (puede variar segn la
implementacin) hay otro tipo de variable entera, short, que trabaja con la mitad
de bits, o sea que en un procesador de 32 bits trabajara con 8 bits que nos
dara un rango de valores de -127 a 127. Pero eso no es todo todos estos,
aparte del char y los flotantes (float y double), se pueden hacer sin signo la
palabra unsigned, en el caso del unsigned int cambia el rango de 0 a 65535,
pero bueno, volvamos a nuestras funciones, ya sabemos que esta funcin va a
regresar un valor long, que puede ser muy grande, que se va a llamar fact y que
va a recibir un valor entero. En los prototipos de funcin no es necesario poner el
nombre de las variables, solo el tipo, si quieres puedes poner el nombre de la
variable, pero no pasa nada si no lo haces.
printf(" El factorial de %d es %d\n", num, fact(num))
En este printf llamamos la funcin fact como un parmetro ms, cmo es esto?
Ya lo habamos visto antes, el valor que regresa fact es el valor que printf
imprime en nuestro mensaje. Al poner fact(num), le estamos mandando ese
entero para que trabaje con l.
long fact(int a)
{
...
...
return fact;
}
Aqu empezamos a trabajar con nuestra funcin aqu s debemos poner el
nombre de la variable que va a recibir, puede ser diferente, puede llamarse como
sea siempre que siga siendo entero, as que se me ocurri ponerle a. Escribimos
todas las instrucciones y al ltimo est el return fact, puedes poner el return
donde se te antoje, puede ser dentro de un if o un switch, como sea, pero
generalmente va al ltimo. Return manda el valor de regreso a quien quiera que
haya llamado a la funcin, en este caso le manda el valor de fact a la funcin
printf para que lo imprima.
38

PARTE III: FUNCIONES


Como ves no es tanto show, simplemente ir por pasos y fijarse bien como es la
sintaxis de C. Las funciones pueden recibir un chorro de parmetros, pero solo
pueden regresar un valor, vamos a hacer un clon de la funcin pow para sacar
potencias, pero limitada a solo trabajar con enteros.
#include<stdio.h>
long pot(long, int);
int main(int argc, char **args)
{
int num;
int exp;
printf("Introduce el numero: ");
scanf("%d", &num);
printf("Introduce el exponente: ");
scanf("%d", &exp);
printf("La potencia es %d\n", pot(num, exp));
return 0;
}
long pot(long n, int e);
{
int i;
long p = 1;
for(i=0; i<e; i++)
{
p *= n;
}
return p;
}
39

PARTE III: FUNCIONES

Como puedes ver, una funcin puede recibir cualquier tipo de parmetro y
pueden ser diferentes entre s, adems de que pueden ser varios de ellos.
Hasta ahora hemos llamado funciones y les hemos mandado parmetros, pero
han sido pasos "por valor". Lo que quiere decir es que el programa crea una
copia de las variables y trabaja con las copias, a veces es mejor (ya sea por
espacio en memoria o por otras cosas) que las variables no se copien, sino que
la funcin trabaje con las originales. Esto lo vamos a ver despus, vamos a
hacer un programa que muestre cmo se copian las variables. Este programa va
a tomar un valor entero y le va a sacar su cuadrado.
#include<stdio.h>
int cuad(int);
int main(int argc, char **args)
{
int a;
printf("Introduce un numero para sacar su cuadrado: ");
scanf("%d", &a);
printf("El cuadrado de %d es %d\n", a, cuad(a));
return 0;
}
int cuad(int a)
{
a *= a;
return a;
}
Espero que lo hayas escrito y hayas visto como funciona, en la funcin cuad
estamos cambiando el valor de a, incluso lo estamos regresando a main, pero el
40

PARTE III: FUNCIONES


valor de la a de main no cambi, as se ve muy claro que la a de cuad era solo
una copia. Ahora vamos a ver una peculiaridad de C, si decimos que una funcin
va a recibir enteros le mandamos enteros, pero en realidad esto puede cambiar,
mira el siguiente cdigo.
#include<stdio.h>
float mitad(float);
int main(int argc, char **args)
{
int a;
printf("Introduce un numero para sacar su cuadrado: ");
scanf("%d", &a);
printf("El cuadrado de %d es %f\n", a, cuad(a));
}
float cuad(float a)
{
a *= a;
return a;
}
Es el mismo programa (ms o menos), pero le estamos mandando un entero a
una funcin que supuestamente solo recibe flotantes, lo que pasa es que C
convierte el tipo de dato siempre y cuando no haya perdida de datos, as que
puedes hacer una funcin que acepte double's y mandarle short's sin ningn
problema, pero no trates de mandarle un float a una funcin que reciba enteros
porque vas a tener problemas2, al menos warnings por parte del compilador y
prdida de datos.
Ya te sabes algunos trucos y ya sabes que una funcin te puede servir para
2

Actualmente se puede hacer conversin implcita a tipos de dato ms pequeos, lo cual


implica prdida de datos, pero el compilador no genera un error, slo una alerta.

41

PARTE III: FUNCIONES


dividir el programa en pedazos ms sencillos, aparte de que si haces el mismo
procedimiento varias veces dentro de un programa te ahorras escribir el mismo
cdigo varias veces, lo pones en una sola funcin y lo mandas llamar las veces
que quieras. Pero que tal si te digo que saques el factorial de un nmero sin
usar ciclos? Te voy a ensear el siguiente cdigo y por favor ponle mucha
atencin, esto es un detalle de C que te va a servir muchsimo.
#include<stdio.h>
long fact(int);
int main(int argc, char **args)
{
int num;
printf("Introduce un numero para sacar su factorial: ");
scanf("%d", &num);
printf("El factorial de %d es %d\n", num, fact(num));
return 0;
}
long fact(int a)
{
if(a != 0)
{
return(a * fact(a-1));
}
else
{
return 1;
}
}
42

PARTE III: FUNCIONES

Recuerdas la frmula del factorial? Era algo como !n = n * (n-1) * (n-2) * ... * (1).
En forma recursiva la frmula sera !n = n * !(n-1). En este programa vemos dos
cosas, que se pueden poner operaciones entre parntesis para que return se
encargue de ellas, pero bueno eso es parte integral de C, y se puede hacer en
todos lados, como en fact(a-1), no hay problema por eso, lo otro es que una
funcin se puede llamar a s misma, eso se llama recursividad, si miras la
frmula recursiva de la frmula del factorial es exactamente la misma que escrib
en el return, funciona y lo hace bien.
Hay dos tipos de recursividades, la interna y la externa, la interna es esta que
acabamos de ver, la externa es cuando la funcin 1 llama a la funcin 2, y esta
llama otra vez a la funcin 1 y as las dos funciones recurren entre s. Mira bien
la funcin fact, mira que tiene una condicin, y en un punto tiene que regresar un
valor fijo, en la recursividad debe haber una condicin para que en un punto
dado la funcin no se vuelva a llamar a s misma (o a otra en el caso de la
recursividad externa), en este caso me fui siguiendo la frmula, el uno es el
ltimo numero que se multiplica y el factorial de 0 es 1 as que hice que cuando
el valor recibido fuera 0 la funcin regresara un 1, ese es el tope o caso base.
Ya hemos avanzado bastante, supongo que en este punto haz de pensar que los
programas que estamos haciendo estn muy simples, pero no puedo poner
programas ms complejos en el libro, solo puedo darte las herramientas para
que escarbes y te sea ms fcil aprender a programar en C. De todas formas el
libro no te sirve de nada si no tratas de complicarte la vida un poco y tratas de
hacer experimentos, puedes combinar lo que quieras, poner ciclos dentro de los
switchs, o poner switchs dentro de los ciclos, puedes mezclar todo con todo,
programar es como jugar lego, tienes las piezas y las armas a tu gusto. Yo solo
te estoy dando las piezas para que juegues, lo dems es cosa tuya. Creo que
vamos bien hasta este momento, no hemos visto muchas funciones de la
biblioteca estndar, pero hemos visto algunos trucos y propiedades de C que te
van a servir mucho. Ya estamos listos para entrar al siguiente tema, los arreglos,
aparte en el siguiente tema vamos a ver las estructuras, que son un tipo de dato
que tarde o temprano vas a usar mucho, como no hallo donde ponerlo pues lo
vamos a ver en la siguiente parte del libro. Por favor tmate un descanso repasa
un poco lo que ya vimos, porque de ahora en adelante vamos a hacer
programas de ms de 100 lneas, porque me interesa que aprendas a leer
cdigo y que veas unos buenos ejemplos. En realidad C es muy sencillo y muy
pequeo, y hasta este punto vamos muy bien y creo que te he podido explicar
todo de la forma ms simple, pero vamos a ver ms ejemplos, y va a ser lo largo
del libro.

43

PARTE IV:ARREGLOS Y ESTRUCTURAS

PARTE IV:ARREGLOS Y ESTRUCTURAS


Arreglos unidimensionales
Creo que te has de seguir preguntando cmo poder guardar el nombre de una
persona o cualquier cadena, aparte vamos a suponer que queremos almacenar
unos cincuenta nmeros flotantes para sacar su promedio despus, creo que
est muy difcil poner 50 variables diferentes y llevar el control de todas ellas.
Para cosas como esas tenemos los arreglos, es un tipo de dato que puede
contener muchos elementos del mismo tipo. El siguiente programa que vamos a
hacer va a almacenar la temperatura diaria promedio durante una semana, o sea
que son siete nmeros diferentes, les va a sacar su promedio y los va a imprimir.
#include<stdio.h>
#define N 7 /*numero de elementos del arreglo*/
int main(int argc, char **args)
{
float arr[N]; /*un arreglo de 7 flotantes*/
int i;
float prom = 0; /*este va a ser el promedio*/
printf("A continuacin introduce las temperaturas\n");
for(i=0; i<N; i++)
{
printf("Temperatura %d: ", i+1);
scanf("%d", &arr[i]);
prom += arr[i];
}
prom /= N;
printf("El promedio es %.3f", prom);
return 0;
}

44

PARTE IV:ARREGLOS Y ESTRUCTURAS


Como ves aqu agrupamos 7 flotantes dentro de una sola variable, aunque las 7
son independientes entre s es ms fcil tratarlas como un grupo que por
separado. Vamos a ver el programa parte por parte para que entiendas como
funciona esto de los arreglos.
#define N 7
En esta parte defin un numero, que voy a utilizar para definir el tamao del
arreglo y el ciclo que se va a usar.
float arr[N];
Aqu viene la novedad, float arr se entiende, es un flotante que se llama arr,
pero float arr[N] qu? Cuando escribo float arr[N] estoy diciendo que voy a
hacer N variables flotantes, pero que las quiero tratar como un arreglo o matriz,
como sea. Digamos que tengo una caja dividida en 7 ms pequeas puedo
guardar 7 flotantes diferentes en la misma "caja".
float prom =0;
Hay un detalle importante que haba olvidado, vamos a usar prom para ir
sumando las temperaturas y sacarles su promedio, me gustara que cambiaras
esta lnea por float prom; y vieras lo que pasa. En C cuando creamos una
variable esta puede tomar cualquier valor, no sabemos cul puede ser, si se va a
usar una variable hay que inicializarla primero.
for(i=0; i<N; i++)
{
printf("Temperatura %d: ", i+1);
scanf("%d", &arr[i]);
prom += arr[i];
}
Quiero que pongas atencin a este ciclo, como puedes ver hay que especificar
en qu casilla del arreglo hay que introducir la variable, en este ciclo puedes ver
que se introduce arr[0], luego arr[1] y as hasta llegar a arr[6]. En C el primer
elemento de un arreglo es el subndice 0, no olvides eso, aparte, por ejemplo en
este arreglo de 7 elementos el ltimo lugar es el 6, nunca olvides eso. As si
quisiramos imprimir el tercer valor del arreglo lo haramos as, printf("%f",
45

PARTE IV:ARREGLOS Y ESTRUCTURAS


arr[2]). En la lnea que dice prom += arr[i] es donde se nota bastante como,
aunque todas formen parte del mismo arreglo, pueden ser tratadas como
variables independientes y hacer operaciones con ellas.
prom /= N;
El nmero que definimos puede ser usado como una constante, as que tambin
se pueden hacer operaciones con l. Entonces aqu ya tenemos el promedio de
toda la semana.
printf("El promedio es %.3f", prom);
Fjate en la parte que dice %.3f, ya he puesto cosas as, pero no te haba
explicado para qu sirve, si has hecho tus programas ya te habrs dado cuenta
de al poner %f imprime el nmero con seis decimales, esto se puede aumentar o
reducir poniendo %.7f o %.1f, creo que sobra decir cuantos decimales pone el
%.3f.
Ahora vamos a ver cmo se puede inicializar un arreglo, es sencillo. Mira el
siguiente cdigo, aparte vas a ver el ordenamiento burbuja.
#include<stdio.h>
#define N 10
int main(int argc, char **args)
{
int arr[] = {10, 1, 9, 2, 8, 3, 7, 4, 6, 5}; /*as se inicializa un arreglo*/
int i, j, swap; /*swap es una variable de intercambio para el ordenamiento*/
printf(" El ordenamiento burbuja\n\n");
printf("El arreglo en desosren es: ");
for(i=0; i<N; i++)

46

PARTE IV:ARREGLOS Y ESTRUCTURAS


printf("%d, ", arr[i]); /*recuerda que cuando es solo una instruccin no se
necesitan comillas*/
printf("\b\b.\n");
/*aqu empieza el ordenamiento burbuja*/
for(i=0; i<N-1; i++){ /*esta es simplemente otra forma de poner las llaves*/
for(j=0; j<N-1; j++){
if(arr[j] > arr[j+1]){ /*si el actual es mayor al que sigue*/
swap = arr[j]; /*lo pone en el intercambio*/
arr[j] = arr[j+1]; /*baja el que estaba adelante*/
arr[j+1] = swap; /*el que estaba abajo lo pone arriba*/
}
}
}
printf("El arreglo ordenado: ");
for(i=0; i<N; i++){
printf("%d, ", arr[i]);
}
return 0;
}
Vamos por partes...
int arr[] = {10, 1, 9, 2, 8, 3, 7, 4, 6, 5};
Sencillo, simplemente indicamos que la variable va a ser un arreglo, eso lo
hacemos con los corchetes ([]), y entre llaves y separados por comas ponemos
los elementos del arreglo. Aqu C cuanta cuantos son y les guarda el espacio
que necesiten.
47

PARTE IV:ARREGLOS Y ESTRUCTURAS

for(i=0; i<N-1; i++){


for(j=0; j<N-1; j++){
if(arr[j] > arr[j+1]){
swap = arr[j];
arr[j] = arr[j+1];
arr[j+1] = swap;
}
}
}
El ordenamiento burbuja en este caso pone los elementos de menor a mayor,
pero puede ser al revs. Funciona con dos ciclos y en base a una comparacin,
trata de hacer una prueba de escritorio (con lpiz y papel) de cmo funciona este
mtodo y cmo trabajara paso por paso. Deduce por qu son dos ciclos y por
qu no llegan hasta el ltimo elemento del arreglo sino hasta el penltimo. Digo,
este libro no es de algoritmos, espero poder terminar el que s es de estructuras
de datos y algoritmos, pero esa es la siguiente entrega.
Un arreglo como toda una variable puede ser el argumento de una funcin,
vamos a hacer un programa que trabaje con una sola funcin aparte de main,
este programa va a capturar el arreglo, y luego va a preguntar si el arreglo debe
ser ordenado de menor a mayor o de menor a mayor mediante un men, pero el
truco es que este men debe estar siempre, va a ser un programa ms de "a de
veras", aparte lo nuevo es que el no se pude salir del programa a menos que se
elija "salir" en el men (o que cierres la ventana de MS-DOS o que des un
control+c).
#include<stdio.h>
#include<stdlib.h>
#define N 10
void limpia(char a){
if(a != '\n'){/*recuerdas eso acerca de limpiar el buffer del teclado?*/
48

PARTE IV:ARREGLOS Y ESTRUCTURAS


getchar();
}
}
char minuscula(char a){
if(a >= 65 && a <= 90){ /*Trbajamos sobre el cdigo ascci*/
a += 32; /*si es mayuscula la hacemos minuscula A = 65 y a = 97*/
}
return a;
/*si a era mayuscula ya la hicimos minuscula y la mandamos de regreso*/
}
void llenar(int []); /*los corchetes indican que se va a recibir un arreglo*/
void ordena(int [], char);/*tambien se pueden meter varios parametros*/
int main(int argc, char **args)
{
int arr[N];
char op;
int a = 0;/*indica si el arreglo ya se lleno, si vale 0 el arreglo esta vacio*/
do{
system("cls"); /*limpiar la pantalla (llama a ms-dos)*/
printf("

MENU\n\n");

printf("

a) Llenar el arreglo\n");

printf("

b) Ordenar de menor a mayor\n");

printf("

c) Ordenar de mayor a menor\n");

printf("

d) Salir\n");

printf("

Tu opcion: ");

op = getchar();
49

PARTE IV:ARREGLOS Y ESTRUCTURAS


limpia(op);
op = minuscula(op);
switch(op){
case 'a':
llenar(arr);
a = 1;
getchar();
break;
case 'b':
if(a == 1){
ordena(arr, 'a'); /*a de ascendente*/
}else{
printf("Aun no has llenado el areglo\n");
}
break;
case 'c':
if(a == 1){
ordena(arr, 'd'); /*d de descendente*/
}else{
printf("Aun no has llenado el arreglo\n");
}
break;
case 'd':
printf("Hasta luego!!!\n");
break;
default:
printf("Tecla incorrecta\n");
}
system("pause"); /*otra llamada a ms-dos*/
50

PARTE IV:ARREGLOS Y ESTRUCTURAS


}while(op != 'd' && op != 'D');
return 0;
}
void llenar(int a[]){
int i;
for(i=0; i<N; i++){
printf(" Elemento %d: ", i+1);
scanf("%d", &a[i]);
}
}
void ordena(int a[], char o){
int swap, i, j;
/*ordena en forma ascendente*/
if(o == 'a'){
for(i=0; i<N-1; i++){
for(j=0; j<N-1; j++){
if(a[j] > a[j+1]){
swap = a[j];
a[j] = a[j+1];
a[j+1] = swap;
}
}
}
}
/*ordena en forma descendente*/
if(o == 'd'){
for(i=0; i<N-1; i++){
51

PARTE IV:ARREGLOS Y ESTRUCTURAS


for(j=0; j<N-1; j++){
if(a[j] < a[j+1]){
swap = a[j];
a[j] = a[j+1];
a[j+1] = swap;
}
}
}
}
printf("Arreglo ordenado: ");
for(i=0; i<N; i++){
printf("%d, ", a[i]);
}
printf("\b\b.\n");
}
Este programa ya se ve ms serio, te dije que solo iba a tener una funcin
aparte de main, pero me emocion un poco cuando lo estaba haciendo,
francamente este libro lo estoy escribiendo al vuelo, me siento y dejo que las
ideas fluyan, de repente todo se me olvida, pero a veces me emociono un poco.
Te lo voy a tratar de explicar por partes, como siempre, pero confo en que ya lo
tecleaste y viste como funciona, supongo que pudiste ver el men y la forma en
que imprime los resultados. Si no lo has hecho te recomiendo que lo hagas
porque este programa est ms complejo que los que hemos visto juntos y no
sera tan fcil entenderle sin haberlo visto funcionando antes.
#include<stdlib.h>
Esta es otra librera donde vienen muchas funciones de utilera. Vienen
funciones para crear nmeros aleatorios, para trabajar con memoria dinmica y
para hacer llamadas al sistema operativo.

52

PARTE IV:ARREGLOS Y ESTRUCTURAS


void limpia(char a){
if(a != '\n'){
getchar();
}
}
Ya te haba explicado cmo el enter empuja a los datos desde el teclado, pero
se queda atorado en el buffer del teclado y nos jala broncas, en esta parte donde
agarramos el caracter y lo metemos y si no fue un enter solo jalamos el enter
que se qued atorado. Habrs notado que esta funcin no tiene prototipo, es
que le escrib al modo antiguo, hasta arriba. Recuerda que si haces eso tienes
que escribirla arriba de donde est la primera vez que la invoques, si invocas
una funcin que no tiene prototipo y est escrita abajo de tu invocacin el
programa no la va a encontrar, estos son problemas.
char minuscula(char a){
if(a >= 65 && a <= 90){
a += 32;
}
return a;
}
En el switch solo contempl a las minsculas, o sea que si metes maysculas
pues no va a pasar nada de lo que queras, pero esta funcin cambia las
maysculas a minsculas usando el cdigo ASCII. Las letras maysculas estn
entre el 65 y el 90 ASCII, y las minsculas entre el 97 y el 122, as que si a una
mayscula le sumamos un 32 la hacemos minscula. Acurdate que un char es
un int disfrazado de letra.
void llenar(int []);
Esta funcin va a recibir un arreglo de enteros, se sabe porque tiene unos
corchetes, sencillo, no?
system("cls");
system("pause");
La funcin system llama al shell, se pueden hacer muchas llamadas aqu,
53

PARTE IV:ARREGLOS Y ESTRUCTURAS


puedes poner system("dir") o system("md lynx"). Esta funcin est en la librera
stdlib.h
Lo dems se me hace ms complicado explicarlo, trata de leer bien el programa
y de ver cmo funciona para que entiendas bien cmo est estructurado el
programa.
Ahora vamos a ver otro detalle de los arreglos y las funciones, recuerdas que al
pasar una variable a una funcin el programa haca una copia de esta? Pues
con los arreglos es diferente, veamos...
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 10
void llena(int []);
void imprime(int []);
void ordena(int []);
int main(int argc, char **args){
int a[N];
llena(a);
printf("El arreglo original: ");
imprime(a);
printf("\n");
ordena(a);
printf("El arreglo despues de ordenarse: ");
imprime(a);
printf("\n");
return 0;
}
54

PARTE IV:ARREGLOS Y ESTRUCTURAS

void llena(int b[]){


int i;
srand(time(NULL));
for(i=0; i<N; i++){
b[i] = rand() % 20;
}
}
void imprime(int c[]){
int i;
for(i=0; i<N; i++){
printf("%d ", c[i]);
}
}
void ordena(int d[]){
int i,j, swap;
for(i=0; i<N-1; i++){
for(j=0; j<N-1; j++){
if(d[j] > d[j+1]){
swap = d[j];
d[j] = d[j+1];
d[j+1] = swap;
}
}
}
}
Ok, vamos a empezar, en este ejemplo, espero que lo hayas escrito corrido en tu
55

PARTE IV:ARREGLOS Y ESTRUCTURAS


computadora. Se ve como el arreglo pasa por todas las funciones pero sin
copiarse, el arreglo pasa ntegro, as que ten cuidado porque los arreglos no se
copian, esto es porque no son variables normales, son parte de los apuntadores
o punteros, esos los vemos despus. Ahora te voy a explicar las cosas nuevas
que hay en este programa.
#include<time.h>
La librera time.h tiene las funciones para saber y manipular la hora. No es que
queramos cambiar la hora en este programa, pero hay una funcin que nos
servir.
void llena(int b[]){
int i;
srand(time(NULL));
for(i=0; i<N; i++){
b[i] = rand() % 20;
}
}
En la librera stdlib.h hay una funcin para crear nmeros aleatorios, en realidad
estos nmeros no son aleatorios, pero se pueden alterar un poco para que
parezca que s lo son. Antes de usar la funcin rand(), que es la que nos da los
nmeros a la suerte debemos "sembrar la semilla", con una semilla diferente el
orden de los nmeros vara, as que s se puede obtener cierta aleatoriedad.
Para sembrar una semilla usamos la funcin srand(), esta funcin recibe un
nmero y lo planta como semilla, si las semillas son diferentes los resultados
devueltos tambin lo son. Escrib srand(time(NULL)) porque la funcin time
regresa la cantidad de segundos que han pasado desde las 00:00:00 del primero
de enero de 19703, as que siempre ser un nmero diferente y cada vez que se
use ser una semilla diferente. La funcin rand() regresa un nmero entero que
va desde 0 a 32767 (puede variar segn la implementacin) usamos la
operacin de mdulo para asegurarnos de que el nmero devuelto no llegue a
32767 sino a 19, as por ejemplo si pusiramos rand() % 6 el nmero devuelto
estara entre 0 y 5, si quisiramos que el nmero estuviera entre 1 y 6
escribiramos as rand() % 6 + 1 o (rand() % 6) + 1.

Esto nos lleva a hablar del Y2K38

56

PARTE IV:ARREGLOS Y ESTRUCTURAS

Arreglos de tamao no predefinido


Existe una forma ms conveniente de crear arreglos, le podemos pedir al
usuario que nos diga el tamao antes de crear el arreglo. Mira el siguiente
programa.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void llenar(int n, int a[]){
int i;
srand(time(NULL));
for(i=0; i<n; i++){
a[i] = rand()%(n*4);
}
}
void mostrar(int n, int a[]){
int i;
for(i=0; i<n-1; i++){
printf("%d, ", a[i]);
}
printf("%d\n", a[i]);
}
void ordenar(int n, int a[]){
int i, j, t;
for(i=0; i<n-1; i++){
for(j=0; j<n-i-1; j++){
if(a[j]>a[j+1]){
57

PARTE IV:ARREGLOS Y ESTRUCTURAS


t = a[j];
a[j] = a[j+1];
a[j+1] = t;
}
}
}
}
int main(int argc, char **args){
int t;
printf("Dime el tamao de tu arreglo: ");
scanf("%d", &t);
if(t>1){
int arreglo[t];
llenar(t, arreglo);
printf("Tu arreglo es:\n");
mostrar(t, arreglo);
ordenar(t, arreglo);
printf("Tu arreglo ordenado es:\n");
mostrar(t, arreglo);
}else{
printf("Ese tamao no es vlido\n");
}
return 0;
}
scanf("%d", &t);
if(t>1){
int arreglo[t];
Aqu le preguntamos al usuario el tamao y luego vemos que el valir sea
58

PARTE IV:ARREGLOS Y ESTRUCTURAS


correcto, no podemos tener arreglos de tamao -2 y un tamao de 1 sera
absurdo. EL tamao del arreglo se define utilizando la variable t (int arreglo[t]),
as si t vale 5 el tamao del arreglo ser 5.
void llenar(int n, int a[])
void mostrar(int n, int a[])
void ordenar(int n, int a[])
Las funciones que trabajan con ese arreglo deben saber el tamao que este
tendr, por eso se este define como parmetro, ya que puede ser cualquier
cosa. A la hora de invocar la funcin se le mandan el tamao y el arreglo.

Arreglos de dos o ms dimensiones.


Tambin puede haber arreglos de varias dimensiones, y se puede hacer lo
mismo que con los arreglos de una sola dimensin. Se puede declarar un
arreglo flotante de 3x3 con la lnea float arr[3][3], o de 4x5x3 con la lnea float
arr[4][5][3]. En el siguiente programa vamos a llenar un arreglo de 3x3, lo vamos
a multiplicar por 2 y lo vamos a imprimir.
#include<stdio.h>
#define N 3
void llenar(int [][N]);
void multiplica(int [][N]);
void imprime(int [][N]);
int main(int argc, char **args){
int a[N][N]; /*declaro un arreglo de NxN*/
llena(a);
multiplica(a);
imprime(a);
return 0;
}
59

PARTE IV:ARREGLOS Y ESTRUCTURAS

void llena(int b[][N]){


int i, j;
printf(" Vamos a llenar el arreglo\n");
for(i=0; i<N; i++){
for(j=0; j<N; j++){
printf("Introduce el elemento (%d, %d): ", i, j);
scanf("%d", &b[i][j]);
}
}
printf("Listo!!!\n");
}
void multiplica(int c[][N]){
int i,j;
for(i=0; i<N; i++){
for(j=0; j<N; j++){
c[i][j] *= 2;
}
}
}
void imprime(int d[][N]){
int i,j;
printf("El arreglo despues de multiplicarlo por dos\n");
for(i=0; i<N; i++){
printf("

");

for(j=0; j<N; j++){


printf("| %3d ", d[i][j]);
}
60

PARTE IV:ARREGLOS Y ESTRUCTURAS


printf("|\n");
}
}
El programita no requiere de mucha explicacin, lo que cambia es la forma de
declarar el parmetro en las funciones y cmo declarar el arreglo. Aparte del
tratamiento que hay que darle ya que hay que llenar dos dimensiones. Mira bien
la forma en que declar las funciones, puse que iba a recibir un arreglo con los
corchetes correspondientes, pero en el segundo corchete tuve que poner el
tamao del arreglo por ejemplo si declaro un long arr[2][3], al declarar una
funcin tendra que escribir void funcin(long [][3]), por ejemplo. Un detalle que
deb de haberte explicado hace mucho, observa la lnea printf("| %3d ", d[i][j]), al
poner %3d estoy diciendo que ponga tres espacios, un entero que valga 4023 s
se va a imprimir completo, a veces es necesario que la impresin que de ms o
menos presentable y esto nos ayuda, ya te lo haba explicado con los flotantes,
como %.3f, pero la verdad es que hasta ahora supe que se poda hacer con los
enteros tambin (y aun as me atrev a escribir un libro).
En este programa vamos a ver cmo inicializar un arreglo de dos dimensiones.
#include<stdio.h>
#define N 4
#define M 3
void imprime(int [][M]);
int main(int argc, char **args){
int a[][M] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
imprime(a);
return 0;
}

61

PARTE IV:ARREGLOS Y ESTRUCTURAS


void imprime(int b[][M]){
int i, j;
for(i=0; i<N; i++){
for(j=0; j<M; j++){
printf("| %2d ", b[i][j]);
}
printf("|\n");
}
}
Bueno, vamos a revisar el programa parte por parte.
#define N 4
Definimos un "lado" del arreglo este lado van a ser los renglones.
#define M 3
Este otro "lado" va a ser las columnas.
int a[][M] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
Definimos un arreglo de tres columnas, el compilador divide la cantidad de
valores entre M y el resultado ser el tamao de la casilla sin nmero ([]), pude
haber puesto int a[N][M] = {...}, pero no es del todo necesario (aunque ms
correcto). En este caso hemos creado una matriz de N renglones por M
columnas, o sea de 4 renglones por 3 columnas (batallo mucho en esta rea
porque soy dislxico). Cmo se va llenando la matriz? En este caso se llena
rengln por rengln.
void imprime(int b[][M]){
int i, j;
for(i=0; i<N; i++){
for(j=0; j<M; j++){
printf("| %2d ", b[i][j]);
62

PARTE IV:ARREGLOS Y ESTRUCTURAS


}
printf("|\n");
}
}
Esta funcin se encarga de imprimir el arreglo, lo imprime rengln por rengln,
claro que para hacer eso tiene que ir al primer rengln y luego imprimir la casilla
correspondiente de cada columna.
Francamente es complicado explicar bien cmo se mueven los programas
dentro de los arreglos multidimensionales, este programa es sencillo, pero
puede resultar muy fcil confundir los renglones con las columnas y el cmo
moverse sobre ellas. Vamos a ver un programa, este mismo, pero que ordene
los nmeros para que se presenten de menor a mayor de izquierda a derecha y
de arriba a abajo.
#include<stdio.h>
#define N 4
#define M 3
void imprime(int [][N]);
void ordena(int [][M]);
int main(int argc, char **args){
int a[][M] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
printf("Este es el arreglo original\n\n");
imprime(a);
ordena(a);
printf("\nEste es el arreglo ordenado\n\n");
imprime(a);
return 0;
}

63

PARTE IV:ARREGLOS Y ESTRUCTURAS


void imprime(int b[][M]){
int i, j;
for(i=0; i<N; i++){
for(j=0; j<M; j++){
printf("| %2d ", b[i][j]);
}
printf("\x7c\n");
}
}
void ordena(int c[][M]){
int i, j, k, swap;
/*antes que nada recuerda esto: arreglo[renglones][columnas]
y que N = renglones y que M = columnas*/
for(i=0; i<N; i++){/*esto va a ordenar las columnas*/
/*claro que se avanza renglon por renglon para recorrer toda la columna :)*/
for(j=0; j<M-1; j++){/*acaba de empezar la burbuja*/
for(k=0; k<M-1; k++){
/*se nota que estamos tratando con las columnas nada mas?*/
if(c[i][k] > c[i][k+1]){
swap = c[i][k];
c[i][k] = c[i][k+1];
c[i][k+1] = swap;
}
}
}
}
/*las columnas ya estan ordenadas, siguen los renglones*/
for(i=0; i<M; i++){
/*igual, avanzamos columna por columna para abarcar todo un renglon*/
64

PARTE IV:ARREGLOS Y ESTRUCTURAS


for(j=0; j<N-1; j++){/*la burbuja otra vez*/
for(k=0; k<N-1; k++){
if(c[k][i] > c[k+1][i]){
swap = c[k][i];
c[k][i] = c[k+1][i];
c[k+1][i] = swap;
}
}
}
}
/*ya se ordeno nodo el arreglo*/
}
Est muy complicado explicar como funciona el mtodo de la burbuja en este
caso, bsicamente lo que hace es separar el arreglo primero en renglones, eso
lo hace en el ciclo for(i=0; i<N; i++) as la burbuja trabaja con tres vectores
separados de tres elementos cada uno. Luego de que termin de ordenar cada
rengln separa el arreglo por columnas con el ciclo for(i=0; i<M; i++), as la
burbuja trabaja con tres vectores separados de 4 elementos cada uno y los va
tratando de uno por uno.
Fjate en la lnea que dice printf("\x7c\n"), recuerdas qu significa \xnn? Era
para poner algn valor hexadecimal, la clave ASCII de "|" es 124, que es lo
mismo que 7c en hexadecimal, el escribir \x7c es como decir "oye, tradceme
ese cdigo ASCII para que se vea bien en la pantalla" prueba el poner esto
printf("\x48\x6f\x4c\x61\x20\x4d\x75\x4e\x64x4f\x21\x21\x21"), tendrs un bonito
HoLa MuNdO!!! en la pantalla, pero es mucho ms lento.

Arreglos con tamao no predefinido


Se puede solicitar el tamao de cualquier arreglo y trabajar con funciones,
haremos algo parecido a como le hicimos con los arreglos de una dimensin.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
65

PARTE IV:ARREGLOS Y ESTRUCTURAS

void llenar(int m, int n, int a[][n]){


int i, j;
srand(time(NULL));
for(i=0; i<m; i++){
for(j=0; j<n; j++){
a[i][j] = rand()%(n*4);
}
}
}
void mostrar(int m, int n, int a[][n]){
int i, j;
for(i=0; i<m; i++){
for(j=0; j<n; j++){
printf("%d\t", a[i][j]);
}
putchar('\n');
}
}
void transponer(int m, int n, int a[][n]){
int i, j, t;
for(i=0; i<n; i++){
for(j=0; j<m; j++){
printf("%d\t", a[j][i]);
}
putchar('\n');
}
}
66

PARTE IV:ARREGLOS Y ESTRUCTURAS

int main(int argc, char **args){


int a, b;
printf("Dime la cantiad de filas: ");
scanf("%d", &a);
printf("Dime la cantiad de columnas: ");
scanf("%d", &b);
if(a>1 && b>1){
int arreglo[a][b];
llenar(a, b, arreglo);
printf("Tu arreglo es:\n");
mostrar(a, b, arreglo);
printf("Tu arreglo transpuesto es:\n");
transponer(a, b, arreglo);
}else{
printf("Ese tamao no es vlido\n");
}
return 0;
}
Bsicamente es lo mismo, lo que vale la pena que veas es que en las funciones
los parmetros se definen utilizando las dimensiones, las cuales son parmetros
tambin (void transponer(int m, int n, int a[][n])).

Arreglos de ms dimensiones
Llevo dos aos programando en C, es muy poco, pero ya me s algunos buenos
trucos, es que C es muy sencillo y compacto. En este tiempo nunca he
necesitado una matriz de ms de dos dimensiones, pero el siguiente programa
(lo hice por curiosidad) trabaja con una matriz de tres dimensiones. Es muy
complicado ver como inicializa los valores y cmo se pueden acomodar (al
menos lo es para m), pero pens que sera una buena referencia.

67

PARTE IV:ARREGLOS Y ESTRUCTURAS


#include<stdio.h>
#define N 4
#define M 3
#define O 2
void imprime(int [][N][O]);
int main(int argc, char **args){
int a[][M][O] = {23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,
5, 4, 3, 2, 1, 0};
imprime(a);
return 0;
}
void imprime(int b[][M][O]){
int i, j, k;
for(i=0; i<N; i++){/*avanza sobre los renglones*/
for(j=0; j<M; j++){/*avanza sobre las columnas*/
for(k=0; k<O; k++){/*avanza "hacia al fondo" :/ ? */
printf("| %2d ", b[i][j][k]);
}
printf("- ");
}
printf("\n");
}
}
Ya que el trabajar con matrices en programacin es un poco complicado
(supongo que porque no vemos los datos fsicamente) creo que es necesario
que te ponga dos programas, uno es como de prctica y el otro es un pequeo
68

PARTE IV:ARREGLOS Y ESTRUCTURAS


reto, aunque la respuesta es sencilla.
El primer programa debe presentar un men con cuatro opciones, la primera es
cargar dos arreglos de 3 x 3 con los datos del usuario, la segunda es sumar las
dos matrices y el resultado hay que almacenarlo dentro del segundo arreglo, la
tercer opcin sera imprimir el arreglo (y que se vea presentable), por ltimo la
opcin para salir.
El segundo va a tener cuatro opciones tambin, la primera es cargar dos
arreglos de 3 x 3, la segunda es multiplicar los dos arreglos y guardar el
resultado en un tercer arreglo, por ltimo hay que imprimir el resultado en la
opcin tres, y la opcin 4 sera salir del programa.
Mucha suerte, pareciera que no, pero en el libro viene todo lo que necesitas para
hacer los programas, el ingenio lo pones t, dale una repasada al libro, ya
hicimos un programa que presenta un men ms o menos decente y ya hemos
trabajado con arreglos de dos dimensiones, el primer programa no est tan
difcil, pero el segundo tiene su chiste. Te recomiendo que hagas un trazado de
cmo lo haras si t fueras la computadora, usa lpiz y papel para ir paso por
paso y as te inventas un mtodo, Cuando hagas tus programas has las pruebas
de escritorio, que son escribir paso por paso lo que va haciendo el programa, as
vez qu te falta o qu te sobra. chale ganas y tomate el tiempo que necesites.
Cuando termines con eso y ya te sientas bien con los arreglos vamos a pasar al
siguiente tema y vamos a ver las estructuras, adems de otro uso de los
arreglos, las cadenas de caracteres, que para entonces sern pan comido para
ti.

Estructuras.
Ya vimos que un arreglo puede contener varias variables del mismo tipo, pero
ahora hay un problema, el siguiente problema es que necesitamos capturar y
listar la informacin de 10 alumnos, la informacin de cada alumno tiene su
nombre, su matrcula y su promedio. Son muchos datos, necesitamos alguna
estructura de datos que los pueda contener todos. El siguiente programa va a
contener tres variables distintas dentro de la misma estructura, vamos a ver
cmo se da de alta una estructura (declararla), y cmo manipular sus datos.
#include<stdio.h>
struct datos{
int a;
float b;
69

PARTE IV:ARREGLOS Y ESTRUCTURAS


char c;
};
int main(int argc, char **args){
struct datos misdatos;/*asi podremos usar la estructura*/
misdatos.a = 4; /*asi se accede a los datos de la estructura*/
misdatos.b = 5.5;
misdatos.c = '!';
printf("Dentro de la estructura a vale %d, b vale %.1f. Que padre%c\n",
misdatos.a, misdatos.b, misdatos.c);
return 0;
}
No es tan difcil, una estructura es como un tipo de dato, cuando escrib struct
datos misdatos fue como decir "oye, crame una variable que se llame misdatos
para usar la estructura esta". Y para acceder a los datos de la estructura pues lo
hacemos con el punto, para separar el nombre de nuestra variable especial
(misdatos) y el elemento que est adentro de la estructura.
#include<stdio.h>
struct datos{
int a;
float b;
char c;
};
int main(int argc, char **args){
struct datos misdatos;/*asi podremos usar la estructura*/
misdatos.a = 4; /*asi se accede a los datos de la estructura*/
misdatos.c = '!';
70

PARTE IV:ARREGLOS Y ESTRUCTURAS


printf("Cuanto quieres que valga b? ");
scanf("%f", &misdatos.b);
printf("Dentro de la estructura a vale %d, b vale %.1f. Que padre%c\n",
misdatos.a, misdatos.b, misdatos.c);
return 0;
}
Ves? Puedes hacer lo que sea, no hay problema, puedes hacer arreglos de
estructuras, pasarlas a las funciones, ordenarlas, lo que sea. En el siguiente
programa vas a ver otra forma de declarar una estructura, en los dos programas
anteriores misdatos es local, pero se puede hacer como una variable global,
recuerdas qu es una variable global?
#include<stdio.h>
struct datos{
int a;
float b;
char c;
}misdatos;
int main(int argc, char **args){
misdatos.a = 4; /*asi se accede a los datos de la estructura*/
misdatos.c = '!';
printf("Cuanto quieres que valga b? ");
scanf("%f", &misdatos.b);
printf("Dentro de la estructura a vale %d, b vale %.1f. Que padre%c\n",
misdatos.a, misdatos.b, misdatos.c);
return 0;
}
As se declara el nombre de la estructura en forma global, creo que ya ests listo
71

PARTE IV:ARREGLOS Y ESTRUCTURAS


para seguir con el problema del principio de la seccin.

Cadenas de caracteres
Vamos a hacer un arreglo de diez estructuras, pero a nivel local en main, ese
arreglo de estructuras va a almacenar los datos de los diez alumnos (nombre,
matrcula y promedio) los va a ordenar en base a la matrcula y por ltimo los va
a imprimir en orden.
#include<stdio.h>
#include<string.h>
#define A 10 /*corresponde a alumnos*/
#define N 50 /*largo del nombre*/
struct alumnos{
int mat;
char nombre[N]; /*un arreglo de caracteres*/
float prom;
};
void captura(struct alumnos []);
void ordena(struct alumnos []);
void imprime(struct alumnos []);
int main(int argc, char **args){
struct alumnos a[A]; /*se supone que as declaramos el arreglo de estructuras*/
captura(a);
ordena(a);
imprime(a);
return 0;
}
72

PARTE IV:ARREGLOS Y ESTRUCTURAS

void captura(struct alumnos a[]){


int i, j;
for(i=0; i<A; i++){/*unna vualta para cada alumno*/
printf("\nIntroduce los datos del alumno %d\n", i+1);
printf(" Matricula: ");
scanf("%d", &a[i].mat);
getchar();
printf(" Nombre: ");
j = 0;
/*este ciclo tan raro captura el nombre*/
while((a[i].nombre[j] = getchar()) != '\n' && (j++ < N));
a[i].nombre[j] = '\0';
printf(" Promedio: ");
scanf("%f", &a[i].prom);
}
}
void ordena(struct alumnos a[]){
int i,j;
/*variables de intercambio*/
int smat;
char snombre[N];
float sprom;
for(i=0; i<A-1; i++){
for(j=0; j<A-1; j++){
if(a[j].mat > a[j+1].mat){
/*cambiamos miembro por mienbro*/
smat = a[j].mat;
strcpy(snombre, a[j].nombre);
73

PARTE IV:ARREGLOS Y ESTRUCTURAS


sprom = a[j].prom;
a[j].mat = a[j+1].mat;
strcpy(a[j].nombre, a[j+1].nombre);
a[j].prom = a[j+1].prom;
a[j+1].mat = smat;
strcpy(a[j+1].nombre, snombre);
a[j+1].prom = sprom;
}
}
}
}
void imprime(struct alumnos a[]){
int i;
printf("Lista de los alumnos\n");
for(i=0; i<A; i++){
printf("\nAlumno %d\n", i+1);
printf(" Matricula: %d\n", a[i].mat);
printf(" Nombre: %s\n", a[i].nombre);
printf(" Promedio: %.2f\n", a[i].prom);
}
}
Como ves no hay limitaciones cuando se trabaja con estructuras (struct),
pudimos hacer varias cosas con los datos de las estructuras, no creo que sea
muy complicado para ti entender este programa. Simplemente tratamos a las
estructuras como cualquier otro tipo de dato. No hay mucho problema por eso,
hasta para pasar la estructura a las funciones no se batall mucho, solo pusimos
que era la estructura fulana de tal y que era un arreglo. Lo que me interesa que
veas es como manejamos las cadenas de caracteres.
En la lnea char nombre[N] estamos declarando un arreglo un arreglo de N
caracteres, no es algo nuevo, pero hay un detalle que es ms o menos
74

PARTE IV:ARREGLOS Y ESTRUCTURAS


importante. Mira las siguientes lneas del programa.
while((a[i].nombre[j] = getchar()) != '\n' && (j++ < N));
a[i].nombre[j] = '\0';
En el ciclo while estoy metiendo los caracteres de uno por uno hasta que se
introduce un enter, es un ciclo que no lleva nada adentro, todo serio, pero hay
una lnea despus, que es a[i].nombre[j] = '\0', qu es '\0'? Diagonal inversa
cero quiere decir "fin de lnea", has un experimento, escribe un programa cuya
nica lnea de salida sea printf("Hola mun\0do\n") y ve que es lo que pasa. En el
programa que acabamos de ver declar el nombre con 50 caracteres, siempre
que se declara un arreglo el ltimo elemento es '\0', solo que es invisible para el
programador, ese espacio no lo contamos y est bien, podemos meter 50
caracteres a nuestro arreglo char nombre[N] y todo va a estar bien. Pero en el
caso de que solo metamos 23 caracteres y luego mandemos a imprimir el
arreglo vamos a toparnos con un mal detalle, imprime nuestros 23 caracteres
mas 27 de pura basura, por eso puse la lnea a[i].nombre[j] = '\0' al final, para
que ponga el fin de la cadena justo despus de que terminemos de escribir.
Espero que eso haya quedado ms o menos claro, ahora quiero que veas la
lnea strcpy(snombre, a[j].nombre) Esta es una funcin que viene en la librera
string.h y copia cadenas, lo que hace es copiar el contenido de a[j].nombre a
snombre, hay ms funciones para el manejo de cadenas de caracteres en esa
librera, pero no se pueden ver todas.
Por otro lado hay otras funciones de entrada y salida, que las podemos
encontrar en la librera stdio.h, te acuerdas de la funcin putc? De getchar ni te
pregunto, pues estas funciones son las hermanas, solo que trabajan con
cadenas. Mira el siguiente cdigo.
#include<stdio.h>
int main(int argc, char **args){
char cadena[50];
printf("Introduce una frase: ");
gets(cadena);
puts(cadena);
printf("%s", cadena);
return 0;
}
75

PARTE IV:ARREGLOS Y ESTRUCTURAS

Con estas funciones, me refiero mucho ms a gets4, nos podemos olvidar,


aunque no del todo por favor, del detalle del famoso '\0', funcionan bien.
Solo falta el cmo inicializar una cadena, sencillo...
#include<stdio.h>
int main(int argc, char **args){
char cadena[] = "Hola peque\xa4o saltamontes";
puts(cadena);
return 0;
}
Ya estamos muy avanzados en el lenguaje C, solo necesitas mucha prctica, te
voy a poner un problema ms o menos interesante. Has un programa que
capture una cadena, no importa el largo, siempre que sean ms de cinco letras,
e imprima la cadena en orden inverso, te recomiendo que no uses gets sino el
ciclo while para capturar y tambin para imprimir.
Pues a practicar y a practicar, una vez que te sientas bien con lo de las cadenas
y las estructuras vamos a pasar a la parte 5, vamos a ver lo bueno de C, lo que
le da su potencia, son los apuntadores, es un tema que puede ser un fastidio,
pero cuando entiendes bien como funcionan los apuntadores puedes hacer
bastantes cosas sin mucha dificultad.

PARTE V: APUNTADORES
Algo que hace de C un lenguaje muy potente son los apuntadores, nos dan
mucha flexibilidad y potencia. Digamos que la memoria principal del sistema es
como un barrio, un apuntador sera el viejito que se sabe las direcciones de
todos los que viven ah, te puede decir la informacin que necesitas de cada
persona que vive en ese barrio sin que tengas que ir hasta ella. En los
problemas de memoria dinmica, por ejemplo, hay una funcin que encuentra un
espacio libre y lo aparta, luego esa funcin regresa la direccin de la memoria
que puede ser utilizada, para acceder a esa direccin se necesitan apuntadores.
O para manejar cadenas de caracteres y arreglos (es lo mismo), en realidad lo
que se hace es trabajar con apuntadores. Es mucho ms fcil y eficiente poner
4

La funcin gets se considera insegura y se recomienda fgets en cambio

76

PARTE V: APUNTADORES
un dato en una direccin de memoria, hacer un apuntador hacia esa variable y
poder trabajar con esa variable a travs del apuntador, a distancia. En este tema
vamos a trabajar mucho tiempo, al principio los apuntadores son complicados,
sobre todo al tratarse de cadenas y arreglos (aunque sea lo mismo los vamos a
diferenciar), pero se usan mucho, ya lo vers. Entonces es mejor trabajar mucho
con esto.

Apuntadores a variables.
Lo ms bsico de un apuntador es el que apunta a una variable sencilla, vamos
a ver qu significa el ampersand (&) de la funcin scanf y tambin los dos
trminos bsicos tratando con apuntadores.
Lo primero que quiero que hagas (por el momento), es que aprendas esto.
& Significa "direccin de" y * significa el "valor que esta en (tal direccin)".
Cmo es esto? Mira el siguiente cdigo y por favor escrbelo y hazlo correr.
#include<stdio.h>
int main(int argc, char **args){
int var;
printf("La direccion en memoria de var es %p\n", &var);
return 0;
}
La salida del programa es una lnea como "printf("La direccion en memoria de
var es %p\n", &var);", espero que ya te haya cado el veinte de lo que quiere
decir el &, se refiere a la direccin de memoria de la variable. Entonces ahora te
puedo decir que al escribir scanf("%d", &variable) estamos diciendo algo como
"recoge un valor entero decimal y lo pones en la direccin de memoria fulana de
tal".
Un apuntador es una variable pequea que guarda la direccin de memoria de
una variable, puede ser de cualquier tipo, desde int hasta struct, aparte puede
ser void (apuntador genrico). En el siguiente cdigo se muestra como usar un
apuntador.
#include<stdio.h>
77

PARTE V: APUNTADORES

int main(int argc, char **args){


int a;
int *b = &a; /*el apuntador se inicializa con la direccin a donde va a apuntar*/
printf("La direccion de a es %p (en hexadecimal)\n", &a);
printf("El puntero b apunta hacia esta direccin %p\n", b);
printf("Introduce el valor de a: ");
scanf("%d", &a);
printf("El valor de *b es %d\n", *b);
printf("Vuelve a cambiar el valor de a: ");
scanf("%d", b);
printf("Ahora a vale %d\n", a);
printf(" a + 2 es %d y *b + 2 es %d\n", a+2, *b+2);
return 0;
}
En este programa hay dos cosas muy importantes, *puntero representa lo que
vale la variable o sea que variable es ms o menos igual a *puntero y puntero
representa la direccin de la variable o sea que &variable es como puntero.
Vamos a ver el cdigo parte por parte, espero que ya lo hayas compilado y
hecho correr.
int a;
int *b = &a;
En estas lneas declaramos una variable entera, luego declaramos un puntero y
lo inicializamos, se debe inicializar con la direccin de la variable a la que va a
apuntar, aparte ambos deben de ser del mismo tipo, un puntero char no puede
apuntar a un flotante, as que tuve que declarar un puntero int para apuntar a
una variable int.
printf("La direccion de a es %p (en hexadecimal)\n", &a);
printf("El puntero b apunta hacia esta direccin %p\n", b);
78

PARTE V: APUNTADORES
En estas lneas imprimimos la direccin de la variable a en cdigo hexadecimal,
en la segunda lnea imprimimos el valor verdadero del apuntador b, que es la
direccin de a.
printf("Introduce el valor de a: ");
scanf("%d", &a);
printf("El valor de *b es %d\n", *b);
En este pedazo vemos como podemos cambiar el valor de a y verlo usando el
apuntador, nota que al poner el asterisco ponemos el valor que est en dicha
direccin de memoria, recuerda lo que escrib antes: "& significa 'direccin de' y *
significa el 'valor que esta en (tal direccin)'".
printf("Vuelve a cambiar el valor de a: ");
scanf("%d", b);
printf("Ahora a vale %d\n", a);
Aqu cambiamos el valor de a usando su apuntador en la funcin scanf,
acurdate lo que significa poner el ampersand en una variable normal, aqu
usamos el apuntador y obtenemos el mismo resultado.
printf(" a + 2 es %d y *b + 2 es %d\n", a+2, *b+2);
En esta lnea puedes ver cmo se pueden hacer operaciones tanto con la
variable como con el apuntador (usando su asterisco claro).

Apuntadores y funciones.
Vamos a ver un uso de los apuntadores, uno bueno, espero recuerdes que
cuando pasamos un valor a una funcin lo que se haca en realidad era copiarlo
y trabajar con la copia. A veces no queremos que un programa haga eso,
observa el siguiente cdigo y ve la diferencia.
#include<stdio.h>
void min(char *letra){
if(*letra >= 65 && *letra <= 90){
*letra += 32;
79

PARTE V: APUNTADORES
}
}
int main(int argc, char **args){
char letra;
char *pletra = &letra;
printf("Introduce una letra en mayusculas ");
letra = getchar();
min(pletra);
printf("Ahora la letra es %c\n", letra);
return 0;
}
Acurdate del programa que hicimos hace rato donde cambiaba las letras a
minsculas, en este programa escrib una versin usando punteros, hay una
diferencia muy grande, el valor no se copia, en realidad cambia el original.
void min(char *letra){
if(*letra >= 65 && *letra <= 90){
*letra += 32;
}
}
En la declaracin de la funcin especifico que va a recibir un apuntador, luego le
doy el tratamiento como a cualquier variable, cambio su valor y la funcin
termina,
min(pletra);
Cuando llamo a la funcin le paso el apuntador que haba declarado e
inicializado con la variable que nos interesa cambiar a minsculas.
Lo que pasa es lo siguiente, cuando declaro la funcin le digo que va a recibir un
apuntador, nada del otro mundo, cuando llamo la funcin le mando un
apuntador, todo en orden. Dentro de la funcin se trabaja como con cualquier
80

PARTE V: APUNTADORES
variable normal, pero no regresa ningn valor. Lo que pasa es que el puntero s
se copia, el truco es que aunque sea una copia el apuntador de la funcin
apunta hacia donde mismo, entonces la variable original cambia aunque no est
dentro de la funcin, es algo as como una llamada de larga distancia.
Otra forma de escribir el programa sera esta.
#include<stdio.h>
void min(char *letra){
if(*letra >= 65 && *letra <= 90){
*letra += 32;
}
}
int main(int argc, char **args){
char letra;
printf("Introduce una letra en mayusculas ");
letra = getchar();
min(&letra);
printf("Ahora la letra es %c\n", letra);
return 0;
}
La diferencia es que no declaramos un apuntador en main, al llamar a la funcin
simplemente le mandamos la direccin de la variable que nos interesa cambiar.
Aqu el apuntador no se copia, simplemente que el apuntador de la funcin min
se carga con la direccin de la variable que vamos a convertir en minsculas.
Vamos a ver un ltimo cdigo antes de pasar al siguiente tema, este programa
va a capturar un nmero flotante y lo va a convertir a su cuadrado.
#include<stdio.h>

81

PARTE V: APUNTADORES
void cuad(float *);
void main(void){
float num;
printf("Introduce un numero: ");
scanf("%f", &num);
cuad(&num);
printf("El cuadrado de tu numero es %f\n", num);
}
void cuad(float *a){
*a *= *a;
}

Apuntadores y arreglos.
Escribir este librito me va a servir mucho, el tema que vamos a ver enseguida se
me complica bastante. Hay algo que has de saber, un arreglo es un apuntador
que apunta a espacios de memoria contiguos. Si pones una lnea como int a[3]
ests apartando tres espacios de memoria que estn uno tras otro y a es su
apuntador, ciertamente. Cuando ponemos algo como a[2] estamos diciendo
"smale dos espacios al apuntador este a ver que se encuentra ah", podras
poner a[234], no te va a poner ningn error, simplemente va a apuntar a algn
lugar de la memoria, el secreto es que *a es lo mismo que a[0]; y que *(a+1) es
lo mismo que a[1]. Mira el siguiente cdigo y ve el resultado.
#include<stdio.h>
void recorre(int *, int []);
int main(int argc, char **args){
int arr[3] = { 2, 5, 8};
printf(" Te voy a probar que un arreglo es un apuntador\n\n");
recorre(arr, arr);
82

PARTE V: APUNTADORES
return 0;
}
void recorre(int *a, int b[]){
printf(" *a = %d y b[0] = %d\n", *a, b[0]);
printf(" *(a+1) = %d y b[1] = %d\n", *(a+1), b[1]);
printf(" *(a+2) = %d y b[2] = %d\n", *(a+2), b[2]);
printf(" *(a+3) = %d y b[3] = %d, pero ya nos pasamos\n", *(a+3), b[3]);
printf(" *(a-1) = %d Y b[-1] = %d, pero estamos muy atras\n", *(a-1), b[-1]);
}
Mira el cdigo y ve como es lo mismo tratar con el arreglo que con el apuntador,
porque un apuntador y un arreglo son bsicamente lo mismo.
Otra forma de ver que un arreglo es lo mismo que un apuntador esta en este
cdigo.
#include<stdio.h>
void recorre(int []);
int main(int argc, char **args){
int arr[3] = {2, 5, 8};
printf(" Te voy a probar que un arreglo es un apuntador\n\n");
recorre(arr);
return 0;
}
void recorre(int b[]){
printf(" *b = %d\n", *b);
printf(" *(b+1) = %d\n", *(b+1));

83

PARTE V: APUNTADORES
printf(" *(b+2) = %d\n", *(b+2));
printf(" *(b+3) = %d, pero ya nos pasamos\n", *(b+3));
printf(" *(b-1) = %d, pero estamos muy atrs\n", *(b-1));
}
Es lo mismo, aqu es ms clara la relacin entre un arreglo y un apuntador, son
como gemelos todos los gemelos tienen sus diferencias, pero tienen mucho
parecido.
Ten cuidado en el uso de los parntesis, no es lo mismo *(b+1) que *b+1, *b+1
quiere decir que se le sume 1 a la variable que est en dicha direccin, y *(b+1)
es la variable que est una casilla despus de b.
Casi siempre se obtiene mejor desempeo cuando se usan punteros, espero te
acuerdes de lo que hacen las funciones gets y puts, porque las vamos a clonar
usando punteros.
#include<stdio.h>
void clon_de_gets(char *);
void clon_de_puts(char *);
int main(int argc, char **args){
char str[10];
clon_de_puts("Escribe tu cadena aqui: ");
clon_de_gets(str);
clon_de_puts(str);
return 0;
}
void clon_de_gets(char *a){
while((*a++ = getchar()) != '\n');
*--a = '\0';
}
84

PARTE V: APUNTADORES

void clon_de_puts(char *a){


while(*a != '\0'){
putchar(*a++);
}
}
Prueba el programa, creo que los resultados son muy buenos, la funcin
clon_de_gets tiene la misma falla que la gets original, se desborda. En la funcin
clon_de_puts '\0' se utiliza para poner un lmite, si no existiera se hara un ciclo
infinito. Fjate el cmo est la lnea putchar(*a++), efectivamente, el puntero se
va incrementando, al incrementarse el puntero pasa a la posicin siguiente del
arreglo. Lo mismo pasa en la lnea while((*a++ = getchar()) != '\n'), el puntero
recibe el valor que le manda getchar y luego se pasa a la siguiente posicin del
arreglo, aqu poner *a++ es lo mismo que poner *(a++) por eso no puse
parntesis.
En la parte anterior te ped que hicieras un programa que capturara una cadena
y luego la imprimiera al revs, vamos a hacer ese programa usando punteros y
unas variaciones de nuestros clones de puts y gets.
#include<stdio.h>
#define N 10
int llena(char *, int);
void imprime(char *, int);
void clon_de_puts(char *);
int main(int argc, char **args){
char str[N];
int posicion;
clon_de_puts("Escribe tu cadena aqui: ");
posicion = llena(str, N);
85

PARTE V: APUNTADORES
imprime(str, posicion);
return 0;
}
int llena(char *a, int largo){
int i = 0;
while((*(a+i) = getchar()) != '\n' && i++ < largo);
return i;
}
void clon_de_puts(char *a){
while(*a != '\0'){
putchar(*a++);
}
}
void imprime(char *a, int p){
while(p >= 0){
putchar(*(a + --p));
}
putchar('\n');
}
Sencillo, la funcin llena va contando los caracteres que van entrando y vigila
que no se introduzcan ms caracteres de los que caben en la funcin, as se
corrige la falla del clon de gets, luego regresa el nmero de caracteres que
entraron a la funcin.
La funcin que imprime al revs recibe como parmetro el nmero de caracteres
que entraron a la cadena y a partir de ah empieza a imprimirlos en reversa
usando la notacin para los arreglos "*(a + --p)" as que lo que hace es restar
uno a p y luego sumarlo a la posicin de a para ir imprimiendo, puse --p en vez
de p-- porque el ltimo caracter que entr fue un enter, as que con --p el enter
86

PARTE V: APUNTADORES
no aparece, puedes cambiarlo por p-- para que veas la diferencia. Algo que se
me hizo muy interesante fue que la funcin se pudo escribir as.
void imprime(char *a, int p){
while(p >= 0){
putchar(a[--p]);
}
putchar('\n');
}
Puedes convertir arreglos para trabajarlos como apuntadores o puedes tratar
apuntadores como si fueran arreglos. Claro que esto lo puedes hacer solo hasta
cierto punto.

Arreglos de apuntadores.
Hace tiempo hice un juego que te responde tus preguntas acerca del futuro con
frases como s, no, seguramente, no me interesa, etc. haba respuestas muy
largas y otras muy cortas, todas esas cadenas de caracteres las tuve que
guardar en un arreglo para seleccionar la respuesta aleatoriamente, pero en las
respuestas muy cortas se desperdiciara mucho espacio ya que era un arreglo
de 20 por 30, as que en respuestas como si o no desperdiciara 28 espacios de
memoria. Funcionara bien, pero lo ptimo sera un arreglo de apuntadores.
El siguiente cdigo es el cdigo que hice, lo he mejorado un poquito, pero
bsicamente es lo mismo.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define N 20
char *respuesta(void);

87

PARTE V: APUNTADORES
int main(int argc, char **args){
do{
system("cls");
puts("\t\t\t
puts("

EL ORCULO DE LYNX\n");

Escribe tu pregunta, si lo que quieres es salir pulsa control + c: ");

printf("\t

>>>");

gets(NULL);
printf("\n

%s\a\n", respuesta());

printf("\n

Pulsa enter para seguir");

getchar();
}while(1);
return 0;
}
char *respuesta(void){
char *resps[] = {
"Definitivamente s :D",
"S :)",
"Busca la respuesta en tu corazn :o",
"Es muy probable :)",
"Eso me parece bien :D",
"Es mejor cambiar de tema :\\",
"Concntrate y vuelve a hacer la pregunta :|",
"Cualquier cosa puede suceder :\\",
"Las cosas son inciertas :$",
"No te puedo contestar ahora :|",
"No s y no me importa :@",
"Los ngenles de Charlie fu un churro cinematogrfico :o",
"Hay cosas que es mejor ignorarlas :o",
"Sigue as y no habr problemas :)",
88

PARTE V: APUNTADORES
"Eso me parece mal :|",
"Las posibilidades son pocas :(",
"Prefiero no contestar :@",
"Tal vez no :(", "No :\\",
"Definitivamente no :b"
};
srand(time(NULL));
return (resps[rand() % N]);
}
Prueba el programa, a m me gust, para ser de menos de cincuenta lneas est
bien. Vmonos parte por parte para que cheques el cdigo.
char *respuesta(void);
Te has de preguntar por qu puse un asterisco en el nombre de la funcin, lo
que pasa es que esta funcin regresa un apuntador, creme, no es ningn error.
system("cls");
Ya la habamos visto antes, solo para recordar, la funcin system llama a los
comandos del sistema, aqu lo que se hace es limpiar la pantalla.
gets(NULL);
Recuerda que gets recibe como parmetro un arreglo de caracteres (char []),
aunque en realidad su declaracin es gets(char *), para el caso es lo mismo,
recibe un apuntador para poner las letras ah, pero en este caso le mandamos
un NULL, as agarra los datos del teclado, pero no los pone en ningn lado.
printf("\n

%s\a\n", respuesta());

La funcin respuesta regresa un apuntador a una cadena, pero se supone que


%s recibe un arreglo de caracteres (char []), el caso es lo mismo (te lo repito una
ves ms, un arreglo es mas o menos un apuntador), respuesta le da a %s su
cadena de caracteres para que los imprima.

89

PARTE V: APUNTADORES

char *resps[] = {
"Definitivamente s :D",
"S :)",
...
...
...
"Definitivamente no :b"
};
En estas lneas estoy declarando un arreglo, un arreglo de apuntadores a char
(char * []). Recuerda que declarar char a[] = "hola" es lo mismo que char *b =
"hola", aqu lo que hice fue hacer un "arreglo de apuntadores a char" o un
"arreglo de arreglos char" o un "arreglo de cadenas", como lo digas a mi parecer
est bien dicho.
srand(time(NULL));
return (resps[rand() % N]);
Acurdate de la funcin para generar nmeros aleatorios, lo que se hace es
seleccionar un puntero de entre el arreglo podra ser resps[1] o resps[6], pero es
al azar, luego lo mandamos con el return.
Para que te quede ms claro el asunto de los apuntadores y arreglos mira el
siguiente cdigo.
#include<stdio.h>
void pon_cadena(char *a){
while(*a != '\0'){
putchar(*a++);
}
}

90

PARTE V: APUNTADORES
int main(int argc, char **args)
{
int i = 0;
char *rollo[] = {
"Hola ",
"saltamontes, ",
"espero ",
"que ",
"todo ",
"este ",
"rollo ",
"te ",
"sirva ",
"de ",
"algo ",
'\0'
};
while(rollo[i] != '\0'){
pon_cadena(rollo[i++]);
}
pon_cadena("\n");
return 0;
}
Para que te sea ms fcil entenderle este cdigo es ms sencillo. La diferencia
es que el ltimo elemento del arreglo de apuntadores char es que el ltimo
elemento es '\0', se lo puse para que el ciclo donde imprime todo el rollo no se
pase, porque de lo contrario imprime el rollo este y luego se pone a imprimir la
marca del compilador. Si s de donde saca esos datos, pero no tiene caso
explicrtelo todo, solo que son cadenas que el programa tiene perdidas por ah.
91

PARTE V: APUNTADORES

Argumentos de la lnea de comandos.


Hay programas que reciben argumentos para realizar su trabajo, por ejemplo el
programa del, que tiene que recibir el nombre del archivo que va a eliminar. O el
programa mplayer que puede recibir como parmetros qu archivo va a
reproducir y si la reproduccin va a ser automtica.
Vamos a escribir un programa que reciba nuestro nombre como parmetro y lo
imprima junto con un saludo.
#include<stdio.h>
int main(int argc, char **args){
int i;
printf("Hola ");
for(i=1; i<argc; i++){
printf("%s ", args[i]);
}
puts("Cmo ests?");
return 0;
}
Es un estndar que todos los programas reciban como parmetros un contador
de argumentos y un arreglo de apuntadores con los argumentos separados por
palabras. No tiene mucha ciencia, todo lo dems ya lo sabes. El programa recibe
el arreglo y t le das el tratamiento que quieras. El contenido del arreglo que se
recibe es el siguiente: nombre del programa, Argumento 1, Argumento 2 ... Por
eso empec a imprimir el contenido de este desde la segunda posicin para no
imprimir el nombre del programa.
Ya terminamos lo referente a apuntadores y su relacin con los arreglos. Algo
extra es que un apuntador nos sirve para ms de lo que vimos aqu, por medio
de punteros puedes acceder a los puertos de la computadora, a los dispositivos
como el disco duro y a casi todo. Todos los dispositivos de la computadora
tienen una direccin de memoria y esta puede ser accedida por medio de
apuntadores, te recomiendo que tengas mucho cuidado cuando trabajes con
apuntadores, nunca dejes un apuntador sin inicializar porque puede apuntar
hacia cualquier lado, he conocido gente que ha perdido su disco duro por no
92

PARTE V: APUNTADORES
tener cuidado. Ya estamos listos para pasar a la siguiente parte del libro, los
archivos, vamos a ver si podemos hacer una pequea agenda con la
informacin de tus amigos. Tambin vamos a hacer un programa que te haga un
test y guarde el resultado en un archivo para que lo leas despus. Tambin
vamos a hacer un programa que lea texto desde un archivo y lo vaya
imprimiendo en la pantalla.

PARTE VI: ARCHIVOS


En la parte anterior vimos que los apuntadores se usan mucho, en los archivos
tambin se usan apuntadores, vamos a usar apuntadores de aqu hasta que
termine el libro. Este tema no me gusta, nunca me ha gustado y nunca me
gustar, pero se supone que tienes que aprender archivos. Voy a tratar de irme
rpido de aqu para entrar a un tema ms interesante, que trata con la memoria
dinmica.
El siguiente programa va a escribir una lnea de texto en un archivo, luego lo va
a leer.
#include<stdio.h>
int main(int argc, char **args){
FILE *archivo;
char c;
archivo = fopen("1.txt", "w");/*abrir para escritura*/
fprintf(archivo, "Este es un texto de prueba\a\n");/*escribir en el archivo*/
fclose(archivo);/*cerrar archivo*/
archivo = fopen("1.txt", "r");/*abrir para lectura*/
while((c = getc(archivo)) != EOF){
printf("%c", c);
}
fclose(archivo);
return 0;
}

93

PARTE VI: ARCHIVOS


No te creas, esto de los archivos es ms o menos interesante, pero a mi parecer
es de lo ms tedioso que hay porque debes que tener mucho cuidado en cmo
abres un archivo, en el hecho de que tienes que cerrar el archivo, para leer los
datos del archivo tienes que irte con mucho cuidado de no pasarte y separar
todo. Vamos a ver el programa parte por parte.
FILE *archivo;
Es un apuntador de tipo archivo (FILE), este lo vamos a usar para acceder al
archivo. No podemos ir hasta el disco duro y traer los datos, pero acurdate que
un apuntador es como una llamada de larga distancia, as que usamos un
apuntador y no batallamos.
archivo = fopen("1.txt", "w");
La funcin fopen recibe dos parmetros, el nombre del archivo entre comillas y
el modo, en este caso puse el modo w, que es de escritura, hace dos cosas, si el
archivo no existe lo crea, y si ya existe lo borra y lo vuelve a crear, as que ten
cuidado con eso. La funcin fopen regresa una direccin de memoria, el puntero
archivo guarda esa direccin y ya podemos trabajar con nuestro nuevo archivo.
fprintf(archivo, "Este es un texto de prueba\a\n");
Esta funcin, que tambin est en la librera stdio.h escribe todo lo que
queramos en el archivo, funciona exactamente igual que printf, solo que el
primer parmetro es el archivo (o ms bien la direccin del archivo) a donde
vamos a mandar los datos.
fclose(archivo);
Fclose cierra el archivo cuando ya terminamos de trabajar con l.
archivo = fopen("1.txt", "r");
Ya terminamos de escribir en el archivo, ahora tenemos que leerlo. Ahora el
modo no es w sino r, de lectura.
while((c = getc(archivo)) != EOF){
printf("%c", c);
94

PARTE VI: ARCHIVOS


}
Todos los archivos (al menos los que estn bien escritos) tienen al final un
caracter espacial que se llama EOF (End Of File), significa que el archivo ya
termin. La funcin getc recibe como parmetro el archivo al cual va a leer, lee el
primer caracter, regresa su valor y luego pasa al siguiente caracter y espera ah
hasta que vuelve a ser llamado. Ms claro, este ciclo va leyendo caracter por
caracter, cada caracter se guarda en c (c = getc(archivo)) mientras no sea el fin
de archivo, imprime c en la pantalla (printf("%c", c)) y lee el caracter siguiente.
As imprime todo el archivo sin detenerse.
El programa funciona bien, si lo compilaste y no lo corriste el archivo 1.txt no
existe, pero si lo corriste debe haber un archivo en el mismo directorio que se
llame 1.txt, brelo y ve qu es lo que tiene adentro.
Para el siguiente programa necesito que hagas algo, escribe un archivo que se
llame numeros.dat, luego escribe dos nmeros, los que quieras cuya suma sea
menor que 32767, pueden ser negativos si quieres. Los nmeros deben estar
separados por un espacio. En mi caso mi archivo qued as:
12 -7
Ya que hayas terminado de escribir el archivo numeros.dat, en el mismo
directorio escribe este programa. Crrelo y ve el resultado.
#include<stdio.h>
int main(int argc, char **args){
FILE *archivo;
int a,b;
const char *nombre = "numeros.dat";
archivo = fopen(nombre, "r");
if(archivo != NULL){
fscanf(archivo, "%d", &a);
fscanf(archivo, "%d", &b);
fclose(archivo);
printf("La suma de tus numeros es %d\n", a+b);
95

PARTE VI: ARCHIVOS


}else{
printf("No se puede abrir el archivo %s\n", nombre);
}
return 0;
}
El programa este tiene una nueva funcin, fscanf, es lo mismo que scanf, solo
que con archivos. Ah te va parte por parte.
const char *nombre = "numeros.dat";
El nombre del archivo qued como una constante, para no escribir tanto.
archivo = fopen(nombre, "r");
Se abre el archivo y se guarda el "resultado" en FILE *archivo.
if(archivo != NULL)
La funcin fopen regresa la direccin en memoria del archivo, pero si no pudo
abrir el archivo regresa un NULL, este if revisa que eso no haya pasado.
fscanf(archivo, "%d", &a);
fscanf(archivo, "%d", &b);
Estas funciones hacen el mismo trabajo que scanf, solo que en vez de recoger
los nmeros desde el teclado lo hacen desde el archivo. No es la gran cosa.
else{
printf("No se puede abrir el archivo %s\n", nombre);
}
Si FILE *archivo es NULL pues no se pudo abrir el archivo, es bueno decirle al
usuario cuando algo malo ha pasado.
Con lo que sabes hasta ahora podemos hacer un programa que nos lea texto y
lo imprima en la pantalla de modo secuencial, la cosa es que cuente caracteres
y cuando llegue a los 1760 caracteres se espere y avance slo cuando se
96

PARTE VI: ARCHIVOS


oprima una tecla.
#include<stdio.h>
#include<stdlib.h>
#define N 50
void leer(char *);
int main(int cuantos, char *argumentos[]){
if(cuantos > 1){
leer(argumentos[1]);
}else{
char cual[N];
printf("Escribe un nombre de archivo, pero no te pases de %d caracteres\n",
N);
printf(" >>>");
gets(cual);
leer(cual);
}
return 0;
}
void leer(char *nombre){
FILE *archivo;
int espacios = 0;
char caracter = ' ';
if((archivo = fopen(nombre, "r")) != NULL){
system("cls");
do{
97

PARTE VI: ARCHIVOS


caracter = getc(archivo);
switch(caracter){
case '\n':
espacios = espacios/80 + 1;
espacios *= 80;
break;
case '\t':
espacios += 8;
break;
case '\v':
espacios += 80;
break;
case '\b':
espacios -= 1;
break;
case '\r':
espacios = espacios/80;
espacios *= 80;
break;
default:
espacios++;
}
if(espacios >= 1760){
putchar('\n');
system("pause");
espacios = 0;
system("cls");
}
putchar(caracter);
}while(caracter != EOF);
98

PARTE VI: ARCHIVOS


putchar('\n');
}else{
printf("El archivo no se abrio\n");
}
}
Creo que no hay mucho que explicar, es un programa de 62 lneas y no tiene
nada nuevo para ti. El programa lee los caracteres del archivo y va contando los
caracteres que se imprimen en la pantalla, recuerda que estamos trabajando en
una consola de 80 x 25, por eso us tanto el nmero 80. En el switch lo que
hago es analizar los caracteres de entrada, por ejemplo si se recibe un enter
('\n') primero calculo las lneas que se han impreso con espacios = espacios / 80
+ 1, al sumarle un uno al ltimo se supone que agrego una lnea a las que ya se
imprimieron, luego multiplico esto por 80 y ya tengo los espacios de la pantalla
que se han utilizado, por ejemplo digamos que llevamos 135 caracteres, si
dividimos esto entre 80 nos dara 1 (porque estamos ponindolo en un int), le
sumamos 1 y tenemos dos lneas completas, lo multiplicamos por 80 y
obtenemos el espacio que se ha utilizado en la pantalla.
Hasta ahora hemos tenido que abrir y cerrar los archivos, para volverlos a abrir,
si primero queremos escribir algo en ellos y luego queremos leerlo. Hay otros
modos de trabajar con los archivos, los modos son los siguientes.

"w" Abre un archivo para escritura, si no existe lo crea y si existe lo


borra.
"r"

Abre un archivo para lectura, si no existe regresa error.

"a" Crea un archivo si no existe o lo abre para escritura, este modo


forza todas las escrituras al final del archivo.

"w+" Abre un archivo para escritura, si no existe lo crea, pero lo


trunca al igual que "w", pero aparte se puede leer su contenido cuando
se introducen datos.

"r+" Abre un archivo para lectura y escritura, si el archivo no existe la


llamada falla.

"a+" Abre el archivo para lectura y escritura, pero todas las escrituras
del archivo se aaden al final.

Este programa explica un poco cmo funcionan los modos de cmo abrir un
archivo y algo acerca de ciertos archivos especiales.

99

PARTE VI: ARCHIVOS

#include<stdio.h>
int main(int argc, char **args){
FILE *archivo;
char c;
archivo = fopen("texto.txt", "w+");
fputs("Este archivo se supone que se abri como lectura escritura\n", archivo);
rewind(archivo);
while((c = fgetc(archivo)) != EOF){
putchar(c);
}
puts("Escribe lo que quieras y termina con un EOF (control + z en dos y control
+ d en linux)");
while((c = getchar()) != EOF){
putc(c, archivo);
}
rewind(archivo);
fputs("\nEste es el contenido del archivo\n", stdout);
while((c = fgetc(archivo)) != EOF){
putchar(c);
}
100

PARTE VI: ARCHIVOS

fclose(archivo);
archivo = fopen("texto.txt", "a+");
fputs("\n final del archivo\n", archivo);
fclose(archivo);
return 0;
}
archivo = fopen("texto.txt", "w+");
Abre el archivo para escribir sobre l (hasta el final del archivo) y para leerlo.
rewind(archivo);
La funcin rewind se regresa al principio del archivo para volverlo a leer o para
reescribirlo desde el principio.
puts("Escribe lo que quieras y termina con un EOF (control + z en dos y
control + d en linux)");
while((c = getchar()) != EOF){
putc(c, archivo);
}
En dos el EOF es control + z, en este ciclo se pueden introducir caracteres
directamente sobre el archivo hasta que se detecte un EOF. Te recomiendo que
estudies la funcin int fflush(FILE *nombre_de_archivo), te aseguro que te ser
de utilidad algn da.
fputs("\nEste es el contenido del archivo\n", stdout);
Existen tres archivos que siempre usamos y nunca nos damos cuenta, son
stdout, stderr y stdin, y se refieren a la salida estndar, el error estndar y la
entrada estndar, generalmente son el monitor (stdout y stderr) y el teclado
(stdin), pero se pueden re-direccionar a algn otro dispositivo. En esta lnea la
funcin fputs imprime la cadena en la pantalla.

101

PARTE VI: ARCHIVOS


while((c = fgetc(archivo)) != EOF){
putchar(c);
}
La funcin fgetc es similar a getc, solo que sta s es una funcin y getc es una
macro. Aunque el resultado es el mismo.
fclose(archivo);
archivo = fopen("texto.txt", "a+");
fputs("\n final del archivo\n", archivo);
fclose(archivo);
En estas lneas cerramos el archivo y lo volvemos a abrir en modo append,
agregamos la lnea "\n final de archivo\n", que se coloca al final por la opcin
append y por ltimo cerramos el archivo.
El siguiente cdigo es un pequeo directorio de bsqueda secuencial, es muy
sencillo y es lo ltimo que vamos a ver en esta parte, para ya pasar a la
siguiente parte que son las listas enlazadas y otros mtodos para la memoria
dinmica, en la siguiente parte vamos a trabajar con ste mismo programa, pero
va a ser ms poderoso. Vamos a ver el cdigo y luego lo vamos a ver parte por
parte como siempre.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define D 80 /*largo de los dtos*/
#define NOM 12 /*largo del nombre*/
#define DAT "libro.dat" /*nombre del archivo*/
struct batos{
char nombre[NOM];
char datos[D];
};
102

PARTE VI: ARCHIVOS

int buscar(char *, FILE *);


void f_gets(char *, FILE *);
void agregar(FILE *, struct batos);
void consultar(FILE *, struct batos);
void minuscula(char *a){
if(*a >= 65 && *a <= 90){
*a += 32;
}
}
void limpiar(char a){
if(a != '\n'){
getchar();
}
}
int main(int argc, char **args){
FILE *dir;
char op;
struct batos bato;
do{
system("cls");
printf("

DIRECTORIO\n\n");

printf("\tMenu\n");
printf("

a) Consultar\n");

printf("

b) Agregar persona\n");

printf("

c) Salir\n");

printf("

Tu opcion: ");

op = getchar();
103

PARTE VI: ARCHIVOS


limpiar(op);
minuscula(&op);
switch(op){
case 'a':
/*trata de abrir el archivo como solo lectura*/
if((dir = fopen(DAT, "r")) != NULL){
consultar(dir, bato);
}else{
printf("El archivo %s no se puede abrir\n", DAT);
}
break;
case 'b':
/*trata de abrir el archivo como lectura y escritura y coloca el apuntador del
archivo al final de este*/
if((dir = fopen(DAT, "a+")) != NULL){
agregar(dir, bato);
}else{
printf("El archivo %s no se puede abrir\n", DAT);
}
break;
}
fclose(dir);
printf(" Pulsa enter para seguir ");
getchar();
}while(op != 'c');
printf("\n Hasta luego\n\n");
return 0;
}

104

PARTE VI: ARCHIVOS


void consultar(FILE *a, struct batos p){
int i = 0;
printf(" Cual es la persona que buscas? ");
gets(p.nombre);
while(p.nombre[i] != '\0'){
minuscula(&p.nombre[i++]);
}
if(buscar(p.nombre, a) == 0){
printf(" Contenido: ");
i = 0;
f_gets(p.datos, a);
puts(p.datos);
}
else
{
printf("El nombre %s no se encuentra en el directorio\n", p.nombre);
}
}
int buscar(char *n, FILE *a){
char b[NOM];
do{
f_gets(b, a);
if(strcmp(b, n) == 0){
return 0;
}
f_gets(NULL, a);
}while(!feof(a));
105

PARTE VI: ARCHIVOS


return 1;
}
void agregar(FILE *a, struct batos p){
int i = 0;
printf("Introduce el nombre: ");
gets(p.nombre);
while(p.nombre[i] != '\0'){
minuscula(&p.nombre[i++]);
}
printf("Contenido: ");
gets(p.datos);
rewind(a);
if(buscar(p.nombre, a) != 0){
printf("Guardando a %s\n", p.nombre);
fprintf(a, "%s\n%s\n", p.nombre, p.datos);
}else{
printf("Ese nombre ya existe, elige otro\n");
}
}
void f_gets(char *c, FILE *a){
do{
*c = getc(a);
}while(*c != EOF && *c++ != '\n');
*--c = '\0';
}
int buscar(char *, FILE *);
106

PARTE VI: ARCHIVOS


La funcin buscar regresa 0 si encontr lo que estaba buscando, recibe dos
parmetros, el primero es la cadena que va a buscar y el segundo es el archivo
donde va a buscar.
void f_gets(char *, FILE *);
f_gets es una funcin que lee una cadena de un archivo para ponerla dentro del
primer parmetro, el segundo parmetro es el archivo donde va a buscar.
void agregar(FILE *, struct batos);
void consultar(FILE *, struct batos);
Tanto agregar como buscar reciben dos parmetros, en archivo en donde van a
trabajar y la estructura con los datos que van a utilizar.
Dentro de los casos (switch) trata de abrir el archivo segn corresponda a la
opcin que se elige, en la opcin para consultar el archivo se abre como solo
lectura, en la opcin de agregar se abre como append mas lectura para poder
ver si los valores no se repiten (en el caso de querer meter nombres duplicados)
y colocar los datos nuevos hasta el final del archivo.
La funcin de consulta es muy sencilla, pide la cadena que se vaya buscar y se
la manda a la funcin buscar, si la encuentra imprime el contenido referente a la
persona buscada y si no pues avisa que no se encuentra el nombre, el nombre
de las personas siempre se convierte a minsculas, checa el ciclo donde se
hace esto, esto es para asegurar de cierto modo que la persona no se
equivoque y quiera buscar un PEDRO cuando escribi Pedro en realidad.
La funcin para agregar pide el nombre que se va a agregar y luego los datos
que van con esa persona, convierte el nombre a minsculas y lo busca, antes de
buscar el nombre hace un rewind porque la opcin append siempre coloca el
apuntador del archivo hasta el final, as que hay que regresarlo para buscar una
palabra. Si la funcin buscar no encuentra el nombre este junto con sus datos es
almacenado en el archivo (hasta el final de este), en caso de que encuentre el
nombre dentro del archivo avisa que el nombre est repetido y no lo graba. Esta
es una restriccin del programa, pero en el tema siguiente se puede arreglar
muy fcil.
La funcin que es ms usada en el programa es la que busca los nombres. Esta
lee una cadena del archivo y la almacena en otra para compararla despus.
Luego utiliza la funcin strcmp, que compara el nombre buscado con el nombre
que se acaba de leer, si son iguales regresa 0, si no lee otra cadena, que sera
la de los datos de ese nombre. Luego continua ese ciclo mientras no sea fin de
archivo, se sabe cuando es fin de archivo con la funcin feof, que regresa 0
107

PARTE VI: ARCHIVOS


cuando se ha llegado al fin del archivo.
Por mi parte es todo acerca de archivos, no es un tema muy amplio y tampoco
muy de mi agrado, el programa del test no lo vamos a hacer porque tengo el
tiempo encima, aparte de que con lo que vimos de archivos ya tenemos
bastante. Ya estamos listos para pasar a la siguiente parte, que es la memoria
dinmica, es la parte ms pesada de todo el libro, pero lo que vamos a ver es
muy importante. As que dale una repasada a lo que acabamos de ver y por
favor a la seccin de los apuntadores porque ahora s los vamos a usar mucho.

PARTE VII: MEMORIA DINMICA


Vamos a suponer que queremos un arreglo de estructuras, pero no sabemos
cuantos elementos sern necesarios, si declaramos un arreglo de 100
elementos y el usuario necesita solamente 23 vamos a tener 77 elementos
desperdiciados. Lo ptimo sera que se crearan los elementos conforme se van
necesitando, si has practicado te habrs topado con que no podemos declarar
un arreglo usando una variable para cambiar su tamao despus. El siguiente
tema se trata de crear listas de elementos que sean dinmicas, o sea que
aumenten de tamao conforme se necesiten.

Listas enlazadas simples.


La forma ms simple de tratar con la memoria dinmica es la lista enlazada
simple. Recuerda que es importante que entiendas bien lo que son los
apuntadores y cmo funcionan, de todas formas en esta parte vamos a hacer un
buen repaso de los apuntadores.
#include<stdio.h>
#include<stdlib.h>
struct lista{
int n;
struct lista *sig;
};
struct lista *inicio = NULL; /*lista vaca*/
/*funcion que crea un nuevo elemento de la lista*/
108

PARTE VII: MEMORIA DINMICA


struct lista *nuevo(int i){
/*creamos un nuevo elemento usando malloc*/
struct lista *n = (struct lista *) malloc(sizeof(struct lista));
if(n != NULL){
n->n = i;
n->sig = NULL;
}
return n;
}
/*pone un nuevo elemento en la lista*/
void poner(int *j){
/*el apuntador a la estructura original*/
struct lista *n;
if((n = nuevo(*j))!= NULL){
printf(" Se ha creado el elemento %d\n", (*j)++);
n->sig = inicio;
inicio = n;
}else{
printf("No se ha podido agregar otro elemento\n");
}
}
/*quita un elemento de la lista*/
void quitar(void){
struct lista atras, *s, *q, *p;
int cual;
printf("Cual es el elemento que quieres quitar? ");
scanf("%d", &cual);
getchar();
109

PARTE VII: MEMORIA DINMICA


atras.sig = inicio;
p = &atras;
while(p->sig != NULL){
/*si el siguiente es el que se va a eliminar*/
if(p->sig->n == cual){
/*s apuntara al que sigue del que se vava eliminar
en caso de que no exista tal s sera NULL*/
s = p->sig->sig;
/*q apunta al que se vava eliminar*/
q = p->sig;
/*p->sig apunta al que este adelante del que se vava eliminar*/
p->sig = s;
/*si el que se va a quitar es el inicio movemos el inicio hacia adelante*/
if(inicio == q)
inicio = s;
/*liberamos el espacio asignado*/
free(q);
/*queda un elemento menos*/
printf("Elemento %d eliminado con exito\n", cual);
return;
}
p = p->sig;
}
printf("El elemento %d no existe\n", cual);
}
void recorre(void){
struct lista *p = inicio;
while(p != NULL){
printf(" %d->", p->n);
110

PARTE VII: MEMORIA DINMICA


p = p->sig;
}
printf(" NULL\n");
}
int main(int argc, char **args){
int j = 0; /*el numero del elemento que se va a crear*/
char op;
do{
printf("
printf("

LISTA ENLAZADA SIMPLE\n\n");


Menu\n");

printf(" a) Agregar elemento\n");


printf(" b) Eliminar elemento\n");
printf(" c) Recorre la lista\n");
printf(" d) Salir\n");
printf("

Tu opcion: ");

op = getchar();
if(op != '\n'){
getchar();
}
switch(op){
case 'a':
poner(&j);
break;
case 'b':
quitar();
break;
case 'c':
recorre();
break;
111

PARTE VII: MEMORIA DINMICA


}
printf("Pulsa enter para seguir");
getchar();
}while(op != 'd');
return 0;
}
Espero que hayas escrito este programa y que hayas visto como funciona. Todo
este libro no te servir de nada si no has practicado porque no es muy explcito
ni muy claro, ms bien es prctico y si no haces experimentos no vas a aprender
nada.
struct lista{
int n;
struct lista *sig;
};
Esta estructura es auto referenciada, quiere decir que tiene un apuntador que
apunta hacia s misma, aunque ms bien este apuntador apunta hacia otra
estructura igual.
struct lista *nuevo(int i){
/*creamos un nuevo elemento usando malloc*/
struct lista *n = (struct lista *) malloc(sizeof(struct lista));
if(n != NULL){
n->n = i;
n->sig = NULL;
}
return n;
}
Recuerda que una funcin puede regresar datos de cualquier tipo, en este caso
regresa un apuntador a una estructura. Esta funcin crea un nuevo elemento de
la lista y regresa la direccin de dicho elemento, esto lo hace usando la funcin
112

PARTE VII: MEMORIA DINMICA


malloc (void *malloc(size_t)) la funcin malloc busca un espacio que sea del
tamao adecuado y regresa la direccin del mismo, pero la direccin la regresa
por medio de un apuntador genrico, por eso usamos la notacin "struct lista *n
= (struct lista *) malloc...) struct lista *n es un apuntador a una estructura struct
lista, pero lo que malloc nos regresa es un puntero void as que debemos
transformarlo a un apuntador estructura lista 5. Nota la manera en que llamo al
tipo de dato, no le digo estructura, le digo estructura lista, un apuntador que
apunte a una estructura a no puede apuntar a una estructura b porque son tipos
de datos distintos, ten presente eso. Por otro lado la funcin malloc necesita
recibir como parmetro el tamao de memoria que debe apartar, pero no
sabemos cuanto mide la estructura, por eso usamos la funcin sizeof(), esta
funcin forma parte de C y regresa el tamao de lo que le metamos adentro, por
ejemplo sizeof(int) nos regresa un 16, o sea 16 bits de espacio en memoria. Por
eso escribimos ...malloc(sizeof(struct lista)) as calculamos el espacio necesario
y se lo mandamos a malloc para que el se encargue de todo lo dems. Cuando
malloc puede apartar un espacio en la memoria nos regresa la direccin, pero si
no puede nos regresa un valor NULL, por eso hice la prueba en el if, si n !=
NULL quiere decir que malloc lo logr y que ya tenemos una estructura nueva
para trabajar. Dentro de ese if ponemos los valores adecuados en la estructura
nueva, pero fjate en la forma en que trabajo con los datos de la estructura,
antes lo hacamos con un punto, pero eso es cuando no estamos con un
apuntador, cuando usamos la estructura con un apuntador debemos usar una
flecha -> (guin y mayor qu). Al final de todo regresamos la direccin de la
nueva estructura con el return. Acurdate que *n es el dato en s, pero n es la
pura direccin.
void poner(int *j){
/*el apuntador a la estructura original*/
struct lista *n;
if((n = nuevo(*j))!= NULL){
printf(" Se ha creado el elemento %d\n", (*j)++);
n->sig = inicio;
inicio = n;
}else{
printf("No se ha podido agregar otro elemento\n");
}
5

Ahora con el casting implcito no es necesario, pero puede hacer el cdigo ms legible.

113

PARTE VII: MEMORIA DINMICA


}
En la funcin que pone un elemento nuevo en la lista si es que se pudo crear
if((n = nuevo(*j))!= NULL). Bsicamente lo que hace es colocar la nueva
estructura al principio, de tal suerte que lo que estaba al principio ahora est en
segundo lugar n->sig = inicio y luego se hace que el inicio de la lista sea el
elemento nuevo inicio = n.
void quitar(void){
struct lista atras, *s, *q, *p;
int cual;
printf("Cual es el elemento que quieres quitar? ");
scanf("%d", &cual);
getchar();
atras.sig = inicio;
p = &atras;
while(p->sig != NULL){
/*si el siguiente es el que se va a eliminar*/
if(p->sig->n == cual){
/*s apuntara al que sigue del que se vava eliminar
en caso de que no exista tal s sera NULL*/
s = p->sig->sig;
/*q apunta al que se vava eliminar*/
q = p->sig;
/*p->sig apunta al que este adelante del que se vava eliminar*/
p->sig = s;
/*si el que se va a quitar es el inicio movemos el inicio hacia
adelante*/
if(inicio == q)
inicio = s;
/*liberamos el espacio asignado*/
free(q);
114

PARTE VII: MEMORIA DINMICA


/*queda un elemento menos*/
printf("Elemento %d eliminado con exito\n", cual);
return;
}
p = p->sig;
}
printf("El elemento %d no existe\n", cual);
}
Esta funcin para quitar elementos es ms complicada, cuando apenas estaba
aprendiendo no poda encontrar cdigos que explicaran como quitar elementos.
El chiste es que el apuntador que est atrs del elemento que se va a eliminar
(que apunta a ese elemento) deje de apuntar al elemento que se va a eliminar y
apunte al que est despus del que se va a eliminar. Checa el cdigo donde se
busca al elemento que se va a quitar, checa el if donde est la condicin p->sig>n == cual, esto es vlido, as se puede ver si la estructura de adelante es la que
se busca sin estar ah, es el trabajo sucio de los apuntadores, el fisgonear, como
en los archivos. En el caso de que s sea la estructura que queremos quitar se
hace toda la faramalla de cambiar el apuntador anterior para que apunte a la
siguiente estructura (la que est adelante de la que vamos a quitar), despus de
esto se hace que q apunte a la estructura que se va a eliminar y liberamos ese
espacio con free(void *) que libera el espacio que se apart con malloc, despus
de esto nos salimos de la funcin con un return. Si terminamos el ciclo y nunca
se elimin la estructura deseada pues quiere decir que no existe.
void recorre(void){
struct lista *p = inicio;
while(p != NULL){
printf(" %d->", p->n);
p = p->sig;
}
printf(" NULL\n");
}
La funcin ms fcil de todas, esta funcin recorre toda la lista mientras y va
imprimiendo los valores que estn en ella.
115

PARTE VII: MEMORIA DINMICA


Todo este trabajo no se puede hacer sin apuntadores, te recomiendo que revisas
el cdigo varias veces para que le termines de entender, porque con esta
explicacin dudo que te haya quedado del todo claro. Se supone que estos son
temas muy difciles. Hay un detalle que quiero que veas, podemos movernos
hacia adelante con ptr = ptr->siguiente, pero si quisiramos movernos hacia
atrs no podramos, no hay modo.

rboles binarios.
Ya llegamos a la recta final, el ltimo programa que te voy a ensear es uno de
los que ms me han gustado, es la versin de nuestro directorio que tiene
memoria dinmica, este programa va a ordenar todos los elementos en orden
alfabtico y va a poder eliminar elementos o actualizarlos. Antes que nada es
necesario explicarte los rboles binarios, un rbol binario es el rbol donde cada
nodo puede tener dos o menos hijos y solo un padre, aparte los elementos estn
ordenados de modo que los menores a la raz van en el subrbol izquierdo y los
mayores en el subrbol derecho. El recorrido en orden es en el cual se recorre el
subrbol izquierdo, luego la raz y al final el subrbol derecho.
Te recomiendo que antes de ver este programa repases lo que es la recursividad
y el uso de cadenas.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define NOM 12
#define D 65
#define DAT "libro.dat"
struct binario{
char nombre[NOM];
char datos[D];
int valido;
struct binario *i;
struct binario *d;
116

PARTE VII: MEMORIA DINMICA


};
struct binario *raiz = NULL;
/*crea una nueva estructura y regresa su direccion*/
struct binario *nuevo(char *nombre, char *datos){
struct binario *n = malloc(sizeof(struct binario));
if(n){
strcpy(n->nombre, nombre);
strcpy(n->datos, datos);
n->i = NULL;
n->d = NULL;
n->valido = 1;
}
return n;
}
/*coloca el nuevo elemento en el lugar indicado*/
void colocar(struct binario *hoja, char *nombre, char *datos){
char op;
int i;
/*primero ver si el arbol no est vaco*/
if(raiz){
/*comparar los dos nombres*/
i = strcmp(hoja->nombre, nombre);
if(i){
/*si el nombre va despues de raiz->nombre*/
if(i < 0){
/*si hay hijo derecho*/
if(hoja->d != NULL){
117

PARTE VII: MEMORIA DINMICA


/*ponemos el nuevo nombre e el lado derecho*/
colocar(hoja->d, nombre, datos);
}else{
hoja->d = nuevo(nombre, datos);
}
}else{
if(hoja->i != NULL){
colocar(hoja->i, nombre, datos);
}else{
hoja->i = nuevo(nombre, datos);
}
}
}else{
printf(" Ese nombre ya existe, pulsa s + enter para actualizar sus datos ");
op = getchar();
if(op != '\n'){
getchar();
}
if(op == 's'){
strcpy(hoja->datos, datos);
}
}
}else{
raiz = nuevo(nombre, datos);
}
}
/*cambia el contenido de una cadena a mayusculas*/
void mayusculas(char *s){
while(*s != '\0'){
118

PARTE VII: MEMORIA DINMICA


*s = toupper(*s);
s++;
}
}
/*se encarga de agregar nuevos elementos*/
void agregar(void){
char nombre[NOM];
char datos[D];
printf(" Nombre: ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
printf(" Datos: ");
fgets(datos, D, stdin);
colocar(raiz, nombre, datos);
}
/*carga el archivo DAT en la memoria*/
int cargar(void){
FILE *a = fopen(DAT, "r");
if(a != NULL){
char nombre[NOM];
char datos[D];
do{
fgets(nombre, NOM, a);
fgets(datos, D, a);
if(!feof(a)){
colocar(raiz, nombre, datos);
}
}while(!feof(a));
119

PARTE VII: MEMORIA DINMICA


fclose(a);
return 0;
}else{
return 1;
}
}
/*recorre el arbol y guarda cada elemento en el archivo DAT*/
void archivar(struct binario *hoja, FILE *a){
if(hoja && a){
if(hoja->i != NULL){
archivar(hoja->i, a);
}
if(hoja->valido){
fprintf(a, "%s", hoja->nombre);
fprintf(a, "%s", hoja->datos);
}
if(hoja->d != NULL){
archivar(hoja->d, a);
}
}
}
/*guarda el contenido de la memoria en el archivo DAT usando la funcion
archivar*/
void guardar(void){
FILE *a = fopen(DAT, "w");
if(a != NULL){
if(raiz){
archivar(raiz, a);
120

PARTE VII: MEMORIA DINMICA


}
fclose(a);
}else{
printf(" No es posible guardar la informacion\n");
}
}
/*busca un nombre y regresa su direccin*/
struct binario *buscar(struct binario *hoja, char *s){
if(hoja){
int i = strcmp(hoja->nombre, s);
if(i != 0){
/*si s esta a la derecha*/
if(i < 0){
if(hoja->d != NULL){
return(buscar(hoja->d, s));
}else{
return NULL;
}
}else{
if(hoja->i != NULL){
return(buscar(hoja->i, s));
}else{
return NULL;
}
}
}else{
return (hoja->valido?hoja:NULL);
}
}else{
121

PARTE VII: MEMORIA DINMICA


return NULL;
}
}
/*imprime los datos de una persona en la pantalla*/
void ver(void){
char nombre[NOM];
struct binario *p;
printf(" Cual es el nombre que buscas? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
p = buscar(raiz, nombre);
if(p){
printf("\n\t%s\n", p->nombre);
printf("

Datos: %s\n", p->datos);

}else{
printf(" El nombre %s no existe\n", nombre);
}
}
/*utiliza la funcion buscar para modificar los datos*/
void modificar(void){
struct binario *p;
char nombre[NOM];
printf(" Cual es el nombre que quieres actualizar? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
if((p = buscar(raiz, nombre))){
printf(" Introduce los nuevos datos: ");
fgets(p->datos, D, stdin);
122

PARTE VII: MEMORIA DINMICA


}else{
printf(" El nombre %s no existe\n", nombre);
}
}
/*elimina un elemento*/
void eliminar(void){
char nombre[NOM];
struct binario *p;
printf(" Que nombre quieres eliminar? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
if((p = buscar(raiz, nombre))){
p->valido = 0;
printf(" %s ser eliminado al salir del programa\n", nombre);
}else{
printf(" El nombre %s no existe\n", nombre);
}
}
/*recorre el arbol n orden alfabetico*/
void recorre(struct binario *hoja, int *i, int *j){
if(hoja){
if(hoja->i != NULL){
recorre(hoja->i, i, j);
}
if(*i >= 20){
printf("

Pulsa enter para continuar");

getchar();
*i = 0;
123

PARTE VII: MEMORIA DINMICA


}
if(hoja->valido){
(*j)++;
printf("

%s\n", hoja->nombre);

printf(" Datos: %s\n\n", hoja->datos);


*i += 3;
}
if(hoja->d != NULL){
recorre(hoja->d, i, j);
}
}else{
printf(" El arbol est vaco\n\n");
}
}
int main(int argc, char **args){
FILE *aux;
char op;
int i, j;
raiz = NULL;
if(cargar() == 0){
do{
i = j = 0;
printf("
printf("

DIRECTORIO\n");
Menu\n");

printf(" a) Agregar\n");
printf(" b) Buscar\n");
printf(" c) Modificar\n");
printf(" d) Eliminar\n");
printf(" e) Recorrer el directorio\n");
124

PARTE VII: MEMORIA DINMICA


printf(" f) Salir\n");
printf("

Tu opcion: ");

op = getchar();
if(op != '\n'){
getchar();
}
switch(op){
case 'a':
agregar();
break;
case 'b':
ver();
break;
case 'c':
modificar();
break;
case 'd':
eliminar();
break;
case 'e':
recorre(raiz, &i, &j);
printf(" Hay %d personas en el directirio\n", j);
break;
case 'f':
continue;
default:
printf(" Tecla incorrecta\n");
}
printf("Pulsa enter para seguir");
getchar();
125

PARTE VII: MEMORIA DINMICA


}while(op != 'f');
guardar();
printf("

Hasta luego :)\n");

}else{
if((aux = fopen(DAT, "w")) != NULL){
printf(" El archivo %s no se puede abrir, se ha creado el archivo ", DAT);
printf("pero necesitas reiniciar el programa\n");
}else{
printf(" No se encuentra el archivo %s\n", DAT);
}
}
return 0;
}
Modestia aparte este programita me qued muy bien, lo he estado revisando y
no tiene bugs, tena uno, pero lo encontr. Prubalo y ve qu tal, es como una
base de datos muy (muchsimo) primitiva, pero los rboles se usan como no
tengas una idea y son lo que se usa en las bases de datos. Esto es lo ltimo que
vamos a ver, fue el programa de despedida de este pequeo curso. Te lo voy a
explicar con todo detalle para que sea tu graduacin, hay mucho ms del
lenguaje, pero solo son libreras que no vimos aqu, lo dems ya lo sabes y no te
costar mucho trabajo aprender lo que sigue. Te voy a explicar pues este
programa.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
Las libreras que se van a usar, como vamos a trabajar mucho con cadenas de
caracteres necesitamos la librera string.h, aunque solo se van a usar dos
funciones de aqu. Adems de esto incluimos la librera ctype.h, en la cual se
incluyen algunas funciones de manejo de caracteres.

126

PARTE VII: MEMORIA DINMICA


#define NOM 12
#define D 65
#define DAT "libro.dat"
Definimos el largo del nombre, de los datos y el nombre del programa. Si,
tambin se pueden definir cadenas de datos.
struct binario{
char nombre[NOM];
char datos[D];
int valido;
struct binario *i;
struct binario *d;
};
Esta estructura tiene los datos que se van a usar en nuestra base de datos. La
variable int valido va a valer 1 cuando el elemento est normal, pero valdr 0
cuando el elemento se vaya a eliminar. Nota que ahora tiene apuntadores para
el hijo izquierdo y el hijo derecho.
void colocar(struct binario *hoja, char *nombre, char *datos){
char op;
int i;
/*primero ver si el arbol no est vaco*/
if(raiz){
/*comparar los dos nombres*/
i = strcmp(hoja->nombre, nombre);
if(i){
/*si el nombre va despues de raiz->nombre*/
if(i < 0){
/*si hay hijo derecho*/
if(hoja->d != NULL){
/*ponemos el nuevo nombre e el lado derecho*/
127

PARTE VII: MEMORIA DINMICA


colocar(hoja->d, nombre, datos);
}else{
hoja->d = nuevo(nombre, datos);
}
}else{
if(hoja->i != NULL){
colocar(hoja->i, nombre, datos);
}else{
hoja->i = nuevo(nombre, datos);
}
}
}else{
printf(" Ese nombre ya existe, pulsa s + enter para actualizar sus
datos ");
op = getchar();
if(op != '\n'){
getchar();
}
if(op == 's'){
strcpy(hoja->datos, datos);
}
}
}else{
raiz = nuevo(nombre, datos);
}
}
En el rbol binario el hijo de la izquierda debe ser menor a la raz y el hijo de la
derecha debe ser mayor que la raz, y as es para cada nodo del rbol, esta
funcin es recursiva y se basa en esa regla la funcin strcmp compara las dos
cadenas, si la primera es menor regresa un nmero negativo, si es mayor
regresa un nmero positivo y si son iguales regresa un 0. Revisa bien el cdigo,
si son diferentes la funcin ve de que lado va, revisa si el nodo tiene un hijo
128

PARTE VII: MEMORIA DINMICA


izquierdo (en caso de que el nombre que queremos meter vaya de ese lado), si
no lo tiene pone el nuevo elemento como hijo izquierdo, eso no requiere mucha
explicacin, pero si lo tiene, tiene que recurrir a s misma y le dice a la funcin
que busque a partir de ese hijo, lo hace as "colocar(raz->i, nombre, datos)", la
funcin hace lo mismo para el caso de que el nombre vaya del lado derecho. Si
el nombre es encontrado la funcin pregunta si los datos deben ser actualizados.
struct binario *nuevo(char *nombre, char *datos){
struct binario *n = malloc(sizeof(struct binario));
if(n){
strcpy(n->nombre, nombre);
strcpy(n->datos, datos);
n->i = NULL;
n->d = NULL;
n->valido = 1;
}
return n;
}
Esta funcin ya la conocemos bien, pero observa la lnea que dice n->valido = 1,
eso quiere decir que el elemento est activo, o sea que no ha sido borrado.
int cargar(void){
FILE *a = fopen(DAT, "r");
if(a != NULL){
char nombre[NOM];
char datos[D];
do{
fgets(nombre, NOM, a);
fgets(datos, D, a);
if(!feof(a)){
colocar(raiz, nombre, datos);
}
129

PARTE VII: MEMORIA DINMICA


}while(!feof(a));
fclose(a);
return 1;
}else{
return 0;
}
}
Esta funcin se encarga de volcar el contenido del archivo en la memoria, lee las
cadenas del archivo y luego las coloca usando la funcin colocar.
void agregar(void){
char nombre[NOM];
char datos[D];
printf(" Nombre: ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
printf(" Datos: ");
fgets(datos, D, stdin);
colocar(raiz, nombre, datos);
}
Funciona igual que la funcin cargar, solo que los datos los lee desde el teclado
y no del archivo.
void mayusculas(char *s){
while(*s != '\0'){
*s = toupper(*s);
s++;
}
}
Convierte el contenido de una cadena a maysculas, es sencillo el cmo
funciona, simplemente convierte hasta que se topa con un fin de cadena. Utilia la
130

PARTE VII: MEMORIA DINMICA


funcin toupper para convertir un caracer a maysculas
void guardar(void){
FILE *a = fopen(DAT, "w");
if(a != NULL){
if(raiz){
archivar(raiz, a);
}
fclose(a);
}else{
printf(" No es posible guardar la informacion\n");
}
}
guardar vuelca el contenido de la memoria al archivo, as guarda todos los datos
actualizados y en orden alfabtico. Utiliza a la funcin archivar que viene siendo
el motor, como la funcin colocar.
void archivar(struct binario *hoja, FILE *a){
if(hoja && a){
if(hoja->i != NULL){
archivar(hoja->i, a);
}
if(hoja->valido){
fprintf(a, "%s", hoja->nombre);
fprintf(a, "%s", hoja->datos);
}
if(hoja->d != NULL){
archivar(hoja->d, a);
}
}

131

PARTE VII: MEMORIA DINMICA


}
La funcin archivar hace un recorrido en orden, o sea que recorre el subrbol
izquierdo, luego la raz y al final el subrbol derecho. Es muy simple y tambin
es recursiva. Primero revisa si hay hijo izquierdo, si lo hay se va para all con
archivar(hoja->i, a), tambin le pasa el archivo donde debe escribir los datos.
Luego que termina con el subrbol izquierdo se va a la raz y ve si el elemento
no se va a borrar con if(raiz->valido), si no se va a borrar lo guarda en el archivo
con las funciones fprintf, si el elemento s se va a borrar simplemente no lo
guarda y se quita de problemas. Ya que trabaj en la raz se va a ver que onda
con el subrbol derecho.
void ver(void){
char nombre[NOM];
struct binario *p;
printf(" Cual es el nombre que buscas? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
p = buscar(raiz, nombre);
if(p){
printf("\n\t%s\n", p->nombre);
printf("

Datos: %s\n", p->datos);

}else{
printf(" El nombre %s no existe\n", nombre);
}
}
Esta funcin utiliza a la funcin buscar para que le diga la posicin del nombre
que busca, si la encuentra imprime la informacin, si no avisa que el nombre no
existe.
struct binario *buscar(struct binario *hoja, char *s){
if(hoja){
int i = strcmp(hoja->nombre, s);
if(i != 0){
132

PARTE VII: MEMORIA DINMICA


/*si s esta a la derecha*/
if(i < 0){
if(hoja->d != NULL){
return(buscar(hoja->d, s));
}else{
return NULL;
}
}else{
if(hoja->i != NULL){
return(buscar(hoja->i, s));
}else{
return NULL;
}
}
}else{
return (hoja->valido?hoja:NULL);
}
}else{
return NULL;
}
}
Buscar toma la cadena que va a buscar y hace lo mismo que la funcin para
colocar los datos, compara los nombres, si el que busca est a la izquierda se va
a ese lado, si est a la derecha se va a ese otro, por ejemplo si el nombre
debiera estar a la izquierda y no hay hijo izquierdo regresa un NULL, pero si s
hay un hijo izquierdo trata de buscarlo ah. En el caso de que lo encuentre
regresa la direccin de donde est.
void modificar(void){
struct binario *p;
char nombre[NOM];
133

PARTE VII: MEMORIA DINMICA


printf(" Cual es el nombre que quieres actualizar? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
if((p = buscar(raiz, nombre))){
printf(" Introduce los nuevos datos: ");
fgets(p->datos, D, stdin);
}else{
printf(" El nombre %s no existe\n", nombre);
}
}
Para modificar los datos de una persona usa la funcin buscar para que le diga
donde est, si la encuentra le cambia los datos directamente.
void eliminar(void){
char nombre[NOM];
struct binario *p;
printf(" Que nombre quieres eliminar? ");
fgets(nombre, NOM, stdin);
mayusculas(nombre);
if((p = buscar(raiz, nombre))){
p->valido = 0;
printf(" %s ser eliminado al salir del programa\n", nombre);
}else{
printf(" El nombre %s no existe\n", nombre);
}
}
Cuando se elimina a una persona del directorio simplemente se busca con
buscar y luego se cambia el valor de valido a 0 para indicar que ese dato se
debe eliminar.
void recorre(struct binario *hoja, int *i, int *j){
134

PARTE VII: MEMORIA DINMICA


if(hoja){
if(hoja->i != NULL){
recorre(hoja->i, i, j);
}
if(*i >= 20){
printf("

Pulsa enter para continuar");

getchar();
*i = 0;
}
if(hoja->valido){
(*j)++;
printf("

%s\n", hoja->nombre);

printf(" Datos: %s\n\n", hoja->datos);


*i += 3;
}
if(hoja->d != NULL){
recorre(hoja->d, i, j);
}
}else{
printf(" El arbol est vaco\n\n");
}
}
Recorre hace un recorrido en orden, o sea que ve que onda con el subrbol
izquierdo, visita la raz y luego se va al lado derecho.
Pues eso fue todo, hemos terminado, espero que esto te haya servido, fue algo
intensivo, pero me pareci bastante conciso y trat de hacerlo lo ms entendible
posible, aunque creo que no me sali bien en algunas partes del libro, pero te da
las bases para que practiques, as fue como yo aprend tambin, practicando y
cometiendo errores, durante este libro aprend mucho y descubr que saba
cosas que ignoraba que saba. Espero que te pase lo mismo a ti tambin, sigue
practicando, este libro no inculca el amor al lenguaje, pero muestra lo flexible y
135

PARTE VII: MEMORIA DINMICA


poderoso que puede ser. Solo fue una embarrada del pastel, pero lo dems te
toca a ti. La siguiente parte es un apndice con algunas funciones que te pueden
resultar tiles y otras cosas que no se vieron en el trayecto.

APNDICE A: LA BIBLIOTECA ESTANDAR


Lo siguiente es un resumen del apndice B del libro "El lenguaje de
programacin C" de Brian Kernighan y Dennis Ritchie, solo puse algunas
funciones de las que vienen en el libro, cuando hice el resumen encontr un par
de errores, los correg, pero lo dems puede tener falla, las funciones no estn
explicadas al cien por ciento, en parte porque esto ya no es de mi autora y
porque considero que es mejor que te apegues a la librera de tu compilador,
porque aunque esta librera es estndar hay funciones que no son del todo
iguales, aparte algunas no estn en todos los compiladores. Comprob algunas
funciones usando mi compilador, algunas explicaciones no estn basadas en el
libro de Ritchie sino que las bas en la ayuda de mi compilador, que es el Pacific
C de Hi-Tech software.
Aclarando, las libreras de la biblioteca estndar no son parte del lenguaje en s,
pero al ser la librera estndar vienen en todos los compiladores como parte
integral del lenguaje C. Al haber una biblioteca estndar tambin puede haber
una biblioteca propia con funciones que sirvan para nuestros fines particulares,
por eso se dice que el lenguaje C es extensible, ya que se pueden extender sus
alcances.
stdio.h

Archivos.
FILE *fopen(char *nombre, char *modo)
Abre un archivo con el nombre y el modo especificado, si el archivo se puede
abrir regresa el apuntador y si no se puede regresa un NULL, los modos son los
siguientes.

"r" Abre el archivo como solo lectura, si el archivo no existe regresa un


NULL.

"r+" Abre el archivo como lectura y escritura, igual si el archivo no existe


la llamada falla.

"w" Crea un archivo nuevo, en caso de que el archivo exista lo sobre


escribe borrando su contenido, el archivo se abre para escritura.

"w+" Igual que el modo w, pero aparte permite escribir en el archivo.

"a" Abre el archivo para escritura o lo crea si no existe, todas las


136

APNDICE A: LA BIBLIOTECA ESTANDAR


escrituras en el archivo se forzan a ir hasta el final.

"a+" Aparte de modo escritura abre el archivo para lectura, funciona


igual que a pero con la opcin de leer.

Aparte de estas llamadas existe la opcin b, esta opcin se usa para archivos
binarios, una llamada "w+b" o "wb+" creara un archivo para escribir y leer de el
en modo binario.
int fflush(FILE *nombre)
Escribe en el archivo de salida todos los datos que estn almacenados en el
buffer asociado, esto para dejar el buffer despejado. Regresa un EOF si existe
algn error o un 0 si todo sali bien.
int fclose(FILE *nombre)
Cierra el archivo especificado, si hay algn error regresa un EOF o 0 si todo est
bien.
int remove(char *nombre)
Elimina el archivo nombre del directorio. Regresa 0 cuando no hay error.
int rename(char *nombre, char nuevo_nombre)
Renombra un archivo segn las cadenas introducidas, regresa 0 si todo sale
bien.
FILE *freopen(const char *archivo, const char *modo, FILE *archivo2)
Reabre archivo con el modo estipulado y asocia al archivo2 con el, o sea que
freopen("archivo.txt", "w", stdout) re-direcciona la salida stdout a archivo.txt, as
que al poner printf("Hola a todos :)\n") la salida no se imprime en la pantalla sino
en archivo.txt. freopen regresa un apuntador a archivo.

Salida con formato.


int fprintf(FILE *archivo, const char *formato, ...)
Regresa el nmero de caracteres escritos o un valor negativo si ocurre algn
error. Los formatos empiezan con % y terminan con un caracter de conversin.
Tiene banderas que son las siguientes.

+ Indica que el numero siempre sea impreso con signo, un printf("%+d",


6) imprimira +6.

Espacio, si el primer caracter no es un signo imprime un espacio.

# Es una forma alternativa para la impresin, %#x antepone un 0x a los


nmeros hexadecimales, el %#o antepone un 0 al principio de los
137

APNDICE A: LA BIBLIOTECA ESTANDAR


octales, para g, G, E y f siempre e pondr un punto decimal.
Hay conversiones para printf como d, c o f, aparte estn las siguientes.

d,i

int, notacin decimal con signo.

int, notacin octal sin signo.

x,X

int, Notacin hexadecimal sin signo.

int, notacin decimal sin signo.

int, caracter sin signo despus de ser convertido a unsigned char.

s
char *, se imprime la cadena hasta encontrar un '\0' o hasta la
cantidad de caracteres indicados por la precisin.

f
double, notacin decimal, la precisin se puede estipular como %.2f
para limitar la salida a dos decimales, la precisin por defecto son seis
decimales.

e,E double, notacin cientfica. Imprime un nmero y luego los


decimales, la precisin se define igual que en %f.

g,G double, imprime como %f si la precisin es menor que -4 o mayor o


igual que la precisin estipulada, la precisin se escribe igual que con %f.

Se imprime como %.

int printf(const char *formato, ...)


Es lo equivalente a fprintf(stdout, ...)
*NOTA: En el caso de mi compilador para escribir un entero largo (long) la
conversin es %ld, para imprimir un entero largo en notacin decimal. Por
ejemplo para escribirlo en notacin octal sera %lo.

Entrada con formato.


int fscanf(FILE *archivo, const char *formato, ...)
Lee del archivo especificado y convierte la entrada a lo que se pide en el
formato, regresa EOF si ocurre algn error, de lo contrario regresa el nmero de
elementos de entrada convertidos y asignados. Bsicamente funcione igual que
fprintf, solo que con datos de entrada y no de salida.
int scanf(const char *formato, ...)
Es como fscanf(stdin, const char *formato, ...)

138

APNDICE A: LA BIBLIOTECA ESTANDAR

Entrada y salida de caracteres.


int fgetc(FILE *archivo)
Regresa el siguiente caracter del archivo o un EOF si hay fin de archivo o algn
error.
char *fgets(char *cadena, int n, FILE *archivo)
Lee los caracteres del archivo hasta que sean n-1 caracteres ledos o hasta
encontrar un '\n' y los almacena en char *cadena. fgets regresa EOF en el fin de
archivo o en caso de error.
int fputc(int c, FILE *archivo)
Escribe el caracter en el archivo, regresa un EOF si hay error o el caracter
escrito.
int fputs(char *s, FILE *archivo)
Escribe una cadena en el archivo, regresa un valor no negativo o un EOF si hay
un error.
int getc(FILE *archivo)
Lee un caracter del archivo y lo regresa.
int getchar(void)
Es como getc(stdin).
char *gets(char *cadena)
Lee caracteres de la entrada estndar y los pone en la cadena, regresa cuando
hay un '\n' y lo sustituye por un '\0'. El valor de retorno es el apuntador a la
cadena o un NULL si ocurre un EOF o un error.
int putc(int c, FILE *archivo)
Es lo equivalente a putc, pero es una macro.
int putchar(int c)
Es lo equivalente a putc(c, stdout).
int puts(const char *s)
Escribe la cadena mas un '\n' en la salida estndar, regresa EOF si ocurre algn
error o un nmero positivo si todo sale bien.
int ungetc(int c, FILE *archivo)
Regresa el ltimo caracter ledo de nuevo al archivo, de donde ser regresado
en la prxima lectura. Solo se puede regresar un caracter por archivo, EOF no
se puede devolver. La funcin regresa un EOF si hay un error o el caracter
139

APNDICE A: LA BIBLIOTECA ESTANDAR


regresado.

Funciones de entrada y salida directa.


size_t fread(void *apuntador, size_t tamao, size_t n_objetos, FILE
*archivo)
Lee directamente desde el archivo y regresa la cantidad de objetos que se
pudieron leer. Donde void *apuntador es donde va a colocar la informacin leda,
size_t tamao es el tamao de lo que va a leer, size_t n_objetos es cuantas
veces va a leer antes de poner la informacin en el apuntador y FILE *archivo es
donde la va a poner. Escribir fread(cadena, sizeof(cadena), 2, archivo) hace que
fread lea el tamao de cadena del archivo dos veces y almacene el ltimo valor
ledo en cadena. Por ejemplo para leer una estructura se puede hacer as:
struct estructura{
...
...
}s;
fread(&s, sizeof(struct estructura), 1, archivo)
As fread lee el contenido del archivo hasta que cumple con el tamao
especificado y coloca la informacin leda en la estructura, esto lo hara una sola
vez.
size_t fwrite(void *apuntador, size_t tamao, size_t n_objetos, FILE
*archivo)
Funciona igual que fread, pero para escribir sobre un archivo.

Funciones de colocacin en archivos.


int fseek(FILE *archivo, long offset, int origen)
Fija la posicin en el archivo especificado, donde offset es el desplazamiento
dentro del archivo, o sea cuanto nos tenemos que desplazar dentro del archivo
para llegar a la posicin deseada. Origen es de donde empezamos a buscar, 0
es empezar desde el principio del archivo, 1 es empezar desde la posicin actual
y 2 es empezar desde al final del archivo. As que escribir fseek(archivo, 2, 1) es
decirle a fseek que avance dos bytes a partir de la posicin actual. fseek regresa
un valor diferente de 0 en caso de error.
140

APNDICE A: LA BIBLIOTECA ESTANDAR


long ftell(FILE *archivo)
Regresa la posicin actual del apuntador del archivo o -1 en caso de error.
void rewind(FILE *archivo)
Regresa el apuntador del archivo hasta el principio, equivale a
{
fseek(archivo, 0, 0);
clearerr(archivo)
}

Funciones de error.
void clearerr(FILE *archivo)
Limpia los indicadores de error y de EOF para el archivo.
int feof(FILE *archivo)
Regresa un valor diferente de cero si encuentra un fin de archivo.
int ferror(FILE *archivo)
Igual que feof, pero para el error.
* NOTA: En el caso de mi compilador clearerr es clrerr y clreof, donde clrerr
limpia el error y clreof limpia el estado de EOF.

string.h
char *strcpy(char *destino, char *fuente)
Copia la cadena fuente en la cadena destino incluyendo el '\0', regresa destino.
char *strncpy(char *destino, char *fuente, size_t n)
Copia la cadena fuente en destino hasta n caracteres.
char *strcat(char *destino, char *fuente)
Concatena las cadenas destino y fuente en destino.
char *strncat(char *destino, char *fuente, size_t n)
Igual, pero concatena hasta n caracteres.
int strcmp(char *c1, char *c2)
Compara c1 con c2, si c1 es menor regresa un nmero negativo, si c1 es mayor
regresa un nmero positivo, si son iguales regresa cero.

141

APNDICE A: LA BIBLIOTECA ESTANDAR


int strncmp(char *c1, char *c2, size_t n)
Igual que strcmp, pero compara hasta n caracteres.
char *strchr(char *c1, char c)
Regresa un apuntador a la primer ocurrencia de c en ct o NULL si no hay.
char strrchr(char *c1, char c)
Regresa un apuntador a la ltima ocurrencia de c en ct o NULL si no hay.
size_t strlen(char c1)
Regresa la longitud de c1.
Las funciones mem... manipulan objetos como cadenas de caracteres.
void *memcpy(void *arreglo1, void *arreglo2, size_t n)
Copia n caracteres de arreglo2 a arreglo1 y regresa arreglo1.
int memcmp(void *arreglo1, void *arreglo2, size_t n)
Regresa lo mismo que strcmp o strncmp, comparando hasta n objetos, pero no
se detiene al encontrar un '\0'.
void *memset(void *arreglo, char c, size_t n)
Coloca el caracter c en las primeras n posiciones del arreglo.

math.h
Todas las funciones trigonomtricas estn en radianes.
double sin(double x)
Seno de x.
double cos(double x)
Coseno de x.
double tan(double x)
Tangente de x.
double asin(double x)
Inverso del seno en el rango de -pi/2 a pi/2.
double acos(double x)
Inverso del coseno de x en el rango de 0 a pi.
142

APNDICE A: LA BIBLIOTECA ESTANDAR


double atan(double x)
Inverso tangente de x en el rango de -pi/2 a pi/2.
double atan2(doublex, double y)
Inverso tangente de x/y en al rango de -pi a pi.
double sinh(double x)
Seno hiperblico de x.
double cosh(double x)
Coseno hiperblico.
double tanh(double x)
Tangente hiperblica.
double exp(double x)
e a la x.
double log(double x)
Logaritmo natural de x.
double log10(double x)
Logaritmo base 10.
double pow(double x, double y)
Potencia de x a la y.
double sqrt(double x)
Raz cuadrada de x.
double ceil(double x)
Regresa el entero no mayor que x.
double floor(double x)
El entero no menor que x.
double fabs(double x)
Valor absoluto de x
double ldexp(double x, int n)
Regresa x2^n.
double frexp(double x, int *exp)
Divide a x en una fraccin normalizada dentro del rango 1/2 a 1, este valor se
143

APNDICE A: LA BIBLIOTECA ESTANDAR


regresa, y una potencia de 2 se almacena en exp.
double modf(double x, double *ip)
divide a x en una parte entera y otra fraccionaria, cada una con el mismo signo
que x. Almacena la parte entera en *ip y regresa la parte fraccionaria.
double fmod(double x, double y)
Regresa el modulo flotante de x/y.

stdlib.h
int rand(void)
Devuelve un nmero pseudoaleatorio entre 0 y RAND_MAX, que suele ser
32767.
void srand(unsigned int seed)
Siembra una semilla para crear una nueva secuencia de nmeros
pseudoaleatorios usando a seed.
void *calloc(size_t nobj, size_t size)
Devuelve un apuntador al espacio para un arreglo de nobj objetos, cada uno de
tamao size. Regresa NULL si no puede encontrar el espacio, mismo que se
inicia con ceros.
void *malloc(size_t size)
Regresa un apuntador a un espacio de tamao size o NULL si no puede hacerlo.
void realloc(void *p, size_t size)
Cambia el tamao del objeto al que apunta p, si la llamada es efectiva realloc
regresa el apuntador al espacio nuevo, si no regresa NULL, en este caso p no
cambia.
void free(void *p)
Libera el espacio a donde apunta p, debe ser un espacio asignado por malloc,
calloc o realloc.
void exit(int estado)
Termina el programa y regresa al invocador mandndole el estado de salida, el
estado de salida depende del sistema.
int system(const char *s)
Pasa la cadena al entorno para que la ejecute, el valor de regreso depende de el
sistema.

144

APNDICE A: LA BIBLIOTECA ESTANDAR


double atof(char *s)
Convierte s a un numero doble.
int atoi(char *s)
Convierte s a un numero entero.
long atol(char *s)
Convierte s a un numero entero largo.

assert.h
void assert(int exp)
Se usa para buscar errores en los programas, cuando la expresin exp vale 0
assert interrumpe el programa y luego imprime la expresin que fallo, el nombre
del archivo donde se encuentra dicha expresin y el nmero de linea donde se
interrumpi el programa.

time.h
La librera time.h tiene funciones para manipular la hora del sistema. La
estructura struct tm tiene los componentes del calendario.
int tm_sec; segundos despus del minuto (0-59)
int tm_min; minutos despus de la hora (0-59)
int tm_hour; horas despus del a medianoche (0-23)
int tm_day; dia del mes (1-31)
int tm_mon; meses desde enero (0-11)
int tm_year; aos desde 1900
int tm_wday; dias desde el domingo (0-6)
int tm_yday; dias desde enero 1 (0-365)
int tm_isdst; bandera de "daylight saving time"
time_t time(time_t *tp)
Regresa la hora y la fecha actual, regresa -1 si el tiempo no est disponible. Si
tp no es NULL el valor de retorno se le asigna a tp.
char *asctime(const struct tm *tp)

145

APNDICE A: LA BIBLIOTECA ESTANDAR


Convierte la hora almacenada en tp a una cadena de la forma "sat feb 21
14:29:10 2004\n\0".
char *ctime(const time_t *tp)
Convierte la hora almacenada en tp a una cadena de la forma "sat feb 21
14:29:10 2004\n\0". Del mismo modo que lo hara asctime.
struct tm *gmtime(const time_t *tp)
Convierte la hora almacenada en tp a la hora coordinada universal, regresa
NULL si no est disponible.
struct tm *localtime(const time_t *tp)
Convierte la hora almacenada en tp a la hora local.

APNDICE B: ALGUNOS PROBLEMAS PROPUESTOS


1. En la aritmtica de C hay varios tipos de datos. Haz un programa que
calcule la divisin 5/2 eliminando los decimales.
2. Escribe un programa que calcule el residuo de una divisin.
3. Escribe un archivo de cabecera (.h) con dos nmeros enteros, luego, en
la funcin main imprime el resultado de la divisin de estos dos en
notacin decimal con punto flotante (6.45673 por ejemplo).
4. Inventa un programa que capture una calificacin y que diga si la
calificacin es aprobatoria o no. Usa if's
5. Escribe un programa que acepte una temperatura (float) y que la
clasifique por rangos, si la temperatura va de -40 a 0 escribe un mensaje
que diga "congelamiento", de 0.0001 a 10 "mucho fro", de 10.0001 a 20
"Es mejor usar un abrigo" de 20.0001 a 30 "buen tiempo" y de 30.0001 en
adelante que imprima "mucho calor", adems si la temperatura es menor
a -40.0001 o mayor que 50 debe de imprimir un mensaje que diga "Eso es
inhumano". Usa if's anidados.
6. Captura una calificacin entera del 5 al 10, para cada calificacin escribe
un mensaje, como "excelente" para el 10 o "regular" para el 7, pero si la
calificacin no est comprendida entre el 10 o el 5 debe de imprimir un
mensaje que diga "Calificacin incorrecta". Usa la estructura switch-case.
7. Escribe un programa que imprima una secuencia de nmeros del 1 al 20,
pero omitiendo los nmeros pares, puedes usar el ciclo for combinado
con un if o solo el ciclo for.
8. Escribe un programa que muestre un men, la opcin 'a' debe de imprimir
las tablas de multiplicar del uno al diez (ciclos for anidados), la opcin 'b'
146

APNDICE B: ALGUNOS PROBLEMAS PROPUESTOS


debe de mostrar la serie del dos a la potencia n mientras n sea menor o
igual a 10, usando el ciclo while. El men lo puedes hacer usando if's
anidados o un switch-case.
9. Al programa anterior mejrale el men haciendo que se imprima
indefinidamente hasta que se oprima la 'c' para salir, usa la funcin
system para borrar la pantalla y el ciclo do-while para imprimir el men
correctamente.
10. Usa el mismo programa anterior, solo que ahora separa la opcin 'a' y la
opcin 'b' en dos funciones aparte.
11. Escribe una funcin que reciba una letra como parmetro, la funcin debe
de regresar un 1 si la letra es mayscula o un 0 si es minscula.
12. Usando algo parecido a la funcin del programa anterior construye un
programa que acepte una cadena de 10 caracteres, y mande el arreglo a
una funcin que regrese la primer posicin donde encuentre una
mayscula, por ejemplo si se escribe Perro debe de regresar un 0, o si se
escribe un peRRo debe regresar un 2, en caso de que no encuentre
ninguna mayscula debe regresar un -1.
13. Escribe una funcin que acepte un arreglo bidimensional como parmetro
y que regrese el nmero de la columna con la suma mayor de elementos.
14. Escribe un programa que capture 5 estructuras con los siguientes datos
cada una:
Nombre
Telfono
Direccin
15. Debes de usar un arreglo de estructuras, La funcin que captura los datos
no debe de ser main, el arreglo de estructuras debe de ser declarado en
main y la funcin que imprime los datos debe de ser una funcin aparte
tambin.
16. Escribe un programa que acepte una variable cualquiera y que imprima
su direccin en memoria junto con su valor. Usa lo visto en apuntadores.
17. Escribe una funcin que sume un valor a una variable, dicha funcin debe
ser void suma(tipo *v, tipo s), donde *v es el nmero que va a cambiar su
valor y s es la cantidad que se va a agregar.
18. Revisa el siguiente cdigo, hazlo funcionar y explica cmo es que
funciona en base a lo que se vio en el tema de apuntadores.
#include<stdio.h>
147

APNDICE B: ALGUNOS PROBLEMAS PROPUESTOS


int main(int argc, char **args){
long direccion;
int *apuntador;
int variable = 5;
printf("El valor de la variable es
%d\n", variable);
printf("La direccion de la variable
es %ld\n", (long)&variable);
direccion = (long)&variable;
printf("El valor de direccion es
%ld\n", direccion);
apuntador = (int *)direccion;
printf("El valor almacenado en %ld
es %d\n", (long)apuntador, *apuntador);
return 0;
}
19. Escribe una funcin que acepte como parmetro un apuntador a una
cadena de caracteres, que convierta todos las letras a maysculas y que
regrese el nmero de caracteres que hay en la cadena. La funcin sera
de la forma int convertir(char *cadena).
20. Escribe con base al programa del problema 14 otro programa que guarde
las estructuras en un archivo y que luego las recupere usando fwrite y
fread respectivamente, las estructuras ya no deben de estar declaradas
como arreglo.
21. Por ltimo escribe un programa que permita almacenar un nmero
indefinido de estructuras, usando una lista enlazada doble, el programa
debe de permitirnos recorrer la lista en ambas direcciones (atrs y
adelante), insertar un elemento en su posicin correcta y eliminar
elementos. Al final la lista debe de grabarse en un archivo y quedar con
un formato agradable a la vista, esto no se puede hacer con fread y fwrite,
as que debes de usar fprintf y fscanf. Al decir que los elementos deben
insertarse en orden correcto me refiero a que el elemento tiene un
nmero de ndice, as que si hay un 1 y un 4 al aadir un 2 este debe de
quedar entre el 1 y el 4. La estructura a usar es la siguiente:
struct p20{

148

APNDICE B: ALGUNOS PROBLEMAS PROPUESTOS


int lugar;
char nombre[20];
struct p20 *atras;
struct p20 *adelante;
}

APNDICE C: RECOMENDACIONES Y TEMAS NO


VISTOS
En s, como se vio en este libro, C es un lenguaje muy pequeo, flexible y
extensible. Las estructuras que usa no son tan complicadas y nos permiten
hacer casi cualquier cosa. Mi recomendacin es que despus de haber ledo
este libro y haber comprendido bien cmo funciona C, busques ms bibliografa
ms avanzada, en cuanto apuntadores, que fue el tema fuerte del libro y es la
mejor arma de C a mi gusto, el libro me pareci bastante completo, pero hay
mucho ms acerca de la programacin en C que no puede ser vista en un solo
libro ni tampoco escrita por un solo autor.
El libro te va a servir muy bien para conocer el lenguaje, pero necesitas usar un
buen compilador y tratarlo, algunos detalles de C pueden variar un poco. La
librera del compilador que uses puede tener funciones diferentes, de hecho las
funciones de la librera estndar que se vieron en este libro son muy pocas, hay
ms, que no las inclu porque varan de un compilador a otro y porque no es algo
que vas a usar comnmente. El compilador que elijas puede tener sus propias
libreras con funciones para un modo grfico en una interfaz windows o kde,
funciones para comunicacin en red, otras funciones matemticas, funciones
especficas para el sistema, ya sea DOS, Windows, Unix, Linux, etc. La
programacin aplicada especficamente a un sistema operativo en particular
requiere de bibliografas ms avanzadas.
Hay dos temas que no se ven en este libro, el preprocesador de C y el enlazado
de varios cdigos fuentes dentro del mismo proyecto. El preprocesador de C,
que se usa para incluir archivos por medio de #include o para definir constantes
con el uso de #define tiene otras aplicaciones, por ejemplo las funciones macro,
o la compilacin condicional. En cuanto a el enlazado de varios archivos fuente,
no se vio porque depende en gran parte de el compilador que uses. De todas
formas creo necesario darte una breve explicacin de cmo se hace.
Digamos que tienes un archivo fuente que se llama 2.c, en este archivo debes
de poner lo siguiente.
/* 2.c */
149

APNDICE C: RECOMENDACIONES Y TEMAS NO VISTOS


#include<stdio.h>
int parte2(char *nombre){
int n;
printf("Hola %s, introduce un numero: ", nombre);
scanf("%d", &n);
return n;
}
Ahora el archivo 1.c debe de tener lo siguiente.
/* 1.c */
#include<stdio.h>
/* se declara una funcion externa */
extern int parte2(char *);
int main(int argc, char **args){
char nombre[10];
int numero;
printf("Como te llamas? ");
gets(nombre);
numero = parte2(nombre);
printf("%d es un buen numero\n", numero);
return 0;
}
Solo puede haber una funcin main y todas las funciones y variables que se
encuentren en otro archivo fuente deben de ser declaradas como externas
donde se vayan a usar, Las funciones de cabecera se deben incluir en todos los
archivos fuentes, pero hay que ser cuidadoso con las inclusiones mltiples, las
150

APNDICE C: RECOMENDACIONES Y TEMAS NO VISTOS


funciones de la librera estndar no tiene ese problema.
Un ejemplo de pre-procesamiento sera el siguiente. En el archivo config.h se
selecciona la opcin 0 o 1 para signar a N, en base a esta desicin el
preprocesador elije qu archivos incluir y qu valores definir.
/* archivo config.h */
#if !defined YA
Si YA no est definido lo define
#define YA
esto
asegura que no se defina a N varias veces.
#define N 1 /* 1 = dos 0 = unix */
asigna el valor adecuado a N.

Se le

#endif
Fin del if.
/* fin de config.h */
/* archivo main.h */
#include "config.h"
Incluye el archivo de configuracin
#if N
Si N vale 1 incluye el archivo d.h y asigna
#include "d.h"
0 a SIS.
#define SIS 0
#else
De lo contrario incluye el archivo u.h y asigna
#include "u.h"
1 a SIS.
#define SIS 1
#endif
Fin del if.
151

APNDICE C: RECOMENDACIONES Y TEMAS NO VISTOS

extern void funcion(void);


Declara la funcin externa.
/* fin de main.h */
/* archivo d.h */
#define OP 2
El archivo d.h asigna 2 a OP.
/* fin de d.h */
/* archivo u.h */
#define OP 3
El archivo u.h asigna 3 a OP.
/* fin de u.h */
/* archivo main.c */
#include<stdio.h>
#include"main.h"
main(){
char *sistema[] = {"dos", "Unix", "es bueno", "es
exelente"};
printf("%s, %s\n", sistema[SIS], sistema[OP]);
funcion();
}
/* fin de main.c */
/* archivo dor.c */
#include<stdio.h>
#include"main.h"

152

APNDICE C: RECOMENDACIONES Y TEMAS NO VISTOS


void funcion(void){
printf("jejeje %d je\n", N);
}
/* fin de dos.h */
Esto se pudo hacer mucho ms sencillo, pero lo escrib as para que veas cmo
el pre-procesamiento puede decidir qu archivos incluir o qu valores asignar.
Aun hay ms acerca del lenguaje C, pero por mi parte ha sido todo por el
momento. Espero sinceramente que todo esto te sirva bastante, que seas un
buen programador gracias a tu prctica y a este libro ;) y que la pases de
maravilla. Hasta pronto...

153

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