Documentos de Académico
Documentos de Profesional
Documentos de Cultura
1. Introducción al lenguaje C 1
1.1. Historia de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Características de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3. Inconvenientes de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4. Entorno de desarrollo de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5. Primer programa en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5.1. ¿Cómo crear el programa? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5.2. ¿Cómo compilar y ejecutar el programa? . . . . . . . . . . . . . . . . . . . . . . 4
2. Fundamentos de C 7
2.1. Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.1. Compilación y enlace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2. Palabras reservadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3. Tipos de datos elementales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4. Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.5. La función printf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.5.1. Secuencias de escape . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.5.2. Especificadores de ancho de campo . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.6.1. Declaración de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.7. Expresiones y sentencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.8. Sentencia de asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.9. Función scanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.10. Introducción a la directiva #define . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.11. Errores de programación comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3. Operadores y expresiones 17
3.1. Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1.1. Operadores aritméticos. Conversión de tipos . . . . . . . . . . . . . . . . . . . . 18
3.2. Conversión de tipos o cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.1. Prioridad de los operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . 19
3.2.2. Operadores de incremento y decremento . . . . . . . . . . . . . . . . . . . . . . 19
3.3. Operadores relacionales y lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.1. Prioridad de los operadores relacionales . . . . . . . . . . . . . . . . . . . . . . 21
3.4. Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.4.1. Tablas de verdad de los operadores lógicos . . . . . . . . . . . . . . . . . . . . . 21
3.4.2. Prioridad de los operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.5. Resumen de prioridades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4. Sentencias de control 29
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.2. Sentencia if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.3. Sentencia if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.4. Sentencia for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.5. Sentencia while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.6. Sentencia do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.7. Sentencia switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.8. Bucles anidados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.9. Sentencia break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5. Funciones 43
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
5.2. Definición de una función . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.3. Declaración de funciones: prototipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.4. Llamadas a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.5. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.6. Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.7. Programación estructurada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
8. Vectores y matrices 69
8.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.2. Definición de un vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.3. Procesamiento de un vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.4. Paso de vectores a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.5. Punteros y vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.6. Vectores y cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
8.7. Vectores multidimensionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8.8. Punteros y vectores multidimensionales . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
9. Estructuras de datos 79
9.1. Estructuras (struct) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.1.1. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.2. Procesamiento de una estructura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9.3. Paso de estructuras a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.4. Punteros a estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
9.5. Vectores de estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.6. Uniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.7. Tipos enumerados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.8. Definición de tipos de datos (typedef) . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
9.9. Estructuras de datos autorreferenciadas . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.10. Listas enlazadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.11. Ejemplo de lista enlazada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.12. Diferencia entre vectores y listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.Entrada/salida 91
10.1. Apertura y cierre de un archivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.2. Lectura y escritura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.3. Argumentos en la línea de mandatos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
11.Aspectos avanzados 95
11.1. Campos de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
11.2. Punteros a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
11.3. Funciones como argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
11.4. Funciones con un número variable de argumentos . . . . . . . . . . . . . . . . . . . . . 98
11.5. Compilación condicional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Introducción al lenguaje C
1.1. Historia de C
La historia de C puede resumirse en los siguientes puntos:
Muchas ideas provienen de los lenguajes BCPL (Martin Richards, 1967) y B (Ken Thompson,
1970).
C fue diseñado originalmente en 1972 para el sistema operativo UNIX en el DEC PDP-11 por
Dennis Ritchie en los Laboratorios Bell.
En 1989 aparece el estándar ANSI C, que está soportado por la casi totalidad de los compiladores.
En 1990 aparece el estándar ISO C. WG14 se convierte en el comité oficial del estándar ISO C.
1.2. Características de C
Las principales características del lenguaje C pueden resumirse en los siguientes puntos:
C es un lenguaje de propósito general ampliamente utilizado.
Presenta características de bajo nivel: C trabaja con la misma clase de objetos que la mayoría
de los computadores (caracteres, números y direcciones). Esto permite la creación programas
eficientes.
Está estrechamente asociado con el sistema operativo UNIX. UNIX y su software fueron escritos
en C.
B
(Ken Thompson, 1970)
UNIX C
1ª versión (1969) (Dennis Ritchie, 1972)
Ken Thompson
Dennis Ritchie
No ofrece mecanismos de E/S (entrada/salida). Todos los mecanismos de alto nivel se encuentran
fuera del lenguaje y se ofrecen como funciones de biblioteca.
Permite la creación de programas portables, es decir, programas que pueden ejecutarse sin cam-
bios en multitud de computadores.
A pesar de su bajo nivel, es el lenguaje más portado a todo tipo de plataformas. Existen compi-
ladores de C para casi todos los sistemas.
1.3. Inconvenientes de C
Aunque C ofrece innumerables ventajas, también presenta una serie de inconvenientes:
Sin una programación metódica puede ser propenso a errores difíciles de encontrar.
La versatilidad de C permite crear programas difíciles de leer, como el que se muestra a conti-
nuación.
Editor de texto.
Compilador.
Ficheros de cabecera.
Ficheros de biblioteca.
Enlazador.
Depurador.
i n t main ( v o i d )
{
/∗ Primer programa en C ∗/
Editarlo con un editor de texto (en Linux: vi, pico, emacs, etc.)
Un compilador de C permite:
Genera el código objeto ejemplo.o, que incluye el código que resulta de compilar un único fichero
fuente.
Genera el ejecutable a.out, que se obtiene a partir de uno o varios ficheros objeto.
Fundamentos de C
2.1. Comentarios
Los comentarios comienzan con /* y finalizan con */. Algo importante a tener en cuenta es que no
se pueden anidar, es decir, dentro de un comentario no puede aparecer el símbolo /*.
También se pueden poner comentarios en una línea utilizando //. Todo lo que aparece a partir de
estos símbolos se consideran comentarios hasta el final de la línea.
El siguiente programa ilustra la utilización de los comentarios en C en el primer programa Hola
Mundo.
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
/∗ Esto e s un c o m e n t a r i o ∗/
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
// e s t o e s tambien o t r o c o m e n t a r i o
La función main, que se corresponde con el punto de entrada al programa. Es la primera función
en ser llamada. El prototipo de la función (nombre, argumentos, tipo de los argumentos y tipo de
retorno de la llamada) indica que la función no acepta ningún argumento (void) y que la función
devuelve un valor de tipo entero (int).
Preprocesado: procesa todas las directivas del preprocesador. En el caso anterior include.
Ensamblado: ensambla a código objeto. Esta fase está normalmente integrada con la compila-
ción.
Enlazado: resuelve las referencias externas entre distintos módulos objetos y bibliotecas para
genera el fichero ejecutable.
g c c HolaMundo . c −E −o h o l a . i
Este mandato preprocesa el fichero HolaMundo y obtiene como resultado el fichero preprocesado
hola.i, que será el punto de entrada al compilador. El fichero obtenido tiene 754 líneas.
El código ensamblador se puede obtener de la siguiente forma:
g c c HolaMundo . c −S −o h o l a . s
g c c HolaMundo . c −c −o h o l a . o
g c c HolaMundo . c −o h o l a
g c c HolaMundo . c
El tipo intN_t designa a un entero con sigo de tamaño N bits. El tipo unitN_t designa a un entero
sin sigo de tamaño N bits.
Además el tipo void comprende un conjunto vacío de valores. Cuando se declara una función de
tipo void, lo que se está indicando es que la función no devuelve ningún valor.
En las primeras versiones de C, no existía el tipo booleano. Un valor igual a 0 se interpretaba como
falso y un valor distinto de 0 como verdadero. En las últimas versiones de C se han añadido además el
tipo boolean _Bool.
En el siguiente programa se puede ver su uso:
#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d b o o l .>
i n t main ( v o i d )
{
_Bool a ;
i f ( a == t r u e )
p r i n t f ( " t r u e \n " ) ;
return 0;
}
En las últimas versiones de C también existe como tipo básico el número complejo _Complex. Se
pueden definir variables de tipo:
i n t main ( v o i d )
{
d o u b l e _Complex c1 , c2 , c3 ;
c1 = 1 . 0 + 3 ∗ I ;
c2 = 1 . 0 + 3 ∗ I ;
c3 = c1+c2 ;
2.4. Constantes
Los distintos tipos de constantes en C son:
p r i n t f ( formato , argumentos ) ;
donde formato especifica entre otras cosas, los tipos de datos que se van a imprimir y argumentos
los datos o valores a imprimir.
A continuación se presenta un ejemplo de utilización.
Los especificadores de formato permite dar un determinado formato a la salida que se imprime con
printf. Los principales especificadores de formato son:
2.6. Variables
Una variable es un identificador utilizado para representar un cierto tipo de información. Cada
variable es de un tipo de datos determinado. Una variable puede almacenar diferentes valores en
distintas partes del programa.
Toda variable debe comenzar con una letra o el caracter _. El resto sólo puede contener letras,
números o _
Ejemplos de variables válidas son:
numero
_color
identificador_1
Algo importante a tener en cuenta es que C es sensible a mayúsculas y minúsculas, lo que
quiere decir que las siguientes variables, por tanto, son todas distintas:
pi PI Pi pI
Sentencia simple:
t e m p e r at u r a = 4 + 5 ;
i n t main ( v o i d )
{
int a = 1;
float b = 4.0;
int c , d;
char l e t r a ;
return 0;
}
El programa anterior declara las variables a y b y les asigna valores iniciales.
i n t main ( v o i d )
{
i n t numero ;
i n t cuadrado ;
return 0;
}
#d e f i n e CIERTO 1
#d e f i n e FALSO 0
#d e f i n e AMIGA "Marta"
i n t main ( v o i d )
{
f l o a t radio ;
f l o a t area ;
a r e a = PI ∗ r a d i o ∗ r a d i o ;
p r i n t f ( " El a r e a d e l c i r c u l o e s %5.4 f \n " , a r e a ) ;
return 0;
}
Comentarios incompletos.
Comentarios anidados.
#d e f i n e PI 3.141593
/∗ Programa que c a l c u l a e l a r e a de un c i r c u l o ∗/
i n t main ( v o i d )
{
f l o a t radio ;
a r e a = PI ∗ r a d i o ∗ Radio ;
p r i n t f ( " El a r e a d e l c i r c u l o e s %5.4 f \n " , a r e a ) ;
return 0;
}
Operadores y expresiones
Operador Función
+ suma
− resta
∗ producto
/ división
% operador módulo
resto de la división entera
La división entera: división de una cantidad entera por otra , se desprecia la parte decimal del
cociente.
El operador % requiere que los dos operandos sean enteros.
La mayoría de las versiones de C asignan al resto el mismo signo del primer operando.
Los valores negativos con el signo −. Ejemplo: −4
Ejemplo Si a = 10 y b = 3, entonces
Expresión Valor
a+b 13
a−b 7
a∗b 30
a/b 3
a %b 1
Si a = 11 y b = −3, entonces
4. En otro caso, si es unsigned long int el otro se se convierte a unsigned long int.
Si el unsigned int puede convertirse a long int el operando unsigned int se convertirá
en long int.
En otro caso, ambos operandos se convertirán a unsigned long int.
( ( i n t ) 5.5 % 4)
Prioridad Operación
Primero ( )
Segundo Negación (signo menos)
Tercero ∗, /, %
Cuarto +, −
Postincremento, i + +
Preincremento, + + i
Postdecremento, i − −
Predecremento, − − i
Ejemplo
La expresión i + +; es equivalente a i = i + 1
La expresión + + i; es equivalente a i = i + 1
La expresión i − −; es equivalente a i = i − 1
La expresión − − i; es equivalente a i = i − 1
Operador Función
< menor que
> mayor que
<= menor o igual que
>= mayor o igual que
== igual que
!= distinto que
Estos operadores se utilizan para formar expresiones lógicas. El resultado de una expresión lógica
es un valor entero que puede ser:
Ejemplo Si a = 1 y b = 2, entonces:
Prioridad Operación
Primero ( )
Segundo > < >= <=
Tercero == ! =
Los operadores aritméticos tienen mayor prioridad que los operadores relacionales.
Dentro de cada grupo las operaciones se realizan de izquierda a derecha.
Ejemplo Si a = 1 y b = 2
Operador Función
&& Y lógica (AND)
|| O lógica (OR)
! NO lógico (NOT)
Estos operadores actúan sobre operandos que son a su vez expresiones lógicas que se interpretan
como:
falso, el valor 0
Se utilizan para formar expresiones lógicas y su resultado es un valor entero que puede ser:
Prioridad Operación
Primero ( )
Segundo !
Tercero &&
Cuarto ||
Ejemplo Si a = 7 y b = 3
Las expresiones lógicas con && o || se evalúan de izquierda a derecha, sólo hasta que se ha esta-
blecido su valor cierto/falso.
Operador Evaluación
+ + − − (post) I→D
+ + − − (pre) D→I
! − (signo menos) D→I
(tipo) (cast) D→I
∗ / % I→D
+ − I→D
< > <= >= I→D
== ! = I→D
&& I→D
|| I→D
Ejemplo Si a = 7 y b = 3
a = 3;
area = lado ∗ lado ;
El operador de asignación = y el el igualdad == son distintos. Este es un error común que se
comete cuando se empieza a programar en C.
C también permite la realización de asignaciones múltiples como la siguiente:
id_1 = id_2 = . . . = e x p r e s i o n
Las asignaciones se efectúan de derecha a izquierda. Así, en i = j = 5
1. A j se le asigna 5
2. A i se le asigna el valor de j
Si los dos operandos en una sentencia de asignación son de tipos distintos, entonces el valor del
operando de la derecha será automáticamente convertido al tipo del operando de la izquierda.
Además:
1. Un valor en coma flotante se puede truncar si se asigna a una variable de tipo entero.
2. Un valor de doble precisión puede redondearse si se asigna a una variable de coma flotante
de simple precisión.
3. Una cantidad entera puede alterarse si se asigna a una variable de tipo entero más corto o
a una variable de tipo carácter.
3.7.2. Ejemplo
Programa que convierte grados Fahrenheit a grados centígrados. Para ello se utiliza la siguiente
expresión:
C = (5/9) ∗ (F − 32)
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
float centigrados ;
float fahrenheit ;
p r i n t f ( " I n t r o d u z c a una t e m p e r at u r a en g r a d o s
p r i n t f (" % f g r a d o s f a h r e n h e i t = %f g r a d o s
c e n t i g r a d o s \n " , f a h r e n h e i t , c e n t i g r a d o s ) ;
return 0;
}
¿Qué ocurriría si se hubiera utilizado la siguiente sentencia?
c e n t i g r a d o s = 5/9 ∗ ( f a h r e n h e i t − 3 2 ) ;
i n t main ( v o i d )
{
u n s i g n e d i n t n = 0 x4325 ;
n = 0x4325
~n = 0xffffbcda
Operador Función
& AND binario
| OR binario
^ OR exclusivo binario
Las operaciones se realizan de forma independiente en cada par de bits que corresponden a cada
operando.
a b a & b a | b a ^ b
1 1 1 1 0
1 0 0 1 1
0 1 0 1 1
0 0 0 0 0
Primero se comparan los bits menos significativos, luego los siguientes bits menos significativos y
así sucesivamente.
A continuación se muestra un ejemplo de uso
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
return 0;
}
Este programa imprime: 0, 1, 11, 6
3.12. Máscaras
El enmascaramiento es un proceso en el que un patrón dado de bits se convierte en otro patrón por
medio de una operación lógica a nivel de bits. El segundo operando se llama máscara.
El siguiente programa obtiene los cuatros bits menos significativos de un número dado.
#i n c l u d e <s t d i o . h>
#d e f i n e MASCARA 0xF
i n t main ( v o i d )
{
int n;
return 0;
}
Expresión Valor
a << 1 16
a << 2 32
a << 3 64
a >> 3 1
Sentencias de control
4.1. Introducción
Los principales tipos de estructuras de programación (ver figura 4.1) son:
NO SI NO
A C C
SI
B A B A
4.2. Sentencia if
Su forma general es:
i f ( expresion )
sentencia
Si expresion es verdadera (valor mayor que 0) se ejecuta sentencia. La expresion debe estar
entre paréntesis.
En el caso de que sentencia sea compuesta, la forma general debería ser la siguiente:
SI
expresion > 0
NO
sentencia
i n t main ( v o i d )
{
i n t numero ;
/∗ l e e r e l numero ∗/
p r i n t f ( " I n t r o d u z c a un numero : " ) ;
s c a n f (" %d " , &numero ) ;
i f ( ( numero % 2 ) == 0 )
p r i n t f ( " El numero %d e s par . \ n " , numero ) ;
return 0;
i n t main ( v o i d )
{
i n t numero ;
i n t cuadrado ;
/∗ l e e r e l numero ∗/
p r i n t f ( " I n t r o d u z c a un numero : " ) ;
s c a n f (" %d " , &numero ) ;
i f ( ( numero % 2 ) == 0 ) {
cuadrado = numero ∗ numero ;
p r i n t f ( " El cuadrado de %d e s %d . \ n " , numero ,
cuadrado ) ;
}
return 0;
}
sentencia 2 sentencia 1
i n t main ( v o i d )
{
i n t numero ;
/∗ Leer e l numero ∗/
p r i n t f ( " I n t r o d u z c a un numero : " ) ;
s c a n f (" %d " , &numero ) ;
i f ( ( numero % 2 ) == 0 )
p r i n t f ( " El numero %d e s par . \ n " , numero ) ;
else
p r i n t f ( " El numero %d e s impar . \ n " , numero ) ;
return 0;
expresion 1
NO
expresion2 > 0
SI
sentencia
expresion 3
i n t main ( v o i d )
{
i n t numero ;
return 0;
}
La figura 4.5 muestra el diagrama de flujo del programa anterior.
NO
expresion2 > 0
SI
sentencia
expresion 3
Figura 4.5: Diagrama de flujo para el programa que imprime los 100 primeros números naturales
i n t main ( v o i d )
{
i n t N;
i n t suma = 0 ;
/∗ l e e r e l numero N ∗/
p r i n t f ( "N: " ) ;
s c a n f (" %d " , &N ) ;
SI
sentencia
w h i l e (N > 0 ) {
suma = suma + N;
N = N − 1; /∗ e q u i v a l e n t e a N−− ∗/
}
return 0;
}
sentencia
SI
expresion > 0
NO
Ejemplo El siguiente programa lee de forma repetida un número e indica si es par o impar. El
programa se repite mientras el numero sea distinto de cero.
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
i n t numero ;
do {
/∗ s e l e e e l numero ∗/
p r i n t f ( " I n t r o d u z c a un numero : " ) ;
s c a n f (" %d " , &numero ) ;
i f ( ( numero % 2 ) == 0 )
p r i n t f ( " El numero %d e s par . \ n " , numero ) ;
else
p r i n t f ( " El numero %d e s par . \ n " , numero ) ;
} w h i l e ( numero != 0 )
c a s e exp N:
c a s e exp M:
s e n t e n c i a N1 ;
s e n t e n c i a N2 ;
.
.
break ;
default :
s e n t e n c i a D;
.
}
La expresión expresion devuelve un valor entero (también puede ser de tipo char).
Las expresiones exp 1, . . . , exp no representan expresiones constantes de valores enteros (también
caracteres).
El diagrama de flujo de este tipo de sentencias se muestra en la figura 4.8.
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
char l e t r a ;
switch ( l e t r a ) {
c a s e ’A’ ’ :
case ’a ’ :
p r i n t f ( " Vocal %c \n " , l e t r a ) ;
break ;
c a s e ’E ’ :
case ’ e ’ :
p r i n t f ( " Vocal %c \n " , l e t r a );
break ;
case ’ I ’ :
case ’ i ’ :
p r i n t f ( " Vocal %c \n " , l e t r a );
break ;
c a s e ’O’ :
case ’o ’ :
p r i n t f ( " Vocal %c \n " , l e t r a );
break ;
c a s e ’U ’ :
case ’u ’ :
p r i n t f ( " Vocal %c \n " , l e t r a );
break ;
default :
p r i n t f ( " Consonante %c \n " , letra );
}
return 0;
}
do {
/∗ l e e r e l numero N ∗/
p r i n t f ( " I n t r o d u z c a N: " ) ;
s c a n f (" %d " , &N ) ;
suma = 0 ;
f o r ( j = 0 ; j <= N; j ++) /∗ b u c l e anidado ∗/
suma = suma + j ;
p r i n t f ( " 1 + 2 + . . . + N = %d\n " , suma ) ;
} w h i l e (N > 0 ) ; /∗ f i n d e l b u c l e do ∗/
return 0;
}
#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d b o o l . h>
i n t main ( v o i d )
{
float valor ;
f l o a t suma = 0 . 0 ;
f l o a t media ;
i n t cont ;
f o r ( c o n t = 0 ; c o n t < t o t a l ; c o n t++) {
p r i n t f (" Introduzca va lo r : " ) ;
l e i d o s = s c a n f (" % f " , &v a l o r ) ;
i f ( l e i d o s == 0 ) {
error = true ;
break ;
}
suma = suma + v a l o r ;
}
i f ( error )
p r i n t f ( " E r r o r en l a l e c t u r a de l o s d a t o s \n " ) ;
else {
i f ( t o t a l > 0) {
media = suma / t o t a l ;
p r i n t f ( " Media = %f \n " , media ) ;
}
else
p r i n t f ( " El numero de d a t o s e s 0\n " ) ;
}
return 0;
}
#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d b o o l . h>
i n t main ( v o i d )
{
float valor ;
f l o a t suma = 0 . 0 ;
f l o a t media ;
i n t cont = 0 ;
int total ;
int error = false ;
int leidos ;
w h i l e ( ( c o n t < t o t a l ) && ( ! e r r o r ) ) {
p r i n t f (" Introduzca va lo r : " ) ;
l e i d o s = s c a n f (" % f " , &v a l o r ) ;
i f ( l e i d o s == 0 )
error = true ;
else {
suma = suma + v a l o r ;
cont = cont + 1 ;
}
}
i f ( error )
p r i n t f ( " E r r o r en l a l e c t u r a de l o s d a t o s \n " ) ;
else {
i f ( t o t a l > 0){
media = suma / t o t a l ;
p r i n t f ( " Media = %f \n " , media ) ;
}
else
p r i n t f ( " El numero de d a t o s e s 0\n " ) ;
}
return 0;
}
Funciones
En este capítulo se describe el uso de las funciones en los programas escritos en C, y se presentan
las bases de la programación estructurada.
5.1. Introducción
Una función es un segmento de programa que realiza una determinada tarea. Todo programa C
consta de una o más funciones. Una de estas funciones se debe llamar main(). Todo programa comienza
su ejecución en la función main().
El uso de funciones permite la descomposición y desarrollo modular. Permite dividir un programa
en componentes más pequeños: funciones.
Ejemplo El siguiente programa que calcula el máximo de dos números, utilizando la función maximo.
#i n c l u d e <s t d i o . h>
i n t maximo ( i n t a , i n t b ) ; /∗ D e c l a r a c i o n de f u n c i o n ( p r o t o t i p o ) ∗/
i n t main ( v o i d )
{
int x , y ;
i n t max ;
return 0;
}
i n t maximo ( i n t a , i n t b ) /∗ d e f i n i c i o n de l a f u n c i o n ∗/
{
i n t max ;
void e x p l i c a c i o n ( void ) ;
i n t cuadrado_perfecto ( i n t x ) ;
i n t main ( v o i d )
{
int n;
int perfecto ;
explicacion ();
s c a n f (" %d " , &n ) ;
p e r f e c t o = cuadrado_perfecto (n ) ;
i f ( perfecto )
p r i n t f (" %d e s cuadrado p e r f e c t o . \ n " , n ) ;
else
p r i n t f (" %d no e s cuadrado p e r f e c t o . \ n " , n ) ;
return 0;
}
void e x p l i c a c i o n ( void )
{
p r i n t f ( " Este programa d i c e s i un numero " ) ;
p r i n t f ( " e s cuadrado p e r f e c t o \n " ) ;
p r i n t f ( " I n t r o d u z c a un numero : " ) ;
return ;
}
i n t cuadrado_perfecto ( i n t x )
{
int raiz ;
int perfecto ;
return ( perfecto ) ;
}
Para compilar este programa, se puede utilizar el siguiente mandato en el que se inidica que se
enlace el ejecutable con la biblioteca matemática (-lm).
g c c −Wall −o p e r f e c t o p e r f e c t o . c −lm
Los argumentos se denominan parámetros formales. Como puede apreciarse la función devuelve
un valor de tipo tipo. Si se omite tipo se considera que devuelve un int. Cuando la funcion no devuelve
ningún tipo se indica con void. De igual manera, si la función no tiene argumentos, se colocará void
entre los paréntesis, tal y como se muestra en siguiente ejemplo.
void e x p l i c a c i o n ( void )
Entre llaves se encuentra el cuerpo de la función (igual que main()), que incluye la sentencias de
la función. La sentencia return finaliza la ejecución y devuelve un valor a la función que realizó la
llamada.
return ( expresion ) ;
En C hay que tener en cuenta dos cosas relacionadas con las funciones:
La declaración o prototipo de una función especifica el nombre de la función, el valor que devuelve y
los tipos de parámetros que acepta. Esta declaración finaliza con ; y no incluye el cuerpo de la función.
No es obligatorio declarar un función pero si aconsejable, ya que permite la comprobación de errores
entre las llamadas a una función y la definición de la función correspondiente.
f l o a t potencia ( f l o a t x , int y) /∗ d e f i n i c i o n ∗/
{
int i ;
f l o a t prod = 1 ;
f o r ( i = 0 ; i < y ; i ++)
prod = prod ∗ x ;
r e t u r n ( prod ) ;
}
Constantes.
Variables simples.
Expresiones complejas
Pero deben ser del mismo tipo de datos que el argumento formal correspondiente. Cuando se pasa un
valor a una función se copia el argumento real en el argumento formal. Se puede modificar el argumento
formal dentro de la función, pero el valor del argumento real no cambia: paso de argumentos por
valor. En C todos los parámetros se pasan por valor.
El proceso de llamada a una función se puede apreciar en la figura 5.1.
Ejemplo El siguiente programa que lee el número de caracteres introducidos hasta fin de fichero.
#i n c l u d e <s t d i o . h>
i n t cuenta_caracteres ( void ) ;
i n t main ( v o i d )
{
i n t num_car ;
num_car = c u e n t a _ c a r a c t e r e s ( ) ;
p r i n t f ( " Hay %d c a r a c t e r e s \n " , num_car ) ;
return 0;
x = 2;
max toma el y = 4;
valor 8 max = maximo(x, 2*y);
3 .
.
llamada a la funcion
1 a toma el valor de x
b toma el valor de y*2
a=2
int maximo(int a, int b) b=8
{
int max;
if (a > b)
2 max = a;
else
la funcion max = b;
devuelve el valor
de max (8) return(max);
}
i n t cuenta_caracteres ( void )
{
char c ;
i n t cont = 0 ;
c = getchar ( ) ;
w h i l e ( c != EOF)
{
cont = cont + 1 ;
c = getchar ( ) ;
}
return ( cont ) ;
}
5.5. Recursividad
Una función recursiva es aquella que se llama a sí misma de forma repetida hasta que se cumpla
alguna condición.
Un ejemplo de función recursiva es el factorial de un número, cuya definición se muestra a conti-
nuación.
5.6. Macros
La sentencia #define se puede utilizar para definir macros. Una macro es un identificador equi-
valente a una expresión, sentencia o grupo de sentencias.
Ejemplo el siguiente programa calcula el máximo de dos números utilizando una macro denominada
maximo.
#i n c l u d e <s t d i o . h>
#d e f i n e maximo ( a , b ) (( a > b) ? a : b)
i n t main ( v o i d )
{
int x , y ;
i n t max ;
return 0;
}
El preprocesador sustituye todas las referencias a la macro que aparezcan dentro de un programa
antes de realizar la compilación, por lo que es importante que no haya blancos entre el identificador y
el paréntesis izquierdo. Así el código anterior se sustituye por el siguiente:
i n t main ( v o i d )
{
int x , y ;
i n t max ;
return 0;
}
Utilizando macros no se produce llamada a función por lo tanto el programa ejecuta con mayor
velocidad. Sin embargio, se repite el código en cada uso de la macro con lo que se obtiene un código
objeto más grande. Hay que tener siempre en mente que una macro no es una llamada a función.
B A B A
i n t main ( v o i d )
{
float a , b;
float h;
int error ;
return 0;
}
f l o a t hipotenusa ( f l o a t a , f l o a t b)
{
float h;
h = s q r t ( pow ( a , 2 ) + pow ( b , 2 ) ) ;
return (h ) ;
}
Un char 1 byte.
Un int 4 bytes (en un computador de 32 bits).
A cada byte o palabra se accede por su dirección. Si x es una variable que representa un determinado
dato el compilador reservará los bytes necesarios para representar x (4 bytes si es de tipo int).
Direcciones 0 Contenido
1
2
6.2. Punteros
Si x es una variable, &x representa la dirección de memoria de x, es decir, la dirección de memoria
donde se almacena el valor de la variable x. Al operador & se le denomina operador de dirección.
Un puntero es una variable que almacena la dirección de otro objeto (variable, función, . . . ).
i n t main ( v o i d )
{
int x ; /∗ v a r i a b l e de t i p o e n t e r o ∗/
int y ; /∗ v a r i a b l e de t i p o e n t e r o ∗/
i n t ∗px ; /∗ v a r i a b l e de t i p o
p u n t e r o a e n t e r o ∗/
x = 5;
px = &x ; /∗ a s i g n a a px l a d i r e c c i o n de x ∗/
y = ∗px ; /∗ a s i g n a a y e l c o n t e n i d o de l a
d i r e c c i o n almacenada en px ∗/
return 0;
}
La expresión *px representa el contenido almacenado en la dirección a la que apunta px. El operador
* se denomina operador de indirección y opera sobre una variable de tipo puntero (ver figura 6.2).
Es importante recordar que un puntero representa la dirección de memoria del objeto al que apunta,
NO su valor.
int x;
int y; 1 x = 5;
int *px;
px = &x;
2 (&x) 1200 5
Ejemplos
int *numero;
float *p;
char *letra;
La variable variable_ptr sólo puede almacenar la dirección de variables de tipo tipo_dato. Para
usar un puntero se debe estar seguro de que apunta a una dirección de memoria correcta.
Es importante recordar que un puntero no reserva memoria. El siguiente fragmento de programa
es incorrecto:
i n t ∗p ;
∗p = 5 ;
letra = ’a ’ ;
p = &l e t r a ;
En este caso, p almacena la dirección de letra, es decir, apunta a una dirección conocida. La
variable *p representa el valor almacenado en letra (i’a’).
Ejemplo El siguiente ejemplo muestra como pueden asignarse punteros del mismo tipo entre sí.
float n1 ;
float n2 ;
float ∗p1 ;
float ∗p2 ;
n1 = 4 . 0 ;
p1 = &n1 ;
p2 = p1 ;
n2 = ∗p2 ;
n1 = ∗p1 + ∗p2 ;
v o i d f u n c i o n ( i n t a , i n t b ) ; /∗ p r o t o t i p o ∗/
i n t main ( v o i d )
{
int x = 2;
int y = 5;
funcion (x , y ) ;
return 0;
}
void funcion ( i n t a , i n t b)
{
a = 0;
b = 0;
return ;
}
La figura 6.3 ilustra el paso de parámetros por valor.
Ejemplo El siguiente ejemplo muestra como se puede simular el paso de parámetros por referencia
en C.
funcion(x, y);
llamada a la función
void funcion(int a, int b) a toma el valor de x
{ b toma el valor de y
a = 0;
b = 0; a=2
2 b=5
printf("Dentro a = %d, b = %d\n", a, b);
la función
finaliza return;
}
#i n c l u d e <s t d i o . h>
v o i d f u n c i o n ( i n t ∗a , i n t ∗b ) ; /∗ p r o t o t i p o ∗/
i n t main ( v o i d )
{
int x = 2;
int y = 5;
f u n c i o n (&x , &y ) ;
return 0;
}
v o i d f u n c i o n ( i n t ∗a , i n t ∗b )
{
∗a = 0 ;
∗b = 0 ;
main()
{
int x = 2;
int y = 5;
funcion(&x, &y);
1
printf("Despues x = %d, y = %d\n", x, y);
} Se pasa la dirección
de x e y
a = &x b = &y
a &x b &y
v o i d swap ( i n t ∗a , i n t ∗b ) ; /∗ p r o t o t i p o ∗/
i n t ain ( void )
{
int x = 2;
int y = 5;
return 0;
}
v o i d swap ( i n t ∗a , i n t ∗b )
{
i n t temp ;
return ;
}
i n t main ( v o i d )
{
f l o a t num ;
return 0;
}
El tipo de dato se refiere al tipo de información que representa la variable (int, char, . . . ). El tipo
de almacenamiento se refiere a su permanencia y a su ámbito.
El ámbito de una variable es la porción del programa en la cual se reconoce la variable. Según el
ámbito, las variables pueden ser:
Variables locales.
Variables globales.
Variables automáticas.
Variables estáticas.
Variables externas.
i n t main ( v o i d )
{
int a = 1; /∗ v a r i a b l e l o c a l ∗/
int b = 2; /∗ v a r i a b l e l o c a l ∗/
funcion1 ( ) ;
p r i n t f ( " a = %d , b = %d \n " , a , b ) ;
return 0;
}
void funcion1 ( void )
{
int a = 3; /∗ v a r i a b l e l o c a l ∗/
int c = 4; /∗ v a r i a b l e l o c a l ∗/
p r i n t f ( " a = %d , c = %d \n " , a , c ) ;
return ;
}
i n t main ( v o i d )
{
int b = 2; /∗ v a r i a b l e l o c a l ∗/
funcion1 ( ) ;
p r i n t f ( " a = %d , b = %d \n " , a , b ) ;
return 0;
}
void funcion1 ( void )
{
int c = 4; /∗ v a r i a b l e l o c a l ∗/
p r i n t f ( " a = %d , c = %d \n " , a , c ) ;
return ;
}
Las variables globales mantienen los valores que se les asignan en las funciones. Es mejor hacer uso
de variables locales para evitar efectos secundarios o laterales, como el que se muestra en el siguiente
ejemplo.
#i n c l u d e <s t d i o . h>
void funcion1 ( void ) ;
int a ; /∗ v a r i a b l e g l o b a l ∗/
i n t main ( v o i d )
{
return 0;
}
void funcion1 ( void )
{
a = 1000;
return ;
}
i n t main ( v o i d )
{
auto i n t v a l o r ; /∗ e q u i v a l e n t e a i n t v a l o r ∗/
valor = 5;
p r i n t f ( " El v a l o r e s %d\n " , v a l o r ) ;
return 0;
}
Ejemplo El siguiente ejemplo hace uso de una variable de tipo static para contabilizar el número
de veces que se invoca a una función.
#i n c l u d e <s t d i o . h>
void funcion ( void ) ;
i n t main ( v o i d )
{
funcion ( ) ;
funcion ( ) ;
funcion ( ) ;
return 0;
}
veces = veces + 1;
p r i n t f ( " Se ha llamado %d v e c e s a f u n c i o n \n " , veces ) ;
return ;
}
i n t main ( v o i d )
{
register int j ;
f o r ( j = 0 ; j < 1 0 ; j ++)
p r i n t f ( " Contador = %d\n " , j ) ;
return 0;
}
Ejemplo Considere un programa compuesto por dos módulos. El Módulo principal que contiene la
función main (almacenado en el fichero main.c) es el siguiente:
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
funcion ( ) ;
p r i n t f ( " Valor = %d\n " , v a l o r ) ;
return 0;
}
Cadenas de caracteres
7.1. Introducción
Una cadena de caracteres es un conjunto o vector de caracteres.
Por ejemplo:
i n t main ( v o i d )
{
c h a r cadena [ ] = " h o l a " ;
return 0;
}
En el programa anterior, cadena[i] representa el i-ésimo caracter de la cadena.
La declaración
c h a r cadena [ 8 0 ] ;
Declara una cadena de caracteres denominada cadena compuesta por 80 caracteres incluido el
carácter nulo.
La declaración
c h a r cadena [ 4 ] = " Hola " ;
Declara una cadena de exactamente 4 caracteres que no incluye el carácter nulo por lo que no se
tratará de forma correcta como una cadena de caracteres.
i n t main ( v o i d )
{
c h a r cadena [TAM_CADENA] ;
return 0;
}
La función de biblioteca scanf deja de buscar cuando encuentra un blanco. Por lo tanto si se
introduce
Hola a todos
solo se leerá Hola. No es necesario el operador de dirección (&) ya que cadena representa de forma
automática la dirección de comienzo.
#d e f i n e TAM_LINEA 80
void l e e r _ l i n e a ( char l i n e a [ ] ) ;
i n t main ( v o i d )
{
c h a r l i n e a [TAM_LINEA ] ;
leer_linea ( linea );
p r i n t f ( " La l i n e a e s : " ) ;
printf ( linea );
p r i n t f ("\n " ) ;
return 0;
}
void l e e r _ l i n e a ( char l i n e a [ ] )
{
7.6. Ejemplos
El siguiente programa lee líneas hasta fin de fichero y cuenta el número de caracteres de cada línea:
#i n c l u d e <s t d i o . h>
#d e f i n e TAM_LINEA 80
i n t l o n g i t u d ( c h a r cadena [ ] ) ;
i n t main ( v o i d )
{
c h a r l i n e a [TAM_LINEA ] ;
i n t num_car ;
w h i l e ( g e t s ( l i n e a ) != NULL) {
num_car = l o n g i t u d ( l i n e a ) ;
p r i n t f ( " Esta l i n e a t i e n e %d c a r a c t e r e s \n " , num_car ) ;
}
return 0;
}
i n t l o n g i t u d ( c h a r cadena [ ] )
{
int j = 0;
w h i l e ( cadena [ j ] != ’ \ 0 ’ )
j ++;
return ( j ) ;
}
El siguiente programa lee una línea en minúsculas y la convierte a mayúsculas.
#i n c l u d e <s t d i o . h>
#i n c l u d e <c t y p e . h>
#d e f i n e TAM_LINEA 80
return 0;
}
w h i l e ( min [ j ] != ’ \ 0 ’ ) {
may [ j ] = t o u p p e r ( min [ j ] ) ;
j ++;
}
may [ j ] = ’ \ 0 ’ ;
return ;
}
En <stdlib.h> se declaran:
Función Significado
atoi Convierte una cadena a un entero (int)
atol Convierte una cadena a un entero largo (long)
atof Convierte una cadena a un real (double)
Vectores y matrices
8.1. Introducción
Un vector o array es un conjunto de valores, todos del mismo tipo, a los que se da un nombre
común, distinguiendo cada uno de ellos por su índice.
V = (V0 , V1 , . . . Vn )
donde
Por ejemplo:
i n t v_numeros [ 2 0 ] ;
float n[12];
char v e c t o r _ l e t r a s [ 5 ] ;
En C el primer índice del vector es 0. Sólo se puede asignar valores iniciales a vectores estáticos y
globales.
i n t n [ 5 ] = {1 , 2 , 18 , 24 , 3};
#d e f i n e TAM_VECTOR 10
i n t main ( v o i d )
{
i n t vector_a [TAM_VECTOR] ;
i n t vector_b [TAM_VECTOR] ;
i n t j ; /∗ v a r i a b l e u t i l i z a d a como i n d i c e ∗/
/∗ l e e r e l v e c t o r a ∗/
f o r ( j = 0 ; j < TAM_VECTOR; j ++) { {
p r i n t f ( " Elemento %d : " , j ) ;
s c a n f (" %d " , &vector_a [ j ] ) ;
}
/∗ c o p i a r e l v e c t o r ∗/
f o r ( j = 0 ; j < TAM_VECTOR; j ++)
vector_b [ j ] = vector_a [ j ] ;
/∗ e s c r i b i r e l v e c t o r b ∗/
f o r ( j = 0 ; j < TAM_VECTOR; j ++)
p r i n t f ( " El e l e m e n t o %d e s %d \n " , j , vector_b [ j ] ) ;
return 0;
}
i n t main ( v o i d )
{
i n t v_numeros [MAX_TAM] ;
i n t media ;
l e e r _ v e c t o r ( v_numeros ) ;
media = media_vector ( v_numeros ) ;
return 0;
}
i n t media_vector ( i n t v e c t o r [ ] )
{
int j ;
i n t media = 0 ;
r e t u r n ( media /MAX_TAM) ;
}
v e c t o r == &v e c t o r [ 0 ]
El nombre del vector es realmente un puntero al primer elemento del vector.
v o i d l e e r _ v e c t o r ( i n t v e c t o r [ ] , i n t dim ) ;
i n t media_vector ( i n t v e c t o r [ ] , i n t dim ) ;
i n t main ( v o i d )
{
i n t ∗v_numeros ;
i n t d i m e n s i on ;
i n t media ;
v_numeros = ( i n t ∗ ) m a l l o c ( d i m e n s i on ∗ s i z e o f ( i n t ) ) ;
l e e r _ v e c t o r ( v_numeros , d i m e n s i on ) ;
media = media_vector ( v_numeros , d i m e n s i on ) ;
return 0;
}
i n t media_vector ( i n t v e c t o r [ ] , i n t dim )
{
int j ;
i n t media = 0 ;
r e t u r n ( media /dim ) ;
}
El programa anterior es equivalente al siguiente:
#i n c l u d e <s t d i o . h>
#i n c l u d e < s t d l i b . h>
i n t ∗ c r e a r _ v e c t o r ( i n t dim ) ;
v o i d l e e r _ v e c t o r ( i n t ∗ v e c t o r , i n t dim ) ;
i n t media_vector ( i n t ∗ v e c t o r , i n t dim ) ;
i n t main ( v o i d )
{
i n t ∗v_numeros ;
i n t d i m e n s i on ;
i n t media ;
v_numeros = c r e a r _ v e c t o r ( d i m e n s i on ) ;
l e e r _ v e c t o r ( v_numeros , d i m e n s i on ) ;
media = media_vector ( v_numeros , d i m e n s i on ) ;
return 0;
}
vec = ( i n t ∗ ) m a l l o c ( dim∗ s i z e o f ( i n t ) ) ;
r e t u r n ( vec ) ;
}
v o i d l e e r _ v e c t o r ( i n t ∗ v e c t o r , i n t dim )
{
int j ;
r e t u r n ( media /dim ) ;
}
Una cadena de caracteres es un vector de caracteres. Cada elemento del vector almacena un caracter.
La siguiente función copia una cadena en otra:
f l o a t ∗∗ c r e a r _ m a t r i z ( i n t f i l , i n t col );
v o i d d e s t r u i r _ m a t r i z ( f l o a t ∗∗mat , int f i l );
v o i d l e e r _ m a t r i z ( f l o a t ∗∗mat , i n t fil , int col );
v o i d imprimir_matriz ( f l o a t ∗∗mat , int f i l , int col );
i n t main ( v o i d )
{
f l o a t ∗∗ m a t r i z ;
int f i l , col ;
matriz = crear_matriz ( f i l , c o l ) ;
l e e r _ m a t r i z ( matriz , f i l , c o l ) ;
imprimir_matriz ( matriz , f i l , c o l ) ;
d e s t r u i r _ m a t r i z ( matriz , f i l ) ;
return 0;
}
f l o a t ∗∗ c r e a r _ m a t r i z ( i n t f i l , i n t c o l )
{
int j ;
f l o a t ∗∗mat ;
mat = ( f l o a t ∗ ∗ ) m a l l o c ( f i l ∗ s i z e o f ( f l o a t ∗ ) ) ;
f o r ( j = 0 ; j < f i l ; j ++)
mat [ j ] = ( f l o a t ∗ ) m a l l o c ( c o l ∗ s i z e o f ( f l o a t ) ) ;
r e t u r n ( mat ) ;
}
v o i d d e s t r u i r _ m a t r i z ( f l o a t ∗∗mat , i n t f i l )
{
int j ;
f o r ( j = 0 ; j < f i l ; j ++)
f r e e ( mat [ j ] ) ;
f r e e ( mat ) ;
return ;
}
v o i d l e e r _ m a t r i z ( f l o a t ∗∗mat , i n t f i l , i n t c o l )
{
int i , j ;
f l o a t dato ;
f o r ( i = 0 ; i < f i l ; i ++)
f o r ( j = 0 ; j < c o l ; j ++)
{
p r i n t f ( " Elemento %d %d : " , i , j ) ;
s c a n f (" % f " , &dato ) ;
∗ ( ∗ ( mat + i ) + j ) = dato ;
}
return ;
}
v o i d imprimir_matriz ( f l o a t ∗∗mat , i n t f i l , i n t c o l )
Estructuras de datos
9.1.1. Ejemplos
struct fecha {
int mes ;
int dia ;
int anno ;
};
a y e r = hoy ;
Pero no se pueden comparar estructuras.
#i n c l u d e <s t d i o . h>
struct fecha {
int dia ;
int mes ;
int anno ;
};
s t r u c t cuenta {
i n t cuenta_no ;
c h a r nombre [ 8 0 ] ;
f l o a t saldo ;
s t r u c t f e c h a ultimo_pago ;
};
i n t main ( v o i d )
{
s t r u c t c u e n t a c1 , c2 ;
/∗ r e l l e n a l a e s t r u c t u r a c1 ∗/
c1 . cuenta_no = 2 ;
s t r c p y ( c1 . nombre , " Pepe " ) ;
c1 . s a l d o = 1 0 0 0 0 0 ;
c1 . ultimo_pago . d i a = 1 2 ;
c1 . ultimo_pago . mes = 5 ;
c1 . ultimo_pago . anno = 1 9 9 7 ;
/∗ a s i g n a c i o n de e s t r u c t u r a s ∗/
c2 = c1 ;
return 0;
}
return ( f ) ;
}
En el programa principal:
i n t main ( v o i d )
{
s t r u c t f e c h a fecha_de_hoy ;
fecha_de_hoy = l e e r _ f e c h a ( ) ;
return 0;
}
i n t main ( v o i d )
{
s t r u c t punto punto_1 ;
s t r u c t punto ∗punto_2 ;
punto_1 . x = 2 . 0 ;
punto_1 . y = 4 . 0 ;
punto_2 = &punto_1 ;
return 0;
}
En una variable de tipo puntero a estructura los miembros se acceden con ->
Se pueden pasar punteros a funciones. En este caso los miembros de la estructura se pueden modi-
ficar dentro de la función (se simula el paso por referencia).
Un puntero a una estructura no reserva memoria, para los miembros de la estructura. Solo se reserva
espacio para el propio puntero, que solo permite almacenar una dirección de memoria.
v o i d l e e r_p u n t o ( s t r u c t punto ∗p ) ;
v o i d imprimir_punto ( s t r u c t punto p ) ;
i n t main ( v o i d )
{
s t r u c t punto ∗p1 ;
p1 = ( s t r u c t punto ∗ ) m a l l o c ( s i z e o f ( s t r u c t punto ) ) ;
l e e r _p u n t o ( p1 ) ;
return 0;
}
v o i d l e e r_p u n t o ( s t r u c t punto ∗p )
{
p r i n t f (" x = " ) ;
s c a n f (" % f " , &(p−>x ) ) ;
p r i n t f (" y = " ) ;
s c a n f (" % f " , &(p−>y ) ) ;
return ;
}
v o i d imprimir_punto ( s t r u c t punto p )
{
p r i n t f ( " x = %f \n " , p . x ) ;
p r i n t f ( " y = %f \n " , p . y ) ;
return ;
}
f o r ( j = 0 ; j < 1 0 ; j ++) {
p r i n t f ( "x_ %d = %f \n " , j , vector_puntos [ j ] . x ) ;
p r i n t f ( "y_ %d = %f \n " , j , vector_puntos [ j ] . y ) ;
}
return 0;
}
9.6. Uniones
Una unión contiene miembros cuyos tipos de datos pueden ser diferentes (igual que las estructuras).
Su declaración es similar a las estructuras:
Todos los miembros que componen la unión comparten la misma zona de memoria Una variable de
tipo unión sólo almacena el valor de uno de sus miembros.
#i n c l u d e <s t d i o . h>
#i n c l u d e < s t d l i b . h>
union numero{
int entero ;
float real ;
};
i n t main ( v o i d )
{
union numero num ;
/∗ l e e r un e n t e r o e i m p r i m i r l o ∗/
p r i n t f ( " Entero : " ) ;
s c a n f (" %d " , &(num . e n t e r o ) ) ;
p r i n t f ( " El e n t e r o e s %d\n " , num . e n t e r o ) ;
/∗ l e e r un r e a l e i m p r i m i r l o ∗/
p r i n t f ( " Real : " ) ;
s c a n f (" % f " , &(num . r e a l ) ) ;
p r i n t f ( " El e n t e r o e s %f \n " , num . r e a l ) ;
return 0;
}
Por ejemplo:
return 0;
}
letra c ;
tipo_punto p ;
s t r u c t elemento {
i n t num ;
s t r u c t elemento ∗ e n l a c e ;
};
t y p e d e f s t r u c t e l e m e n t o lista_num ;
lista_num ∗ l i s t a ;
Una lista es una estructura de datos dinámica que puede crecer según las necesidades del programa
(a diferencia de los vectores).
#i n c l u d e <s t d i o . h>
#i n c l u d e < s t d l i b . h>
2 6 8 NULL
lista
2. insertar(&lista, 2);
p1 2 NULL
lista
3. insertar(&lista, 3);
lista 2 3 NULL
p1
p2
4. Insertar(&lista, 5);
lista 2 3
p1
p2 5 NULL
s t r u c t elemento {
i n t num ;
s t r u c t elemento ∗ e n l a c e ;
};
t y p e d e f s t r u c t e l e m e n t o lista_num ;
i n t main ( v o i d )
{
lista_num ∗ l i s t a = NULL;
i n t numero ;
p1 = ∗ p t r ;
i f ( p1 == NULL) {
p1 = ( lista_num ∗ ) m a l l o c ( s i z e o f ( lista_num ) ) ;
p1−>num = num ;
p1−>e n l a c e = NULL;
∗ p t r = p1 ;
}
else
{
w h i l e ( p1−>e n l a c e != NULL)
p1 = p1−>e n l a c e ;
p2 = ( lista_num ∗ ) m a l l o c ( s i z e o f ( lista_num ) ) ;
p2−>num = num ;
p2−>e n l a c e = NULL;
p1−>e n l a c e = p2 ;
}
return ;
}
v o i d m os t rar ( lista_num ∗ p t r )
{
w h i l e ( p t r != NULL) {
p r i n t f (" %d \n " , ptr−>num ) ;
p t r = ptr−>e n l a c e ;
}
return ;
}
v o i d b o r r a r ( lista_num ∗ p t r )
{
l i s t a _ n u m e r o s ∗ aux ;
w h i l e ( p t r != NULL) {
aux = ptr−>e n l a c e ;
f r e e ( ptr ) ;
p t r = aux ;
}
Entrada/salida
Modo Significado
"r" Abre un archivo existente para lectura.
"w" Abre un nuevo archivo para escritura. Si existe el archivo se borra
su contenido. Si no existe se crea.
"a" Abre un archivo existente para añadir datos al final. Si no existe
se crea.
"r+" Abre un archivo existente para lectura y escritura.
"w+" Abre un archivo nuevo para escritura y lectura. Si existe lo borra.
Si no existe lo crea.
"a+" Abre un archivo para leer y añadir.
#i n c l u d e <s t d i o . h>
i n t main ( v o i d )
{
FILE ∗ d e s c ;
/∗ a l f i n a l s e c i e r r a ∗/
f c l o s e ( desc ) ;
}
return 0;
}
i n t main ( v o i d )
{
FILE ∗ f e n t ;
FILE ∗ f s a l ;
char car ;
fclose ( fent );
fclose ( fsal );
return 0;
}
argc indica el número de argumentos que se pasa incluido el nombre del programa.
Ejemplo El siguiente programa recibe como argumento el nombre de dos archivos compuestos de
números enteros:
copiar_enteros archivo_entrada archivo_salida
el programa copia archivo_entrada en archivo_salida.
#i n c l u d e <s t d i o . h>
i f ( a r g c != 3 ) {
p r i n t f ( " Uso : %s f i c h _ e n t r a d a f i c h _ s a l i d a \n " , argv [ 0 ] ) ;
r e t u r n −1;
}
f s a l = f o p e n ( argv [ 2 ] , "w" ) ;
i f ( f s a l == NULL) {
p r i n t f ( " E r r o r c r e an d o e n t r a d a . t x t \n " ) ;
close ( fent );
r e t u r n −1;
}
fclose ( fent );
fclose ( fsal );
return 0;
}
Aspectos avanzados
#i n c l u d e <s t d i o . h>
struct registro {
unsigned a : 2;
unsigned b : 4;
unsigned c : 1;
unsigned d : 1;
};
i n t main ( v o i d )
{
struct registro r ;
r . a = 5; r . b = 2; r . c = 1; r . d = 0;
p r i n t f (" %d %d %d %d\n " , r . a , r . b , r . c , r . c ) ;
p r i n t f ( " r r e q u i e r e %d\n " , s i z e o f ( r ) ) ;
#d e f i n e FALSE 0
#d e f i n e TRUE 1
i n t sumar ( i n t a , i n t b )
{
r e t u r n ( a+b ) ;
}
i n t main ( v o i d )
{
i n t (∗ operacion ) ( i n t a , i n t b ) ;
i n t a , b , oper , e r r o r , r e s ;
p r i n t f (" a , b : " ) ;
s c a n f (" %d %d " , &a , &b ) ;
e r r o r = FALSE ;
switch ( oper ) {
c a s e 0 : o p e r a c i o n = sumar ;
break ;
case 1: operacion = restar ;
break ;
d e f a u l t : e r r o r = TRUE;
}
return 0;
}
i n t sumar ( i n t a , i n t b )
{
r e t u r n ( a+b ) ;
}
i n t e j e c u t a r ( i n t (∗ f ) ( i n t a , i n t b ) , i n t a , i n t b)
{
int res ;
r e s = (∗ f ) ( a , b ) ;
return ( res ) ;
}
i n t main ( v o i d )
{
i n t a , b , oper , e r r o r , r e s ;
p r i n t f (" a , b : " ) ;
s c a n f (" %d %d " , &a , &b ) ;
p r i n t f ( " Operacion (0=suma , 1= r e s t a ) : " ) ;
s c a n f (" %d " , &o p e r ) ;
e r r o r = FALSE ;
switch ( oper ) {
c a s e 0 : r e s = e j e c u t a r ( sumar , a , b ) ; break ;
return 0;
}
va_end, debería ser llamada una vez procesados todos los argumentos.
v a _ s t a r t ( ap , c o n t a d o r ) ;
f o r ( i = 0 ; i < c o n t a d o r ; i ++) {
a r g = va_arg ( ap , i n t ) ;
t o t a l = t o t a l + arg ;
}
va_end ( ap ) ;
return ( total ) ;
}
v a _ s t a r t ( ap , s 1 ) ;
p r i n t f (" %s \n " , s 1 ) ;
w h i l e ( ( a r g = va_arg ( ap , c h a r ∗ ) ) != NULL)
p r i n t f (" %s \n " , a r g ) ;
va_end ( ap ) ;
return ;
}
i n t main ( v o i d )
{
v a _ s t a r t ( ap , a r g s ) ;
w h i l e ( ( t i p o = ∗ a r g s++) != NULO) {
switch ( tipo ) {
c a s e INTARG:
p r i n t f ( " i n t %d\n " , va_arg ( ap , i n t ) ) ;
break ;
c a s e FLOATARG:
p r i n t f ( " f l o a t %f \n " , va_arg ( ap , d o u b l e ) ) ;
break ;
default :
p r i n t f ( " Tipo d e s c o n o c i d o \n " ) ;
}
}
i n t main ( v o i d )
{
enum t i p o s _ a r g a r g s [ 6 ] = {INTARG,
INTARG,
FLOATARG,
INTARG,
NULO} ;
m i p r i n t (& a r g s [ 0 ] , 5 , 2 , 3 . 4 , 6 ) ;
return 0;
}
#i n c l u d e <s t d i o . h>
#i n c l u d e <s t d a r g . h>
i n t sumar ( i n t contador , . . . )
{
v a _ l i s t ap ;
i n t arg ;
int total = 0;
int i = 0;
v a _ s t a r t ( ap , c o n t a d o r ) ;
f o r ( i = 0 ; i < c o n t a d o r ; i ++){
a r g = va_arg ( ap , i n t ) ;
#i f d e f DEBUG
p r i n t f ( " t o t a l = %d\n " , t o t a l ) ;
p r i n t f ( " a r g = %d\n " , a r g ) ;
t o t a l = t o t a l + arg ;
}
va_end ( ap ) ;
return ( total ) ;
}
i n t main ( v o i d )
{
i n t suma ;
suma = sumar ( 4 , 1 , 2 , 3 , 4 ) ;
p r i n t f ( " suma = %d\n " , suma ) ;
return 0;
}
-v Verbose
Como ejemplo:
Creación de una biblioteca con objetos que manejan distintas estructuras de datos
a r −rv $HOME/ l i b / l i b e s t . a p i l a . o l i s t a . o
a r −rv $HOME/ l i b / l i b e s t . a a r b o l . o hash . o
next: Ejecuta la siguiente línea. Si se trata de una llamada a función, la ejecuta completa
step: Ejecuta la siguiente línea. Si se trata de una llamada a función, ejecuta sólo la llamada y
se para al principio de la misma.
Sin argumentos make activa la primera regla. Se pueden definir también macros:
NOMBRE=VALOR
Para acceder al valor de una macro:
$ (NOMBRE) o ${NOMBRE}
Existen macros especiales: $@ corresponde con el nombre del objetivo.
Se pueden especificar reglas basadas en la extensión de un fichero. Algunas de ellas están predefi-
nidas (p.ej. la que genera el .o a partir del .c).
CC=g c c
CFLAGS=−g
OBJS2=p r a c 2 . o aux2 . o
a l l : prac1 prac2
p r a c 1 : p r a c 1 . o aux1 . o
g c c −g −o p r a c 1 p r a c 1 . o aux1 . o
p r a c 2 : $ (OBJS2)
${CC} ${CFLAGS} −o $@ ${OBJS2}
cleano :
rm −f p r a c 1 . o aux1 . o ${OBJS2}
<complex.h>: define macros y declara funciones para trabajar con números complejos.
<ctype.h>: declara funciones para clasificar y trabajar con caracteres. Por ejemplo: saber si un
carácter es un número, un blanco, etc.
<stdargs.h>: Permite trabajar con listas de argumentos cuyo número y tipo se desconoce en la
llamada a la función.
<stdlib.h>: declara funciones de utilidad general, que permite realizar, por ejemplo, conversiones
entre números y caracteres, números psuedoaleatorios, procesar cadenas de caracteres, funciones
de gestión de memoria (como malloc() y free()), etc.
NAME
cos, cosf, cosl - cosine function
SYNOPSIS
#include <math.h>
DESCRIPTION
The cos() function returns the cosine of x, where x is given in radians.
RETURN VALUE
On success, these functions return the cosine of x.
ERRORS
See math_error(7) for information on how to determine
whether an error has occurred when calling these functions.
En esta salida se indica el archivo de cabecera a incluir (<math.h>) y cómo debe realizarse el enlace
(Link with -lm). Además se muestra la descipción de la función, el valor que devuelve y los posibles
errores.