Beruflich Dokumente
Kultur Dokumente
ndice general
1
1.1
1.2
. . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.1
Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.2
GNU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3
1.4
Comentarios
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.1
Historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2
Enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3
Flotantes
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4
Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Programacin en C
3.1
Prlogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1
3.1.2
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Anexos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3
Enlaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4
Licencia y autores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
3.5
10
3.2
ndice de contenidos
3.2.1
Programacin en C/Expresiones
11
13
5.1
13
5.2
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
La funcin scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
5.2.1
6
15
6.1
15
6.1.1
16
Operadores de comparacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
ii
NDICE GENERAL
6.1.2
Operadores lgicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
6.1.3
Evaluacin de cortocircuito
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
6.2
17
6.3
El bucle while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
6.4
El bucle for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
6.5
El bucle do...while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
6.6
La sentencia goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
19
7.1
Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
7.1.1
La sentencia return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
7.1.2
Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
7.1.3
Declaracin y denicin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
7.1.4
Paso de Parmetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
7.1.5
21
Funciones Recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
7.2.1
22
7.2.2
22
7.2
Programacin en C/Vectores
24
26
28
10.1 Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
10.1.1 fopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
10.1.2 fclose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
10.1.3 feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
10.1.4 rewind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
10.2 Lectura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
10.2.1 fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
10.2.2 fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
10.2.3 fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
10.2.4 fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
10.3 Escritura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
10.3.1 fputc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
10.3.2 fputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
10.3.3 fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
10.3.4 fprintf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
33
11.1 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
34
NDICE GENERAL
iii
11.2 Uniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
11.3 Enumeraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
12 Programacin en C/Punteros
37
13 Concepto
38
14 Declarando punteros
39
15 Explicacin
40
16 Operadores
41
17 Operaciones Aritmticas
42
18 Punteros Constantes
43
19 Punteros Genericos
44
20 Punteros y Matrices
45
46
22 Matrices de Punteros
47
23 Punteros a Punteros
48
49
25 Ejemplos
50
50
52
27 Concepto
53
54
55
30 Concepto
56
57
31.1 Explicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
31.2 Ejemplos
58
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
32 Mtodo de Insercin
59
32.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
32.2 Explicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
32.3 Ejemplos
59
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iv
NDICE GENERAL
33 Mtodo QuickSort
60
33.1 Concepto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
33.2 Explicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
33.3 Ejemplos
60
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
62
35.1 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36 Programacin en C/Los errores
62
64
64
65
65
66
67
68
68
68
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
69
69
40 Programacin en C/Glib
70
40.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
71
40.3 Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
41 Programacin en C/Referencia
72
72
41.2 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
41.3 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
74
41.5 Preprocesador de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
76
76
77
78
78
44.1.1 Espaol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
78
NDICE GENERAL
44.1.2 Ingls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
78
78
45 Programacin en C/Bibliografa
79
79
79
46 Fundamentos de programacin
80
81
47.1 Deniciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
81
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
82
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
82
82
48 Programacin en C/Historia de C
83
48.1 Evolucin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
83
48.2 Ms informacin
84
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49 Programacin en C/Introduccin
85
49.1 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85
85
85
86
86
49.6 Requisitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
86
49.7 Herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
86
49.8 Windows
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
86
49.9 GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
87
88
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
88
88
88
89
50.4.1 malloc
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
89
50.4.2 calloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
89
50.4.3 realloc
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
90
50.4.4 free . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
90
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
90
92
50.6.1 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
92
vi
NDICE GENERAL
50.6.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
93
Captulo 1
Windows
Para compilar un programa C en entornos Windows, debemos seguir una serie de pasos que varan segn el compilador de C que queramos utilizar. Antes que nada, sera bueno que se revises la documentacin del compilador elegido
para conocer los comandos exactos.
1
1.2.2
GNU
Si bien existen otros compiladores, lo ms usual y ms sencillo para compilar un programa en GNU/Linux es el
compilador gcc, ya que es el que se incluye en todas las distribuciones.
De cualquier forma, es posible realizar la compilacin desde lnea de comandos o desde el entorno grco.
Para realizarla desde lnea de comandos, ser necesario contar con una terminal (xterm, konsole, gnome-terminal,
etc). No es necesario contar con permisos de root para crear o compilar programas. En esa terminal ser necesario
escribir
gcc hola.c
Si no existen errores en el cdigo, este comando nos crear un archivo ejecutable, que por omisin se llama a.out,
y que podemos ejecutar desde la lnea de comandos de la siguiente forma:
./a.out Hola mundo
Es una buena idea especicar el nombre que el archivo ejecutable tendr, pasando como parmetro al compilador la
opcin -o, de la siguiente forma:
gcc hola.c -o hola
Con lo cual, el nombre del archivo creado ser hola. Este archivo no tiene extensin ya que es la forma usual de
llamar a los archivos ejecutables en los entornos UNIX y GNU/Linux, sin embargo funcionara de la misma forma si
se llamara hola.exe.
Para ejecutarlo, haremos los mismo que en el caso anterior:
./hola Hola mundo
Existen otros parmetros que podemos especicar al compilador en la lnea de comandos, dependiendo del tipo de
programa, y en funcin de la complejidad del mismo. Por ejemplo, podemos agregar las siguientes opciones:
gcc hola.c -o hola -Wall -pedantic
La opcin -Wall nos mostrar todos los avisos que produzca el compilador, no solamente los errores. Los avisos nos
indican dnde y/o porqu podra surgir algn error en nuestro programa.
La opcin -pedantic nos aporta ms informacin sobre los errores y los avisos mostrados por GCC.
es que se naliza el programa, comunicndole al sistema operativo que el valor de retorno (un cdigo numrico
que el sistema utiliza para saber si el programa ha funcionado bien o ha dado fallos) es 0, es decir, correcto.
Las dos ltimas sentencias se encuentran encerradas entre llaves. De esta manera, forman un bloque, es decir, un
grupo de sentencias que se ejecutarn siempre de forma correlativa.
Y qu es esa lnea que precede (en realidad, que da nombre) al bloque?
int main (int argc, char **argv)
Pues es la denicin de una funcin, en este caso llamada main. En C (y en general en todos los lenguajes de
programacin estructurada) todo se hace a base de funciones, como main y printf.
La funcin main es especial, porque es el la que se invoca cuando se ejecuta el programa. Todos los programas en C
comienzan su ejecucin al principio de la funcin main, y cuando sta acaba, el programa tambin.
Veamos con ms detalle la denicin de la funcin:
int main (int argc, char **argv) { ... }
1.4 Comentarios
Una vez escrito un cdigo, tratar de entenderlo un ao ms tarde solo con leerlo puede ser frustrante: no hay manera
de saber (si el programa es medianamente complicado) qu es cada variable, o qu hace cada bloque de cdigo. Por
esto, en cualquier lenguaje de programacin son importantes los comentarios.
Un comentario en C es todo lo que se encuentre entre los smbolos /* y */. Hay que tener en cuenta que los comentarios
no se pueden anidar: si dentro de un comentario hay un /*, seguir siendo el primer */ el que nalice el comentario,
no se esperar al segundo.
Hay otro tipo de comentarios en C, procedentes del lenguaje C++, e incorporadas al estndar de C a partir de C99:
//. Todo lo que est despus de estos signos, hasta el nal de la lnea, se considerar un comentario y el compilador
no lo tomar en cuenta.
En el ejemplo presentado pueden verse tres lneas con comentarios, que documentan someramente las distintas funcionalidades del cdigo. En los prximos captulos podrn verse mejores usos de los comentarios dentro del cdigo.
Tambin podra decirse que es una herramienta bsica basada en compilador
[1] En un captulo posterior podr ver un ejemplo del uso de los parmetros que recibe main.
[2] Es importante sealar que el estndar dice que main deber denirse como funcin que retorna un entero, o de lo contrario
el resultado queda indenido.
Captulo 2
2.2 Enteros
Los enteros son el tipo de dato ms primitivo en C. Se usan para representar nmeros enteros. Pero siempre se pueden
encontrar otras aplicaciones para los nmeros enteros. En general se pueden usar para representar cualquier variable
discreta.
Los tipos de datos enteros son: short, int, long y long long, cada uno representando un nmero entero de un tamao
o capacidad determinado. Segn el compilador y la plataforma de hardware, cada uno de estos tipos de dato puede
ocupar desde 1 byte hasta 8 bytes en memoria (para ms detalles busca en la referencia).
Adems, el lenguaje C hace la distincin de si el entero es con signo (signed) o sin signo (unsigned). En caso de que
no se declare si es con signo o sin signo, se toma con signo.
Algunos ejemplos de declaraciones de enteros:
int a; unsigned int a; signed long a; signed long long a = 10000000;
Todos los nmeros son representados en memoria mediante una cadena de bits. En el caso de los nmeros con signo,
el bit ms signicativo es el que se usa para representar el signo. La representacin de los nmeros negativos se
realiza mediante el complemento a dos, que es una tcnica que permite operar con los nmeros negativos de forma
lgica.
A modo de ejemplo, la representacin en memoria del nmero 8 en una variable de 2 bytes, entera, con signo, sera
la siguiente:
1111111111111000
2.3 Flotantes
Se denomina otantes a los tipos de datos que representan a los nmeros reales, ya que utilizan un sistema de representacin basado en la tcnica de coma otante, que permite operar con nmeros reales de diversas magnitudes,
5
mediante un nmero decimal llamado mantisa y un exponente que indica el orden de magnitud.
El tipo de dato otante en lenguaje C slo tiene dos tamaos: el oat y el double, que son 4 bytes y 8 bytes respectivamente. Se los puede utilizar tanto para representar nmeros decimales, como para representar nmeros enteros
con un orden de magnitud muy grande.
La forma de declarar una variable otante es escribiendo en una lnea uno de los tipos de datos otantes y a continuacin el nombre de la variable y tal vez algn valor que se les quiera dar.
Algunos ejemplos:
oat a; double a = 1e23; double a = 3.1416; oat a = 4e-9; double a = 78;
Hay que tener en cuenta que aunque los valores otantes son ms convenientes para algunas aplicaciones, hay casos
en los que se preeren los enteros. Esto se debe a que los nmeros otantes no necesariamente tienen soporte de
hardware, en particular en las plataformas integradas. Una alternativa que se utiliza en estas situaciones es interpretar
los enteros como decimales de forma que 150 se interprete como 1.5 y 2345 como 23.45.
Para el caso de los otantes de 4 bytes, se utiliza 1 bit para el signo, 8 bits para el exponente y 23 bits para el valor
del nmero. El procedimiento para almacenar un nmero en una variable otante es el siguiente:
1. Se convierte a binario la parte entera.
2. Se coloca el signo en el bit ms signicativo de la misma manera que en los enteros (1 para el - y 0 para el +).
3. Se mueve la coma (en la representacin binaria de la parte entera) hasta que est a la derecha del primer uno
y ste se descarta (el uno ms signicativo). El valor del exponente ser el nmero de posiciones que se movi
la coma. El exponente usa la representacin de un entero con complemento a dos.
4. Se convierte en binario la parte decimal del nmero. Esto usando el peso de los bits. el bit decimal ms signicativo vale 1/2, el siguiente vale 1/4, el otro 1/8, el otro 1/16 y as hasta completar lo que falta para los 23bits
del valor.
5. Se concatena todo y ese es el valor otante representado en memoria.
2.4 Caracteres
Los caracteres se representan utilizando el tipo char, que tiene slo 1 byte de tamao. Este tipo se utiliza para representar los 256 caracteres de la tabla de caracteres del sistema. El tipo char es tambin un tipo entero, ya que puede
tomar valores de 0 a 255. Por lo tanto tambin puede ser signed o unsigned.
En cuanto a la forma de declarar variables de tipo char es la misma forma que con los otros tipos.
char a; char a = 's; unsigned char a = 48;
Como puedes ver, se le puede asignar un nmero a una variable char, ya que se trata de un tipo entero. En muchas
situaciones se utiliza el tipo char para almacenar nmeros pequeos, ya que ocupa en memoria slamente un byte.
Es importante notar que con la llegada de la codicacin UTF-8, los caracteres de los diversos idiomas pueden ocupar
1, 2, 3 o 4 bytes, de modo que el tipo char ya no alcanza para la representacin de todos los caracteres posibles. Por
ello, el estndar C99 introduce el tipo wchar que puede ocupar ms de 1 byte, segn sea necesario para la codicacin
utilizada por el sistema.
Captulo 3
Programacin en C
3.1 Prlogo
El avance de la tecnologa y la concurrente entrega de informacin, nos permite generar una edicin, variable y
alternativa en la enseanza del lenguaje de programacin que ms impacto social ha tenido en la historia de la informtica. Este libro ha sido forjado con la incansable ayuda de informticos de habla hispana, quienes byte a byte han
colaborado por hacer de la informacin una fuente de conocimiento global. De la misma forma, contina hoy siendo
modicado en una constante bsqueda de superacin de la calidad.
Esta obra est llamada a ser la piedra angular en la enseanza de la programacin, logrando abarcar todos los aspectos
del lenguaje en diversos niveles y de esta forma ser tanto una referencia tcnica para quienes ya dominan el lenguaje
como una introduccin sencilla para quienes estn empezando a conocerlo.
Viajaremos por la historia del lenguaje, veremos su propsito e indagaremos en la ciencia de la programacin. El
n es otorgar al lector una doctrina clara de la programacin y el lenguaje C; lo induciremos a conseguir un manejo
importante del lenguaje.
3.1.1
Este libro est dirigido a todos los que deseen obtener conocimientos de programacin, pues el objetivo explcito que
nos ha motivado a crearlo es difundir la importancia del lenguaje C en el mundo de la informtica. Si nos enfocamos
a un grupo social especco, podremos indicar que este libro contribuir con los estudiantes de carreras del rea
informtica, debido a que los temas convenidos, son parte de su plan de estudios.
3.1.2
Porque el lenguaje C es la base fundamental de la programacin. Para quienes estn en el ambiente de la informtica
es crucial tener por lo menos nociones de este lenguaje. Varios sistemas operativos, cientos de bibliotecas, y miles de
programas estn construidos utilizando C, al conocerlo es posible entender, colaborar y desarrollar en este lenguaje.
Los sistemas, programas, juegos y herramientas que nosotros disfrutamos hoy fueron construidos por personas como nosotros, que empezaron con nuestro mismo potencial y fueron aprendiendo a hacer cosas fantsticas con las
herramientas que tenan a mano.
Una razn importante para otro libro de lenguaje C es tambin que los libros existentes muestran muy poca documentacin de calidad. En Internet existe una cantidad inmensa de informacin publicada pero est dispersa, y mal
manejada en algunos sitios. Es la intencin de este libro crear un buen compendio de informacin, que permita a los
interesados aprender a programar en C.
CAPTULO 3. PROGRAMACIN EN C
(a) Herramientas necesarias
2. Historia de C
3. Fundamentos de programacin
4. Primer programa en C
(a) Compilacin de programas en C
5. Tipos de datos
(a) Enteros
(b) Reales
(c) Caracteres
6. Expresiones
7. Interaccin con el usuario
8. Instrucciones de control
(a) La estructura condicional if...else
(b) La estructura condicional switch...case
(c) El bucle for
(d) El bucle while
(e) El bucle do...while
(f) La sentencia goto
9. Uso de Funciones
10. Vectores
11. Cadenas de caracteres
12. Manejo de archivos
(a) Lectura
(b) Escritura
(c) fread y fwrite
13. Estructuras y Uniones
(a) Estructuras
(b) Uniones
14. Punteros
15. Manejo dinmico de memoria
16. Matrices Dinamicas
17. Algoritmos y Estructuras de Datos
(a) Punteros a Estructuras
(b) Ordenamiento de Datos
i. Burbuja
ii. Induccion
iii. QuickSort
18. Detalles sobre el proceso de compilacin
19. Los errores
3.3. ENLACES
(a) Chequeos del C
(b) El depurador
20. Herramientas externas tiles
21. Resumen de ejemplos
(a) El Hola Mundo
(b) El Hola Mundo comentado
(c) El Hola Mundo estructurado en funciones
(d) Ejemplo de clculo con enteros
(e) Ejemplo comentado de clculo con enteros
(f) Calendario en C
(g) Clculo del logaritmo binario
22. La librera glib
23. Programacin paralela en memoria compartida
3.2.1
Anexos
1. Referencia
(a) Tipos de datos
(b) Operadores
(c) Estructuras
(d) Bibliotecas y funciones
2. Cmo compilar un programa
(a) Un poco ms sobre compilacin
3. Cdigo embebido
4. Recursos en la red
(a) Manuales, tutoriales y otros documentos
(b) Compiladores e IDEs
5. Bibliografa
3.3 Enlaces
Nociones bsicas de programacin (generales, independientes del lenguaje):
Fundamentos de programacin
Wikilibro similar a ste en ingls
C Programming
esquema de operadores y expresiones
10
CAPTULO 3. PROGRAMACIN EN C
Captulo 4
Programacin en C/Expresiones
Vamos a tratar ahora de que el ordenador haga un poco de matemticas para nosotros. Por ejemplo, que realice unas
pocas sumas, restas multiplicaciones y divisiones.
#include <stdio.h> int main(void) { int resultado; resultado=5+2; printf(Resultado de la suma: %i\n,resultado);
resultado=5-2; printf(Resultado de la resta: %i\n,resultado); resultado=5*2; printf(Resultado de la multiplicacin:
%i\n,resultado); resultado=5/2; printf(Resultado de la divisin: %i\n,resultado); return(0); }
Despus de grabarlo (por ejemplo, con el nombre ejemplo.c), lo compilamos y ejecutamos, con (respectivamente):
$ gcc ejemplo.c $ ./a.out Resultado de la suma: 7 Resultado de la resta: 3 Resultado de la multiplicacin: 10 Resultado
de la divisin: 2 $
Fijmonos en la lnea del principio de la funcin main:
int resultado;
Esta lnea lo que hace es reservar un trozo de memoria, del tamao de un int (normalmente 4 bytes), y asignarle el
nombre resultado, para poder despus referirnos a l. A partir de este momento, podemos considerar que en nuestro
programa existe una variable, que no tiene valor denido, pero a la que le podremos dar valor posteriormente.
Las lneas con printf() ya las conocemos, pero hay algo en ellas que no habamos visto antes. Esos %i y la parte de
resultado son nuevas para nosotros.
La funcin printf() no slo sabe imprimir cadenas simples, como Hola Mundo\n, sino tambin imprimir variables.
Para ello, en el lugar de la cadena donde queremos que aparezca el valor de la variable, introducimos lo que se llama
una cadena de conversin de printf(). Estas cadenas siempre empiezan por %, siendo %i la cadena para imprimir un
entero, como es en nuestro caso int resultado. Finalmente, printf() debe saber qu valor escribir, por eso le damos
otro argumento (u otros), usando , como separador, que contienen las variables cuyos valores queremos mostrar.
En el resto del programa hemos visto cmo decirle al ordenador que ejecute una suma, una resta, una multiplicacin
y una divisin entera, con los operadores +, -, * y /. Es de notar que el resultado de una operacin como estas
entre nmeros enteros ser siempre otro entero, como se puede observar en la divisin, en la que no obtenemos un
bonito decimal, sino un resultado entero. Adems, hemos visto que el resultado de esas operaciones, que llamamos
expresiones, puede ser asignado a una variable:
resultado = 7;
Esa asignacin se hace mediante el operador de asignacin: =. Con l, ya conocemos cinco operadores.
Pero, como = tambin es un operador, cmo sabe el ordenador qu operador debe ejecutar primero? Y si es un
operador, por qu no da un resultado? No crea una expresin?
Empezando por las ltimas preguntas, el operador de asignacin s crea una expresin, como los operadores de suma,
resta, multiplicacin y divisin, y esa expresin tiene un resultado, que es el valor que obtiene el lado izquierdo al
realizar la operacin. En cuanto a saber qu se debe ejecutar primero, el ordenador tiene una lista de precedencia,
segn la cual siempre ejecuta primero las multiplicaciones y divisiones, de izquierda a derecha, a continuacin las
sumas y restas, de izquierda a derecha, y a continuacin las asignaciones, de derecha a izquierda. Para ms detalles
11
12
Captulo 5
14
int numero;
numero=3;
printf(El doble de %i es %i y su cuadrado es %i\n,numero,numero*2,numero*numero);
5.2.1
La funcin scanf()
scanf() es una de las funciones ms usadas por los principiantes para hacer entrada de datos en el lenguaje C. Tiene
una sintaxis muy parecida a printf: recibe una cadena con el formato de los datos y luego se ponen las variables en
orden que correspondan a ese tipo de datos. Es decir, as como en printf se pueden mostrar por pantalla los datos de
varias variables en una misma sentencia, en scanf se pueden capturar varios datos en una sola sentencia.
#include <stdio.h> int main() { int a; printf (diga un valor para a:"); scanf("%i,&a); printf (el valor es: %i\n,a);
return 0; }
Por ahora no nos interesan las dems sentencias, slo la que contiene scanf. En el cdigo se ve lo siguiente:
scanf("%i,&a);
Se observa que la funcion printf dej en pantalla una peticin para que el usuario introdujera un valor. Entonces,
scanf recibe como argumento una cadena del formato en que se van a capturar los datos y la lista de variables que
van a recibir valores y que deben coincidir con los del formato.
En este caso la cadena de formato, "%i, especica que el usuario ingresar un nmero entero. Luego se designa a la
variable a para contener a ese nmero. El smbolo (&) que precede a a es para especicar que lo que se est enviando
como argumento no es el valor que posee la variable a sino la direccin de memoria en que se encuentra. En este
momento eso no tiene mucha relevancia, slo hay que recordar que se debe usar el smbolo & dentro del scanf. En el
momento en que hablemos de punteros veremos ms detalles de esto.
Otro ejemplo del uso de scanf:
#include <stdio.h> int main() { int a,b; printf (introduzca dos valores con el formato \"a,b\" :"); scanf("%i,%i,&a,&b);
printf (el primer valor : %i\n,a); printf (el segundo valor : %i\n,b); return 0; }
Aqu hemos introducido una nueva variable en el cdigo. La cadena de formato, "%i,%i especica que el usuario
ingresar un nmero, seguido de una coma, y luego otro nmero. El primer %i ser capturado por la variable a y el
segundo por b.
Captulo 6
Programacin en C/Instrucciones de
control
Como ya se ha mencionado, C es un ejemplo de programacin estructurada. En este tipo de programacin, es necesario contar con ciertas estructuras que permitan controlar el ujo del programa, es decir, tomar decisiones y repetir
acciones.
15
16
6.1.1
Operadores de comparacin
El smbolo > visto en el ltimo ejemplo es un operador, que en este caso compara dos nmeros enteros y devuelve
verdadero si el primero es mayor, falso en caso contrario.
A continuacin un listado de los posibles operadores de comparacin en C y su signicado.
Teniendo en cuenta que en C se toma como falso el valor 0, y como verdadero cualquier otro valor, una prctica
comn es expresar condiciones sin utilizar ningn operador:
oat division(int dividendo, int divisor) { if (divisor) { return dividendo / divisor; } else { printf (No se puede dividir
por cero\n); return 0; } }
En este caso, la expresin (divisor) es equivalente a (divisor != 0).
6.1.2
Operadores lgicos
Los operadores && (y), || (o) y ! (no) son operadores lgicos. Permiten operar con expresiones lgicas para
generar expresiones ms complejas.
Por ejemplo: determinar si un ao es bisiesto o no. Los aos son bisiestos si son divisibles por 4, pero no si son
divisibles por 100, a menos que tambin sean divisibles por 400.
if ( (!(a % 4) && (a % 100)) || !(a % 400) ) { printf(es un ao bisiesto.\n); } else { printf(no es un ao bisiesto.\n); }
En realidad, teniendo en cuenta la prioridad de los operadores utilizados, podemos simplicar la expresin anterior
del siguiente modo:
if ( !(a % 4) && (a % 100) || !(a % 400) ) { printf(es un ao bisiesto.\n); } else { printf(no es un ao bisiesto.\n); }
Adems, como a cada rama del if le sigue una nica instruccin, podemos expresar la expresin anterior del siguiente
modo:
if ( !(a % 4) && (a % 100) || !(a % 400) ) printf(es un ao bisiesto.\n); else printf(no es un ao bisiesto.\n);
En este caso, se utiliza el operador mdulo (%), que obtiene el resto de la divisin entera de un nmero por otro.
Cuando un nmero es divisible por otro, el resto de su divisin entera ser cero. Siendo que cero es equivalente a
falso, y cualquier valor distinto de cero es equivalente a verdadero, podemos usar el operador % para vericar si el
nmero es mltiplo de 4, de 100 o de 400.
6.1.3
Evaluacin de cortocircuito
La evaluacin en corto circuito es una caracterstica del lenguaje C que se utiliza para optimizar la ejecucin de
programas. Consiste en que el programa puede vericar si una expresin es verdadera o falsa antes de haber evaluado
toda condicin.
Por ejemplo, si se tiene una condicin como la siguiente:
if ((a > 2) || (b < 4)) { ... }
Al ejecutarse el programa, se evaluar primero si a > 2. En el caso en que sea verdadero, no continuar con la siguiente
condicin, ya que el resultado ser de cualquier modo verdadero.
De la misma forma, si la condicin fuera:
if ((a > 2) && (b < 4)) { ... }
En este caso, si no se cumple que a > 2, no se evaluar la siguiente condicin, ya que el resultado ser falso de todos
modos.
Esta caracterstica no tiene demasiada importancia al comenzar a programar, pero facilitar ciertas operaciones y
17
18
no.
Incremento: es una sentencia que ejecuta al nal de cada iteracin del bucle. Por lo general, se utiliza para incrementar
la variable con que se inicio el ciclo. Luego de ejecutar el incremento, el bucle revisa nuevamente la condicin, si es
verdadera tiene lugar una ejecucin ms del cuerpo del ciclo, si es falsa se termina el ciclo y as.
Aqu se muestra el mismo ejemplo visto para el bucle while, pero implementado con un bucle for:
int i; for (i=0; i < 100; i = i + 1) { printf("%d\n, i); }
Nota: En C, la sentencia i = i + 1 puede escribirse en forma ms reducida como i++. Esta forma se utiliza ms
comnmente en el bucle for:
int i; for (i=0; i < 100; i++) { printf("%d\n, i); }
Captulo 7
7.1.1
La sentencia return
La sentencia return puede utilizarse dentro de una funcin para terminar su ejecucin.
En el ejemplo anterior, la funcin holamundo fue declarada con valor de retorno de tipo void (es decir, valor de retorno
nulo). En ese caso, la sentencia return no lleva ningn parmetro adicional, ya que la funcin no debe devolver ningn
valor a la funcin que la llama.
En cambio, la funcin main tiene un valor de retorno de tipo int, por lo que return debe ir seguido de un valor entero
(0 en el ejemplo). El valor 0 se utiliza para indicar que el programa ha llegado a un punto en el que todo se ha
desarrollado correctamente y se utiliza cualquier otro valor para indicar que ha habido algn tipo de error.
La instruccin return no es una funcin, se trata de una sentencia que lo que hace es retornar como valor de la funcin
el valor que se le proporciona como argumento.
7.1.2
Argumentos
Las funciones tambin pueden recibir argumentos o parmetros, para modicar su comportamiento. Por ejemplo, la
denicin de una funcin para sumar dos nmeros sera de la siguiente manera:
#include <stdio.h> int sumar(int numero1, int numero2) { return numero1 + numero2; } int main(void) { int suma
= sumar(5, 3); printf(La suma es: %d ", suma); return 0; }
19
20
En este ejemplo, la funcin sumar recibe dos argumentos de tipo int y su valor de retorno tambin es de tipo int. Dentro
de la funcin main, se llama a la funcin sumar poniendo entre parntesis los valores deseados para sus argumentos,
en orden, separados por una coma. As, dentro de sumar el nmero 5 ser asignado a la variable numero1 y el nmero
3 a numero2.
7.1.3
Declaracin y denicin
En el ejemplo anterior podemos notar que la funcin sumar gura en el cdigo antes que main. Qu pasara si las
escribiramos en distinto orden?
#include <stdio.h> int main(void) { int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada an */ printf(La
suma es: %d ", suma); return 0; } int sumar(int numero1, int numero2) { return numero1 + numero2; }
En este caso el programa es errneo y no compila, ya que en la lnea donde se llama a la funcin sumar, el compilador
an no conoce ninguna funcin con ese nombre, y cules son sus argumentos y valor de retorno.
Una posible solucin es declarar el prototipo de la funcin al principio, para informar al compilador que existe, y
luego denir el cuerpo de la misma en cualquier lugar del programa:
#include <stdio.h> /* Declaracin */ int sumar(int numero1, int numero2); int main(void) { int suma = sumar(5, 3);
printf(La suma es: %d ", suma); return 0; } /* Denicin */ int sumar(int numero1, int numero2) { return numero1
+ numero2; }
7.1.4
Paso de Parmetros
Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de enviar los datos hacia una
funcin por valor ypor referencia, las cuales modican en diferente forma el comportamiento de el programa.
Por Valor
El paso por valor enva una copia de los parmetros a la funcin por lo tanto los cambios que se hagan en ella no son
tomados en cuenta dentro de la funcin main(). Ejemplo:
/* * por_valor.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void sumar_valor(int numero); /* prototipo de la funcin */ int main(void) { int numero = 57; /* denimos numero con valor de 57*/
sumar_valor(numero); /* enviamos numero a la funcin */ printf(Valor de numero dentro de main() es: %d\n,
numero); /* podemos notar que el valor de numero se modica * slo dentro de la funcin sumar_valor pero en la
principal * nmero sigue valiendo 57 */ return 0; } void sumar_valor(int numero) { numero++; /* le sumamos 1 al
numero */ /* el valor de nmero recibido se aumenta en 1 * y se modica dentro de la funcin sumar_valor() */
printf(Valor de numero dentro sumar_valor() es: %d\n, numero); return; }
Por Referencia
El paso por referencia se hace utilizando apuntadores. Se enva la direccin de memoria de la variable, por lo tanto
los cambios que haga la funcin si afectan el valor de la variable. Ejemplo:
/* * por_referencia.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void sumar_referencia(int
*numero); /* prototipo de la funcin */ int main(void) { int numero = 57; /* denimos numero con valor de 57*/
sumar_referencia(&numero); /* enviamos numero a la funcin */ printf("\nValor de numero dentro de main() es:
%d ", numero); /* podemos notar que el valor de numero se modica * y que ahora dentro de main() tambin se ha
modicado * aunque la funcin no haya retornado ningn valor. */ return 0; } void sumar_referencia(int *numero) {
*numero += 1; /* le sumamos 1 al numero */ /* el valor de numero recibido se aumenta en 1 * y se modica dentro
de la funcin */ printf("\nValor de numero dentro sumar_referencia() es: %d, *numero); return; }
7.1.5
21
Adems de pasar valores a una funcin, tambin se pueden declarar tipos de datos dentro de las funciones, estos tipos
de datos declarados dentro de una funcin solo son accesibles dentro de esta misma funcin y se les conocen como
variables locales, as pues podemos denir los mismos nombres de variables en diferentes funciones, ya que estas
variables solo son accesibles dentro de esas funciones. Ejemplo:
/* * locales.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void funcion1() { int dato = 53; /* denimos dato en 53*/ char num1 = 'a'; /* num1 vale a */ /* imprimimos */ printf(Funcion1, dato=%d, num1=%c\n,
dato, num1); return; } void funcion2() { int dato = 25; /* denimos dato en 25*/ char num2 = 'z'; /* num2 vale z*/
/* imprimimos */ printf(Funcion2, dato=%d, num2=%c\n, dato, num2); return; } int main(void) { funcion1(); /*
llamamos a funcion1() */ funcion2(); /* llamamos a funcion2() */ return 0; }
En este caso la variable dato, esta denida dentro de cada una de las funciones y son totalmente distinta una de otra
y no se puede utilizar fuera de esta, as pues num2 no puede ser utilizada por la funcion1() y num1 tampoco puede
ser utilizada por funcion2().
Existen pues variables que se denen fuera de la funcin principal main() y fuera de cualquier otra funcin creada
por nosotros, estas variables se les conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de
main() y dentro de cualquier funcin creada por nosotros. Ejemplo:
/* * global.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> int variable_global = 99; /* inicializamos la variable global */ void funcion(); int main(void) { /* imprimimos el valor*/ printf(main(), acceso a
variable_global %d\n, variable_global); /* llamamos a la funcin */ funcion(); return 0; } void funcion() { /* imprimimos el valor*/ printf(funcion(), acceso a variable_global %d\n, variable_global); return; }
n! =
n N
k=1
1
n(n 1)!
Si n = 0
Si n > 0
n N.
22
base */ return 1; /* como 0! = 1, se retorna 1*/ else return n * factorial (n - 1); /* llamada a esta misma funcin */ } int
main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%ld ", factorial(5)); return 0; }
Tambin existen otros tipos de funciones recursivas como lo es el producto de dos nmeros. El producto de a b,
donde a y b son nmeros enteros positivos seria:
Solucin iterativa:
a + a + + a b
{z
}=
ab=|
i=1 a
b veces
Solucin recursiva:
{
0
ab=
a + a (b 1)
Si b = 0
Si b > 0
As pues 7 3 es:
7 3 = 7 + 7 2 = 7 + 7 + 7 1 = 7 + 7 + 7 + 0 = 21
Podemos ver que la multiplicacin de dos nmeros a, b se puede transformar en otro problema ms pequeo multiplicar a por (b-1), el caso base se produce cuando b = 0 y el producto es 0. Ejemplo:
/* * producto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> int producto(int a, int b) { if (b
== 0) /* caso base */ return 0; /* como b = 0, se retorna 0*/ else return a + producto (a, b - 1); /* llamada a esta
misma funcin */ } int main(void) { /* en este caso se llama a la funcin y se imprime directamente*/ printf("%i ",
producto( 7, 3)); return 0; }
7.2.1
Esta se produce cuando una funcin llama a otra, que esta a su vez terminar llamando de nuevo a la primera funcin.
El siguiente programa visualiza el alfabeto utilizando recursin indirecta o mutua:
/* * elalfabeto.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin
en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> void funcionA(char c); /* se declara
el prototipo de la funcin para que el llamado */ void funcionB(char c); /* a la misma en la funcin no sea implcita
*/ int main(void) { funcionA('z'); /* llamado a funcionA */ return 0; } void funcionA(char c) { if (c > 'a') /* caso
base mientras c no sea menor que A */ funcionB(c); /* llamado a la funcionB */ printf("%c ", c); /* imprimimos el
valor de c */ *la variable es un parametro no utilizado para este proceso } void funcionB(char c) { funcionA(--c); /*
llamado a la funcionA decrementando el valor de 'z' */ }
7.2.2
Tanto la iteracin como la recursin se basan en estructura de control: la iteracin utiliza una estructura repetitiva y
la recursin una estructura de seleccin. La iteracin utiliza explcitamente una estructura repetitiva mientras que la
recursin consigue la repeticin mediante llamadas repetitivas a funciones.
La iteracin termina si la condicin del bucle no se cumple, mientras que la recursin termina cuando se reconoce
un caso base.
La recursin puede presentar desventajas ante la iteracin ya que se invoca repetidas veces al mecanismo de llamada
de funciones y se necesita un tiempo mayor para realizar cada llamada.
La razn por la cual se puede elegir u optar por usar recursividad es que existen muchos problemas complejos que
poseen naturaleza recursiva y, en consecuencia, son mas fciles de implementar.
23
Ejemplo Iterativo
/* * iterativo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero); int main(int
argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10; contador++
) printf("%d! = %ld\n, contador, factorial( contador )); return 0; } /* funcion factorial iterativa */ long factorial( int
numero ) { long resultado = 1; int i = 0; /* declaracion de la funcin factorial iterativa */ for ( i = numero; i >= 1; i-) resultado *= i; return resultado; }
Ejemplo Recursivo
/* * recursivo.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> long factorial(int numero); int
main(int argc, char** argv) { int contador = 0; /* calcula el factorial de 0 a 10 */ for ( contador = 0; contador <= 10;
contador++ ) printf("%d! = %ld\n, contador, factorial( contador )); return 0; } /* funcin factorial recursiva */ long
factorial( int numero ) { if ( numero <= 0 ) /* caso base */ return 1; /* casos bases: 0! = 1 y 1! = 1 */ else /* llamada
recursiva */ return numero * factorial( numero - 1 ); /* llamada a la funcin factorial */ }
Captulo 8
Programacin en C/Vectores
Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada
uno de los valores contenidos tiene una posicin asociada que se usar para accederlos. Est posicin o ndice ser
siempre un nmero entero positivo.
En C la cantidad de elementos que podr contener un vector es jo, y en principio se dene cuando se declara el
vector. Los vectores se pueden declarar de la siguiente forma:
tipo_elemento nombre[largo];
Esto declara la variable nombre como un vector de tipo_elementos que podr contener largo cantidad de elementos,
y cada uno de estos elemento podr contener un valor de tipo tipo_elemento.
Por ejemplo:
double valores[128];
En este ejemplo declaramos un vector de 128 elementos del tipo double, los ndices de los elementos iran entre 0
(para el primer elemento y 127 para el ltimo).
De la misma forma que con las otras declaraciones de variables que hemos visto se le puede asignar un valor iniciar
a los elementos.
O tambin se pueden declarar:
tipo_elemento nombre[largo]={valor_0, valor_1, valor_2};
En caso estamos asignadole valores a los primeros 3 elementos del vector nombre. Notar que largo debe ser mayor
o igual a la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta
informacin, por lo que el lenguaje nos permite escribir:
tipo_elemento nombre[]={valor_0, valor_1, valor_2};
Que declarar nombre como el vector de largo 3.
Para acceder a un elemento accederemos a travs de su posicin. Es decir:
tipo_elemento elemento; ... elemento = nombre[2];
Asumiendo que tenemos el vector anterior denido estaramos guardando valor_2 en elemento.
Veamos algunos ejemplos:
/* * Ejemplo : El producto escalar de dos vectores */ #include <stdio.h> double producto_escalar(double v1[], double v2[], int d); int main() { const int largo = 3; double vector_1[] = {5,1,0}; double vector_2[] = {1,5,3}; double
resultado = producto_escalar(vector_1, vector_2, largo); // imprime el resultado printf("(%f, %f, %f) . (%f, %f,
%f) = %f\n, vector_1[0], vector_1[1], vector_1[2], vector_2[0], vector_2[1], vector_2[2], resultado); return 0; } /*
producto escalar entre dos vectores */ double producto_escalar(double v1[], double v2[], int d) { double resultado =
24
25
0; int i; for (i=0; i < d; i++) { resultado += v1[i] * v2[i]; } return resultado; }
En el ejemplo anterior usamos los vectores de C para representar vectores matemticos y calcular el producto escalar
entre ellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una funcin no se especica el largo,
volveremos a esto en un captulo posterior.
Otra funcin clsica es la bsqueda de un mximo o mnimo, que podemos escribirla de la siguiente manera:
int buscar_maximo(double valores[], int num_valores) { int maximo_pos = 0; for (int i = 1; i < num_valores; i++) {
if (valores[i] > valores[maximo_pos]) { maximo_pos = i; } } return maximo_pos; }
Otro ejemplo sencillo, calcular el promedio de los valores.
double promedio(double valores[], int largo) { double suma=0; for (int i=0;i<largo;i++) { suma+=valores[i]; } return
suma/largo; }
Cuando una funcin recibe un vector por parmetro y cambia su contenido y el cambio es permanente (se ve an
fuera de la funcin). Esto puede parecer extrao despus del nfasis que pusimos en resaltar que todos los parmetros
de una funcin se reciben por valor, pero se aclarar en el siguiente capitulo.
Mientras tanto usemos esto para denir una funcin que le aplique otra funcin que recibe por parmetro a cada
elemento del vector, guardando el resultado en el mismo vector y una llamada de ejemplo a esta.
void cuadrados(double vector[], int largo) { for (int i=0;i<largo;i++) { vector[i]=cuadrado(vector[i]); } } ... double
cuadrado(double valor) { return valor*valor; } ... cuadrados(elementos,num_elem); ...
De la misma forma que venimos usando vectores de tipos bsicos, podemos tener vectores de vectores, estos se
declaran de la siguiente forma:
int matriz[3][7]; int tabla[3][4]={ { 1, 2, 3, 4}, { 5, 6, 7, 8}, /* los espacios y saltos de lneas no son tomados en cuenta */ { 9,10,11,12} }; double v[2][2][2]; ... printf(tabla[0][1]: %i\n, tabla[0][3]); // Imprime 4 printf(tabla[2][0]:
%i\n, tabla[2][0]); // Imprime 9 ...
En este ejemplo tabla es un vector de longitud 3, cuyos elementos son vectores de longitud 4 de elementos de tipo int.
En resumen, suponiendo que v[n] es un vector de cualquier tipo de dato con n cantidad de posiciones, al vector v se
le aplican las siguientes reglas:
1. La primera posicin siempre ser v[0]
2. La ltima posicin es v[n-1]
3. En versiones previas a C99 n es una constante denida antes de la declaracin de v[n]
Captulo 9
26
27
de caracter dentro de cadena posicion = strstr(cadena,subcadena) // Devuelve la posicin en memoria de la primer //
aparicin de subcadena dentro de cadena
Veamos algunos ejemplos usando <string.h>:
#include <stdio.h> #include <string.h> ... char color[] = rojo"; char grosor[] = grueso"; ... char descripcion[1024];
strcpy(descripcion, Lapiz color "); strncat(descripcion, color, 1024); strncat(descripcion, " de trazo ", 1024); strncat(descripcion, grosor, 1024); // descripcion contiene Lapiz color rojo de trazo grueso ...
void intercambiar(char vector[], int pos1, int pos2); void invierte_cadena(char cadena[]) { int largo = strlen(cadena);
for (int i=0; i < (largo/2); i++) { intercambiar(cadena, i, (largo-1)-i); } } void intercambiar(char vector[], int pos1,
int pos2) { char aux=vector[pos1]; vector[pos1]=vector[pos2]; vector[pos2]=aux; }
Captulo 10
10.1 Ficheros
El estndar de C contiene varias funciones para la edicin de cheros, estas estn denidas en la cabecera stdio.h y
por lo general empiezan con la letra f, haciendo referencia a le. Adicionalmente se agrega un tipo FILE, el cual
se usar como apuntador a la informacin del chero. La secuencia que usaremos para realizar operaciones ser la
siguiente:
Crear un apuntador del tipo FILE *
Abrir el archivo utilizando la funcin fopen y asignndole el resultado de la llamada a nuestro apuntador.
Hacer las diversas operaciones (lectura, escritura, etc).
Cerrar el archivo utilizando la funcin fclose.
10.1.1
fopen
10.2. LECTURA
29
10.1.2
fclose
10.1.3
feof
Esta funcin sirve para determinar si el cursor dentro del archivo encontr el nal (end of file). Existe otra forma
de vericar el nal del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado
dentro de stdio.h, pero este mtodo no ofrece la misma seguridad (en especial al tratar con los archivos binarios).
La funcin feof siempre devolver cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresar un
valor distinto de cero (Verdadero).
El prototipo correspondiente de feof es:
int feof(FILE *chero);
10.1.4
rewind
10.2 Lectura
Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro.
Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podramos usar
para leer un archivo son:
char fgetc(FILE *archivo)
30
Las primeras dos de estas funciones son muy parecidas entre si. Pero la tercera, por el numero y el tipo de parmetros,
nos podemos dar cuenta de que es muy diferente, por eso la trataremos aparte junto al fwrite que es su contraparte
para escritura.
10.2.1
fgetc
Esta funcin lee un caracter a la vez del archivo que esta siendo sealado con el puntero *archivo. En caso de que la
lectura sea exitosa devuelve el caracter ledo y en caso de que no lo sea o de encontrar el nal del archivo devuelve
EOF.
El prototipo correspondiente de fgetc es:
char fgetc(FILE *archivo);
Esta funcin se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemos
un archivo de texto llamado prueba.txt en el mismo directorio en que se encuentra el fuente de nuestro programa.
Un pequeo programa que lea ese archivo ser:
#include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracter; archivo = fopen(prueba.txt,"r);
if (archivo == NULL){ printf("\nError de apertura del archivo. \n\n); }else{ printf("\nEl contenido del archivo de
prueba es \n\n); while (feof(archivo) == 0) { caracter = fgetc(archivo); printf("%c,caracter); } } fclose(archivo);
return 0; }
10.2.2
fgets
Esta funcin est diseada para leer cadenas de caracteres. Leer hasta n-1 caracteres o hasta que lea un cambio de
lnea '\n' o un nal de archivo EOF. En este ltimo caso, el carcter de cambio de lnea '\n' tambin es ledo.
El prototipo correspondiente de fgets es:
char *fgets(char *buer, int tamao, FILE *archivo);
El primer parmetro buer lo hemos llamado as porque es un puntero a un espacio de memoria del tipo char (podramos usar un arreglo de char). El segundo parmetro es tamao que es el limite en cantidad de caracteres a leer
para la funcion fgets. Y por ultimo el puntero del archivo por supuesto que es la forma en que fgets sabra a que
archivo debe leer.
#include <stdio.h> #include <stdlib.h> int main() { FILE *archivo; char caracteres[100]; archivo = fopen(prueba.txt,"r);
if (archivo == NULL) exit(1); printf("\nEl contenido del archivo de prueba es \n\n); while (feof(archivo) == 0) {
fgets(caracteres,100,archivo); printf("%s,caracteres); } system(PAUSE); fclose(archivo); return 0; }
Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de fgetc. La funcin fgets
se comporta de la siguiente manera, leer del archivo apuntado por archivo los caracteres que encuentre y a ponerlos
en buer hasta que lea un caracter menos que la cantidad de caracteres especicada en tamao o hasta que encuentre
el nal de una linea (\n) o hasta que encuentre el nal del archivo (EOF). En este ejemplo no vamos a profundizar
mas que para decir que caracteres es un buer, los pormenores seran explicados en la seccin de manejo dinmico
de memoria.
El benecio de esta funcin es que se puede obtener una linea completa a la vez. Y resulta muy til para algunos nes
como la construccin de un parser de algn tipo de archivo de texto.
10.3. ESCRITURA
10.2.3
31
fread
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
Esta funcin lee un bloque de una stream de datos. Efecta la lectura de un arreglo de elementos count, cada
uno de los cuales tiene un tamao denido por size. Luego los guarda en el bloque de memoria especicado por
ptr. El indicador de posicin de la cadena de caracteres avanza hasta leer la totalidad de bytes. Si esto es exitoso la
cantidad de bytes ledos es (size*count).
PARAMETROS:
ptr : Puntero a un bloque de memoria con un tamao mnimo de (size*count) bytes.
size : Tamao en bytes de cada elemento (de los que voy a leer).
count : Nmero de elementos, los cuales tienen un tamao size.
stream: Puntero a objetos FILE, que especica la cadena de entrada.
10.2.4
fscanf
La funcin fscanf funciona igual que scanf en cuanto a parmetros, pero la entrada se toma de un chero en lugar del
teclado.
El prototipo correspondiente de fscanf es:
int fscanf(FILE *chero, const char *formato, argumento, ...);
Podemos ver un ejemplo de su uso, abrimos el documento chero.txt en modo lectura y leyendo dentro de el.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buer[100]; fp = fopen ( chero.txt, r );
fscanf(fp, "%s ,buer); printf("%s,buer); fclose ( fp ); return 0; }
10.3 Escritura
As como podemos leer datos desde un chero, tambin se pueden crear y escribir cheros con la informacin que
deseamos almacenar, Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones
que podramos usar para escribir dentro de un archivo son:
int fputc(int caracter, FILE *archivo)
int fputs(const char *buer, FILE *archivo)
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
int fprintf(FILE *archivo, const char *formato, argumento, ...);
10.3.1
fputc
Esta funcin escribe un carcter a la vez del archivo que esta siendo sealado con el puntero *archivo. El valor de
retorno es el carcter escrito, si la operacin fue completada con xito, en caso contrario ser EOF.
El prototipo correspondiente de fputc es:
int fputc(int carcter, FILE *archivo);
Mostramos un ejemplo del uso de fputc en un chero.txt, se escribira dentro del chero hasta que presionemos la
tecla enter.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char caracter; fp = fopen ( chero.txt, r+" );
printf("\nIntrouce un texto al chero: "); while((caracter = getchar()) != '\n') { fprintf("%c, fputc(caracter, fp)); }
32
fclose ( fp ); return 0; }
10.3.2
fputs
La funcin fputs escribe una cadena en un chero. No se aade el carcter de retorno de lnea ni el carcter nulo
nal. El valor de retorno es un nmero no negativo o EOF en caso de error. Los parmetros de entrada son la cadena
a escribir y un puntero a la estructura FILE del chero donde se realizar la escritura.
El prototipo correspondiente de fputs es:
int fputs(const char *buer, FILE *archivo)
para ver su funcionamiento mostramos el siguiente ejemplo:
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char cadena[] = Mostrando el uso de fputs en un
chero.\n"; fp = fopen ( chero.txt, r+" ); fputs( cadena, fp ); fclose ( fp ); return 0; }
10.3.3
fwrite
Esta funcin est pensada para trabajar con registros de longitud constante y forma pareja con fread. Es capaz de
escribir hacia un chero uno o varios registros de la misma longitud almacenados a partir de una direccin de memoria
determinada. El valor de retorno es el nmero de registros escritos, no el nmero de bytes. Los parmetros son: un
puntero a la zona de memoria de donde se obtendrn los datos a escribir, el tamao de cada registro, el nmero de
registros a escribir y un puntero a la estructura FILE del chero al que se har la escritura.
El prototipo correspondiente de fwrite es:
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
Un ejemplo concreto del uso de fwrite con su contraparte fread y usando funciones es:
10.3.4
fprintf
La funcin fprintf funciona igual que printf en cuanto a parmetros, pero la salida se dirige a un archivo en lugar de
a la pantalla.
El prototipo correspondiente de fprintf es:
int fprintf(FILE *archivo, const char *formato, argumento, ...);
Podemos ver un ejemplo de su uso, abrimos el documento chero.txt en modo lectura/escritura y escribimos dentro
de el.
#include <stdio.h> int main ( int argc, char **argv ) { FILE *fp; char buer[100] = Esto es un texto dentro del
chero."; fp = fopen ( chero.txt, r+" ); fprintf(fp, buer); fprintf(fp, "%s, "\nEsto es otro texto dentro del chero.); fclose ( fp ); return 0; }
Captulo 11
11.1 Estructuras
Una estructura contiene varios datos. La forma de denir una estructura es haciendo uso de la palabra clave struct.
Aqui hay ejemplo de la declaracion de una estructura:
struct mystruct { int int_member; double double_member; char string_member[25]; } variable;
variable es una instancia de mystruct y no es necesario ponerla aqu. Se podria omitir de la declaracion de mystruct y ms tarde declararla usando:
struct mystruct variable;
Tambin es una prctica muy comn asignarle un alias o sinnimo al nombre de la estructura, para evitar el tener que
poner struct mystruct cada vez. C nos permite la posibilidad de hacer esto usando la palabra clave typedef, lo que
crea un alias a un tipo:
typedef struct { ... } Mystruct;
La estructura misma no tiene nombre (por la ausencia de nombre en la primera linea), pero tiene de alias Mystruct.
Entonces se puede usar as:
Mystruct variable;
Note que es una convencion, y una buena costumbre usar mayscula en la primera letra de un sinnimo de tipo. De
todos modos lo importante es darle algn identicador para poder hacer referencia a la estructura: podriamos tener
una estructura de datos recursiva de algn tipo.
Ejemplo de una estructura :
/* * estructura.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico * * Nombre Miembro Tipo * Titulo
char[30] * Artista char[25] * Precio oat * Total Canciones int */ #include <stdio.h> #include <string.h> /* denimos una estructura para cds */ struct cd { char titulo[30]; char artista[25]; oat precio; int canciones; } Cd1 = { /*
inicializamos la estructura Cd1 creaa con sus valores * usando las deniciones iniciales*/ Canciones Bebe, /* titulo
*/ Pinocho, /* artista */ 12.50, /* precio */ 16 /* total canciones */ }; int main(void) { struct cd Cd2; /* denimos
una nueva estructura llamado cd2 */ /* asignamos valores a los tipos de datos del cd2 */ strcpy(Cd2.titulo, New
Age); /* la forma de insertar valores a un * tipo char en una estructura es usando strcpy * de la libreria string.h
33
34
*/ strcpy(Cd2.artista, Old Man); Cd2.precio = 15.00; Cd2.canciones = 12; /* la forma de acceder a los valores de
una estructura */ /* es usando el ". despues de la denicion del dato*/ printf("\n Cd 1); printf("\n Titulo: %s ",
Cd1.titulo); printf("\n Artista: %s ", Cd1.artista); printf("\n Total Canciones: %d ", Cd1.canciones); printf("\n Precio
Cd: %f ", Cd1.precio); printf("\n); printf("\n Cd 2); printf("\n Titulo: %s ", Cd2.titulo); printf("\n Artista: %s ",
Cd2.artista); printf("\n Total Canciones: %d ", Cd2.canciones); printf("\n Precio Cd: %.2f ", Cd2.precio); /* el .2
que esta entre %f * sirve para mostrar unicamente * 2 decimales despues del punto*/ return 0; }
11.1.1
Estructuras Anidadas
Una estructura puede estar dentro de otra estructura a esto se le conoce como anidamiento o estructuras anidadas.
Ya que se trabajan con datos en estructuras si denimos un tipo de dato en una estructura y necesitamos denir ese
dato dentro de otra estructura solamente se llama el dato de la estructura anterior.
Denamos una estructura en nuestro programa:
struct empleado /* creamos una estructura llamado empleado*/ { char nombre_empleado[25]; char direccion[25];
char ciudad[20]; char provincia[20]; long int codigo_postal; double salario; }; /* las estructuras necesitan punto y
coma (;) al nal */
Y luego necesitamos una nueva estructura en nuestro programa:
struct cliente /* creamos una estructura llamada cliente */ { char nombre_cliente[25]; char direccion[25]; char ciudad[20]; char provincia[20]; long int codigo_postal; double saldo; }; /* las estructuras necesitan punto y coma (;) al
nal */
Podemos ver que tenemos datos muy similares en nuestras estructuras, asi que podemos crear una sola estructura
llamada infopersona con estos datos idnticos:
struct infopersona /* creamos la estructura que contiene datos parecidos */ { char direccion[25]; char ciudad[20];
char provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al nal */
Y crear las nuevas estructuras anteriores, anidando la estructura necesaria:
struct empleado /* se crea nuevamente la estructura */ { char nombre_empleado[25]; /* creamos direcc_empleado
con struct del tipo estructura infopersona */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al nal */ <source lang=c> struct cliente /* se crea nuevamente la estructura */ { char
nombre_cliente[25]; /* creamos direcc_cliente con struct del tipo estructura infopersona */ struct infopersona
direcc_cliente; double saldo; }; /* las estructuras necesitan punto y coma (;) al nal */
Y ac el ejemplo completo con estructuras anidadas:
/* * estructura2.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico * * Nombre Miembro Tipo * *
Titulo char[30] * Artista char[25] * Precio oat * Total Canciones int */ #include <stdio.h> #include <string.h>
/* creamos nuestra estructura con datos similares */ struct infopersona { char direccion[25]; char ciudad[20]; char
provincia[20]; long int codigo_postal; }; /* las estructuras necesitan punto y coma (;) al nal */ /* creamos nuestra estructura empleado */ struct empleado { char nombre_empleado[25]; /* agregamos la estructura infopersona
* con nombre direcc_empleado */ struct infopersona direcc_empleado; double salario; }; /* las estructuras necesitan punto y coma (;) al nal */ /* creamos nuestra estructura cliente */ struct cliente { char nombre_cliente[25]; /*
agregamos la estructura infopersona * con nombre direcc_cliente */ struct infopersona direcc_cliente; double saldo;
}; /* las estructuras necesitan punto y coma (;) al nal */ int main(void) { /* creamos un nuevo cliente */ struct
cliente MiCliente; /*inicializamos un par de datos de Micliente */ strcpy(MiCliente.nombre_cliente,"Jose Antonio); strcpy(MiCliente.direcc_cliente.direccion, Altos del Cielo); /* notese que se agrega direcc_cliente haciendo
referencia * a la estructura infopersona por el dato direccion */ /* imprimimos los datos */ printf("\n Cliente: ");
printf("\n Nombre: %s, MiCliente.nombre_cliente); /* notese la forma de hacer referencia al dato */ printf("\n
Direccion: %s, MiCliente.direcc_cliente.direccion); /* creamos un nuevo empleado */ struct empleado MiEmpleado; /*inicializamos un par de datos de MiEmplado */ strcpy(MiEmpleado.nombre_empleado,"Miguel Angel);
strcpy(MiEmpleado.direcc_empleado.ciudad,"Madrid); /* para hacer referencia a ciudad de la estructura infoperso-
11.2. UNIONES
35
na * utilizamos direcc_empleado que es una estructura anidada */ /* imprimimos los datos */ printf("\n); printf("\n
Empleado: "); printf("\n Nombre: %s, MiEmpleado.nombre_empleado); /* notese la forma de hacer referencia al
dato */ printf("\n Ciudad: %s, MiEmpleado.direcc_empleado.ciudad); return 0; }
11.2 Uniones
La denicion de union es similar a la de estructura, La diferencia entre las dos es que en una estructura, los
miembros ocupan diferentes areas de la memoria, pero en una union, los miembros ocupan la misma area de memoria.
Entonces como ejemplo:
union { int i; double d; } u;
El programador puede acceder a travs de u.i o de u.d, pero no de ambos al mismo tiempo. Como u.i y u.d
ocupan la misma rea de memoria, modicar uno modica el valor del otro, algunas veces de maneras impredecibles.
El tamao de una union es el de su miembro de mayor tamao.
Ejemplo de una unin:
/* * uniones.c * * Julio Csar Brizuela <brizuelaalvarado@gmail.com> 2009 * * para el wikilibro Programacin en
C (fundamentos)" * bajo licencia FDL, adaptado del Dominio Pblico */ #include <stdio.h> #include <string.h>
/*Creamos una union*/ union frases { char mensajes[50]; char ayudas[50]; char lineas[50]; } palabra; /*Creamos una estructura*/ struct comparte { char mensajes[50]; char ayudas[50]; char lineas[50]; }Sistema; /*Ntese
que la estructura y la union tienen los mismos tipos de datos*/ int main(int argc, char** argv) { /*Inicializamos*/
strcpy(palabra.mensajes, Primer Mensaje); /*Inicializamos*/ strcpy(palabra.ayudas, Una Ayuda); printf("\nFrases
en Union: "); /*Imprimimos mensajes de union*/ printf("\n1- %s, palabra.mensajes); /*Imprimimos ayudas de
union*/ printf("\n2- %s, palabra.ayudas); /*Inicializamos*/ strcpy(Sistema.mensajes, Primer Mensaje); /*Inicializamos*/ strcpy(Sistema.ayudas, Una Ayuda); /* Podemos notar que aunque inicializamos los valores * al imprimir
se tiene el mismo valor para cada miembro * de la estructura, esto se debe a que las uniones usan el * mismo espacio
de memoria para todos los elementos * de la union, siendo del tamao de su miembro de * mayor tamao, en este
caso 50 bytes. * Entonces los tres miembros creados dentro de la * union comparten esos 50 bytes. * Entonces el
ultimo valor agregado a la union es * el que se tiene. */ printf("\n\nFrases en Struct: "); /*Imprimimos mensajes de
struct*/ printf("\n1- %s, Sistema.mensajes); /*Imprimimos ayudas de union*/ printf("\n2- %s, Sistema.ayudas);
/* En la estructura comparte, se reservan 150 bytes * de memoria para los tres miembros, en este caso * cada uno
es independiente en memoria, asi pues se * puede inicializar cada uno o usar como un campo * independiente. */
return 0; }
11.3 Enumeraciones
Una enumeracion (enum) es un tipo denido con constante de tipo entero. En la declaracion de un tipo enum creamos
una lista de tipo de datos que se asocian con las constantes enteras 0, 1, 2, 3, 4, 5...
su forma de denirlas es la siguiente:
enum { enumerador1, enumerador2, enumeradorn }; enum Nombre { enumerador1, enumerador2, enumeradorn };
En este caso al ser declaradas enumerador1 toma el valor entero de 0, enumerador2 el valor de 1 y asi sucesivamente
para cada una de las expresiones siguientes.
Al declarar la enum se puede asociar a los tipos de datos a valores constantes en vez de la asociacion que por defecto
se realiza (0, 1, 2, ), se utiliza entonces este formato:
enum Nombre { enumerador1 = valor_constante1, enumerador2 = valor_constante2, ... enumeradorn = valor_constanten,
};
Un ejemplo de una enum:
36
Captulo 12
Programacin en C/Punteros
37
Captulo 13
Concepto
Un puntero es una variable que contiene la direccin de memoria de un dato o de otra variable que contiene al dato.
Quiere esto decir, que el puntero apunta al espacio fsico donde est el dato o la variable. Un puntero puede apuntar a
un objeto de cualquier tipo, como por ejemplo, a una estructura o una funcin. Los punteros se pueden utilizar para
referencia y manipular estructuras de datos, para referenciar bloques de memoria asignados dinamicamente y para
proveer el paso de argumentos por referencias en las llamadas a funciones.
Muchas de las funciones estandares de C, trabajan con punteros, como es el caso del scanf o strcpy. Estas reciben o
devuelve un valor que es un puntero. Por Ej. A scanf se le pasa la direccin de memoria del dato a leer (esto es un
puntero)...
char a; scanf ("%c,&a);
38
Captulo 14
Declarando punteros
Ya se dijo que un puntero es una variable que guarda la direccin de memoria de otra variable, haciendo logica a
esto, decimos que un puntero se declara igual que cualquier otra variable, pero anteponiendo un * (asterisco) antes
del nombre de la variable.
Su sintaxis seria:
tipo *NombrePuntero;
Donde tipo es el tipo de dato al que referenciar este puntero, es decir, que si se necesita guardar la direccin de
memoria de un dato int, se necesita un puntero de tipo int.
39
Captulo 15
Explicacin
Veamos el siguiente codigo:
#include <stdio.h> int main() { int a=0; //Declaracin de variable entera de tipo entero int *puntero; //Declaracin
de variable puntero de tipo entero puntero = &a; //Asignacin de la direccin memoria de a printf(El valor de a es:
%d. \nEl valor de *puntero es: %d. \n,a,*puntero); printf(La direccion de memoria de *puntero es: %p,puntero);
return 0; }
Igual que cuando usamos un &, en la lectura de datos con scanf, igual de esta forma lo usamos aqu, tal vez te
acordaras que decamos que las cadenas de caracteres (%s) no usaban este operador, esto es por que en una cadena
de caracteres es un arreglo de caracteres, por lo que el primer carcter es la direccin de inicio de la cadena.
El operador *, nos permite acceder al valor de la direccion del puntero, en este caso nos permite acceder al valor que
contiene a la variable a. De esta forma a y "*puntero muestran el mismo dato, pero esto no quiere decir que sea lo
mismo, uno es un entero el otro un puntero.
La impresin con %p, es para poder imprimir la direccin de memoria en valor hexadecimal (0x...), tambin podemos
imprimir: ...%p,&a) y funciona de la misma forma, y es lgico que tanto a, como puntero tienen la misma direccin
de memoria.
Diferentes direcciones?
Tal vez notaste que en cada ejecucin la direccin de memoria cambia, esto es por que es el sistema operativo es
quien esta encargado de administrar la memoria y es este quien dice que espacios podra tomar el programa.
Esto quiere decir que uno no puede asignarle una direccin de memoria a un puntero directamente, es decir yo no
puedo hacer lo siguiente.
int *puntero=0xbfc5b1c8;
Esto no puedo ni debo hacerlo ya que yo no se que esta haciendo esta direccin de memoria, si el sistema la tiene o
no disponible, etc... Pero si puedo hacer esto:
int *puntero=NULL;
NULL, es el espacio en memoria con direccin 0, esto quiere decir que existe, lo que signica que le asignamos una
direccion valida al puntero, pero el valor que tiene NULL no se nos permite modicarlo, ya que pertenece al sistema.
40
Captulo 16
Operadores
Ya anteriormente te poniamos algunos ejemplos de como asignar la direccin de memoria a un puntero y de como
acceder al valor de este.
Operador de Direccin (&): Este nos permite acceder a la direccion de memoria de una variable.
Operador de Indireccion (*): Ademas de que nos permite declarar un tipo de dato puntero, tambin nos permite
ver el VALOR que esta en la direccin asignada.
Incrementos (++) y Decrementos (--): Te daras cuenta que puedes usar un puntero como si de un array se tratase,
es por esto que permite estos operadores.
De lo anterior podemos decir que:
int a; Es Igual int *puntero=&a; printf("%d,a); Es Igual printf("%d,*puntero);
41
Captulo 17
Operaciones Aritmticas
Un puntero nos permite sumar o restar nmeros enteros, pero su funcionamiento ahora es de posiciones, es decir nos
permitir movernos a la siguiente direccin de memoria.
A un puntero no se le puede realizar multiplicaciones, divisiones, sumas o restas con otro puntero o con un valor de
tipo coma otante (oat, double...). Esto es por que un puntero no es un valor es una direccin.
En un puntero se pueden realizar varias operaciones de tipo enteras, pero en dependencia de como se usen sus
resultados pueden ser muy diferentes, a continuacin les muestro algunas de estas operaciones:
//Denamos estas variables: int x[100],b,*pa,*pb; //... x[50]=10; //Le asignamos el valor de 10, al array #50 pa=&x[50];
//Le asignamos al puntero pa, la direccion de memoria que tiene x[50] //Ahora mostramos algunas posibles operaciones: b = *pa+1; //Esto es como decir el valor que tiene el array de x[50] sumarle 1. //Esto es igual a: b=x[50]+1; =>
Su valor seria igual a 11. b = *(pa+1); //Esto primero pasa a la siguiente direccion de memoria y luego lo referencia
//El resultado es: b = x[51]; pb = &x[10]; //al puntero pb se le asigna la direccion de x[10] *pb = 0; //Al valor que
tiene el puntero se le asigna 0 //Esto es igual que decir: x[10] = 0 *pb += 2; //El valor del puntero se incrementa
en dos unidades, es decir x[10] = 2 (*pb)--; //El valor del puntero se decrementa en una unidad. x[0] = *pb--; //A
x[0] se le pasa el valor de x[10] y el puntero pb, pasa a apuntar a x[9] //recorda, que -- es post-incremento, primero
asignara y luego restara.
42
Captulo 18
Punteros Constantes
Es posible que hayas pensado como declarar un puntero como una constante, tal vez pensaste en un #dene, o en un
atributo const. Bueno es posible usar el atributo const, pero para un puntero hay que hacerlo de otra forma.
FORMA ERRADA:
int a=10,b=20; const int *p = &a; //objeto constante y puntero variable *p = 15; // ERROR: el valor apuntado por p
es constante. p=&b; //Correcto: p pasa a apuntar a un nuevo objeto.
Pero de esta forma no es muy til declararlo, pues el que hicimos constante fue el valor al que apunte p, es decir,
mejor hubisemos hecho que el puntero fuese una constante.
FORMA CORRECTA:
int a=10,b=20; int * const p = &a; //objeto variable y puntero constante *p = 15; // Correcto: El valor apuntado es
variable. p=&b; //ERROR: p es constante.
43
Captulo 19
Punteros Genericos
Un puntero a cualquier tipo de dato puede convertirse a un puntero del tipo void *. Por esto un puntero a void *,
recibe el nombre de puntero generico.
En C, se permite la conversin implcita de punteros, pero en C++ esto no es posible, asi que por compatibilidad y
buena practica recomendamos usar la conversion explicita (cast).
Supongamos:
int *puntero; funcion (*puntero); .... void funcion (void *p) int *q; q=(int *)p; //En C se podria hacer q = p;
Es decir que un puntero a void se puede usar sin importar el tipo de dato, recuerden que uno no puede trabajar con
punteros que referencia a un tipo de dato diferente, como lo es un puntero a char, con un puntero a int.
44
Captulo 20
Punteros y Matrices
Anteriormente decamos que una matriz es una secuencia de espacios en memoria, que nos permitan alojar datos
en cada uno y un puntero es una variable que guarda la direccin de memoria, tambin decamos como recorre las
direcciones de memoria con los operadores ++ y -.
Aqu veremos como puede usarse un puntero como si de una matriz se tratase, luego de que veas que es tcnicamente
lo mismo, te preguntaras por que usar punteros, pero estos son muy necesarios y nicos que nos permiten realizar
cosas que con un array normal, no se puede, como asignarle memoria dinmica, etc...
#include <stdio.h> int main() { int array[10]={0,2,3,5,5,6,7,8,9,0}; //Declarar e inicializar un array. int *puntero =
&array[0]; //Le damos la direccin de inicio del array int i; //variable contadora... for (i=0;i<10;i++) printf("%d\n,*(puntero+i));
//imprimimos los valores del puntero. return 0; }
Habrs notado que he usado *(puntero+i), as como explica la seccin de operaciones aritmticas, pero tambin
podemos acceder de otras maneras como lo son:
array[i] => Accede como un array normal
(array+i) => Tambin accede como un array normal.
puntero[i] => Accedemos al valor de puntero sub i
(puntero+i) => Accedemos al valor de puntero + i.
45
Captulo 21
46
Captulo 22
Matrices de Punteros
Es muy probable que ya te hayas dicho: Si un puntero es una variable, Puedo tener un array de punteros? La respuesta
seria NO Exactamente.
Como ya habiamos explicado antes, un puntero trabaja de la misma forma que un array, tanto que podemos decir que
un puntero es un array, entonces si denimos un array de un array, nos dara como resultados un array bidimensional.
En la practica es mucho mas comn utilizar un array de punteros que un array bidimensional.
Hay que recordar que siguen siendo punteros, es decir solo apuntan a una direccin de memoria, por ende hay que
asignarles la direccin de memoria a cada uno de los elementos del puntero. Sin embargo podemos asignar en un solo
ciclo todas las las.
Ejemplo:
#include <stdio.h> int main() { int *puntero[5]; //array de puntero int a[5][5]; //Array bidimensional. int i; for
(i=0;i<5;i++) puntero[i]=a[i]; //Asignamos las las al puntero. //Pueden imprimir tambien en un ciclo //Tambien
pueden acceder mediante un ciclo anidado a la variables del puntero[i][j] }
47
Captulo 23
Punteros a Punteros
Es una variable que contiene la direccin de memoria de un puntero, el cual a su vez contiene la direccin de memoria
de un tipo de dato. Recuerden que un puntero sigue siendo un espacio en memoria, pero en vez de almacenar un valor
almacena una direccin.
Si decimos que:
int a=0; //Supongamos que es una variable cuya direccion es 0x1601 int *puntero1=&a; //El puntero tiene guardada
la direccion de a, //pero este tiene como direccion 0x1890 int **puntero2=&puntero1; //**puntero 2 guarda la direccion 0x1890
Ahora se entiende mejor. Al uso de punteros se le llama variables con niveles de indireccion, ya que no apuntan
directamente a un valor, sino que apuntan a alguien que lo tiene. Basndonos en esto podemos decir que *puntero1
es un puntero con un nivel de indireccion y *puntero2 es un puntero con dos niveles de indireccion.
En general estos tipos de datos son usados con matrices multidimensionales, es por esto que decid no dejar ejemplos,
pues su uso es bastante fcil de implementar.
48
Captulo 24
49
Captulo 25
Ejemplos
Tenga en cuenta el siguiente bloque de cdigo que declara 2 punteros
/*1*/ struct MyStruct { /*2*/ int m_aNumber; /*3*/ oat num2; /*4*/ }; /*5*/ /*6*/ int * pJ2; /*7*/ struct MyStruct
* pAnItem;
Las primeras 4 lneas denen la estructura. La linea 6 declara una variable que apuntar a un entero, y la lnea 7
declara una variable que apunta a algo de la estructura MyStruct. Entonces declarar un puntero es algo que apunta a
algo de algn tipo, ms que contener el tipo. El asterisco (*) se coloca antes del nombre de la variable.
En las siguientes lneas de cdigo, var1 es un puntero a un entero largo (long) mientras var2 es un entero largo (long)
y no un puntero a un entero largo. En la segunda lnea se declara p3 como un puntero a un puntero de un entero.
long * var1, var2; int ** p3;
Los punteros se usan habitualmente como parmetros de funciones. El siguiente cdigo muestra como declarar una
funcin que usa un puntero como argumento. Teniendo en cuenta que C pasa todos los argumentos por valor, para
poder modicar un valor desde el cdigo de la funcin, se debe usar un puntero al valor a modicar. Tambin se
usan punteros a estructuras como argumentos de una funcin an cuando la estructura no sea modicada dentro de
la funcin. Esto es realizado para evitar copiar todo el contenido de la estructura dentro de la pila.
int MyFunction( struct MyStruct *pStruct );
25.0.1
Continuamos con el proceso de asignar valores a los punteros, para esto utilizamos el operador & o 'la direccin de'.
int myInt; int *pPointer; struct MyStruct dvorak; struct MyStruct *pKeyboard; pPointer = &myInt; pKeyboard =
&dvorak;
Aqu, pPointer apuntara a myInt y pKeyboard apuntara a dvorak.
Los punteros tambin pueden ser asignados a referencias de memorias dinamicas creadas comnmente por las funciones malloc() y calloc().
#include <stdlib.h> ... struct MyStruct *pKeyboard; ... pKeyboard = malloc(sizeof(struct MyStruct)); ...
La funcin malloc retorna un puntero de memoria asignada de manera dinmica (o NULL si falla). El tamao de esta
memoria es denido de modo que pueda contener la estructura MyStruct
El siguiente cdigo es un ejemplo mostrando un puntero siendo asignado a una referencia y se retorna el valor del
puntero en la funcin.
static struct MyStruct val1, val2, val3, val4; ... struct MyStruct *ASillyFunction( int b ) { struct MyStruct *myReturn;
if (b == 1) myReturn = &val1; else if (b==2) myReturn = &val2; else if (b==3) myReturn = &val3; else myReturn
50
51
= &val4; return myReturn; } ... struct MyStruct *strPointer; int *c, *d; int j; ... c = &j; /* puntero asignado usando
el operador & */ d = c; /* asignando un puntero a otro */ strPointer = ASillyFunction( 3 ); /* puntero retornado de
la funcion */
Cuando se retorna un puntero de una funcion, se tiene que tener cuidado de que el valor apuntado no sea de una
referencia de una variable local a la funcion, porque estos valores desaparecen despues de salir de la funcion y el
puntero estaria mal referenciado, causando errores en tiempo de ejecucion en el programa. La memoria creada dinamicamente dentro de la funcion puede devolverse como puntero, el valor de retorno aunque es creado en una variable
local la direccion como tal se devuelve por valor dejando esta informacion valida.
Captulo 26
52
Captulo 27
Concepto
Dejando afuera el uso de matrices en C99, en donde uno puede declarar los valores luego de una lectura. Podemos
decir que solo hemos usado matrices estticas, ya que no permitan el cambio de dimensiones una vez que el programa
fuese compilado.
Con la asignacin dinmica de memoria un puede denir en tipo de ejecucin la cantidad de memoria, con las ventajas
y desventajas que esta posee.
Solo para demostrar rapidamente el potencial y el por que usarlo, vean lo siguiente:
Anteriormente si uno deseaba leer una cadena de caracteres, declaraba un array, supongamos que deseamos leer un
nombre, pero, no todos se llaman igual es por esto que debiamos darle suciente espacio al arreglo.
char nombre[100];
Pero si escribimos: Gustavo Chavarria. Solo necesitamos unos 20 caracteres (20 bytes), es decir desperdiciamos
80 bytes en memoria. Imaginate sucediendo esto en un programa muy grande...
Ejemplo:
//Hecho por: Gustavo Chavarria. //UNAN-LEON - Nicaragua. #include <stdio.h> #include <stdlib.h> int main() {
int *puntero=NULL; puntero = (int *)malloc(100*sizeof(int)); //Asignacion dinamica de memoria if (puntero ==
NULL) //evaluacion de condicion. { printf(NO hay suciente espacio en memoria); //Mensaje return 1; //Cierra
el programa con un error. }else printf(Se asigno memoria "); free(puntero);//liberacion de memoria return 0; } //El
programa solo asigna memoria, imprime un mensaje en dependecia de la condicion y libera //Revisado por: Gustavo
Chavarria. //UNAN-LEON - Nicaragua.
53
Captulo 28
54
Captulo 29
Programacin en C/Algoritmos y
Estructuras de Datos
55
Captulo 30
Concepto
Uno de los procedimientos ms comunes y tiles en el procesamiento de datos, es la ordenacin de los mismos. Se
considera ordenar al proceso de reorganizar un conjunto dado de objetos en una secuencia determinada (patrn de
arreglo). El objetivo de este proceso generalmente es facilitar la bsqueda de uno o ms elementos pertenecientes a
un conjunto.
Como ejemplos de conjunto de datos ordenados tenemos:
Meses del ao (ordenados de 1 al 12).
Listado de estudiantes (ordenados alfabeticamente).
Guias Telefonicas (ordenadas por Pais/por region/por sector/por orden alfabetico)
La ordenacion, tanto numerica como alfanumerica, sigue las mismas reglas que empleamos nosotros en la vida normal.
Esto es, un dato numerico es mayor que otro cuando su valor es mas grande, y una cadena de caracteres es mayor que
otra cuando esta despues por orden alfabetico.
Los metodos de ordenacion, pueden agruparse en dos grandes grupos:
Los internos: Es cuando los datos estan disponibles de una area de la memoria principal, como cuando se leen
un conjunto de datos desde el teclado.
Los externos: Los datos estan guardados en un medio externo, como puede ser un chero, una base de datos,
etc. En donde los datos estan alojados en el disco duro u otro medio sico.
En este seccion explicaremos solo tres modos de ordenamiento, los mas usados como son:
Algoritmo de Ordenamiento Burbuja (Buble Sort)
Algoritmo de Ordenamiento Insercion
Algoritmo de Ordenamiento Quick Sort
56
Captulo 31
31.1 Explicacion
Este metodo necesita de lo siguiente para implementarse:
Un array o estructura que ordenar (>1 elemento).
Dos variables contadoras de ciclos (i,j por ejemplo).
Una variable temporal (para almacenar un dato momentaneamente).
Dos ciclos y un Condicional...
.... //DE MENOR A MAYOR (Ascendente) #dene Nelementos 4 .... int i,j; //Variables contadoras del ciclo. int
lista[Nelementos]={6,9,3,1}; //Declaracion e inicializacion de un arreglo de 4 elementos. int temp=0; //Variable
temporal. for (i=1;i<Nelementos;i++) { for (j=0; j <= Nelementos-1 ;j++) { if (lista[j] > lista[j+1])//Condicion
mayor-menor { temp=lista[j]; lista[j]=lista[j+1]; lista[j+1]=temp; } } } //Para cambiar el modo de ordenamiento
solo debemos cambiar la condicion < > '''<big>Explicando un poco lo que dice el codigo tenemos:</big>''' # Iniciamos i a 1, de esta forma correremos el ciclo solamente 3 veces. Asi evitamos correr ciclos innecesariamente. # El
segundo for, se ejecutara 3 veces por cada primer ciclo. # La condicion nos dice: * Si, el valor de lista 0 es mayor
al valor de lista 1, es decir * '''Si, 6 > 9''', pero como la condicion no se cumple, pasamos del ciclo y '''J=1'''. * Si,
el valor de lista 1 es mayor al valor de lista 2, es decir * '''Si, 9 > 3''', como es '''verdadera''' hacemos: # Guardamos
momentaneamente en la variable temporal el valor de lista 1, es decir 9. # En la posicion de lista 1, guardamos el valor
de lista 2, es decir 3. # En la posicion de lista 2, guardamos el valor de temp, es decir 9 '''Volvemos'' nuevamente
'''al ciclo''', ahora '''J=2'''... * Si, el valor de lista 2 es mayor al valor de lista 3, es decir * Si, '''9 > 1''', (recuerda que
anteriormente '''movimos'' al 9 a la posicion de 3), es verdadera => # Guardamos momentaneamente en la variable
temporal el valor de lista 2, es decir 9. # En la posicion de lista 2, guardamos el valor de lista 3, es decir 1. # En la
posicion de lista 3, guardamos el valor de temp, es decir 9. De esta forma el ciclo se repite hasta que todos los datos
se hallan movido. Como veras hasta ahora solo hemos estado moviendo el 9. Tu lista se veria asi: * ''Original:'' 6
'''9''' 3 1 :* ''1er Mov:'' 6 3 '''9''' 1 :* ''2do Mov:'' 6 3 1 '''9''' Si bien ya esta mas ordenada que la original, aun falta
bastante, pero recuerda que estbamos en el primer ciclo del primer for (i). '''Aca te dejo como seran los demas
57
58
Variante: Restar
Esta es una de las mas usuales que he visto en los libros, folletos y otros. Todo es igual, pero cambian las condiciones,
de esta forma se trabaja a la inversa de nuestro algoritmo.
31.2 Ejemplos
El algoritmo burbuja esta preparado para correr con todo tipo de datos NO atomicos, es por esto que no importa si
el arreglo es de tipo char, int, oat, etc. Funcionara con sus respectivas modicaciones.
El siguiente ejemplo ordenara una lista de tamao 6, que esta ordenada. Recuerden como buena practica:
Denir las variables en un solo punto del codigo.
Denir el tamao del array como constante.
Usar la indentacion correspondiente (Tabulaciones).
//Realizado por Master crack cocaino //UNAN-LEON Nicaragua. #include <stdio.h> #dene TAM 6 int main() { int
lista[TAM]={12,10,5,6,1,3}; //Declaracion e Inicializacion de un array int temp=0; //Variable temporal int i,j; //variables corredoras del ciclo printf(La lista DESORDENADA es: \n); for (i=0;i<TAM;i++) printf("%3d,lista[i]);
//impresion de la lista con espacio de 3 lineas (%3d) for (i=1;i<TAM;i++) { for (j=0;j<TAM-1;j++) { if (lista[j]
> lista[j+1]) //condicion { temp = lista[j]; //temp guarda momentaneamente el valor de lista[j] lista[j]=lista[j+1];
//Asigno al la posicion lista[j], lo que hay en lista[j+1] lista[j+1]=temp; //obtendra un nuevo valor por parte de temp.
} } } printf("\nLos valores ORDENADOS de lista son: \n); for(i=0;i<TAM;i++) printf("%3d,lista[i]); return 0; }
//Revisado por: Gustavo A. Chavarria. //UNAN-LEON Nicaragua
Captulo 32
Mtodo de Insercin
Este mtodo se podra decir que es algo superior al mtodo de la burbuja, ya que logra evaluar menos veces la
condicin. Sin embargo aun es un algoritmo muy pobre y que utiliza recursos tcnicamente igual que el algoritmo
burbuja.
32.1 Concepto
El algoritmo consiste en ordenar los dos primeros elementos de la matriz, luego se inserta el tercer elemento en la
posicin correcta con respecto a los dos primeros, a continuacin se inserta el cuarto elemento en la posicin correcta
con respecto a los tres primeros elementos ya ordenados y as sucesivamente hasta llegar al ultimo elemento de la
matriz.
32.2 Explicacin
32.3 Ejemplos
brbtgn
59
Captulo 33
Mtodo QuickSort
Es el ultimo mtodo del que hablaremos en este libro, es el algoritmo que mas calidad tiene, pudiendo llegar a lo
mismo que el mtodo burbuja e insercin, mas rpido y con uso de menos recursos.
33.1 Concepto
El ordenamiento rpido (quicksort en ingls) es un algoritmo basado en la tcnica de divide y vencers. Esta es
probablemente la tcnica ms rpida conocida. Fue desarrollada por Tony Hoare en 1960.
La idea del algoritmo es simple, se basa en la divisin en particiones de la lista a ordenar, por lo que se puede
considerar que aplica la tcnica divide y vencers. El mtodo es, posiblemente, el ms pequeo de cdigo, ms
rpido, ms elegante, ms interesante y eciente de los algoritmos de ordenacin conocidos.
33.2 Explicacin
El mtodo se basa en dividir los n elementos de la lista a ordenar en dos partes o particiones separadas por un elemento:
una particin izquierda, un elemento central denominado pivote o elemento de particin, y una particin derecha. La
particin o divisin se hace de tal forma que todos los elementos de la primera sublista (particin izquierda) son
menores que todos los elementos de la segunda sublista (particin derecha). Las dos sublistas se ordenan entonces
independientemente. Para dividir la lista en particiones (sublistas) se elige uno de los elementos de la lista y se utiliza
como pivote o elemento de particin. Si se elige una lista cualquiera con los elementos en orden aleatorio, se puede
seleccionar cualquier elemento de la lista como pivote, por ejemplo, el primer elemento de la lista. Si la lista tiene
algn orden parcial conocido, se puede tomar otra decisin para el pivote. Idealmente, el pivote se debe elegir de
modo que se divida la lista exactamente por la mitad, de acuerdo al tamao relativo de las claves.
Una vez que el pivote ha sido elegido, se utiliza para ordenar el resto de la lista en dos sublistas: una tiene todas las
claves menores que el pivote y la otra, todos los elementos (claves) mayores que o iguales que el pivote (o al revs).
Estas dos listas parciales se ordenan recursivamente utilizando el mismo algoritmo; es decir, se llama sucesivamente al
propio algoritmo quicksort. La lista nal ordenada se consigue concatenando la primera sublista, el pivote y la segunda
lista, en ese orden, en una nica lista. La primera etapa de quicksort es la divisin o particionado recursivo de la lista
hasta que todas las sublistas constan de slo un elemento. El algoritmo es ste: Recorres la lista simultneamente
con i y j: por la izquierda con i (desde el primer elemento), y por la derecha con j (desde el ltimo elemento).
Cuando lista[i] sea mayor que el elemento de divisin y lista[j] sea menor los intercambias. Repites esto hasta
que se crucen los ndices. El punto en que se cruzan los ndices es la posicin adecuada para colocar el elemento
de divisin, porque sabemos que a un lado los elementos son todos menores y al otro son todos mayores (o habran
sido intercambiados). Al nalizar este procedimiento el elemento de divisin queda en una posicin en que todos los
elementos a su izquierda son menores que l, y los que estn a su derecha son mayores.
33.3 Ejemplos
60
Captulo 34
61
Captulo 35
35.1 Ejemplos
Tomemos el cdigo C ms simple posible:
int main(void) /*Ejemplo*/ { return(0); }
Al preprocesarlo tendremos:
# 1 prueba.c # 1 "<built-in>" # 1 "<command line>" # 1 prueba.c int main(void) { return(0); }
Vemos que el comentario ha desaparecido. En su lugar aparecen comentarios especcos del preprocesador. Al compilarlo tenemos:
.le prueba.c .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $8, %esp andl
$16, %esp movl $0, %eax addl $15, %eax addl $15, %eax shrl $4, %eax sall $4, %eax subl %eax, %esp movl
$0, %eax leave ret .size main, .-main .ident GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)" .section
.note.GNU-stack,"",@progbits
Un precioso cdigo ensamblador que enseguida convertimos en un ilegible cdigo mquina:
uUttPTRhhQVhhUSQ[t X[U=t+n_used__libc_start_mainGLIBC_2.0$
hU)UWVS [ )Eu [^_]&1G;}r [^_]'UWVS
[ )EHt41G9}uD [^_]USRtCuX[]USP[VX[$
Hp \ Y o$ooGCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU)
4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC:
(GNU) 4.0.2 (Debian 4.0.2-2)GCC: (GNU) 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)GCC: (GNU) 4.0.2 (Debian 4.0.2-2)",\ $$q!y_IO_stdin_used../sysdeps/i386/elf/start.S/ space/debian/glibc/build-area/glibc-2.3.5/ build62
35.1. EJEMPLOS
63
Captulo 36
64
Captulo 37
65
Captulo 38
make
make es un programa para la construccin de cdigo. Su funcin principal es evitarnos recompilar archivos innecesariamente. Se basa en reglas de dependencia, que especican qu cheros dependen de cuales, y rdenes de
construccin.
Para ver un ejemplo sencillo. En la seccin anterior vimos un programa que dependa de tres cheros objeto, los cuales
dependan a su vez de cheros fuente y cheros de cabecera. As, slo ser necesario enlazar de nuevo el programa
cuando cambie alguno de los cheros objeto. Esto lo podemos expresar, con dependencias, de esta manera:
programa: fuente1.o fuente2.o fuente3.o
Igualmente, los cheros objeto dependen de los cheros fuente y los cheros de cabecera de una manera que podemos
expresar como:
fuente1.o: fuente1.c fuente1.h cabecera.h
fuente2.o: fuente2.c cabecera.h
fuente3.o: fuente3.c cabecera.h
De esta manera, si hacemos un cambio en fuente2.c, la regla para fuente2.o lo detectar y recompilar fuente2.o, y
a continuacin la regla para programa detectar el cambio en fuente2.o y reenlazar programa.
Para ello, make necesita saber tambin cmo reconstruir sus objetivos. Estas rdenes (que, ojo, van siempre precedidas
de un tabulador, no de espacios) son las mismas que usaramos a mano y que vimos en el captulo anterior:
programa: fuente1.o fuente2.o fuente3.o
gcc -o programa fuente1.o fuente2.o fuente3.o
fuente1.o: fuente1.c fuente1.h cabecera.h
gcc -c -o fuente1.o fuente1.c
fuente2.o: fuente2.c cabecera.h
gcc -c -o fuente2.o fuente2.c
fuente3.o: fuente3.c cabecera.h
gcc -c -o fuente3.o fuente3.c
Todas estas rdenes van en chero, el chero de make, cuyo nombre suele ser makele. Utilizando la versin de
GNU de make es ms normal llamarlo Makele, ya que aunque el programa buscar primero si existe el nombre con
minscula, el capitalizado aparecer ms arriba al listar un directorio.
Una vez escrito el Makele, para construir nuestro programa basta con ejecutar make:
$ make gcc -c -o fuente1.o fuente1.c gcc -c -o fuente2.o fuente2.c gcc -c -o fuente3.o fuente3.c gcc -o programa
fuente1.o fuente2.o fuente3.o
Esto se debe a que, si no especicamos un objetivo para make, el programa tomar como objetivo por defecto el de
la primera regla que hayamos escrito en el Makele.
Si tenindolo todo ya compilado ejecutamos make de nuevo veremos:
$ make make: `programa' est actualizado.
66
67
Captulo 39
Programacin en C/Ejemplos
39.1 El "Hola Mundo"
#include <stdio.h> int main (int argc,char **argv) { printf(Hola mundo\n); return 0; }
Nota: este programa est tomado del Dominio Pblico
$ ./holamundo Hola Mundo $
69
Captulo 40
Programacin en C/Glib
40.1 Introduccin
El lenguaje C fue creado como un lenguaje multiplataforma capaz de sustituir al ensamblador y generar cdigo
portable. Sin embargo a lo largo de los aos se empiezan a hacer visibles algunas lagunas del diseo original. Algunos
ejemplos que pueden citarse:
No existe estandarizacin en torno a la longitud real (en bits) de un entero. Un tipo int puede variar entre 16,
32 y 64 bits de longitud en funcin del sistema operativo y compilador utilizado.
No existe un soporte estndar en el lenguaje para programacin orientada a objetos.
No existe un soporte estndar para tipos de estructuras de datos frecuentemente utilizadas como listas enlazadas, tablas hash (tambin llamadas diccionarios).
No existe un soporte estndar para textos Unicode (el estndar Unicode es posterior a la creacin del C).
No existe un soporte estndar para patrones de diseo frecuentemente utilizados como el bucle principal de
eventos.
No existe un API comn para manejar aplicaciones multihilo como puede existir en Java y otros lenguajes/plataformas.
La librera glib se cre con el fn de solucionar estas y otras lagunas originales del lenguaje proporcionando una capa
de compatibilidad real multiplataforma en sistemas tipo UNIX (Linux, Solaris, AIX, BSD, ...), Windows, OS/2 y
BeOS.
La librera glib es gratuita y licenciada bajo LGPL (Lesser GPL), lo cual signica en la prctica que puede ser utilizada
tanto en programas de cdigo abierto como cerrado.
glib provee soporte para expresiones regulares tipo PERL, un conjunto de datos tipados ms seguros que el C estndar,
soporte multihilo multiplataforma, un sistema de bucle principal de eventos, colas asncronas, carga dinmica de
mdulos, soporte portable para uso de cheros, tuberas y sockets, programacin orientada a objetos, utilidades de
todo tipo para manipular textos, escaneado sintctico, Timers y multitud de estructuras de datos frecuentemente
utilizadas slices de memoria, listas doblemente enlazadas, colas, secuencias (listas escalables), tablas hash para
bsqueda mediante llaves (donde la llave puede ser un texto o un objeto complejo), arrays dinmicos, rboles binarios
balanceados, rboles N-rios (rboles con N ramas por nodo), quarks, listas de datos indexadas (accesibles mediante
un identicador GQuark, relaciones y tuplas que pueden ser indexadas mediante un nmero arbitrario de campos,
caches para comparticin de estructuras complejas de datos.
glib provee adems un marco base de utilidades para realizar tests de funcionamiento en tiempo de desarrollo.
glib no provee soporte grco. Frecuentemente suele emparejarse con la librera grca GTK+ ya que la misma utiliza
glib internamente como soporte base. Sin embargo glib puede ser utilizado para aplicaciones embebidas o servidor
sin dependencia alguna del sistema grco.
70
71
40.3 Referencias
El Interfaz de Programacin de Aplicaciones (API) puede consultarse (en ingls) en la siguiente URL:
http://library.gnome.org/devel/glib/stable/
El tutorial de la librera grca GTK+ (http://library.gnome.org/devel/gtk-tutorial/stable/) incluye tambin
ejemplos de glib y herramientas para compilar y linkar contra estas dos libreras.
El exitoso escritorio para UNIX de cdigo abierto GNOME (GNU Network Model Environment) as como la
plataforma Moblin de Intel, Nokia Maemo, Google Android y Google Chrome, la versin UNIX de Firefox y
otros programas de cdigo abierto como The Gimp, Inkscape o DIA, etc ... hacen un uso extenso de glib. El
cdigo est disponible gratuitamente en la web y puede servir como una gua y referencia para utilizar glib en
proyectos de software complejos con millones de lneas de cdigo y que son utilizados diariamente por millones
de personas.
Captulo 41
Programacin en C/Referencia
Esta parte pretende ser una referencia completa de los elementos del lenguaje C. Se pretende que aqu se presenten
todos los datos disponibles de los tipos de datos del lenguaje C, toda la informacin relevante de los operadores del
lenguaje C, las formas en la que se deben usar la principales estructuras e informacin acerca de algunas bibliotecas
y funciones que son estndares. Todo esto con la mayor calidad posible. O sea que en un momento aqu va a existir
algo as como una super sntesis del manual de programacin en C con los datos que son importantes pero que son
fciles de olvidar.
41.2. OPERADORES
73
bytes = 8
Mximo = 9,223,372,036,854,775,807 (18,446,744,073,709,551,616 cuando es unsigned)
Mnimo = 9,223,372,036,854,775,808 (0 cuando es unsigned)
short
bytes = 2
Mximo = 32767 (65,535 cuando es unsigned)
Mnimo = 32768 (0 cuando es unsigned)
double
bytes = 8
Mximo Exponente = 10308
Mnimo Exponente = 10308
void -sin tipo-
41.2 Operadores
Todos los operadores estn puestos en el orden en que estn en la jerarqua del lenguaje C. En caso de que estn
en una expresin todos los operadores juntos y de que no se use el operador (), que rompe la precedencia, entonces
van a ser evaluados desde el primero de la tabla hasta el ltimo en ese orden. En caso de que se encuentren varios
operadores iguales o de la misma precedencia en una expresin sin parntesis se van a resolver en el orden en que
diga la columna de Orden de agrupacin. La precedencia es una cosa importante porque nos dice en que orden va
a ser resuelta una expresin y el orden en que se resuelve una expresin inuye en su resultado.
un ejemplito: 1 + 2 / 3 + 4 no es igual a (1 + 2) / (3 + 4) eso porque segn la precedencia: 1 + 2 / 3 + 4 es igual a 1 +
(2 / 3) + 4) (2/3 = 0 por ser una operacin entre enteros, 1 + 4 = 5) (en el otro caso 1 + 2 = 3 y 3 + 4 = 7 y 3 / 7 = 0
por ser una operacin entera)
Como la divisin tiene mayor precedencia que la suma se resuelve primero la divisin.
otro ejemplito: 72 / 3 / 2 / 6 no es igual a (72 / 3) / (2 / 6) eso porque segn la precedencia: 72 / 3 / 2 / 6 es igual
a ((72 / 3) / 2) / 6 (La divisin no es una operacin asociativa y por eso cambia el resultado si resolvemos en orden
diferente, no es lo mismo cuando usamos la precedencia del lenguaje C como en la tabla de precedencias, que cuando
obligamos a que se resuelva la operacin en otro orden usando parntesis).
Para los operadores de Incremento (++) y decremento (--) hay que tener en cuenta el sitio en el que se encuentran con
respecto a la variable que estn inuenciando. Con esto me reero a que siendo a una variable ++a no es igual que
a++, aunque en algunas ocasiones parezca lo contrario. Para el caso de ++a el incremento ser realizado antes
de evaluar la expresin. Y para a++ se evaluara primero la expresin completa y luego se realizara el incremento.
En la tabla hay tres operadores que estn identicados con un calicativo para bits. Con esto me reero a que
inuenciaran los bits de una variable. Si tengo dos valores a y b el resultado de un a && b ser el valor del AND
lgico entre los valores respectivos pero el a & b tendr como resultado varios ANDs lgicos individuales entre cada
uno de los bits de las dos variables.
41.3 Estructuras
Estructura general de un programa de lenguaje C:
inclusin de bibliotecas ... denicin de macros y constantes ... deniciones de funciones o/y prototipos ... deniciones
de variables globales ... int main(argumentos){ sentencias ... return 0; } deniciones de cuerpos de funciones (cuyos
prototipos ya fueron declarados) ...
74
41.5. PREPROCESADOR DE C
75
41.5 Preprocesador de C
Captulo 42
76
Captulo 43
77
Captulo 44
Espaol
44.1.2
Ingls
The C Book
Es la versin online del libro The C Book de Mike Banahan, Declan Brady y Mark Doran.
78
Captulo 45
Programacin en C/Bibliografa
45.1 Bibliografa en espaol
45.2 Bibliografa en ingls
Kernighan, Brian W. y Ritchie, Dennis M.: The C Programming Language (2nd Edition), Prentice Hall,
1988.
Es conocido como Kernighan y Ritchie o como K&R. Este libro es la biblia del lenguaje C.
79
Captulo 46
Fundamentos de programacin
80
Captulo 47
Programacin en C/Fundamentos de
programacin
En este captulo veremos un resumido listado de conceptos bsicos, esta informacin puede encontrarse en forma
ms elaborada en el WikiLibro Fundamentos de programacin.
47.1 Deniciones
Se denomina algoritmo a una secuencia de instrucciones que permiten obtener un resultado en particular.
No necesariamente son programas de computadora, una receta de cocina, o las instrucciones para cambiar un
neumtico son ejemplos de algoritmos de la vida real.
Las computadoras, son maquinas sin inteligencia propia, cuya nica nalidad es interpretar el cdigo que se
les provee.
El lenguaje de mquina es el nico lenguaje que la computadora entiende y es capaz de ejecutar.
Los lenguajes de programacin son el medio de comunicacin entre el programador y una computadora. El
programador escribe en algn lenguaje de programacin y utiliza las herramientas provistas por ese lenguaje
para transformarlo en lenguaje de mquina.
Finalmente, denominamos programa a una secuencia de rdenes a ser ejecutadas por una computadora. Un
programa debe estar escrito en algn lenguaje de programacin, y puede incluir uno o ms algoritmos.
47.2.1
Esquemas de programacin
El esquema de programacin llamado Programacin Imperativa, consiste en escribir una secuencia de instrucciones
una detrs de la otra, que se ejecutarn en orden. Algunas de esas instrucciones pueden hacer que la mquina pase a
una instruccin que no sea la siguiente, tal vez porque se cumpla una condicin que hayamos establecido.
En los ltimos aos ha tomado fuerza otro paradigma de computacin, llamado Programacin Orientada a Objetos
, en el cual se intentan modelar los sistemas creados como extensiones de la realidad mediante la denicin de objetos
que modelan entidades de la vida real y que interactan entre s mediante mensajes llamadas mtodos.
El lenguaje C es un lenguaje imperativo, no orientado a objetos.
81
82
47.2.2
Por otro lado, los lenguajes de programacin se clasican en niveles. Un lenguaje es de ms bajo nivel cuanto ms
cercano est al cdigo de mquina, y un lenguaje que es de ms alto nivel cuanto ms lejano est de la mquina y
ms cercano al lenguaje humano.
C es un lenguaje de alto nivel aunque tiene muchas caractersticas de lenguaje de bajo nivel (como el uso que permite
hacer de la memoria). Estas caractersticas hacen que C sea un lenguaje muy potente, ya que permite optimizar al
mximo los recursos de la mquina. Por ende, esto tambin hace que la dicultad y que los errores que se puedan
cometer programando aumenten. As que a C se le considera de nivel medio.
Lenguajes de ms alto nivel que C son aquellos en los que el programador no necesita encargarse de manipular la
memoria, como Java, C#, Python, Ruby, entre otros.
47.2.3
Compilados o interpretados
Otra forma de clasicar a los lenguajes de programacin que es segn la forma en que se ejecutan sus rdenes. Existen
los lenguajes que son interpretados, cuyas rdenes pasan a travs de un intrprete que se encarga de ejecutarlas (a
partir del cdigo fuente) en el mismo momento en que estn siendo ledas. Algunos de los lenguajes interpretados
son Python, Perl o Tcl, entre muchos otros.
La contraparte de los lenguajes interpretados son los lenguajes compilados (como el mismo C) que se diferencian
en que las rdenes son transformadas a lenguaje de mquina que se almacena en un archivo ejecutable. Ese archivo
puede ejecutarse luego, sin recurrir al compilador.
Los lenguajes compilados tienen la ventaja de la velocidad y la eciencia, pero los interpretados tienen la ventaja de
que, generalmente, son muy portables y de ms alto nivel.
Captulo 48
Programacin en C/Historia de C
El lenguaje de programacin C fue creado por Dennis Ritchie entre 1969 y 1973 cuando trabajaba en Bell Laboratories
de AT&T junto con Ken Thompson en el diseo del sistema operativo UNIX. C fue creado para poder escribir dicho
sistema operativo en un lenguaje de alto nivel, independiente del hardware donde se ejecutara.
Contar con un lenguaje de alto nivel permiti el avance de los sistemas operativos, ya que el mismo cdigo poda ser
utilizado en las distintas plataformas, propiciando la reutilizacin de cdigo y reduciendo los tiempos de desarrollo.
As es que los sistemas operativos basados en UNIX, el sistema BSD, el sistema GNU/Linux y muchos otros fueron
desarrollados en C.
Adems, con el paso del tiempo se han desarrollado cientos de bibliotecas que permiten a los programadores de
C utilizar el cdigo desarrollado por otros para la realizacin de tareas comunes. Esto, a su vez, ha propiciado el
desarrollo de aplicaciones en lenguaje C.
Actualmente es imposible contar la cantidad de aplicaciones y herramientas desarrolladas en C.
48.1 Evolucin
A mediados de los aos 60s, Martin Richards dise el lenguaje BCPL con la nalidad de usarlo para escribir software
de sistemas operativos y compiladores.
En 1969, Ken Thompson escribi el Lenguaje B, en Bell Laboratories, con el objetivo de recodicar UNIX (escrito
hasta ese momento en lenguaje ensamblador) usando un lenguaje de alto nivel ms portable y exible.
Durante los siguientes aos, Dennis Ritchie modic el lenguaje B, llegando a crear el lenguaje C y reescribiendo
el sistema UNIX en dicho lenguaje; aadi caractersticas nuevas, como son el diseo de tipos y las estructuras de
datos.
En 1978, Dennis Ritchie y Brian Kernighan publicaron la primera edicin del libro El lenguaje de programacin C.
Este libro fue durante aos la especicacin informal del lenguaje. El lenguaje descrito en la primera edicin de este
libro, fue conocido como el C de Kernighan y Ritchie o simplemente K&R C. En este libro se introdujeron nuevas
caractersticas al lenguaje: los tipo de datos struct, long int y unsigned int; los operadores =+ y =- fueron sustituidos
por += y -=.
A mediados de los aos 80, Bjarne Stroustrup (tambin de los laboratorios Bell), crea el lenguaje C++, un lenguaje
basado en C, con numerosas caractersticas adicionales, siendo la principal que est orientado a objetos. Si bien se
han creado muchos lenguajes basados en C, C++ es el que ha permanecido ms asociado a C.
En los aos siguientes a la publicacin del C de Kernighan y Ritchie, se aadieron al lenguaje muchas caractersticas
no ociales, que estaban presentes en algunos compiladores y no en otros. Fue por ello que en 1989 ANSI (American
National Standards Institute) public el primer estndar ocial de C, que es conocido como ANSI C.
En este estndar se tomaron muchas de las funcionalidades no ociales y se agregaron funcionalidades nuevas como los
prototipos de funcin, y un preprocesador mejorado. Tambin se cambi la sintaxis de la declaracin de parmetros
de funciones, para que incluyeran el tipo junto con el nombre.
Al ao siguiente, en 1990 se public la estandarizacin ISO del lenguaje. Este estndar es bsicamente el estndar
83
84
ANSI, con unas pocas modicaciones de formato. A este estndar se lo conoce, entonces, como C89, o C90, y se
trata del mismo lenguaje.
Basndose en el estndar ANSI que estaba en preparacin, en 1988 Kernighan y Ritchie publicaron la segunda edicin
de su libro, que es an hoy utilizada como una de las referencias principales del lenguaje.
Durante los siguientes aos, el lenguaje C permaneci sin demasiados cambios. Sin embargo, como haba sucedido
antes, los distintos compiladores fueron incorporando caractersticas adicionales, que otros compiladores no tenan,
siendo C++ la principal inuencia.
Fue por ello que a nales de los noventa se decidi revisar el estndar de C, lo que llev a la publicacin del estndar
C99. Este estndar incluye varias nuevas caractersticas como son: las funciones inline; la posibilidad de declarar
variables en cualquier parte del cdigo; los comentarios de una sola lnea utilizando //; los tipos de datos long long
int, bool y complex, entre otras.
An hoy el proceso de evolucin del lenguaje sigue avanzando, y desde 2007 se est trabajando en el armado de un
nuevo estndar.
48.2 Ms informacin
Lenguaje de programacin BCPL
Historia del lenguaje del programacin C
The Development of the C Language
Captulo 49
Programacin en C/Introduccin
49.1 Objetivos
El objetivo principal de este Wikilibro es que cualquier persona sin conocimientos previos de programacin pueda
ser capaz de programar en el lenguaje C.
Una vez logrado el dominio del lenguaje, es probable que los lectores se interesen por otros temas ms complejos que
superen a los temas bsicos. Tambin les ser ms o menos sencillo aprender cualquier otro lenguaje de programacin
estructurada.
Sin embargo, este no es un libro que apunte nicamente a programadores principiantes. Tambin puede resultar de
inters para quienes ya tengan experiencia en el rea de programacin. En esta introduccin hay dos secciones en
las que se explica para los dos grupos principales de lectores qu camino seguir para comenzar a programar en el
lenguaje C o bien perfeccionar conocimientos.
El lenguaje C es tan usado porque es un lenguaje de programacin que emplea pocas instrucciones en lenguaje
mquina para traducir elementos del cdigo. Esto reduce los tiempos de ejecucin de los programas.
86
49.6 Requisitos
Se presupone que los lectores tienen conocimientos elementales de informtica a nivel de usuario, y son capaces
de instalar un compilador del lenguaje C en sus sistema. Los detalles sobre la instalacin se vern en la seccin
Herramientas.
Con respecto al Hardware, slo ser necesario contar con una PC con sistema operativo, donde sea posible instalar un
compilador, y en lo posible un entorno de desarrollo. Cuanto mejor sea la computadora, ms rpido ser el proceso
de compilacin y ejecucin de los programas. Sin embargo, cualquier PC sirve para aprender con los ejemplos de
este libro.
Para quienes no tengan conocimientos bsicos de programacin, puede ser una buena idea comenzar leyendo los
primeros captulos del Wikilibro Fundamentos de programacin, ya que algunos temas explicados en ese libro se
asumen ya conocidos.
Finalmente, un requisito imprescindible en todo programador o programadora es tener sentido comn. Muchas veces
se pueden adoptar mejores o peores soluciones ante los diversos problemas, y la decisin de cul elegir pasa por la
aplicacin del sentido comn. yapa
49.7 Herramientas
Para programar tanto en C, como en C++, Java o cualquier otro lenguaje de programacin, necesitamos contar con
aplicaciones o herramientas que nos permitan poner en funcionamiento nuestro programa.
El lenguaje de programacin C es compilado, as que en este caso necesitaremos un compilador, que ser el encargado
de transformar nuestro cdigo fuente en cdigo que la computadora pueda ejecutar.
Adems, para facilitar la tarea de los programadores existen los denominados Entorno de desarrollo integrados
(IDE). En muchos casos, estos entornos incluyen un compilador, un depurador, y otras herramientas.
Las herramientas a instalar dependern del sistema operativo utilizado. A continuacin se listan algunas posibilidades
para el sistema operativo Windows o GNU/Linux, no es imprescindible utilizar estas herramientas en particular,
cualquier compilador puede servir.
49.8 Windows
Uno de los entornos de desarrollo ms conocidos entre los programadores de C sobre Windows, tanto novatos como
expertos, es el Bloodshed Dev-C++, que es un entorno libre multiplataforma. Tal entorno de desarrollo fue abando-
49.9. GNU/LINUX
87
nado y retomado mejorndolo pasando a llamarse WxDev-C++. Otro entorno libre y gratuito es el Code::Blocks.
Ambos entornos pueden utilizarse tanto para C como para C++.
Tambin hay otras alternativas privativas como los compiladores de Borland o de Microsoft (Microsoft Visual C++).
49.9 GNU/Linux
En los sistemas GNU/Linux, ser necesario tener instaladas las herramientas gcc y make y la versin 6 de la glibc
con su documentacin, que son las que permitirn compilar los programas.
Para escribir y modicar el cdigo, es posible utilizar cualquier editor de texto plano (en lo posible que cuente con
resaltado de sintaxis), como son emacs, vim, kate, gedit o geany.
Sin embargo, para quienes son novatos en la programacin, es recomendable utilizar un entorno de desarrollo como
son el Anjuta DevStudio (para el entorno GNOME) o KDevelop (para el entorno KDE), ya que incluyen facilidades
adicionales para la ejecucin y solucin de problemas.
Los programas mencionados se incluyen dentro de la instalacin estndar de la mayora de las distribuciones actuales
de GNU/Linux, de modo que para instalarlos slo ser necesario seguir el procedimiento usual de instalacin de
aplicaciones para la distribucin deseada.
Captulo 50
89
ja, que se reserva y libera de forma automtica. En contraste, la memoria dinmica se reserva de forma explcita y
contina existiendo hasta que sea liberada, generalmente por parte del programador.
La memoria dinmica puede afectar el rendimiento. Puesto que con la memoria esttica el tamao de las variables se
conoce en tiempo de compilacin, esta informacin est incluida en el cdigo objeto generado, por lo cual el proceso
es muy eciente. Cuando se reserva memoria de manera dinmica, se tienen que llevar a cabo varias tareas, como
buscar un bloque de memoria libre y almacenar la posicin y tamao de la memoria asignada, de manera que pueda
ser liberada ms adelante. Todo esto representa una carga adicional, aunque esto depende de la implementacin y hay
tcnicas para reducir su impacto.
50.4.1
malloc
La funcin malloc reserva un bloque de memoria y devuelve un puntero void al inicio de la misma. Tiene la siguiente
denicin:
void *malloc(size_t size);
donde el parmetro size especica el nmero de bytes a reservar. En caso de que no se pueda realizar la asignacin,
devuelve el valor nulo (denido en la macro NULL), lo que permite saber si hubo errores en la asignacin de memoria.
Ej:
int *puntero; char *puntcarc; puntero=(int *)malloc(4); puntcarc=(char *)malloc(200);
A continuacin se muestra un ejemplo de su uso:
int *i; /* Reservamos la memoria suciente para almacenar un int y asignamos su direccin a i */ i = malloc(sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if (i == NULL) { /* Error
al intentar reservar memoria */ }
Uno de los usos ms comunes de la memoria dinmica es la creacin de vectores cuyo nmero de elementos se dene
en tiempo de ejecucin:
int *vect1, n; printf(Nmero de elementos del vector: "); scanf("%d, &n); /* reservar memoria para almacenar n
enteros */ vect1 = malloc(n * sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if
(vect1 == NULL) { /* Error al intentar reservar memoria */ }
50.4.2
calloc
La funcin calloc funciona de modo similar a malloc, pero adems de reservar memoria, inicializa a 0 la memoria
reservada. Se usa comnmente para arreglos y matrices. Est denida de esta forma:
void *calloc(size_t nmemb, size_t size);
90
El parmetro nmemb indica el nmero de elementos a reservar, y size el tamao de cada elemento. El ejemplo anterior
se podra reescribir con calloc de esta forma:
int *vect1, n; printf(Nmero de elementos del vector: "); scanf("%d, &n); /* Reservar memoria para almacenar n
enteros */ vect1 = calloc(n, sizeof(int)); /* Vericamos que la asignacin se haya realizado correctamente */ if (vect1
== NULL) { /* Error al intentar reservar memoria */ }
50.4.3
realloc
La funcin realloc redimensiona el espacio asignado de forma dinmica anteriormente a un puntero. Tiene la siguiente
denicin:
void *realloc(void *ptr, size_t size);
Donde ptr es el puntero a redimensionar, y size el nuevo tamao, en bytes, que tendr. Si el puntero que se le pasa
tiene el valor nulo, esta funcin acta como malloc. Si la reasignacin no se pudo hacer con xito, devuelve un puntero
nulo, dejando intacto el puntero que se pasa por parmetro. Al usar realloc, se debera usar un puntero temporal. De
lo contrario, podramos tener una fuga de memoria, si es que ocurriera un error en realloc.
Ejemplo de realloc usando puntero temporal:
/* Reservamos 5 bytes */ void *ptr = malloc(5); /* Redimensionamos el puntero (a 10 bytes) y lo asignamos a un
puntero temporal */ void *tmp_ptr = realloc(ptr, 10); if (tmp_ptr == NULL) { /* Error: tomar medidas necesarias
*/ } else { /* Reasignacin exitosa. Asignar memoria a ptr */ ptr = tmp_ptr; }
Cuando se redimensiona la memoria con realloc, si el nuevo tamao (parmetro size) es mayor que el anterior, se
conservan todos los valores originales, quedando los bytes restantes sin inicializar. Si el nuevo tamao es menor, se
conservan los valores de los primeros size bytes. Los restantes tambin se dejan intactos, pero no son parte del bloque
regresado por la funcin.
50.4.4
free
La funcin free sirve para liberar memoria que se asign dinmicamente. Si el puntero es nulo, free no hace nada.
Tiene la siguiente denicin:
void free(void *ptr);
El parmetro ptr es el puntero a la memoria que se desea liberar:
int *i; i = malloc(sizeof(int)); free(i);
Una vez liberada la memoria, si se quiere volver a utilizar el puntero, primero se debe reservar nueva memoria con
malloc o calloc:
int *i = malloc(sizeof(int)); free(i); /* Reutilizamos i, ahora para reservar memoria para dos enteros */ i = malloc(2 * sizeof(int)); /* Volvemos a liberar la memoria cuando ya no la necesitamos */ free(i);
91
Aunque no hay un consenso, muchos programadores preeren omitir la conversin anterior porque la consideran
menos segura. Si accidentalmente se olvida incluir el archivo stdlib.h (donde estn denidas malloc, calloc, realloc y
free) en un programa que use dichas funciones, el comportamiento puede quedar indenido. Si omitimos la conversin
explcita, el compilador lanzar una advertencia. Si, en cambio, realizamos la conversin, el compilador generar el
cdigo objeto de forma normal, ocultado el bug.
Una posible razn para usar la conversin explcita es si se escribe cdigo en C que se vaya a compilar junto con
cdigo C++, ya que en C++ s es necesario realizar esa conversin.
En cualquier caso, dado que el manejo de memoria es un tema complejo, y ste es un error muy comn, se debe hacer
nfasis en que cuando se trabaja con memoria dinmica, siempre se debe vericar que se incluya el archivo stdlib.h.
Tratar de utilizar un puntero cuyo bloque de memoria ha sido liberado con free puede ser sumamente peligroso.
El comportamiento del programa queda indenido: puede terminar de forma inesperada, sobrescribir otros datos y
provocar problemas de seguridad. Liberar un puntero que ya ha sido liberado tambin es fuente de errores.
Para evitar estos problemas, se recomienda que despus de liberar un puntero siempre se establezca su valor a NULL.
int *i; i = malloc(sizeof(int)); free(i); i = NULL;
92
Text
93
50.6.2
Images
50.6.3
Content license