Вы находитесь на странице: 1из 194

MANUAL

DE
LENGUAJE “C”
Lenguaje “C” Indice

INTRODUCCION: EL LENGUAJE "C".

I.1. ¿Qué es el "C"? ................................................................................................................ 1


I.2. Virtudes del "C" .............................................................................................................. 1
I.3. Futuro del lenguaje "C" ................................................................................................... 2
I.4. Utilización del "C" ........................................................................................................... 2

CAPITULO 1: ELEMENTOS DEL "C"

1.1. Introducción .................................................................................................................... 3


1.2. Conjunto de caracteres .................................................................................................... 3
1.2.1. Letras y dígitos .................................................................................................. 3
1.2.2. Caracteres espacio .............................................................................................. 3
1.2.3. Caracteres de puntuación y especiales ............................................................... 3
1.2.4. Secuencias de escape ......................................................................................... 4
1.2.5. Operadores ......................................................................................................... 4
1.3. Constantes ....................................................................................................................... 6
1.3.1. Constantes enteras .............................................................................................. 6
1.3.2. Constantes de punto flotante .............................................................................. 6
1.3.3. Constantes carácter ............................................................................................ 7
1.3.4. Constantes cadena .............................................................................................. 7
1.4. Identificadores ................................................................................................................ 8
1.5. Palabras clave ................................................................................................................. 8
1.6. Comentarios .................................................................................................................... 9

CAPITULO 2: ESTRUCTURA DE UN PROGRAMA "C"

2.1. Introducción .................................................................................................................... 11


2.2. Usos y utilidades de printf() y scanf() ............................................................................. 11
2.2.1. Utilización de printf() ........................................................................................ 11
2.2.2. Modificadores de especificaciones de conversión en printf() ............................. 13
2.2.3. Utilización de printf() para efectuar conversiones .............................................. 13
2.2.4. Uso de scanf() .................................................................................................... 14
2.3. Programa fuente .............................................................................................................. 14
2.4. Ficheros fuente ................................................................................................................ 15
2.5. Ejecución de un programa ............................................................................................... 16
2.6. Tiempo de vida y visibilidad en "C" ............................................................................... 17

CAPITULO 3: VARIABLES Y TIPOS EN "C"

3.1. Introducción .................................................................................................................... 19


3.2. Tipos ............................................................................................................................... 19
3.3. Declaradores ................................................................................................................... 22
3.4. Declaración de variables ................................................................................................. 22
3.4.1. Declaración de variables simples ....................................................................... 22
3.4.2. Declaración de enumeraciones ........................................................................... 23
3.4.3. Declaración de estructuras ................................................................................. 25
3.4.4. Declaración de uniones ...................................................................................... 27
3.4.5. Declaración de arrays ......................................................................................... 29
3.4.6. Declaración de punteros ..................................................................................... 31
3.5. Declaración de funciones ................................................................................................ 33

Este manual ha sido desarrollado por el departamento técnico de DELTA PC. I


Indice Lenguaje “C”

3.6. Clases de almacenamiento .............................................................................................. 36


3.6.1. Declaración de variables a Nivel Externo .......................................................... 36
3.6.2. Declaración de variables a Nivel Interno ........................................................... 38
3.6.3. Declaración de funciones ................................................................................... 39
3.6.4. Declaraciones complejas .................................................................................... 39
3.7. Inicialización ................................................................................................................... 40
3.7.1. Inicialización básica y de punteros ..................................................................... 41
3.7.2. Tipos agregados. Arrays, estructuras y uniones ................................................. 41
3.7.3. Inicialización de cadenas ................................................................................... 42
3.8. Declaración de tipos ........................................................................................................ 43
3.8.1. Estructuras, uniones y enumeraciones ................................................................ 43
3.8.2. Declaraciones con typedef ................................................................................. 43
3.9. Nombres de tipos ............................................................................................................ 44

CAPITULO 4: OPERADORES Y EXPRESIONES.

4.1. Criterio de precedencia de los operadores ....................................................................... 47


4.2. Expresiones con operadores ............................................................................................ 48
4.2.1. Operador incremento ......................................................................................... 48
4.2.2. Operador decremento ......................................................................................... 50
4.2.3. Precedencia de los operadores incremento y decremento .................................. 50
4.3. Expresiones de tipo cast .................................................................................................. 51
4.4. Operadores binarios ........................................................................................................ 52
4.4.1. Operadores and, or y xor .................................................................................... 52
4.4.2. El operador xor .................................................................................................. 54
4.4.3. Operadores not, >> y << .................................................................................... 54

CAPITULO 5: SENTENCIAS "C"

5.1. Introducción .................................................................................................................... 57


5.2. Sentencias secuenciales ................................................................................................... 57
5.3. Sentencia if ..................................................................................................................... 58
5.3.1. Valores verdad y falso en expresiones ............................................................... 59
5.4. Sentencia break .............................................................................................................. 61
5.5. Sentencia continue ......................................................................................................... 62
5.6. Sentencia switch ............................................................................................................ 63
5.7. Sentencia do ................................................................................................................... 66
5.8. Sentencia while .............................................................................................................. 67
5.9. Sentencia for .................................................................................................................. 69
5.10. Sentencia goto y etiquetas ............................................................................................. 71
5.11. Sentencia return ............................................................................................................ 71

CAPITULO 6: CONSIDERACIONES: ARRAYS, PUNTEROS

6.1. Consideraciones sobre arrays .......................................................................................... 75


6.2. Cadenas ........................................................................................................................... 77
6.3. Punteros .......................................................................................................................... 79
6.4. Punteros a conjuntos dimensionados ............................................................................... 81
6.5. Punteros a cadenas .......................................................................................................... 83
6.6. Punteros a funciones ....................................................................................................... 86

II Este manual ha sido desarrollado por el departamento técnico de DELTA PC.


Lenguaje “C” Indice

CAPITULO 7: FUNCIONES "C"

7.1. Introducción .................................................................................................................... 87


7.2. Definición de funciones .................................................................................................. 89
7.2.1. Clases de almacenamiento ................................................................................. 90
7.2.2. Tipos retornados ................................................................................................ 90
7.2.3. Parámetros formales .......................................................................................... 91
7.2.4. Cuerpo de la función .......................................................................................... 92
7.3. Declaración de funciones ................................................................................................ 92
7.4. Llamadas a funciones ...................................................................................................... 93
7.4.1. Argumentos actuales .......................................................................................... 93
7.4.2. Llamadas con un número variable de argumentos ............................................. 95
7.4.3. Llamadas recursivas ........................................................................................... 96
7.5. Ejemplos recapitulativos ................................................................................................. 98

CAPITULO 8: FUNCIONES PARA MANEJO DE CADENAS

8.1. Funciones de cadena ....................................................................................................... 103


8.2. Funciones de copia de cadenas ....................................................................................... 105
8.3. Funciones de búsqueda y comparación de cadenas ......................................................... 107
8.4. Funciones de transformación de caracteres ..................................................................... 108
8.5. Funciones de transformación de tipo ............................................................................... 109
8.6. Otros ejemplos interesantes ............................................................................................. 111

CAPITULO 9: DIRECTIVAS DE PREPROCESADOR

9.1. Introducción .................................................................................................................... 113


9.2. Constantes y macros ....................................................................................................... 113
9.2.1. Directiva define ................................................................................................. 113
9.2.2. Directivas no definidas ...................................................................................... 117
9.3. Directiva include ............................................................................................................. 118
9.4. Compilación condicional ................................................................................................ 119
9.4.1. Directivas if, elif, elseif, endif ............................................................................ 119
9.4.2. Directivas ifdef y ifndef ..................................................................................... 121
9.5. Control de línea ............................................................................................................... 122

CAPITULO 10: EJEMPLO.................................................................................................. 125

CAPITULO 11: CORRIENTES Y ARCHIVOS

11.1. Concepto de corriente.................................................................................................... 129


11.2. Archivos de cabecera (Nombre.H)................................................................................. 130
11.3. Corrientes estándar......................................................................................................... 131
11.4. Unix y C.E/S estándar y E/S a nivel de sistema............................................................. 131
11.5. Punteros a archivos........................................................................................................ 131
11.6. Apertura y cierre de archivos......................................................................................... 131
11.7. Control del fin del archivo. Constante EOF. Funcion FEOF ()...................................... 132
11.8. Acceso secuencial.......................................................................................................... 133
11.8.1. Escritura/Lectura carácter a carácter................................................................. 133
11.8.2. Escritura/Lectura de enteros.............................................................................. 138
11.8.3. Escritura/Lectura de cadenas de caracteres....................................................... 138

Este manual ha sido desarrollado por el departamento técnico de DELTA PC. III
Indice Lenguaje “C”

11.8.4. Escritura/Lectura formateada............................................................................ 141


11.8.5. Escritura/Lectura de bloques............................................................................. 143
11.8.6. Escritura/Lectura de datos como registros........................................................ 146
11.8.7. Escritura/Lectura de otros tipos de datos.......................................................... 149
11.9. Acceso directo a los datos.............................................................................................. 150

CAPÍTULO 12. ENTRADAS Y SALIDAS A NIVEL DE SISTEMA

12.1. Descriptores de archivo y punteros de archivos............................................................. 153


12.2. Apertura y cierre de archivos......................................................................................... 153
12.3. El buffer de E/S de datos. Consideraciones.................................................................... 155
12.4. Acceso secuencial a los datos........................................................................................ 155
12.5. El control del fin de archivo........................................................................................... 158
12.6. Acceso directo a los datos.............................................................................................. 159
12.7. Reasignación del handle de un archivo.......................................................................... 162

CAPÍTULO 13. ASIGNACIÓN DINAMICA DE MEMORIA EN “C”

13.1. Introducción................................................................................................................... 165


13.2. Funcines de asignación dinámica................................................................................... 165
13.3. Colas.............................................................................................................................. 167
13.3.1. Cola lineal......................................................................................................... 167
13.3.2. Cola circular..................................................................................................... 169
13.4. Pilas............................................................................................................................... 172
13.5. Listas enlazadas............................................................................................................. 174
13.5.1. Estructura base y asignación de memoria......................................................... 175
13.5.2. Liberación de memoria y listado de programa.................................................. 176
13.6. Arboles binarios............................................................................................................. 182
13.6.1. Estructura base.................................................................................................. 183
13.6.2. Ordenación de árboles....................................................................................... 183
13.6.3. Búsqueda en el árbol......................................................................................... 184
13.6.4. Borrado en el árbol binario............................................................................... 185
13.6.5. Creación de un árbol binario............................................................................. 186
13.6.6. Listado completo del programa......................................................................... 187

IV Este manual ha sido desarrollado por el departamento técnico de DELTA PC.


Lenguaje “C” Introducción

INTRODUCCIÓN
LENGUAJE “C”

I.1.-¿QUE ES EL "C"?
El "C" es un lenguaje de programación sencillo y elegante, que se ha transformado en el
medio elegido por un número cada vez mayor de programadores, para comunicarse con su ordenador.

El "C" fue creado por Dennis Ritchie, de los Laboratorios Bell, en 1972, cuando trabajaba,
junto con Ken Thompson, en el diseño del sistema operativo UNIX. El "C" desa-rrollado por Ritchie
deriva del lenguaje B, creado por Thompson previamente.

El lenguaje "C" se está transformando rápidamente en una de las bases de programa-ción más
importantes y populares. Es un lenguaje moderno, que incorpora las características de control
apuntadas como deseables por la teoría y la práctica de la informática. Su propio diseño hace que
resulten naturales para el usuario aspectos como la planificación escalonada, programación
estructurada y diseño modular, obteniendo como resultado programas más fia-bles y comprensibles.

I.2.-VIRTUDES DEL "C".


El "C" es un lenguaje eficiente. Su diseño aprovecha las "habilidades" de los orde-nadores en
uso. Los programas "C" tienden a ser compactos y ejecutarse con rapidez.

El "C" es un lenguaje portátil. Con ello queremos significar que los programas "C" escritos en
un sistema pueden ejecutarse en otros sistemas sin apenas modificación; modifi-caciones que se
pueden reducir a cambiar unas cuantas sentencias de entrada en un fichero de encabezamiento (header)
que acompaña al programa principal. Desde este punto de vista el "C" es el lenguaje líder en lenguajes
portátiles. Existen compiladores "C" para unos 40 sistemas, que abarcan desde microprocesadores de 8
bits hasta los actuales campeones de ve-locidad en ordenadores, los Cray.

El lenguaje "C" es poderoso y flexible. Por ejemplo, la mayor parte del sistema ope-rativo
UNIX (poderoso y flexible como pocos), está escrita en "C". Incluso están escritos en "C" los
compiladores e intérpretes de otros lenguajes, como FORTRAN, APL, PASCAL, LISP, LOGO,
BASIC, etc. Por ello, al ultilizar cualquiera de estos lenguajes, a la postre, hay un programa "C" que
está haciendo el trabajo de producción del programa ejecutable final. Se han utilizado programas "C2
para resolver problemas de física e ingeniería, e incluso para la producción de secuencias animadas en
películas como El Retorno del Jedi.

El "C" posee control sobre aspectos del ordenador asociados generalmente con lengua-jes
ensambladores.

El "C" es un lenguaje "amistoso". Es lo suficientemente estructurado para ejercer buenos


hábitos de programación, pero en ningún caso le encorseta en un maremagnum de res-tricciones.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 1


Introducción Lenguaje “C”

I.3.-FUTURO DEL LENGUAJE "C".


El "C" es ya el lenguaje predominante en el mundo de los miniordenadores en siste-mas bajo
UNIX. En la actualidad es el lenguaje rey en la Universidad y se está extendiendo a pasos de gigante
en los ordenadores personales. Muchas compañías de sotfware están utili-zando "C" con preferencia a
otros lenguajes a la hora de crear sus programas: procesadores de texto, hojas electrónicas,
compiladores, etc. Estas compañías saben que "C" produce pro-gramas compactos y eficientes, y, lo
que es más importante, saben que estos programas son fáciles de modificar y de adaptar a nuevos
modelos de ordenadores.

En resumen, el "C" está destinado a ser uno de los lenguajes más importantes de esta década y
de los años venideros. Se utiliza en miniordenadores y en ordenadores personales. Lo usan compañías
de sotfware, estudiantes en general y entusiastas de todas las clases.

I.4.-UTILIZACION DEL "C".


El "C" se utilizó inicialmente para programación de sistemas. La programación de sis-temas se
refire a la clase de programas que o bién son parte, o interfieren directamente con el sistema operativo
de la máquina. Los programas de sistema hacen que la máquina sea capaz de realizar trabajo útil. Los
siguientes son ejemplo de programas de sistemas que a menudo se escriben en "C".

-Sistemas Operativos. -Compiladores de lenguajes.


-Ensambladores. -Editores de texto.
-Integración de E/S (Spooling). -Controladores de Red.
-Programas de módem. -Bases de datos.
-Intérpretes de lenguajes. -Utilidades.

Hay varias razones por las que se utiliza el "C" para programación de sistemas. A menudo
estos programas deben ejecutarse muy rápidamente. Los programas generados por los compiladores
"C", suelen ejecutarse tan rápidamente como los escritos en lenguaje ensam-blador. En el pasado la
mayoría del sotfware de sistema tenía que escribirse en lenguaje en-samblador, ya que ninguno de los
lenguajes de computadora disponibles podían generar pro-gramas que fueran lo suficientemente
rápidos al ejecutarlos. El escribir en lenguaje ensam-blador es un trabajo duro y tedioso, el "C" reduce
tremendamente los costes.
Otra razón por la que el "C" es frecuentemente utilizado para la programación de sistemas es
que es un lenguaje para los programadores. Los programadores profesionales se ven atraídos hacia él
porque no les impone restricciones y les permite una fácil manipulación de bits, bytes, y de
direcciones. El programador de sistemas necesita de las facilidades del "C" para el control directo de
las funciones de E/S y de gestión de memoria. El "C" permite que un programa refleje la personalidad
del programador.

Otros usos del lenguaje "C" ya han sido mencionados en los puntos anteriores de esta
introducción.
El "C" es un lenguaje "compilado". Por ello los pasos básicos a seguir desde el mo-mento en
que se empieza a escribir el programa hasta ejecutarlo son:

1. Utilizar un editor para escribir el programa "C".


2. Utilizar el compilador para compilar dicho código fuente, el cual comprobará si el programa
tiene algún error, y, en su caso nos los mostrará. En caso con-trario, el compilador acometerá
la tarea de traducir el programa al lenguaje interno de su ordenador, y colocará la traducción
en un nuevo fichero.
3. A continuación, ya se puede ejecutar el programa tecleando el nombre de este nuevo fichero
ejecutable.

2 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 1

CAPÍTULO 1
ELEMENTOS DEL “C”

1.1.-INTRODUCCION.
En este primer apartado describimos los elementos del lenguaje "C", es decir los nombres,
números y caracteres usados para crear un programa "C". Se describen:

- El conjunto de caracteres del "C".


- Constantes.
- Identificadores.
- Palabras reservadas o sentencias.
- Comentarios.

1.2.-CONJUNTO DE CARACTERES.
1.2.1.-LETRAS Y DÍGITOS.

El conjunto de caracteres "C" incluyen las letras mayúsculas y minúsculas del alfabeto inglés
y los 10 dígitos decimales del sistema numérico árabe:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9

Estas letras y dígitos pueden usarse para crear constantes, identificadores y sentencias. El
lenguaje "C" diferencia entre mayúsculas y minúsculas.

1.2.2.-CARACTERES ESPACIO.

Los caracteres <Space>,<Tab> y <Return> son espacios en blanco y todos tienen el mismo
propósito de espacios entre palabras y líneas.

El compilador "C" ignora los espacios en blanco a no ser que ellos formen parte de cadena
literal. Por ello se pueden usar los caracteres espacio para hacer más legibles los programas.

1.2.3.-CARACTERES DE PUNTUACIÓN Y ESPECIALES.

Estos caracteres se usan para múltiples propósitos, por ejemplo para organizar el texto en un
programa, definir las tareas a realizar por el compilador, etc. Son los siguientes:

, . ; : ? ' " ( ) [ ] { } < >

! │ / \ ~ _ # % & ^ * - = +

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 3


Capítulo 1 Lenguaje “C”

El resto de caracteres que no aparecen en esta relación solo pueden figuran como parte de las
cadenas o literales, de las constantes y comentarios.

1.2.4.-SECUENCIAS DE ESCAPE.

Las secuencias de Escape son una combinación de caracteres especiales representados como
espacios en blanco, caracteres no gráficos y caracteres constantes en las cadenas. Estas secuencias se
usan para especificar acciones comunes como pueden ser retornos de carro, movimientos de tabulado;
también se utilizan para representar caracteres que tienen significado para el C, como por ejemplo el
carácter".

Una secuencia de escape consiste en un backslash (\) seguido por una letra o combinación de
dígitos. Las secuencias de escape del lenguaje "C" son las siguientes:

Secuencia de Escape Acción.


-------------------- ---------
\n Nueva línea.
\t Un tabulado horizontal.
\v Un tabulado vertical.
\b Retroceso (Backspace).
\r Retorno de Carro.
\f Alimentación de hoja.
\' Comilla simple.
\" Comillas.
\\ Barra invertida (Backslash).
\ddd Constante ASCII en octal.
\xdd Constante ASCII en hexadecimal.

Si el carácter \ va seguido por un carácter no incluido en la lista anterior, el carácter \ se ignora


y el carácter que le sigue se representa literalmente. Por ejemplo la secuencia "\v" representa el
carácter "v" en una cadena o constante.

Las secuencias "\ddd" y "\xdd" permite asignar un carácter ASCII, dandolo como tres dígitos
para octal y como dos dígitos para hexadecimal. Por ejemplo el carácter backspace se puede dar como
"\010" o "\x08", el carácter nulo ASCII se puede dar como "\0" or "\x0".

En una secuencia de escape octal solo pueden aparecer dígitos octales, y al menos debe figurar
uno de los dígitos. Por ejemplo el carácter backspace se puede también dar como "\10". De la misma
forma una secuencia de escape hexadecimal tiene que contener al menos un dígito, es decir el segundo
puede omitirse si es el caso. Por ejemplo la secuencia de escape para el carácter hexadecimal será
"\x8". En ambos casos si la secuencia de escape figura en una cadena es más seguro incluir todos los
dígitos.

1.2.5.-OPERADORES.

Los operadores son combinaciones especiales de caracteres que especifican como los valores
son transformados y asignados. Los operadores tienen que especificarse exactamente como aparecen
en la relación que sigue, teniendo en cuenta que los operadores multicarácter no llevar espacio en
blanco entre ellos.

- Operadores aritméticos.

+ Suma.
- Resta.
* Multiplicación.

4 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 1

/ División.
% Resto de división entera.
++ Incremento.
-- Decremento.
= Asignación simple.
+= Asignación de adición.
-= Asignación de sustracción.
*= Asignación de producto.
/= Asignación de división.
%= Asignación de resto.

- Operadores de relación.

> Mayor que.


< Menor que.
>= Mayor o igual.
<= Menor o igual.
== Igual.
!= Distinto de.

- Operadores lógicos.

&& And.
|| Or.
! Negación.

- Operadores binarios.

& And.
| Or.
^ Xor.
~ Not (Complemento a uno).
>> Desplazamiento a la derecha.
<< Desplazamiento a la izquierda.
>>= Asignación de desplazamiento a derecha.
<<= Asignación de desplazamiento a izquierda.
&= Asignación de AND binario.
|= Asignación de OR binario.
^= Asignación de XOR binario.

- Operadores especiales.

? Condicional. Formato: (exp)?exp1:exp2


& Devuelve la dirección de memoria del operando situado detrás del operador.
* Devuelve el contenido de la dirección de memoria del puntero situado detrás
del operador.
' Encadena expresiones.
. Selecciona elementos individuales en estructuras y uniones.
-> Indica un puntero a una estructura o unión.
[] Permiten la indexación de los conjuntos dimensionados (arrays).
() Aumentan la prioridad de las expresiones.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 5


Capítulo 1 Lenguaje “C”

1.3.-CONSTANTES.
Una constante es un número, un carácter, o una cadena de caracteres que se pueden usar como
un valor en un programa. El valor de una constante no se puede cambiar durante la ejecución del
programa.

1.3.1.-CONSTANTES ENTERAS.

Una constante entera es número decimal, octal o hexadecimal que representa un valor entero.
Una constante decimal esta formada por uno o más dígitos decimales (de 0 a 9).

Una constante octal tiene el siguiente formato:

0digitos_oct

Donde digitos_oct son uno o más dígitos octales (de 0 a 7). El 0 inicial del formato es
obligatorio.

Una constante hexadecimal tiene el formato:

0xdigitos_hex

Donde dígitos_hex son uno o más dígitos hexadecimales (de 0 a 9 y las mayúsculas o
minúsculas "a" a "f"). El 0 inicial seguido por una x es obligatorio.

No pueden aparecer caracteres en blanco entre los digitos de una constante entera.

Ejemplos:

decimales octales hexadecimales


3 015 0xb ó 0xB
CONSTANTES: 167 0307 0x84
23456 0666673 0x5db3 ó 0x5DB3

Para crear constantes enteras negativas el signo menos (-) se coloca delante de esta, el cual es
tratado como un operador, dando lugar a una expresión de valor negativo.

A las constantes decimales se les asigna el tipo int o el tipo long dependiendo del tamaño del
valor; a las constantes octales y hexadecimales también se les asigna tipo int o long según el valor.

El programador puede forzar directamente al compilador "C" a que una constante entera tenga
tipo long añadiéndole la letra "l" o "L" al final de la constante. Ejemplos:

decimales octales hexadecimales


CONSTANTES: 5L 024L 0xcl ó 0xCl
96l 0117l 0x3fl ó 0x3Fl

1.3.2.-CONSTANTES DE PUNTO FLOTANTE.

Una constante de punto flotante es un número decimal que representa un número real con
signo. El valor de un número real con signo incluye una parte entera, una parte fraccionaria y un
exponente. Las constantes de punto flotante tienen el siguiente formato:

[digitos][.digitos][E[-]dígitos]

6 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 1

Donde dígitos representa uno o más digitos decimales (0 a 9), y E (ó e) es el símbolo del
exponente. Los dígitos antes del punto decimal pueden omitirse, lo mismo para los que están después
del punto decimal; pero no ambos a la vez. El punto decimal solo se puede omitir cuando se incluye el
exponente. Entre los componentes de una constante de punto flotante no puede figurar ningún carácter
blanco.

Para crear constantes de punto flotante negativas el signo menos (-) se coloca delante de esta,
el cual es tratado como un operador, dando lugar a una expresión de valor negativo. Vemos algunos
ejemplos de constantes y expresiones en punto flotante:

CONSTANTES.
25.34 .32
4.141E3 .0092e3
6412e-3 -.324
-0.0016 -.138E-2
-6.3e-2
71E-5

1.3.3.-CONSTANTES CARÁCTER.

Una constante carácter es una letra, un dígito, un carácter de puntuación, o secuencia de


escape encerrada entre comillas simples. El valor de una constante carácter es el carácter mismo, por
ello no se permiten valores de más de un carácter para estas constantes.

Ejemplos:

CONSTANTES.
------------------------
'f' Minúscula f.
'% Signo %.
'\n' Carácter nueva línea.
'\b' Retroceso.
'\'' Comilla simple.
'\\' Backslash.

A las constantes carácter se les asigna tipo char.

1.3.4.-CONSTANTES CADENA.

Una constante cadena es una secuencia de letras, digitos y símbolos encerrados entre comillas.
Una constante cadena es tratada como un array de caracteres, es decir cada elemento del array es un
valor de carácter simple. Para usar las comillas dobles o el símbolo backslash (\) como parte de una
constante cadena se preceden estos con un carácter backslash.

Por ejemplo:

CONSTANTES.
------------------------------
"La pantalla amiga ANTENA 5."
"Pulsa una opción \n o pulsa Return"
"Primero\\Segundo"
"\"Si, es así \" dijo ella."

Los caracteres de una constante cadena se almacenan en orden en posiciones contiguas de


memoria; a la cadena se le añade un carácter nulo (\0) para marcar el fin de esta. A las constantes
cadena se les asigna el tipo char[], es decir son arrays de caracteres, donde el número de elementos del

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 7


Capítulo 1 Lenguaje “C”

array es el número de caracteres en la cadena más uno, ya que el carácter nulo almacenado después del
último carácter cuenta como un elemento del array.

1.4.-IDENTIFICADORES.
Los identificadores son los nombres que se utilizan para nombrar las variables, funciones y
etiquetas en los programas. Un identificador es una secuencia de una o más letras, dígitos o guiones de
subrayado (_) que comienzan con una letra o guión de subrayado. Los identificadores pueden tener
cualquier número de caracteres pero solo lo 31 primeros caracteres son significativos para el
compilador. Los identificadores que comiencen con un guión de subrayado pueden dar lugar a
conflictos con los nombres de las rutinas ocultas del sistema y producir errores.

IDENTIFICADORES.
---------------------
i
cent
temperatura
num_de_pagina
avanzar9

El compilador "C" diferencia entre mayúsculas y minúsculas. Por ello cada uno de los
siguientes identificadores es único.

IDENTIFICADORES.
---------------------
sumar
suMAR
Sumar

1.5.-PALABRAS CLAVE.
Las palabras clave son identificadores predefinidos que tienen un significado especial para el
compilador C, por ello deben de escribirse tal como están definidas. Los nombres de los
identificadores usados por el programador no pueden coincidir con ninguna de las siguientes palabras
clave.

PALABRAS RESERVADAS
auto default float register switch
break do for return typedef
case double goto short union
char else if sizeof unsigned
const enum int static void
continue extern long struct while

Estas palabras clave no pueden ser redefinidas. Sin embargo se puede especificar un texto que
sustituya a la palabra reservada antes de la compilación, usando para ello las directivas del
preprocesador.
1.6.-COMENTARIOS.
Un comentario es una secuencia de caracteres que son tratados como carácter de espacio en
blanco por el compilador es decir es ignorado. Un comentario tiene el formato:

8 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 1

/* Comentario */

Donde Comentario es cualquier combinación del conjunto representable de caracteres,


excluyendo como es lógico la combinación "*/". Los comentarios pueden ocupar más de una línea.
Los comentarios se usan para documentar las sentencias y acciones de un programa fuente del
lenguaje C. Ejemplos:

COMENTARIOS.
---------------------------------------------------------
/* Los comentarios se incluyen para documentar
las lineas de un programa. */

/* Los comentarios pueden contener sentencias del C


como por ejemplo: for y while. */

El siguiente ejemplo daría error:

/* Esto es un /* comentario */ en un programa. */

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 9


Capítulo 1 Lenguaje “C”

10 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 2

CAPÍTULO 2
ESTRUCTURA DE UN PROGRAMA “C”

2.1.-INTRODUCCION.
Este capítulo describe la estructura de un programa fuente en lenguaje "C". En él se habla de
una serie de conceptos que se desarrollarán más a fondo en los sucesivos capítulos, como son las
directivas del procesador, las funciones, etc.

2.2.-USOS Y UTILIDADES DE PRINTF() Y SCANF().


Las funciones printf() y scanf() permiten al programa comunicarse con el exterior. Se
denominan funciones de entrada/salida (E/S) y a pesar de existir otras estas dos son las más versátiles.
Estas funciones no forman parte de la definición del C; de hecho, en "C" se deja la implementación de
E/S a los diseñadores del compilador: así se consigue optimizar las funciones para cada máquina
específica. Por ello puede haber pequeñas diferencias en estas funciones en los diferentes sistemas.

Estas dos funciones, printf() y scanf() funcionan de la misma forma, utilizando cada una de
ellas una "tira de caracteres de control" y una lista de "argumentos".

2.2.1.-UTILIZACIÓN DE PRINTF().

Formato:

printf(Control,campo1,camplo2,......);

Campo1, campo2,etc., son las distintas variables o constantes a imprimir. Pueden ser también
expresiones, las cuales se evalúan antes de imprimir el resultado. Control es una tira de caracteres que
describe la manera en que han de imprimirse los campos. Por ejemplo:

printf("Las %d mujeres se bebieron %f vasos de ron.


\n",numero,ron);

En este ejemplo control es la frase entre comillas y número y ron son los campos.

Las instrucciones que se han de dar a printf() cuando se desea imprimir una variable dependen
del tipo de variable de que se trate. Así tendremos que utilizar al notación %d para imprimir un entero,
y %c para imprimir un carácter. Si se desea imprimir el % como símbolo hay que indicarlo
duplicándolo. Ejemplo:

pc=2*6;
printf("Un %d%% del beneficio era ficticio. \n",pc);

En la siguiente tabla se muestran los identificadores y los tipos que imprimen utilizados por la
función printf().

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 11


Capítulo 2 Lenguaje “C”

Identificador Salida
-------------- ------
%d Entero decimal.
%c Carácter.
%s Tira de caracteres.
%e Número de punto flotante
(Notación exponencial).
%f Número de punto flotante
(Notación decimal).

Identificador Salida
-------------- ------
%g Usa %f ó %e, según el caso.
%u Entero decimal sin signo.
%o Entero octal sin signo.
%x Entero hexadecimal sin signo.

Ejemplo1: Un programa muy sencillo que acepta un nombre y devuelve un saludo.

/* -------------------
Programa: printf1.c
------------------- */
# define ELOGIO " ! Por Júpiter, que tío ! "
main()
{
char nombre[50];
printf("\n\n Como te llamas ? ");
scanf("%s",nombre);
printf("\n Hola, %s. %s \n\n",nombre,ELOGIO);
}

Ejemplo2: Cálculo de la circunferencia y área de una tortilla en base a su radio.

/* -------------------
Programa: printf2.c
------------------- */
#define PI 3.14159
main()
{
float área, circun, radio;
printf("Cual es el radio de su tortilla ? ");
scanf("%f", &radio);
área = PI * radio * radio;
circun = 2.0 * PI * radio;
printf("\n");
printf("Los parametros basicos de su tortilla son: \n");
printf(" circunferencia =%1.2f \n área = %1.2f \n",
circun, área);
}

12 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 2

Ejemplo3: Diferentes formas de utilizar la función printf().

/* -------------------
Programa: printf3.c
------------------- */
#define PI 3.14159
main()
{
int numero = 5;
float ron = 13.5;
int coste = 3100;
printf("\n");
printf(" Las %d mujeres se bebieron %f vasos de ron. \n",
numero,ron);
printf(" El valor de pi es %f. \n",PI);
printf(" Una letra %c de coste %d \n",'h',coste);
}

2.2.2.- MODIFICADORES DE ESPECIFICACIONES DE CONVERSIÓN EN


PRINTF().

Los modificadores son apéndices que se agregan a los especificadores de conversión básicos
para modificar la salida. Se colocan entre el símbolo % y el carácter que define el tipo de conversión.
Los símbolos a emplear son:

Modificador Significado.
----------- ---------------
- El campo correspondiente se comenzará a escribir en el extremo izquierdo.
número Anchura mínima del campo. Si la cantidad a imprimir no entra en el lugar
asignado, se usa automáticamente un campo mayor.
.número Precisión. Es el número de decimales en los tipos flotantes.
l Indica tipo long en vez de int. Ej. %ld.

2.2.3.-UTILIZACIÓN DE PRINTF() PARA EFECTUAR CONVERSIONES.

Se pueden emplear las especificaciones de conversión de printf() para convertir números en


base 10 a números en base 8 ó 16. Simplemente se trata de solicitar el número que se trata de imprimir
con el especificador correspondiente: %d, para obtener decimal, %o para octal, y %x para
hexadecimal con independencia de la forma en que el número aparezca originalmente en el programa.

Ejemplo: Impresión de un número en diferentes notaciones.

/* -------------------
Programa: printf4.c
------------------- */
main()
{
printf("\n");
printf(" <%d> \n",336);
printf(" <%o> \n",336);
printf(" <%x> \n",336);
printf(" <%d> \n",-336);
printf(" <%u> \n",-336);
printf("\n");
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 13


Capítulo 2 Lenguaje “C”

2.2.4.-USO DE SCANF()

Scanf() al igual que printf() emplea una tira de caracteres de control y una lista de argumentos.
Sin embargo la función scanf() emplea en sus listas punteros a variables. Con esta función se siguen
las dos siguientes reglas:

1. Si se desea leer un valor perteneciente a cualquiera de los tipos básicos, se coloca el nombre
de la variable precedido por un &.

2. Si lo que se desea es leer una variable de tipo string no se usa el símbolo &.

Ejemplo: Lectura de un entero, un float y una cadena.

/* ------------------
Programa: scanf1.c
------------------ */
main()
{
int edad;
float sueldo;
char cachorro[30];
printf("Confiese su edad, sueldo y mascota favorita.\n");
scanf("%d %f",&edad,&sueldo);
scanf("%s",cachorro);
printf("\nTienes %d anos. ganas %.0f pts. \n", edad,sueldo;
printf("y tu mascota es (el/la) : %s\n\n",cachorro);
}

Scanf() considera que dos campos de entrada son diferentes cuando están separados por
blancos o tabulados. Va encajando cada especificador de conversión en su campo correspondiente,
ignorando los blancos intermedios.

La función scanf() emplea un juego de identificadores muy semejante al de printf(). Las


diferencias más sobresalientes son:

1. No existe la opción %g.

2. Las opciones %f y %e son equivalentes. Ambas aceptan un signo opcional, una tira de dígitos
con o sin punto decimal y un campo para el exponente, también opcional.

3. Existe una opción %h para leer enteros short.

2.3.-PROGRAMA FUENTE.
Un programa fuente en lenguaje "C" es una colección de una o más directivas, declaraciones
y/o definiciones.

Las sentencias directivas obligan al preprocesador a repetir una serie de acciones especificas
en el texto del programa con anterioridad a la compilación.

Las declaraciones establecen los nombres y atributos de las variables, las funciones y los
tipos usados en el programa.

14 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 2

Las definiciones son declaraciones que describen variables y funciones. Una definición de una
variable da un valor inicial a la variable declarada conjuntamente con su nombre y tipo. La definición
de funciones consiste en definir el cuerpo de la función, le da un nombre, los parámetros formales y el
retorno de un tipo.
Vemos un ejemplo ilustrativo de un programa fuente C.

/* Definición de variables */
int x=1;
int y=2;
/* Declaración de una función */
extern int printf(char *,);

main()
/* Definición de función
para la función main */
{
/* declaración de variables. */
int z;
int w;
/* Sentencias ejecutables. */
z=y+x;
w=y-x;
printf("\n\nz=%d \nw=%d \n\n ",z,w);
}

En este ejemplo "x" e "y" están definidas mientras que las variables "z" y "w" están sólo
declaradas.

2.4.-FICHEROS FUENTE.
Los programas fuente pueden se divididos en uno o más ficheros fuente. Un fichero fuente "C"
es un texto que contiene todo o parte del contenido de un programa fuente C. Un fichero fuente puede
contener por ejemplo algunas de las funciones necesitadas por el programa. Los ficheros fuente
separados se pueden combinar para formar ficheros fuente mayores usando la directiva #include.
Vemos un ejemplo de un programa fuente "C" formado por dos ficheros fuente. Las funciones main()
y max() forman ficheros fuente separados, la ejecución comienza en la función main().

Fichero fuente 1: ejemplo.c

/* -------------------------------------------
Fichero fuente 1 - Función Principal
------------------------------------------- */
#define UNO 1
#define DOS 2
#define TRES 3
/* Declaración de la función. */
extern int mayor(int, int);
main()
/* Definición de la función main() */
{
int w=UNO,x=DOS,y=TRES;
int z=0;
z=mayor(x,y);
w=mayor(z,w);
printf("\n Valor de z : %d ",z);
printf("\n Valor de w : %d ",w);
printf("\n\n");
}

Fichero fuente 2: mayor.c

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 15


Capítulo 2 Lenguaje “C”

/* ------------------------------------------
Fichero fuente 2 - Función mayor
------------------------------------------ */
int mayor(a,b)
/* Definición de la función */
int a,b;
{
if (a>b)
return(a);
else
return(b);
}

En el primer fichero fuente, la función mayor() se declara sin haber sido definida. Esto se
conoce con el nombre de "declaración hacia delante". La definición de la función main() incluye
llamadas a la función mayor().

Las líneas que comienzan con el signo # son directivas del procesador, que fuerzan al
preprocesador a sustituir los identificadores UNO,DOS y TRES con el número especificado por todo
el cuerpo del programa con anterioridad a su compilación.

El segundo fichero fuente contiene la definición de la función mayor(). Esta definición


satisface las llamadas a la función mayor() por el primer fichero fuente.

Una vez creados se pueden compilar, linkar y ejecutar con las órdenes:

# cc -compat ejemplo.c mayor.c


# mv a.out ejecutable
# ./ejecutable

2.5.-EJECUCIÓN DE UN PROGRAMA.
Todo programa tiene que tener una función principal ( main() ). Esta función es el punto de
comienzo de la ejecución del programa y este normalmente acaba su ejecución al final de esta función,
sin embargo la ejecución puede finalizar en otro punto del programa dependiendo del entorno de
ejecución. Los programas normalmente tienen más de una función y cuando una función es invocada
la ejecución comienza en la primera sentencia de la función llamada, la función retorna el control a la
línea siguiente a la llamada cuando se ejecuta una sentencia "return" o bien se encuentra el fin de la
función.

Todas las funciones, incluyendo main(), pueden declararse con parámetros. Las funciones
llamadas por otras funciones reciben valores para sus parámetros, si es el caso. Los parámetros de la
función main() se declaran para recibir valores pasados desde fuera del programa. ( Por ejemplo desde
la línea de comando cuando se invoca la ejecución del programa).

Tradicionalmente los tres primeros parámetros de la función main() se declaran con los
nombres argc, argv y envp. El parámetro argc almacena el número de argumentos o valores pasados a
la función main(). El parámetro argv es un array de punteros donde cada elemento es un puntero a una
cadena de los argumentos pasados a main().

2.6.-TIEMPO DE VIDA Y VISIBILIDAD EN C.

16 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 2

Los conceptos de "Tiempo de vida" y "visibilidad" es importante tenerlos claros a la hora de


comprender la estructura de un programa C. El "Tiempo de vida" de una variable o función puede ser
global o local. Una función o variable global esta presente y abarca todo el programa mientras que si
son locales solo son reconocidas en el bloque en el cual están definidas o declaradas.

Una variable se dice que es "visible" en un bloque o fichero fuente si el tipo y su nombre son
reconocidos en dicho bloque o fichero fuente.

Las declaraciones y definiciones dentro de un bloque se dice que son a "nivel interno",
mientras que si ocurren fuera de todos los bloques se dice que son a "nivel externo". Todas las
variables que se declaran a nivel externo son globales mientras que si se declaran en un nivel interno
son locales. Sin embargo los almacenamientos específicos static y extern se pueden utilizar para
declarar variables globales o referenciar a una variable global dentro de un bloque.

En general: Las variables declaradas o definidas en un nivel interno son visibles desde el
punto donde son declaradas o definidas hasta el final del bloque donde aparecen.

Si una variable declarada dentro de un bloque tiene el mismo nombre que una variable
declarada en un nivel externo, la variable local impera sobre la definida en el nivel externo dentro del
bloque; sin embargo al salir del bloque la visibilidad de la variable externa se restaura.

Vemos un ejemplo ilustrativo de estos conceptos:

/* Identificador a nivel externo */


int i=1;
/* Función main definida a nivel externo */
main()
{
/* impr. de var. de nivel externo */
printf("<%d> \n",i); /* impr. 1 */
/*- Primer bloque anidado -*/
{
/* Definimos i,j en un nivel interno. */
int i=2, j=3;
printf("<%d> \n<%d> \n",i,j); /* impr. 2,3 */
/*-- Segundo bloque anidado --*/
{
/* Redefinimos la variable i */
int i=0;
print("<%d> \n<%d> \n",i,j); /* impr. 0,3 */
}
/*-- Fin del segundo bloque --*/
printf("<%d> \n",i); /* impr. 2 */
}
/*- Fin del primer bloque -*/
/*Imprimimos una variable de nivel externo restaurada*/
printf("<%d> \n",i);
}

En este ejemplo, hay cuatro niveles de visibilidad: el nivel externo y tres niveles de bloques.
La función printf imprimirá los valores 1,2,3,0,3,2,1.

Ejemplo: Un ejemplo muy interesante de la sentencia printf().

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 17


Capítulo 2 Lenguaje “C”

/* ---------------------
Programa: printfs.c
--------------------- */
#include <stdio.h>
main()
{
printf("\n\n");
printf(" %s %d\n","esto es una cadena",100);
printf(" esto es una cadena %d\n",100);
printf(" El numero %d es decimal,y %f es float.\n",10,110.789);
printf(" %c %s * %d - %x \n",'*',"numero decimal y hexadecimal:",10,10);
printf("\n\n\n");
printf(" El numero 123 con formato %%10d es :-> %10d \n",123);
printf(" El numero 123 con formato %%-10d es :-> %-10d \n",123);
printf("\n\n");
printf(" La cadena \"Hola\" con formato %%-10s es :-> %-10s \n", "Hola");
printf(" La cadena \"Hola\" con formato %%10s es :-> % 10s \n", "Hola");
printf(" La cadena \"123456789\" con formato %%5.7s es :-> %5.7s
\n","123456789");
printf("\n\n");
printf(" El numero 123.234 con fomato %%5.2e es :-> %5.2e \n",123.234);
printf(" El numero 123.234 con fomato %%5.2g es :-> %5.2g \n",123.234);
printf(" El numero 123.234 con fomato %%-5.2e es :-> %-5.2e \n",123.234);
printf(" El numero 123.234 con fomato %%-5.2g es :-> %-5.2g \n",123.234);
}

18 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

CAPÍTULO 3
VARIABLES Y TIPOS EN “C”

3.1.-INTRODUCCION.
Este capítulo describe la forma de efectuar las declaraciones de variables, funciones y tipos en
C. Las declaraciones en "C" tienen el formato general:

[almacenmiento][tipo]declarador=[inicial][,declarador...]

Donde:

almacenamiento es uno especificadores de almacenamiento soportados por el C.

tipo es uno de los tipos C, los vemos en este capítulo.

declarador es un identificador que puede ser modificado con los caracteres "[]","*" o "()" para
declarar un array, un puntero o un tipo de función. Cuando se declara una variable
simple (ej. carácter,entero,real), o una estructura o union de variables simples,
declarador esta compuesto solo por un identificador.

inicial es un valor o conjunto de valores asignados a la variable declarada como valores


iniciales.

Todas las variables en "C" tienen que ser declaradas antes de usarse.

Las funciones "C" pueden declararse de forma explícita en una declaración de función o de
forma implícita llamando a la función antes de ser declarada o definida.

El lenguaje "C" define un conjunto estándar de tipos de datos, sin embargo se podrán definir
nuevos tipos en base a los tipos ya definidos.

Se definen cuatro tipos de almacenamiento: auto, extern, register y static. El especificador de


almacenamiento de una declaración afecta a como la variable, función, etc. se almacena e inicializa y
que parte del programa puede referirse a ella.

3.2.-TIPOS.
El lenguaje "C" dispone de un conjunto básico de tipos de datos, llamados "tipos
fundamentales". Son los siguientes:

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 19


Capítulo 3 Lenguaje “C”

TIPOS FUNDAMENTALES.

Integrales Punto_Flotante Otros


------------------------------------------------------------
char float void
int double (long float)
short int
long int
unsigned char
unsigned int
unsigned short int
unsigned long int

El tipo void sólo se utiliza para declarar funciones que no retornan ningún valor. Los tipos de
la tabla "integrales" y "Punto_flotante" se utilizan para declarar variables y funciones. Posteriormente
veremos que se pueden crear tipos adicionales usando en la declaración typedef.

Algunos de los tipos de la tabla anterior se pueden abreviar según la siguiente tabla:

Tipo Abreviación
---------------------------------------
char --
int --
short int short
long int long
unsigned char --
unsigned int --
unsigned short unsigned short
unsigned long int unsigned long
float --
long float double

La tabla que figura a continuación contiene el tamaño de almacenamiento asociado a cada tipo
fundamental y contiene el rango de posibles valores que se pueden almacenar en ellos.

Tipo Almacenamiento Rango de valores.


------------------------------------------------------------------
char 1 byte -128 a 127
int Variable
short 2 bytes -32.768 a 32.767
long 4 bytes -2.147.483.648 a 2.147.483.647
unsigned char 1 byte 0 a 255
unsigned Variable
unsigned short 2 bytes 0 a 65.535
unsigned long 4 bytes 0 a 4.294.967.295
float 4 bytes Standard IEEE
double 8 bytes Standard IEEE

El tipo char se usa para almacenar una letra, dígito o símbolo del conjunto representable de
caracteres. El valor entero de un carácter es el código ASCII correspondiente a ese carácter.

Nótese que los tipos int y unsigned int no están definidos por el lenguaje C, ya que sus
tamaños se corresponden con el tamaño natural de un entero en una máquina dada. Por ejemplo en una
máquina de 16 bits el tamaño del tipo int son 2 bytes (16 bits), si la maquina es de 32 bits el tamaño
usual del tipo int son 4 bytes (32 bits), de esto se deduce que dependiendo de la máquina el tipo int

20 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

puede ser equivalente al tipo short int o al tipo long int. Lo mismo sucede con el tipo unsigned int que
puede ser equivalente a unsigned short o unsigned long.
ú Los números de punto flotante (reales) usan el estándar IEEE ( Institute of Electrical
and Electronics Engineers, inc.), que para los valores de tipo float utiliza 4 bytes. El máximo valor que
se puede representar es normalmente 1.701411E38. Para los valores de tipo double utilizan 8 bytes.

Ejemplo1: Comprobamos lo comentado anteriormente.

/* -----------------
Programa: tipos.c
----------------- */
/* .. ..
Indica el tamaño de los tipos basicos del Turbo C.
.. .. */
main()
{
printf("\n\n\n");
printf(" TIPOS EN TURBO C \n");
printf(" ................ \n\n\n");
printf(" El tipo ( Int ) ocupa :( %d )bytes.\n",sizeof(int));
printf(" El tipo ( Short ) ocupa :( %d )bytes.\n",sizeof(short));
printf(" El tipo ( long ) ocupa :( %d )bytes.\n",sizeof(long));
printf(" El tipo ( float ) ocupa :( %d )bytes.\n",sizeof(float));
printf(" El tipo ( double ) ocupa :( %d )bytes.\n",sizeof(double));
printf(" El tipo ( Char ) ocupa :( %d ) byte.\n",sizeof(char));
printf("\n\n");
printf(" unsigned short int -> (%d) bytes.\n",sizeof(unsigned shortint));
printf(" unsigned long int -> (%d) bytes.\n",sizeof(unsigned long int));
printf(" unsigned int -> (%d) bytes.\n",sizeof(unsigned int));
printf(" unsigned char -> (%d) byte.\n",sizeof(unsigned char));
printf("\n\n");
}

Ejemplo2: Un ejemplo a tener en cuenta acerca de los límites de desbordamiento de los tipos.

/* --------------------
Programa: sinsigno.c
-------------------- */
/* .. ..
Este ejemplo presenta un caso de
desbordamiento para el tipo
short e int.
.. .. */
#include <stdio.h>
main()
{
short int i;
unsigned int j;
int k;
j=32768;
i=j;
k=j;
printf("\n\n\n");
printf(" Valor de i = %d \n\n ",i);
printf(" Valor de j = %u \n\n ",j);
printf(" Valor de k = %d \n\n ",k);
}

3.3.-DECLARADORES.
Los declaradores permiten a los programadores declarar arrays de valores, punteros a valores y
funciones que retornan valores de un tipo especifico. Un declarador es un identificador modificado

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 21


Capítulo 3 Lenguaje “C”

con alguna combinación de "[]","()", y "*" para declarar un array, puntero o tipo de función.
Formatos:

identificador

declarador[]

declarador[constante-expresion]

*declarador

declarador()

declarador(arg-tipo-lista)

(declarador)

3.4.-DECLARACIÓN DE VARIABLES.
En este apartado se describe el formato y significado de la declaración de las variables:

- Variables simples.
- Variables Enumeraciones.
- Estructuras.
- Uniones.
- Arrays.
- Pointers.

El formato general para declarar una variable es:

[almacenamiento]tipo declarador[declarador...]

Donde:

tipo es el tipo de dato de la variable.


declarador es el nombre de la variable(s).
almacenamiento es el modo de almacenamiento de la variable.

3.4.1.-DECLARACIONES DE VARIABLES SIMPLES.

Siguen el formato:

[almacenamiento] tipo identificador[,identificador];

En el formato identificador es el nombre de la variable y tipo es uno de los tipos de datos


conocidos. Los modos de almacenamiento se verán a 'posteriori'. Se pueden declarar múltiples
variables en la misma declaración dando la lista de ellas separadas por comas, con lo cual todas
variables tendrán el mismo tipo.

Ejemplos:

int x;
unsigned long var1,var2;
double otra;

22 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Ejemplo: En el siguiente ejemplo se utiliza la sentencia define para definir valores constantes
utilizados posteriormente para inicializar definiciones de variables.

/* ---------------------
Programa: constante.c
--------------------- */
#define NOMBRE 'J' /* Carácter */
#define APELLIDO_1 'G' /* Carácter */
#define APELLIDO_2 'P' /* Carácter */
#define SALUDO "Buenas tardes" /* cadena */
#define PESETAS 158 /* entero decimal */
#define ENTERO_LARGO 4596538 /* entero largo decimal */
#define ENTERO_1 0x17 /* entero hexadecimal */
#define ENTERO_2 011177 /* entero octal */
main() /* definicion del programa principal */
{
char a=NOMBRE,b=APELLIDO_1,c=APELLIDO_2;
int i=ENTERO_1, j=ENTERO_2;
long x= ENTERO_LARGO;
printf("\n\n");
printf(" :<->: - %s ! :<->:",SALUDO);
printf("\n\n las iniciales de mi nombre son %c.%c.%c.",a,b,c);
printf("\n\n Tengo %d a$os.",i);
printf("\n\n y necesito %d %c ",j,PESETAS);
printf("\n\n Mi telefono es %ld. ",x);
printf("\n\n");
}

3.4.2.-DECLARACIÓN DE ENUMERACIONES.

Una declaración de una enumeración se realiza dando el nombre de la enumeración y


definiendo un conjunto de nombres de constantes enteras, por ello el almacenamiento asociado a una
variable enumeración es el equivalente a una variable entera.
Una variable declarada del tipo enumeración almacena alguno de los valores de la
enumeración definidos por ella.

Los formatos a utilizar son:

enum [nombre]{lista_enum}identificador[,identificador...];
enum nombre identificador[,identificador..];

donde:

En el primer formato:

lista_enum: especifica los nombres de las constantes enteras

Sigue el formato:

identificador[=constante-expresión]
[,identificador[=constante-expresión]]
.
.
.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 23


Capítulo 3 Lenguaje “C”

A cada identificador se le asocia un número, por defecto, al primer identificador se le asocia el


valor 0, al segundo el valor 1 y así hasta el último.

Sin embargo esta secuencia se puede modificar con la opción "=constante-expresión", la cual
si aparece asocia al identificador el valor; dicha constante-expresión tiene que ser de tipo int y puede
ser de tipo negativo, y a partir de aquí el siguiente identificador toma el valor "constante-expresión +
1", sino se le da otro valor. Dos identificadores pueden contener valores de constate duplicados.

nombre: es un identificador que nombra la enumeración.


identificador: son las variables del tipo enumeración.

En el segundo formato:

nombre: es un identificador que nombra la enumeración.


identificador: son las variables del tipo enumeración.

En este caso el tipo de la enumeración tiene que estar previamente definido.

Ejemplos:

1: enum dia {
sabado,
domingo=0,
lunes,
martes,
miercoles,
jueves,
viernes
} dia_trabajo;
2: hoy=miercoles;
3: enum dia vacaciones;

El primer ejemplo define una enumeración del tipo dia y declara la variable dia_trabajo de ese
tipo. El valor 0 se asocia al sabado por defecto. El identificador domingo se asocia de forma explícita
al valor 0. Los restantes identificadores llevan asociados los valores de 1 a 5 por defecto.

El segundo ejemplo un valor del conjunto se asigna a la variable "hoy".

En el último ejemplo se declara un variable de nombre vacaciones del tipo enum dia, dicha
enumeración tiene que ser previamente declarada.

Ejemplo: En este ejemplo se declara una enumeración y se define un array y posteriormente


comprobamos el uso de la enumeración relacinándola con el array.

24 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

/* -------------------
Programa: enumera.c
------------------- */
# include <stdio.h>
enum dineros {peseta,
duro,
diez,
veinticinco=3,
cincuenta,
cien=450,
doscientas,
quinientas};
char nombre_moneda[][20]={
"peseta",
"duro",
"diez",
"veinticinco",
"cincuenta",
"cien",
"doscientas",
"quinientas"
};
main()
{
enum dineros monedas;
printf("\n\n\n");
printf(" peseta = %d, duro = %d, diez = %d \n\n", peseta,duro,diez);
printf("veinticinco=%d,cincuenta= %d, cien = %d \n\n", veinticinco,cincuenta,cien);
printf(" doscientas = %d, quinientas = %d \n\n", doscientas,quinientas);
printf(" %15s ",nombre_moneda[peseta]);
printf(" %15s ",nombre_moneda[duro]);
printf(" %15s \n\n",nombre_moneda[quinientas]);
}

La razón principal para utilizar tipos enum es ayudar a la legilibilidad de los programas. Si
estamos tratando con alguna especie de código es más obvio trabajar con los valores de ese código que
los números asociados. Los tipos enum funcionan normalmente para uso interno y no para operaciones
de E/S.

3.4.3.-DECLARACIÓN DE ESTRUCTURAS.

Una declaración de una estructura se hace indicando el nombre de la variable estructura y


especificando una secuencia de "miembros" de la estructura que pueden ser de diferentes tipos.

Siguen los formatos:

Struct [nombre]{decla_lista_miem} declarador[,declarador...];


Struct nombre declarador[,declarador...];

Donde:

decla_lista_miem: Son los nombres y tipos de los miembros de la estructura.

nombre: Es el nombre de la estructura.

declarador: Son los nombres de las variables del tipo estructura.


En el segundo formato da por supuesto que el tipo de estructura esta previamente definido.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 25


Capítulo 3 Lenguaje “C”

La opción decla_lista_miem es una lista de una o más variables. Cada variable declarada en
esta lista se define como un miembro del tipo de estructura. Los miembros de la estructura pueden
tener cualquier tipo conocido: fundamental, array, puntero, union o estructura. No se permite que un
miembro tenga el tipo de la estructura en la cual aparece, sin embargo un miembro puede declararse
como un puntero al tipo de estructura en el cual aparece..

Los miembros de la estructura se almacenan secuencialmente en el mismo orden en el cual se


declaran.

Ejemplos:

1: struct {
float x,y;
} complejo;
2: struct ejemplo {
char nombre[20];
int var1;
long modelo;
} tiempo;
3: struct ejemplo estudiante, facultad, reunion;
4: struct ejemplo {
char c;
float *pf;
struct ejemplo *proximo;
} r;

El primer ejemplo define una variable estructura de nombre complejo. La estructura tiene dos
miembros x e y de tipo float. El tipo estructura no tiene nombre. En el segundo ejemplo el nombre de
la estructura es ejemplo.

El tercer ejemplo define tres variables, estudiante, facultad y reunión del tipo de estructura
ejemplo definido anteriormente en el ejemplo2.

Mientras que en el cuarto ejemplo se define la variable r del tipo struct ejemplo en el cual hay
un miembro que esta declarado como un puntero al tipo de estructura que esta siendo definido.

Ejemplo: Vemos un ejemplo de declaración de una estructura y su utilización con las sentencias
printf(), scanf() y sizeof().

26 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

/* ------------------
Programa: struct.c
------------------ */
main()
{
struct personal /* define el tipo 'struct personal' */
{
char nombre[30];
long int dni;
} empleado1, empleado2; /* define las variables
/*'empleado1','empleado2' */
/* del tipo struct personal */
printf("\n\n\n");
printf("Tamaño del tipo struct personal (%d) octetos\n\n", sizeof(struct
personal)); ║
printf("nombre empleado1 = ");
gets(empleado1.nombre);
printf("nombre empleado2 = ");
gets(empleado2.nombre);
printf("DNI empleado1 = ");
scanf("%ld",&empleado1.dni);
printf("DNI empleado2 = ");
scanf("%ld",&empleado2.dni);
printf("\n\n\n");
printf(" NOMBRE DEL EMPLEADO1 = <%10s> \n", empleado1.nombre);
printf(" D.N.I. = <%10ld> \n\n",empleado1.dni);
printf(" NOMBRE DEL EMPLEADO2 = <%10s> \n", empleado2.nombre);
printf(" D.N.I. = <%10ld> \n",empleado2.dni);
}

3.4.4.-DECLARACIONES DE UNIONES.

La declaración de una unión define el nombre de la variable unión y especifica los miembros
de la unión los cuales pueden tener diferentes tipos. Una variable del tipo union almacenará uno, y
solo uno, de los valores simples definidos en la unión.

Siguen los formatos:

union[nombre]{decla_lista_miem}declarador[,declarador...];
union nombre declarador[,declarador...];

La declaración de uniones tiene el mismo formato que la declaración de estructuras excepto


que comienzan con la palabra unión en lugar de struct.

El espacio de almacenamiento asociado a una variable unión es el que se requiere para


almacenar el miembro mayor de la unión, de ello se deduce que si se usa un miembro más pequeño de
la unión la variable unión no usa todo el espacio. Todos los miembros se almacenan en el mismo
espacio que comienza en la misma dirección. El valor almacenado es sobreescrito cada vez que un
valor se asigna a un miembro diferente.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 27


Capítulo 3 Lenguaje “C”

Ejemplos:

1: union signo {
int var1;
unsigned var2;
} numero1;
2: union {
*a,b;
float f[20];
} numero2;
3: union {
struct {
char var1;
unsigned color:4;
} ventana1,ventana2,ventana3,ventana4;
} pantalla[25][80];

El primer ejemplo define una variable unión "numero1" con dos miembros: var1 (entero con
signo), var2 (entero sin signo). En este caso el valor que almacene la variable número se podrá
almacenar como un entero con signo o sin signo. El tipo de unión se llama signo.

En el segundo ejemplo el tipo de unión no tiene nombre. En él el espacio reservado para


almacenar un valor de la variable numero2 es un array de 20 elementos de tipo float ya que f es el
mayor miembro de la unión.

El último ejemplo define un array, pantalla, de dos dimensiones de uniones. El array contiene
2000 elementos cada uno de los cuales es una unión con cuatro miembros:
ventana1,ventana2,ventana3,ventana4 donde cada uno de los miembros es una estructura. Cada
elemento de la unión almacenará uno de los cuatro posibles miembros de la estructura cada vez.

Ejemplo: En este ejemplo se declara una union y muestra un caso de utilización de sus
miembros.

/* -----------------
Programa: union.c
----------------- */
main()
{
union entero_flotante /* define el tipo 'union */
/* entero_flotante' */
{
int entero;
float flotante;
} x; /* define la variable 'x' del tipo */
/* union entero_flotante */
printf("\n\n\n");
printf("Tamaño del tipo union entero_flotante (%d) octetos\n",sizeof(union
entero_flotante));
x.entero=1350;
printf("\nMiembro x.entero ..... %d",x.entero);
x.flotante=12.83;
printf("\nMiembro x.flotante ... %.2f\n\n",x.flotante);
}

3.4.5.-DECLARACIONES DE ARRAYS.

28 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

La declaración de un array consiste en definir el nombre del array y el tipo de cada uno de los
elementos. Se puede definir también el número de elementos del array. Una variable del tipo array es
un puntero al tipo de los elementos del array.

Siguen los formatos:

tipo declarador[constante-expresión];
tipo declarador[];

Donde:

declarador: Es el nombre del array, donde los corchetes que siguen indican tipo array.

constante-expresión: Indica el número de elementos del array.

tipo: Es el tipo de cada elemento individual del array. No se permite el tipo void.

En el segundo formato no se incluye constante-expresión con lo cual este formato solo se


puede usar si el array es inicializado, si se declara como un parámetro formal o si es declarado como
una referencia a un array explícitamente definido en alguna parte del programa.

Para declarar un array multidimensional se utiliza el formato:

tipo declarador[cost_expres][const_expres]...

Cada const_expres define el número de elementos de una dimensión dada.

Cuando un array multidimensional se declara dentro de una función, la primera cost_expres se


puede omitir si el array es inicializado, declarado como un parámetro formal o declarado como una
referencia a un array explícitamente definido en alguna parte del programa.

El espacio de almacenamiento reservado para un array es el requerido para todos sus


elementos y estos se almacenan en localizaciones de memoria continuas y crecientes del primer
elemento al último, sin blancos entre los elementos. Los arrays son almacenados por filas.

Por ejemplo el siguiente array consta de 3 filas con dos columnas. En él las dos columnas de la
primera fila se almacenan primero, luego las dos columnas de la segunda fila y por último las dos
columnas de la tercera fila.

char matriz[3][2];

Ejemplos:

1: int marca[10], var1;


2: float matriz[10][15];
3: struct {
float x,y;
} complejo[100];
4: char *nombre[20];

En el primer ejemplo se define la variable marca como un array con 10 elementos de tipo
entero.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 29


Capítulo 3 Lenguaje “C”

El segundo ejemplo define matriz como un array bidimensional de 150 elementos de tipo
float.

El tercer ejemplo define un array de estructuras de 100 elementos, donde cada elemento es una
estructura con dos miembros.

El cuarto ejemplo define un array de punteros de 20 elementos donde cada elemento es un


puntero a un valor de tipo char.

Ejemplo1: Declaración y utilización de un array.

/* ------------------
Programa: array1.c
------------------ */
main()
{
int x[10];
int t;
printf("\n\n");
for(t=0;t<10;t++)
{
x[t]=t;
printf("\n El valor de x[%d] es = %d \n",t,x[t]);
}
}

Ejemplo2: Lectura de un array y cálculo de la media de sus valores.

/* -----------------
Programa array2.c
----------------- */
# include <stdio.h>
main()
{
long ejemplo[10],i,media;
for (i=0;i<4;i++) {
printf("introducir un numero %d: ",i);
scanf("%ld",&ejemplo[i]);
}
media=0;
for (i=0;i<4;i++) media=media+ejemplo[i];
printf("\n\n\n\n La media es %ld \n",media/4);
}

30 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Ejemplo3: Un ejemplo interesante con arrays.

/* -----------------
Programa array3.c
----------------- */
char ch[7];
main()
{
int i;
for (i=0;i<7;i++)
{
ch[i] = 'A'+i;
}
printf("\n\n\n\n ");
for (i=0;i<7;i++)
printf("- %c ", ch[i]);
printf(" -");
printf("\n\n");
}

3.4.6.-DECLARACIONES DE PUNTEROS.

La declaración de un puntero define el nombre de la variable puntero y el tipo del objeto al


cual la variable apunta. Las variables puntero pueden apuntar,valga la redundancia, a variables
fundamentales, funciones, arrays, estructuras o uniones.

Siguen el formato:

tipo *declarador;

Donde:

tipo: es el tipo del objeto al que apunta, puede ser algún tipo fundamental, estructura o
unión.

declarador: nombre de la variable puntero.

Ejemplos:

1: char *mensaje;
2: int *punteros[10];
3: int (*puntero)[10];
4: struct lista *proximo, *previo;
5: struct lista {
char *anillo;
int contador;
struct lista *proximo;
} línea;

En el primer ejemplo se define la variable puntero mensaje que apunta a una variable de tipo
char.

El segundo ejemplo define un array de punteros, punteros, de 10 elementos a variables de tipo


int.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 31


Capítulo 3 Lenguaje “C”

El tercer ejemplo define la variable puntero "puntero" que apunta a un array de 10 elementos
de tipo int.

El cuarto ejemplo define dos variables puntero que apuntan a una estructura de tipo lista.

El quinto ejemplo declara la variable línea como un tipo de estructura lista. El tipo de
estructura lista esta definido con tres miembros, el primero es un puntero a un valor char, el segundo
es un valor entero y el tercero es un puntero a una estructura de tipo lista.

Ejemplo1: Trabajando con punteros podremos trabajar con las direcciones de almacenamiento de
la variables.

/* ------------------
Programa: punte1.c
------------------ */
# include <stdio.h>
main()
{
int *cont_dir, cont, val;
cont = 100;
cont_dir = &cont;
val = *cont_dir;
printf("\n cont = %d \n",cont);
printf("\n &cont = %d \n",&cont);
printf("\n cont_dir = %d \n",cont_dir);
printf("\n *cont_dir = %d \n",*cont_dir);
printf("\n Valor = %d \n",val);
}

Ejemplo2: Imprimiendo direcciones en hexadecimal.

/* ------------------
Programa: punte2.c
------------------ */
# include <stdio.h>
main()
{
int x;
int *p1,*p2;
x=100;
p1 = &x;
p2 = p1;
/* .. ..
Imprimimos el valor hexadecimal de la direccion de x.
.. .. */
printf("\n\n\n La direccion de <x> es <%x> .",p2);
/* .. ..
Imprimimos el valor de x.
.. ..*/
printf("\n\n Su contenido es <%d> .\n\n",*p2);
}

3.5.-DECLARACIONES DE FUNCIONES.

32 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Consisten en definir el nombre y el tipo que retorna una función y posiblemente establecer los
tipos y número de argumentos de la función. Las declaraciones de funciones pueden incluir los tipos
de almacenamiento extern o static, discutidos más adelante.

Siguen el formato:

[tipo] declarador([lista_tipos_arg])[,declarador...];

Donde:

tipo: es el tipo que retorna la función. Si se omite en la declaración de la función el tipo de


retorno que toma por defecto es int.

La lista_tipos_arg establece el número y tipos de los argumentos de la función. Sigue el


formato:

[nombre_tipo][,nombre_tipo...][,]

El primer nombre_tipo es el tipo del primer argumento de la función, el segundo es el tipo del
segundo argumento y así sucesivamente. Si lista_tipos_arg termina con una coma, el número de
argumentos de la función es variable sin embargo a de tener por lo menos un número de argumentos
equivalente al especificado antes de la coma. Si lista_tipos_arg contiene solo una coma, el número de
argumentos de la función es variable y puede ser cero.
El nombre_tipo para un tipo fundamental, un tipo estructura o un tipo unión consiste solo en
especificar solo el tipo (ej. int). El nombre_tipo para punteros,arrays y funciones están formados por la
combinación del tipo especificado con un "declarador abstracto" por fuera del identificador.
El tipo especial void se puede usar en lugar de lista_tipos_arg para declarar una función que
no tiene argumentos. La frase "void *" puede ser la lista_tipos_arg y especifica un argumento de algún
tipo puntero.
La lista_tipos_arg puede omitirse, sin embargo los paréntesis deben figurar en la declaración
vacíos, en este caso no se establecen ni el número ni el tipo de los argumentos de la función. En este
caso el compilador no ejecuta el chequeo de tipos entre los argumentos de la llamada a la función y los
parámetros formales de la definición de la función.

Las funciones pueden retornar valores de cualquier tipo excepto arrays y funciones. El
identificador de la función puede ser modificado con uno o más asteriscos "*" para declarar el retorno
de un tipo puntero. Las funciones no pueden retornar arrays y funciones, como comentábamos, sin
embargo si pueden retornar punteros a arrays y punteros a funciones.

Ejemplos: Prototipos.

1: int sumar(int, int);


2: char *encontrar_cadena(char *,);
3: void dibujar(void);
4: double (*sumar(double,double))[3];
5: int (*(*seleccion(void))(int);
6: char *letra;
short *numero;
int imprimir(void *);

El primer ejemplo declara la función sumar con dos argumentos enteros y que retorna un valor
entero.
El segundo ejemplo declara la función encontrar_cadena que retorna un puntero a un valor
tipo char. La función lleva al menos un argumento, un puntero a un valor char.La función puede llevar
más argumentos.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 33


Capítulo 3 Lenguaje “C”

El tercer ejemplo declara una función que no retorna ningún valor y que no lleva argumentos.

La función sumar del ejemplo cuatro retorna un puntero a un array de tres valores tipo double,
y lleva dos argumentos de tipo float.

El quinto ejemplo declara la función seleccion, sin argumentos, que retorna un puntero a una
función que retorna un puntero. El puntero que retorna apunta a una función que lleva un argumento
int y retorna un valor int.
En el último ejemplo, la función imprimir se declara para llevar un argumento de cualquier
tipo puntero y retornar un entero. En este caso los puntero declarados en el ejemplo, letra (char) y
número (short), podrían serle pasados como argumentos a la función imprimir sin que se produzcan
errores.

Ejemplo1: Programa saludo.

/* --------------------
Programa: funcion1.c
-------------------- */
/* .. ..
Un sencillo programa con dos
funciones.
.. .. */
void hola(void);
main()
{
printf("\n\n\n\n");
hola();
}
/* Definicion de la función hola */
/* ----------------------------- */
void hola()
{
printf(" hola \n\n");
printf(" Bienvenido al lenguaje C. ");
printf("\n\n\n");
}

Ejemplo2: un producto de dos números.

/* --------------------
Programa: funcion2.c
-------------------- */
void multiplicar(int, int);
main()
{
printf("\n\n\n\n");
multiplicar(10, 11);
}
void multiplicar(a, b)
int a, b;
{
printf(" El producto %d x %d es = %d \n\n\n", a, b, a*b);
}

Ejemplo3: retorno del producto de dos números.

34 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

/* -------------------
Programa funcion3.c
------------------- */
/* .. ..
Este programa refleja el uso de la sentencia
return en las funciones de C.
.. .. */
int mul(int, int);
main()
{
int respuesta,a=10,b=11;
respuesta = mul(a,b);
printf("\n\n\n");
printf(" El producto %d x %d tiene por \n\n",a,b)
printf(" respuesta %d \n",respuesta);
}
/* Definicion de la función mul */
/* ---------------------------- */
mul(t,p)
int t;
int p;
{
return t*p;
}

Ejemplo4: Obtención del valor absoluto de tres números.

/* --------------------
Programa: funcion4.c
-------------------- */
int abs(int); /*Prototipo*/
main()
{
int a = 10,b = 0,c = - 22;
int d,e,f;
d = absoluto(a);
e = absoluto(b);
f = absoluto(c);
printf("\n\n\n");
printf(" -----------------------------------------\n");
printf(" -> Valor absoluto %4d es %4d - \n", a, d);
printf(" -> Valor absoluto %4d es %4d - \n", b, e);
printf(" -> Valor absoluto %4d es %4d - \n", c, f);
printf("------------------------------------------");
printf("\n\n\n");
}
/* Función valor absoluto */
/* ====================== */
absoluto(x)
int x;
{
int y;
y=(x<0)?-x:x;
return(y);
}

3.6.-CLASES DE ALMACENAMIENTO.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 35


Capítulo 3 Lenguaje “C”

Las clases de almacenamiento de las variables determinan si esta es "local" o "global".

Una variable global es aquella que esta siempre presente durante la ejecución del programa.
Todas las funciones son globales.

Las variables locales solo están presentes en el bloque en el que son definidas.

Las cuatro clases de almacenamiento disponibles en "C" son:

- auto.
- register.
- static.
- extern.

Los almacenamientos auto y register son locales y los almacenamientos static y extern son
globales.

El lugar de declaración de una función o variable dentro de un fichero fuente afecta al


almacenamiento y a la visibilidad de dichas variables o funciones. Las declaraciones fuera de todas las
definiciones de funciones son declaraciones a "nivel externo". Las declaraciones dentro de las
funciones son declaraciones a "nivel interno".

El correcto significado de cada uno de los tipos de almacenamiento depende de si las


declaraciones son a nivel externo o interno y de si se declara una variable o función. Lo vemos todo en
los siguientes subapartados:

3.6.1.-DECLARACIÓN DE VARIABLES A NIVEL EXTERNO.

La declaración de variables a nivel externo usan las clases de almacenamiento static y extern o
se omite este. Por tanto el almacenamiento auto y register no se permiten a nivel externo.

Existen varias formas de definir variables a nivel externo:

- Declarándola con almacenamiento static, si la variable no se inicializa en la declaración


esta es inicializada a cero en tiempo de compilación.

Ej: 1: static int var1=23;


2: static int var2;

- Inicializando explícitamente la variable a nivel externo sin clase de almacenamiento, es


decir fuera de todas las definiciones de funciones.

Ej: int edad=35;

Una vez definida la variable a nivel externo esta es reconocida en todo el programa en el que
aparece.

La clase de almacenamiento extern se usa para declarar una referencia a una variable definida
en otra parte, puede ser declarado por ejemplo para hacer visible una variable fuera de su definición.
Una referencia a una variable con extern hace que la variable sea visible fuera del alcance del fichero
fuente en la cual se declara.
Las declaraciones que contienen la clase de almacenamiento extern no permiten inicializar las
variables ya que sus valores estarán previamente definidos.

36 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Ejemplo:

Programa: fuente1.c

/*---------------------------*
Fichero fuente uno
*---------------------------*/
extern int i;
/* Referencia a i , definida abajo */

main()
{
i++; /* i vale 4 */
printf("%d \n",i);
proximo();
}
int i=3; /* Definición de i */
proximo()
{
i++;
printf("%d \n",i); /* i vale 5 */
otro();
}

Programa: fuente2.c

/*---------------------------*
Fichero fuente dos
*---------------------------*/
/* referencia a i del primer fichero fuente */
extern int i;
otro()
{
i++;
printf("%d \n",i); /* i vale 6 */
}

Los compilamos, linkamos y ejecutamos conjuntamente:

# cc -compat fuente1.c fuente2.c


# ./a.out

Este programa imprime los valores 4, 5 y 6.


Los dos ficheros fuente contienen un total de tres declaraciones externas de la variable i. Sólo
una de las declaraciones contiene una inicialización "int i=3;", que define una variable global i con
valor inicial 3. La declaración extern de i al principio del primer fichero fuente hace la variable global
visible antes de su definición en el fichero. Sin la declaración extern, la función main() no puede
referenciar la variable global i. La declaración extern de i en el segundo fichero fuente hace la variable
global visible en este fichero fuente.
Si la variable i no hubiese sido inicializada, esta tomaría el valor 0 en tiempo de linkado, en
este caso imprimiría los valores 1, 2 y 3. Queda como ejemplo el comprobarlo en el caso anterior.
3.6.2.-DECLARACIÓN DE VARIABLES A NIVEL INTERNO.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 37


Capítulo 3 Lenguaje “C”

Cuando no se especifica una clase de almacenamiento en la declaración de una variable a nivel


interno, el almacenamiento tomado por defecto es auto.

El almacenamiento auto define a las variables como locales, con los cual solo son visibles
dentro del bloque en el cual son declaradas. Las variables con almacenamiento auto no se inicializan
automáticamente en tiempo de compilación por ello si no se inicializan las variables auto estarán
indefinidas.

El almacenamiento register indica al compilador que almacene la variable en un registro de la


CPU, si es posible, con lo cual se consigue mayor rapidez de acceso. Las variables declaradas con
almacenamiento register tienen la misma visibilidad que las variables auto.

El número de registros que se pueden utilizar para almacenar variables depende de la máquina.
Si no se disponen de registros cuando el compilador encuentra una declaración register, a la variable
se le asigna el almacenamiento auto y se almacena en memoria. El almacenamiento register (si es
posible) solo esta garantizado para los tipos int y puntero.

Una variable declarada a nivel interno con almacenamiento static tiene tiempo de vida global,
sin embargo solo es visible dentro del bloque en el cual se declara. Es decir la variables declaradas
como static retienen el valor cuando la ejecución del bloque finaliza, a diferencia de las variables auto.
Las variables son inicializadas a 0 en tiempo de ejecución si explícitamente no se hace.

Una variable declarada con almacenamiento extern es una referencia a una variable con el
mismo nombre definida a nivel externo en alguno de los ficheros fuente. El propósito de una
declaración extern interna es hacer visible una variable de nivel externo dentro del bloque.

Ejemplo:

Programa interno.c
int i=1;
main()
{ /*referencia a i; definida a nivel externo. */
extern int i;
/* a toma valor inicial 0 y solo es visible dentro de la función main() */
static int a;
/* b es almacenado en un registro, si es posible */
register int b=0;
/* El almacenamiento por defecto es auto. */
int c=0;
/* Se imprimiran los valores: 1,0,0,0 */
printf("%d\n%d\n%d\n%d\n",i,a,b,c);
otra();
otra();
otra();
}
otra
{
/* i es redefinida */
int i=16;
/* Esta variable solo es visible dentro de otra() */
static int a=2;
a+=2;
/* Imprime los valores 16,4 */
printf("%d\n%d\n",i,a);
}

38 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

# cc -compat interno.c
# ./a.out

La variable "i" esta definida a un nivel externo con un valor 1. Con una declaración extern se
hace un referencia al nivel externo de la variable "i". La variable static "a" se inicializa
automáticamente a 0, con lo cual no es necesario iniciarla a 0 explícitamente.

Dentro de la función otra() la variable "i" se redefine como una variable local con valor inicial
16; esto no afecta a la "i" de nivel externo. la variable "a" se declara como una variable static
inicializada a 2 y esta tampoco tiene ningún conflicto con la variable "a" del main() ya que la
visibilidad de las variables static a nivel interno se reduce al bloque en el cual son declaradas.

3.6.3.-DECLARACIÓN DE FUNCIONES.

En la declaración de funciones se pueden usar las clases de almacenamiento static o extern. Las
funciones siempre son globales. Las funciones declaradas a nivel interno tienen el mismo significado
que las funciones declaradas a nivel externo.
Una función declarada como static solo es visible en el fichero fuente en el cual es definido,
por ello puede haber dos funciones declaradas como static en dos fichero fuente diferentes sin
conflicto. Las funciones declaradas como extern son visibles dentro de todos los ficheros fuente que
constituyen el programa. Las funciones que omiten la clase de almacenamiento en la declaración son
declaradas como por defecto como extern.

3.6.4.-DECLARACIONES COMPLEJAS.

A veces las declaraciones pueden llevar paréntesis, los cuales se usan para especificar
interpretaciones particulares de una declaración compleja. Varias combinaciones de los tipos array,
puntero y funciones pueden aplicarse a un mismo identificador, sin embargo algunas combinaciones
son ilegales como que un array este compuesto de funciones o que una función retorne un array o una
función.

En este tipo de declaraciones complejas, los corchetes y paréntesis a la derecha del


identificador tienen preferencia sobre el asterisco a la izquierda del identificador. Los corchetes y
paréntesis tienen la misma preferencia y el criterio es de izquierda a derecha.

Una regla simple para interpretar declaraciones complejas es la siguiente: Leer las
declaraciones de dentro a fuera, comenzar con un identificador y mirar si tiene corchetes o
paréntesis a la izquierda y si existen interpretar estos, luego buscar si tiene asteriscos a la
izquierda. Si se encuentra algún paréntesis derecho en algún momento, volver y aplicar las
reglas anteriores a todo lo que figura dentro de los paréntesis. Por último aplíquese el tipo
especificado. Vemos un ejemplo complejo:

char *(*(*variable)())[10]
7 6 4 2 1 3 5

1. El identificador "variable" es declarado como.


2. Un puntero a
3. Una función que retorna un
4. Un puntero a
5. Un array de 10 elementos, los cuales son
6. punteros a
7. valores char

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 39


Capítulo 3 Lenguaje “C”

Quizá el ejemplo anterior asuste un poco, por ello vemos unos ejemplos más sencillo de los
conceptos anteriores:

1: Un array de punteros a valores enteros.


int *vector[5];
2: Un puntero a un array de valores enteros.
int (*punte)[5];
3: Una función que retorna un puntero a un tipo long.
long *mayor(long,long);
4: Un puntero a una función que retorna un tipo long.
long (*variable)(long,long);
5: Un array de arrays de punteros a punteros a uniones.
union signo{
int x;
unsigned y;
} **vector[5][5];
6: Un array de punteros a arrays de punteros a uniones.
union espejos *(*clien[5])[5];

En el primer ejemplo los "[]" tienen mayor prioridad que el "*", por eso el identificador vector
es un array. El asterisco (puntero) se aplica al tipo de los elementos del array que son valores enteros.

En el segundo ejemplo el asterisco tiene mayor prioridad que los corchetes debido a los
paréntesis, por ello el identificador punte es un puntero a un array de 5 elementos enteros.

El modificador de función, los "()", tienen mayor prioridad que los asteriscos, así que en el
tercer ejemplo el identificador mayor es una función, con dos valores long como argumentos, que
retorna un puntero a un valor tipo long. Mientras que el cuarto ejemplo es similar al segundo, por ello
el identificador variable es un puntero a una función que retorna un valor tipo long; la función tiene
también dos argumentos tipo long.

Un puntero puede apuntar a otro puntero y un array puede contener otros arrays como se ve en
el ejemplo número cinco, en donde el identificador "vector" es un array de 5 elementos donde cada
uno de ellos es un array de 5 elementos punteros a punteros a uniones de dos miembros.

El sexto ejemplo muestra como la localización de los paréntesis altera el significado de una
declaración, en él el identificador "clien" es un array de 5 elementos punteros a arrays de 5 elementos
de puntero a uniones.

3.7.-INICIALIZACIÓN.
Una variable puede inicializarse a un valor inicial asignándoselo en la declaración de la
variable. dicho(s) valor(es) son precedidos por signo "=" en la declaración.

=valor(es)

Todos los tipos de variables pueden ser inicializadas, respetando una serie de restricciones
comentadas a continuación.

La funciones no admiten inicialización.

Las declaraciones que usan almacenamiento extern no admiten inicialización.

40 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Las variables static se inicializan a 0 si no se indica otro valor.

La inicialización de las variables con almacenamiento auto y register se ejecuta cada vez que el
control de ejecución pasa por el bloque en el cual están declaradas. Si no se inicializan su valor inicial
es indefinido.

La inicialización con almacenamiento auto de arrays, estructuras y uniones esta prohibida. Sólo
si están definidas con almacenamiento static pueden ser inicializados.

3.7.1.-INICIALIZACIÓN BÁSICA Y DE PUNTEROS.

Siguen el formato:

var=expresión.

En este caso el valor de la expresión se asigna a la variable.

Ejemplos:

1: int x=10;
2: register int *apuntador=0;
3: int c=(3*1024);
4: int *k=&x;

El primer ejemplo la variable x toma el valor 10. En el segundo el puntero apuntador se


inicializa a 0, es decir se le asigna el valor "null". El tercer ejemplo usa una expresión para inicializar
la variable c y el cuarto usa la dirección de la variable x para inicializar el puntero k.

3.7.2.-TIPOS AGREGADOS. ARRAYS, ESTRUCTURAS Y UNIONES.

Formato:

var={lista_valores};

Donde lista_valores son los valores de inicialización separados por comas. Cada valor puede se
una constante, expresión o otra lista_valores entre "{}".

Los valores de lista_valores se asignan en orden a los miembros de la variable del tipo
agregado. Si hay menos valores lista_valores que en el tipo agregado, los restantes miembros se
inicializan a 0. El caso contrario produce un error.

Ejemplo:

static int matriz[4][3]={


{1,1,1},║
{2,2,2},
{3,3,3,},
{4,4,4,},
};

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 41


Capítulo 3 Lenguaje “C”

En este ejemplo matriz es un array 4x3 con las cuatro filas inicializadas. Nótese que
lista_valores de la tercera y cuarta filas contienen comas después de la última constante y que el
último lista_valores "{4,4,4,}" va seguido de coma. Esta comas extras están permitidas pero no se
requieren en la declaración. Solo son requeridas las comas que separan los valores dentro de
lista_valores.

En el siguiente ejemplo los valores son agregados en orden a los subagregados.

int matriz[4][3]={
1,1,1,2,2,2,3,3,3,4,4,4
};

Las llaves pueden aparecer también rodeando valores individuales en lista_valores.

Ejemplos:

1: struct lista {
int i,j,k;
float m[2][3];
} var1= {
1,
2,
3,
{4.0,4.0,4.0}
};
2: union
char x[2][3];
int i,j,k;
} var2= {
{'1'},
{'6'}
};

3.7.3.-INICIALIZACIÓN DE CADENAS.

Un array puede ser inicializado con una cadena. Por ejemplo:

char codigo[]="xyz";

Código es un array de cuatro valores carácter. Recordar que el cuarto elemento es el carácter
null, el cual termina todas las cadenas.

Si el tamaño del array se especifica y la cadena es mayor que el tamaño indicado los caracteres
extra son descartados. En el siguiente ejemplo se inicializa la cadena extra con los tres primeros
caracteres de "rstuvwxyz".

char extra[3]="rstuvwxyz";

En este ejemplo los caracteres "uvwxyz" y null son descartados. Algunos compiladores
devuelven un error en este tipo de inicializaciones.

42 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

Si la cadena es menor que el tamaño indicado en el array, los restantes elementos del array se
inicializan a 0 (Carácter null).

3.8.-DECLARACIÓN DE TIPOS.
Una declaración de tipo define el nombre y los miembros de un tipo estructura o unión, o el
nombre y el conjunto enumeración de un tipo enumeración. El nombre de un tipo declarado se puede
usar en las declaraciones de variables o funciones para referirse a ese tipo. Esto se usa cuando las
funciones y variables tienen el mismo tipo.

Una declaración typedef define un tipo especificado para un tipo. Estas declaraciones se usan
para configurar un mayor o menor significado para los tipos ya definidos por el "C" o por los tipos ya
declarados por el usuario.

3.8.1.-ESTRUCTURAS, UNIONES Y ENUMERACIONES.

Las declaraciones de tipos estructuras, uniones y enumeraciones tienen el mismo formato


general como la declaración de variables de ese mismo tipo. En la declaración de tipos el identificador
de la variable se omite, de forma que la variable no es declarada, sin embargo la declaración del tipo
estructura, union o enumeración es obligatorio. La lista de declaración de tipos de miembros o la
definición de la lista de la enumeración tiene que aparecer en la declaración del tipo.

Ejemplos:

1: enum estados {
perdido=-1,
var1,
var2=0,
ganar
};
2: struct estudiante {
char nombre[20];
int ind,clase;
};

El primer ejemplo declara un tipo enumeración llamado estados. El nombre del tipo se puede
usar en declaraciones de variables enumeración. El identificador perdido tiene el valor -1 asignado;
var1,var2 toman el valor 0, la variable ganar toma el valor 1.

El segundo ejemplo declara un tipo estructura de nombre estudiante. Las variables estructuras
pueden ser declaradas del tipo estudiante, como por ejemplo:

struct estudiante empollon;

3.8.2.-DECLARACIÓN CON TYPEDEF.

Siguen el formato:

typedef tipo declarador [,declarador...];

Una declaración typedef es análoga a la declaración de variables a diferencia de la palabra


clave typedef que aparece en el lugar del tipo de almacenamiento. Typedef no crea tipos sino que

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 43


Capítulo 3 Lenguaje “C”

sinónimos para los nombres de tipos existentes. Todos los tipos pueden ser declarados con typedef,
incluyendo los tipo puntero, funciones y arrays. La orden typedef para un tipo puntero,una estructura
puede declararse antes de que el tipo puntero, estructura o unión sea definido.

Ejemplos:

1: typedef int TODO;


2: typedef struct club {
char nombre[30];
int tamaño[30];
} GRUPO;
3: typedef GRUPO *PG;
4: typedef void DIBUJAR(int,int);

El primer ejemplo declara TODO como un sinónimo del tipo int.


El segundo ejemplo declara GRUPO como un tipo estructura con tres miembros de nombre
club. Se usa el nombre de typedef GRUPO para especificar la estructura.
El tercer ejemplo usa un nombre typedef previo para declarar un tipo puntero. El tipo PG se
declara como un puntero al tipo GRUPO, el cual en cambio se define como un tipo estructura.
El último ejemplo crea el tipo DIBUJARF para una función que no retorna ningún valor y tiene
dos argumentos enteros. Es decir las siguientes declaraciones son equivalentes:

DIBUJAR caja;
void caja(int, int);

3.9.-NOMBRES DE TIPOS.
Un nombre de tipo especifica un tipo de datos particular. Estos nombres se usan en tres
contextos: en la lista de tipos de argumentos de la declaración de funciones, en tipos cast y en
operaciones sizeof. Discutidas posteriormente.

Los nombres de tipo para los tipos fundamentales, enumeraciones, estructuras y uniones son
simplemente los especificadores de tipo para esos tipos.

Un nombre para un tipo puntero, array o función tiene el formato:

tipo declarador_abstracto

Un declarador abstracto es un declarador sin un identificador, formado por uno o más punteros,
array o modificador de funciones. El modificador de puntero "*" siempre aparece antes del
identificador en el declarador, mientras que los "[]" en el array y los "()" en la función aparecen
después del identificador.

Los declaradores complejos pueden ser muy complicados, ya que los paréntesis tienen una
particular interpretación. El declarador abstracto "()" solo no se permite porque es ambiguo.

Ejemplos:

1: long *
2: double *(double, double)
3: int(*)[5]
4: int(*)(void)

44 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 3

El primer ejemplo da el nombre de tipo para un tipo "puntero a long".

El segundo ejemplo es el nombre para una función que lleva dos argumentos double y retorna
un puntero a un valor double.

El tercer y cuarto ejemplo muestran como los paréntesis modifican los declaradores abstractos.
El ejemplo 3 da el nombre para un puntero a un array de 5 valores enteros. El ejemplo 4 nombra un
puntero a una función sin argumentos y retorna un entero.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 45


Capítulo 3 Lenguaje “C”

46 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 4

CAPÍTULO 4
OPERADORES Y EXPRESIONES

4.1.-CRITERIO DE PRECEDENCIA DE LOS OPERADORES.


La siguiente tabla contiene el criterio de precedencia seguido por los operadores C, relacionada
de mayor a menor precedencia. Si varios operadores aparecen juntos en una expresión y todos ellos
tienen igual precedencia son evaluados según su asociatividad, es decir o bien de izquierda a derecha o
de derecha a izquierda.

PRECEDENCIA Y GRADO DE ASOCIATIVIDAD DE LOS OPERADORES C.


---------------------------------------------------------
Operador Asociatividad
------------ --------------------
() [] . -> Izquierda a derecha.
- ~ ! * & Derecha a izquierda.
++ -- sizeof casts Izquierda a derecha.
* / % Izquierda a derecha.
+ - Izquierda a derecha.
<< >> Izquierda a derecha.
< > <= >= Izquierda a derecha.
== != Izquierda a derecha.
& Izquierda a derecha.
^ Izquierda a derecha.
| Izquierda a derecha.
&& Izquierda a derecha.
|| Izquierda a derecha.
? : Derecha a izquierda.
= *= /= %= Izquierda a derecha.
+= -= <<= >>= Izquierda a derecha.
&= |= ^= Izquierda a derecha.
' Izquierda a derecha.

Notas: Los operadores son listados en orden descendente de precedencia. Los operadores que
aparecen en la misma línea tienen igual precedencia.

Cuando varios operadores con la misma procedencia aparecen al mismo nivel en una expresión
la evaluación se realiza de acuerdo a su asociatividad. Los operadores lógicos son evaluados de
izquierda a derecha evaluando el mínimo número de operandos necesarios para determinar el resultado
de la expresión, con lo cual algunos operandos de la expresión pueden no ser evaluados.

Por ejemplo en la expresión "x && y++", el segundo operando "y++" es evaluado sólo si x es
cierto (no 0). Nótese como en una expresión de este tipo "y" no será incrementada en el caso de que
"x" sea falso ( 0 ).

Vemos unos ejemplos de agrupaciones por defecto.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 47


Capítulo 4 Lenguaje “C”

Expresión Agrupamiento por defecto.


---------- -------------------------
1. a & b || c (a&b)||c
2. a=b||c a=(b||c)
3. q&&r||s-- (q&&r)||s--

El tercer ejemplo es el más interesante, en él el operador lógico AND tiene mayor precedencia
que el operador lógico OR, por ello la expresión "q&&r" es evaluada primero antes que s--. De esto se
deduce que si "q&&r" toma valor distinto de "0" la expresión "s--" no es evaluada con lo cual la
variable "s" no decrementa su valor. Para solucionar esto se colocaría "s" como primer operando de
toda la expresión o bien se decrementaría el valor de "s" en una operación aparte

4.2.-EXPRESIONES CON OPERADORES.


Las expresiones de asignación con operadores pueden ser unarias, binarias, o ternarias.
Una expresión unaria esta formada por un operando que lleva asociado un operador unario o un
operando encerrado entre paréntesis y precedido por la palabra reservada "sizeof".

op_unariooperador
sizeof(operador)

Una expresión binaria se construye con dos operandos unidos por un operador.

operando operador operando

Un expresión terciaria esta formada por tres operandos unidos por un operador ternario (? :).

operando?operando:operando

Los operadores de asignación unarios son el incremento (++) y decremento (--). Los
operadores de asignación binaria son el operador de asignación simple (=) y los operadores de
asignación compuesta. Cada operador de asignación compuesto es una combinación de otros
operadores binarios con el operador de asignación simple. Los formatos de las expresiones de
asignación simples son:

operando++
operando--
++operando
--operando
operando1=operando2

4.2.1-OPERADOR INCREMENTO.

El operador incremento realiza una tarea muy simple: incrementa el valor de su operando en 1.
Se ofrecen en "C" dos variedades. En la primera de ellas, el ++ aparece antes de la variable afectada,
es el llamado modo "prefijo". En la segunda, el ++ se encuentra situado detrás de la variable. A esta
variedad la denominaremos modo "sufijo". El operador incremento tiene la ventaja de generar un
código compilado ligeramente más eficiente, ya que su estructura se asemeja más al código máquina
real.

La diferencia entre ambos modos reside en el preciso momento en que se realiza la operación
de incremento. En primer lugar prestaremos atención a las semejanzas, y volveremos más adelante a
las diferencias. El ejemplo siguiente demuestra el funcionamiento de ambos operadores.

48 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 4

Ejemplo1: utilización del operador incremento.

/* ------------------
Programa: incre1.c
------------------ */
main()
{
int ultra = 0, super = 0;
int a,b;
printf(" .......... .......... \n");
while (super < 6)
{
a = super++;
b = ++ultra;
printf(" super = %d, ultra = %d \n", a,b);
}
printf("\n\n\n");
}

Ejemplo2: Otro ejemplo de este operador.

/* ------------------
Programa: incre2.c
------------------ */
/*
.. ..
Este programa permite ver el uso del operador
incremento como prefijo y como sufijo.
.. ..
*/
main()
{
int a=1,b=1;
int amas, masb;
amas = a++; /* sufijo */
masb = ++b; /* prefijo */
printf("\n\n\n\n\n");
printf(" a amas b masb \n");
printf(" - ---- - ---- \n\n");
printf(" %3d %5d %5d %5d",a,amas,b,masb);
printf("\n\n\n\n\n");
}

que dá como salida

a amas b masb
─ ──── ─ ────
2 1 2 2

donde tanto "a" como "b" se han incrementado en 1. Sin embargo "amas" contiene el valor de "a"
antes de que este fuera cambiado, en tanto que "masb" toma el valor de "b" tras el incremento. Por
tanto:

amas = a++ sufijo : a cambia después de ser usado su valor.


masb = ++b prefijo: b cambia antes de ser usado su valor.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 49


Capítulo 4 Lenguaje “C”

Por tanto cuando se utiliza el operador en solitario en una sentencia, como "amas++", no
importa la modalidad escogida. Si importa, y mucho, cuando el operador y su operando forman parte
de una expresión mayor, en un caso como este, uno debe tener bastante claro el resultado que desea
obtener.

Veamos un ejemplo de su funcionamiento en un bucle:

talla = 29.0;
while (++talla < 48.5)
{
pie=ESCALA*talla+tope;
printf("%10.1f %16.2f cm. \n", talla, pie);
}

Primero se aumenta en uno el valor de talla y se compara con 48.5. Si es menor, el programa se
introduce en el bucle, ejecutándose este una vez. Vuelta a la sentencia while, nuevo incremento en uno
de talla y nueva comparación. El ciclo se repite hasta que talla exceda el valor prefijado. Nótese que la
primera vez talla se incrementa y toma el valor 30.0. Si hubiésemos usado la forma sufijo, talla++,
talla se incrementaría después de realizada la comparación.

Ejemplo: Utilización en un blucle del operador "++".

/* -----------------
Programa incre3.c
----------------- */
#define MAX 15
main()
{
int cont = 0;
printf("\n\n ()()()()()()()()()()()()()()()()()() \n");
printf(" Contare ovejas para dormirme. \n");
printf(" ()()()()()()()()()()()()()()()()()() \n\n");
while (++cont<MAX)
printf(" %d millones de ovejas y aun no me he dormido ...\n",cont);
printf("\n\n %d millones de ovejas y ZZZzzzzzz ...... \n",cont);
}

4.2.2.-OPERADOR DECREMENTO.

Existen también en "C" dos operadores decremento que se corresponden con los incremento
que acabamos de comentar. En ellos se utiliza -- en lugar de ++.

--cont; forma prefijo del operador decremento.


cont--; forma sufijo del operador decremento.

4.2.3.-PRECEDENCIA DE LOS OPERADORES INCREMENTO Y DECREMENTO.

Los operadores de incremento y decremento tienen una precedencia o asociación muy alta; Por
ello x*y++ significa (x)*(y++) y no (x*y)++; por otra parte no puede ser de otra forma, ya que esta
última expresión carece de sentido, por que los operadores de incremento y decremento afectan a una
variable, y el producto x*y no los es.

50 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 4

4.3.-EXPRESIONES DE TIPO CAST.


Vemos un ejemplo de conversión de tipo.

char ch;
int i;
float f;
double d;

result=(ch / i) + (f * d) - (f + i);

int double float

int double float

double

double

Se puede forzar a una expresión a ser de un tipo específico usando la construcción llamada
"cast"

Una expresión tipo cast tiene el siguiente formato general:

(tipo)expresión

donde tipo es uno de los tipos estándares de datos de C. Por ejemplo, si "x" es un entero y se quiere
uno asegurar que la computadora evaluaría la expresión "x/2" como de tipo float para garantizar un
resultado float haremos:

(float) x/2;

Aquí el cast (float) se asocia con x, que provoca que la computadora eleve 2 al tipo float y el
resultado a float. Nótese la diferencia si el caso anterior lo escribimos como

(float) (x/2);

donde la computadora obtiene una división entera y eleva el resultado a tipo float.

Los "cast" son, con frecuencia, considerados como operadores. Como operador, un cast es
unario y tiene la misma precedencia que cualquier operador unario.

Vemos un ejemplo.

Ejemplo: Salida empleando una conversión con el tipo cast.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 51


Capítulo 4 Lenguaje “C”

/* -----------------
Programa: cast1.c
----------------- */
#include <stdio.h>
main()
{
int i;
for (i=1;i<=10;i++)
printf(" %d/2 es : %2.2f \n",i,(float) i/2);
}

4.4.-OPERADORES BINARIOS.
Una característica del lenguaje "C" es la posibilidad de manipular los bits de los datos. Ello se
consigue empleando operadores binarios.

Operador Símbolo
-------------- ---------------
AND &
OR |
XOR ^
NOT ~
desplazamiento derecho >>
desplazamiento izquierdo <<

Los operadores binarios sólo pueden aplicarse a datos de los tipos char, unsigned char, int y
unsigned int.

Los bits de un carácter entero se numeran de derecha a izquierda y de 0 a "n-1" con objeto de
referenciarlos en las operaciones.

Carácter

7 6 5 4 3 2 1 0

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Los operadores AND, OR y XOR requieren dos operandos y producen un tercero (resultado);
en cambio NOT y los desplazamientos sólo requieren un operando para producir el resultado.

4.4.1.-OPERADORES AND, OR Y XOR.

Los formatos de los operadores intersección (AND), unión (OR) y unión excluyente (XOR)
son respectivamente.

op1 & op2 op1 | op2 op1 ^ op2

donde los operadores op1 y op2 se escriben en notación hexadecimal correspondiendo a datos del
mismo tipo.

52 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 4

La siguiente tabla proporciona las reglas para obtener los resultados de los operadores bit a bit.

op1 op2 op1 & op2 op1 | op2 op1 ^ op2


0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

El siguiente programa muestra como trabaja el operador binario AND.

/* --------------
Programa and.c
-------------- */
#define VERDAD 1
#define BORRA_P "\x1B[2J" /* borra la pantalla */
#define INICIO "\x1B[1;1f" /* cursor a la esquina superior izquierda "
*/
#define FINAL "\x1B[25;1f" /* cursor a la esquina inferior izquierda " */

main()
{
unsigned char op1,op2;
printf(BORRA_P);
printf(FINAL);
printf("... Ejemplo de la función logica binaria & ...");
printf(INICIO);
printf("\nIntroduzca dos numeros hexadecimales de dos digitos
(separados por coma):");
scanf("%x,%x",&op1,&op2);
printf("%02x & %02x = %02x\n",op1,op2,op1&op2);
printf(FINAL);
}

El operador AND permite comprobar si un bit de posición dentro de un dato, es 0 ó 1. Ello se


consigue con la operación

dato & 2n

donde n es el lugar que ocupa el bit dentro del dato.

Ejemplos:

El bit 4 del carácter A (ASCII 41 hex. 65 dec.) es 0 porque el resultado de la operación

hexadecimal 41 & 10 = 0
decimal 65 & 16(24) = 0

binario 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
└─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘

es 0 (falso).

Análogamente el bit 4 del carácter '<' (ASCII 3C hex.) es 1 porque el resultado de la operación.

hexadecimal 3C & 10 = 10

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 53


Capítulo 4 Lenguaje “C”

decimal 60 & 16(24) = 16


binario 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0
└─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘

es distinto de cero (o verdadero).

4.4.2.-EL OPERADOR XOR.

El operador XOR se utiliza para cambiar un bit, cuya posición dentro de un dato es conocida:
si es 0 se cambia a 1 y si es 1 se cambia a 0. Ello se consigue con la operación

dato ^2n

donde n es el lugar que ocupa el bit dentro del dato.

Ejemplo:

La siguiente operación cambia el bit 4 del carácter 'ñ' (ASCII A4 hex.).

hexadecimal A5 ^ 10 = B5
decimal 165 ^ 16(24) = 181
binario 1 0 1 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 1 1 0 1 0 1
└─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┘

4.4.3.-OPERADORES NOT, >> Y <<.

Estos operadores son monarios (sólo un operando) y sus formatos son, respectivamente

^op op<<n op>>n

donde "op" es el operando en notación hexadecimal y "n" es el número de lugares de desplazamiento


lateral.

El operador NOT toma cada bit del operando y lo convierte a 1 si es 0 y a 0 si es 1.

El operador "desplazamiento a la derecha" mueve a la derecha cada bit del operando el número
de lugares especificado en la operación. El resultado depende del tipo de datos del operando op.

Si el dato tiene uno de los tipo unsigned char o unsigned int, se insertan tantos ceros a la
izquierda como lugares desplazados. Pero si el dato tiene signo, es decir se de uno de los tipos char o
int, entonces cada vez que se desplaza un bit a la derecha pone el signo (0 si es positivo o 1 si es
negativo) en el bit situado más a la izquierda.

Ejemplos:

Al desplazar 3 bits a la derecha el carácter positivo que en hexadecimal se escribe 60 resulta el


carácter 0c (hex.).

En cambio, al desplazar 3 bits a la derecha el carácter c1 (hex.) el resultado depende de que se


considere o no el signo del carácter ya que el bit más a la izquierda es 1. Si se considera sin signo, esto

54 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 4

es, se declara unsigned char, el resultado es 18 (hex.) mientras que si se considera con signo el
resultado es f18 (hex.).

El operador "desplazamiento a la izquierda" mueve, a la izquierda cada bit del operador el


número de lugares especificado. Los bits insertados por la derecha tienen siempre el valor 0,
independientemente del signo del dato.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 55


Capítulo 4 Lenguaje “C”

56 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

CAPÍTULO 5
SENTENCIAS “C”

5.1.-INTRODUCCION.
Las sentencias de un programa "C" controlan la forma en que se ejecuta dicho programa. Toda
sentencia "C" está formada por palabras clave, expresiones y otras sentencias. Las palabras clave son
las siguientes:

break do if
case else return
continue for switch
default goto while

Una sentencia puede ser una "sentencia compuesta" si esta formada por una o más sentencias;
en este caso estará encerrada entre corchetes "{}".

Toda sentencia en "C" finaliza con un punto y coma ";".

5.2.-SENTENCIAS SECUENCIALES.
Formato:

{
[declaración]
.
.
sentencia
[sentencia]
.
.
}

Este tipo de sentencias se ejecutan en el orden de secuencia en el cual aparecen. Estas


sentencias suelen formar parte de otras sentencias tales como una sentencia "if"; En el ejemplo inferior
si la variable "i" es mayor que "0" todas las sentencias que forman el cuerpo de esta se ejecutan en
secuencia.

Ejemplo:

if (i>0)
{
línea[i]=x;
x++;
i--;
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 57


Capítulo 5 Lenguaje “C”

5.3.-SENTENCIA IF.
Formato:

if (expresión)
sentencia(s)
[else
sentencia(s)]

El cuerpo de una sentencia if se ejecuta de forma selectiva dependiendo del valor de la


expresión. Primero se evalúa la expresión, si la expresión es cierta (distinto de 0), la(s) sentencia(s)
que siguen a la expresión se ejecutan. Si la expresión es falsa se ejecuta(n) la(s) sentencia(s) que
siguen al "else". Si la expresión es falsa y se omite la cláusula "else" la(s) sentencia(s) que siguen a la
expresión son ignoradas, pasando el control a la siguiente sentencia a continuación de la "if".

Ejemplo:

if (i>0)
y=y/i;
else
{
x=i;
y=f(x);
}

"C" no dispone de una sentencia "else if" pero el mismo efecto se consigue anidando varias
sentencias "if".

Cuando se anidan varias sentencias "if" lo mejor es usar llaves para agruparlas y conseguir
mayor claridad; en el caso de que no se utilicen el compilador asocia cada "else" con el "if" más
reciente que no tenga "else", siempre y cuando no existan llaves que indiquen lo contrario.

Ejemplo:

1.- if (i > 0) 2.- if (i > 0)


if (j >i) {
x=j; if (j > i)
else x=j;
x=i; }
else
x=i;

En el primer ejemplo, el "else" se asocia con la sentencia "if" más interna. Por tanto si "i" es
menor o igual a 0 a la variable "x" no se le asocia ningún valor.

En el segundo ejemplo las llaves rodeando el segundo "if" hacen que el "else" sea asociado por
el compilador al primer "if". Por ello si i es menor o igual a 0 la variable "x" toma el valor de "i".

58 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

5.3.1.-VALORES VERDAD Y FALSO EN EXPRESIONES.

Toda expresión en "C" tiene un valor. Esta afirmación es cierta incluso para expresiones de
relación, tal como se demuestra en el siguiente ejemplo:

/* ------------------
Programa: cierto.c
------------------ */
main()
{
int cierto, falso;
cierto = (10 > 2);
falso = (10 == 2);
printf("\n\n Cierto = <%d> ; Falso = <%d> \n\n",
cierto,falso);
}

De la ejecución de este programa se deduce que el valor cierto para el lenguaje "C" es 1, y el
valor falso es 0.

Vemos otro ejemplo.

/* ----------------
Programa: test.c
---------------- */
main()
{
printf("\n\n");
if (1)
printf(" 1 -> Significa cierto. \n");
else
printf(" 1 -> Significa falso. ");
if (0)
printf(" 0 -> Significa cierto. ");
else
printf(" 0 -> Significa falso. \n\n");
}

Comprobamos en el tercer ejemplo inferior como "C" toma cualquier valor distinto de 0 como
"cierto" y que únicamente se toma como "falso" el valor 0.

/* -----------------
Programa: test2.c
----------------- */
main()
{
printf("\n\n");
if (300)
printf(" 300 es cierto. \n");
if (-55)
printf(" -55 es cierto. \n\n";
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 59


Capítulo 5 Lenguaje “C”

De esto deducimos que el "C" tiene una noción de "cierto" muy tolerante, que es aprovechada
por muchos programadores. Por ejemplo las dos siguientes frases son equivalentes:

if (numero != 0) if (numero)

Vemos otros ejemplos con la sentencia if.

Ejemplo1: Evaluación de una expresión.

/* ------------------
Programa: verdad.c
------------------ */
main()
{
char resul;
printf(" -----------\n");
printf("Expresión: !-3==&&7>10\n");
printf(" -----------\n");
printf("Valor:");
resul=(!(-3==0&&7>10))?'V':'F';
if (resul=='V')
printf (" verdadera \n\n");
else
printf(" falsa");
}

Ejemplo2: La expresión anterior negada.

/* -----------------
Programa: falso.c
----------------- */
main()
{
char resul;
printf("\n\n\n");
printf(" -------------------\n");
printf(" Expresión: ( !-3==0&&7>10 )\n");
printf(" --------------------\n");
printf("\n\n Valor:-> ");
resul=(!-3==0&&7>10)?'V':'F';
if (resul=='V')
printf (" verdadera");
else
printf(" falsa\n\n");
}

60 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

Ejemplo3: Programa que realiza conversiones entre los distintos sistemas de numeración.

/* ---------------------
Programa: impresion.c
--------------------- */
#include <stdio.h>
main()
{
int opcion;
int valor;
printf("\n\n\n Conversión \n");
printf(" ---------- \n\n");
printf(" 1: decimal a hexadecimal \n");
printf(" 2: hexadecimal a decimal \n");
printf(" 3: decimal a octal \n");
printf(" 4: octal a decimal \n\n");
printf(" introduzca opcion --> ");
scanf("%d",&opcion);
if (opcion==1)
{
printf("\n\n\n");
printf(" Introduzca un valor decimal: ");
scanf("%d",&valor);
printf("\n\n %d en decimal \n\n en hexadecimal es:%x" ,valor,valor);
printf("\n\n\n");
}
if (opcion==2)
{
printf("\n\n\n");
printf(" Introduzca un valor hexadecimal: ");
scanf("%x",&valor);
printf("\n\n %x en hexadecimal \n\n en decimal es:%d ,valor,valor);
printf("\n\n\n");
}
if (opcion==3)
{
printf("\n\n\n");
printf(" Introduzca un valor decimal: ");
scanf("%d",&valor);
printf("\n\n %d en decimal \n\n en octal es:%o" ,valor,valor);
printf("\n\n\n");
}
if (opcion==4)
{
printf("\n\n\n");
printf(" Introduzca un valor octal: ");
scanf("%o",&valor);
printf("\n\n %o en octal \n\n en decimal es:%d" ,valor,valor);
printf("\n\n\n");
}
}

5.4.-SENTENCIA BREAK.
Formato:

Break;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 61


Capítulo 5 Lenguaje “C”

La sentencia break termina la ejecución del do, for, switch o while más interno, pasando el
control fuera del cuerpo de dichas sentencias. Si una sentencia break aparece fuera de alguna sentencia
do, for, switch, o while se producen error.

Ejemplo:

for (i=0:i<LARGO-1;i++)
{
for (j=0;j<ANCHO-1;J++)
{
if (lineas[i][j] == '\0')
{
longitud[i]=j;
break;
}
}
}

En este ejemplo la sentencia break provoca un abandono del cuerpo del segundo for después de
que se encuentra el carácter de terminación null ('\0') por cada cadena, cuya longitud se almacena en
longitud[i]. El control por tanto pasa al primer for, donde la variable i se incrementa y el proceso se
repite hasta que i sea mayor o igual a LARGO-1.

5.5.-SENTENCIA CONTINUE.
Esta sentencia pasa el control a la siguiente iteración de una sentencia for, do o while.

Dentro de una sentencia do, for o while la siguiente iteración comienza analizando de nuevo la
expresión asociada al do, for o al while, y si esta es cierta provoca otra iteración en caso contrario
abandona el bucle.

Ejemplo:

while (i-->0)
{
x=altura(i);
if (x==1)
continue;
y=x*x;
}

El cuerpo de la sentencia se ejecuta si la variable "i" es mayor que 0. Primero se asigna el valor
de ejecutar la función altura() con el valor "i" a "x", luego si se cumple que x toma valor 1 se ejecuta la
sentencia continue con lo cual el resto de las sentencias del bloque son ignoradas y el control pasa al
inicio del bucle volviéndose a analizar la expresión "i-->0".

62 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

5.6.-SENTENCIA SWITCH.
Formato:

switch (expresión)
{
[declaración]
.
.
[case constante_expresión:]
sentencia
.
.
[case constante_expresión:]
.
.
[sentencia]
.
.
[default:
sentencia]
}

La sentencia "switch" transfiere el control a la(s) sentencia(s) que forma(n) uno de sus bloques.
Esta(s) sentencia(s) cumplen que la constante_expresión de su "case" es igual al valor de la expresión
entre paréntesis. La ejecución de las sentencias comienza en la(s) sentencia(s) seleccionadas y
continua hasta el final del cuerpo o hasta que sea transferido el control fuera de cuerpo con una
sentencia break.

La sentencia default se ejecuta si no existe ningún "case constante_expresión" igual al valor de


la expresión del "switch", si es omitida y no se cumple ninguno de los casos, no se ejecuta ninguno de
los casos y continua la ejecución del programa en la línea siguiente a la "switch".

La expresión de la "switch" ha de ser un valor integral o un valor enum. Los valores de los
"case" han de ser únicos.

Las etiquetas de un switch deben de ser constantes de tipo entero (incluyendo char) o bien
expresiones que contengan únicamente constantes. En ningún caso se pueden emplear variables en las
etiquetas.

Ejemplos:

1.- switch (c) 2.- switch (i)


{ {
case 'A': case -1:
capitulo++; n++;
case 'a': break;
letra++; case 0;
default: z++;
total++; break;
} case 1:
p++;
break;
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 63


Capítulo 5 Lenguaje “C”

En el primer ejemplo, los tres casos de la "switch" son ejecutados en el caso de que "c" tome
valor "A". Si "c" toma el valor "a" se incrementa en 1 el valor de letra y total, mientras que solo se
incrementa el valor de total cuando c toma un valor distinto de "A" y "a".

En el segundo ejemplo cada caso finaliza con una sentencia "break" que fuerza a salir de la
sentencia "switch" una vez ejecutadas las sentencias de cada caso.

Varios "case" pueden asociarse a una misma sentencia como en el siguiente ejemplo:

case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f': función(letra);

Ejemplo1: Un caso muy curioso e interesante de la sentencia switch.

/* -------------------
Programa: switch1.c
------------------- */
/*
.. ..
Programa que refleja la opcionalidad
de la sentencia break en switch.
.. .. */

main()
{
int t;
printf("\n\n\n");
for (t=0;t<10;t++)
switch(t)
{
case 1:
printf("Ahora");
break;
case 2:
printf(" es ");
case 3:
printf("la");
printf(" hora de los buenos hombres.\n");
break;
case 5:
case 6:
printf("a ");
break;
case 7:
case 8:
case 9:
printf(".");
}
printf("\n\n\n");
}

64 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

Ejemplo2: Programa conversor entre sistemas de numeración resuelto con una sentencia switch.

/* -------------------
Programa: conver2.c
------------------- */
/*
.. ..
Programa conversor de numeros
usando la sentencia switch
1: decimal a hexadecimal
2: hexadecimal a decimaln
3: decimal a octal
4: octal a decimal
.. ..
*/
#include <stdio.h>
main()
{
int opcion;
int valor;

printf("Conversión:\n");
printf(" 1: decimal a hexadecimal \n");
printf(" 2: hexadecimal a decimal \n");
printf(" 3: decimal a octal \n");
printf(" 4: octal a decimal \n");
printf(" Introduzca una opcion : ");
scanf("%d", &opcion);

switch(opcion)
{
case 1:
printf("\n\n\n");
printf(" introduzca un valor decimal: ");
scanf("%d",&valor);
printf(" %d en hexadecimal es :%x",valor,valor);
printf("\n\n\n");
break;
case 2:
printf("\n\n\n");
printf(" introduzca un valor hexadecimal: ");
scanf("%x",&valor);
printf(" %x en decimal es : %d",valor,valor);
printf("\n\n\n");
break;
case 3:
printf("\n\n\n");
printf(" introduzca un valor decimal: ");
scanf("%d",&valor);
printf(" %d en octal es : %o",valor,valor);
printf("\n\n\n");
break;
case 4:
printf("\n\n\n");
printf(" introduzca un valor octal: ");
scanf("%o",&valor);
printf(" %o en decimal es : %d",valor,valor);
printf("\n\n\n");
break;
default:
printf("\n\n\n");
printf(" SELECCION NO VALIDA, repita de nuevo....");
printf("\n\n\n");
break;
}
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 65


Capítulo 5 Lenguaje “C”

5.7.-SENTENCIA DO.
Sigue el formato:

do
sentencia(s)
while (expresión);

El cuerpo de una sentencia do se ejecuta una o más veces hasta que la "expresión" pase a ser
falsa. Primero se ejecutan las sentencias del bloque, luego se evalúa la expresión; si esta es falsa
(cero), la sentencia do termina y el control pasa a la siguiente sentencia a continuación del while. Si la
expresión es cierta (distinta de cero) se ejecuta de nuevo el cuerpo del bucle.

La sentencia do puede también finalizar con la ejecución de una sentencia break,goto, o return
dentro del cuerpo de dicho bucle.

Ejemplo:

x=5
do
{
y=altura(i);
x--;
}
while (x > 0);

En este ejemplo se asigna a la variable "y" el valor de la llamada a la función altura(i) y se


decrementa x en uno luego se evalúa "x>0". Si "x" es mayor que 0 se ejecuta de nuevo el cuerpo de la
función y se vuelve a evaluar la condición. El cuerpo de la función se ejecuta-rá repetidamente
mientras "x" sea mayor que 0. La ejecución terminara cuando la variable "x" tome un valor 0 o
negativo.

Ejemplo: La sentencia "do" se utiliza generalmente para crear menús.

/* ---------------
Programa: do1.c
--------------- */

main()
{
int opcion, valor;

do
{
printf("\n\n");
printf(" 1.- Calculo del cuadrado. \n");
printf(" 2.- Calculo del cubo.\n");
printf("\n Seleccione opcion: ");
scanf("%d",&opcion);
}
while (opcion<1 || opcion>2);
printf("\n\n Introduzca un valor decimal: ");
scanf("%d",&valor);
switch(opcion)
{
case 1:
printf(" Su cuadrado es : %d",valor*valor);
break;

66 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

case 2:
printf(" Su cubo es : %d",valor*valor*valor);
break;
}
printf("\n\n");
}

5.8.-SENTENCIA WHILE.
Formato:

while (expresión)
sentencia(s)

El cuerpo de la sentencia while se ejecuta 0 o más veces mientras la expresión sea cierta.
Primero se evalúa la expresión, si inicialmente es falsa (cero), el cuerpo de while no se ejecuta y el
control pasa a la siguiente sentencia del programa a continuación del while. Si la expresión es cierta
(distinto de 0) el cuerpo del while es ejecutado y a continuación se vuelve a evaluar la expresión y así
sucesivamente se ejecutará el cuerpo del bucle mientras la expresión sea cierta.

Una sentencia while puede también finalizar con la ejecución de una sentencia "break", "goto"
o "return" dentro del cuerpo del bucle.

Ejemplo:

while (i >= 0)
{
cadena[i]=cadena2[i];
i--;
}

El ejemplo superior copia todos los caracteres de cadena2 a cadena1. Si i se mayor o igual a
cero cadena2[i] se asigna a cadena1[i] y el valor de i es decrementado en una unidad. Cuando i toma el
valor -1 termina la ejecución de la sentencia while.

Ejemplo1: Las cabeceras van siempre fuera de los bucles.

/* ------------------
Programa: while1.c
------------------ */
/*
.. ..
Este programa convierte numeros de zapatos
a cm. de pie.
.. .. */

#define TOPE 0.933


#define ESCALA 0.6167

main()
{
float zapato,pie;

printf(" N. ZAPATO CM.PIE \n");


printf(" --------- ------ \n");
zapato = 30.0;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 67


Capítulo 5 Lenguaje “C”

while (zapato<48.5)
{
pie=ESCALA*zapato+TOPE;
printf("%10.1f %16.2f cm \n",zapato,pie);
zapato = zapato + 1.0;
}
printf("\n Nota: Ud. sabe donde le aprieta su zapato. \n");
}

Ejemplo2: sumando los 10 primeros números.

/* ------------------
Programa: while2.c
------------------ */

main()
{
int cont, suma;
cont = 0;
suma = 0;

printf("\n\n\n");
while (cont++ < 10)
suma = suma + cont;
printf("La suma de los %d primeros numeros es = %d \n"
,10,suma);
printf("\n\n\n");
}

Ejemplo4: ahora combinamos un bucle "while" con el operador decremento.

/* ------------------
Programa: while3.c
------------------ */
#define MAX 5
main()
{
int cont = MAX + 1;

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


printf(" ()()()()()()()()()()()()()()()()()() \n\n");
while (--cont>0)
{
printf(" -> %d botellas de vino en el estante <-\n ",cont);
printf("Alguien paso por delante y ? que fue de ellas ?");
printf(" Solo quedan %d botellas !. \n\n", cont-1);
}
}

5.9.-SENTENCIA FOR.
Formato:

for ([expr_inicial];[expr_condicion];[expr_bucle])
sentencia(s);

68 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

El cuerpo de una sentencia for se ejecuta 0 o más veces mientras que "expr_condición" sea
cierta. "expr_inicial" y "expr_bucle" son opcionales y se usan para inicializar y modificar los valores
de las variables durante la ejecución de la sentencia for.

El primer paso de la ejecución de la sentencia for es evaluar "expr_inicial", si esta presente,


luego se evalúa "expr_condición" dando lugar a tres posibles resultados:

1. Si esta expresión es cierta (distinta de 0), se ejecuta el cuerpo de la sentencia for; luego se
evalúa expr_bucle, si esta presente, comenzando el proceso de nuevo evaluando
expr_condicion.

2. Si esta expresión se omite la expresión se considera cierta por omisión, y la ejecución se


produce como en el caso primero. Una sentencia for que carece de "expr_condicion" termina
solo con la ejecución de una sentencia break, goto o return dentro del cuerpo del for.

3. Si "expr_condicion" es falsa la ejecución de la sentencia for termina y el control pasa a la


siguiente sentencia del programa a continuación del for.

Una sentencia for también termina si dentro del cuerpo de esta se ejecuta una sentencia for,
break o return.

Ejemplo:

for (i=espacio=tabulado=0;i<MAYOR;i++)
{
if (línea[i] == '\x20')
espacio++;
if (línea[i]=='\t')
{
tabulado++;
línea[i]='\0x20';
}
}

Este bucle cuenta el número de caracteres espacio (\0x20) y tabulado (\t) en un array de
caracteres de nombre línea, reemplazando cada tabulado con un carácter espacio.

Ejemplo1: Calculando cuadrados con una sentencia for nada compleja.

/* ----------------
Programa: for1.c
---------------- */
main()
{
int i;

printf("\n\n");
for(i=0;i<5;i++)
{
printf(" Esto es i : %d",i);
printf(" y su cuadrado es : %d \n",i*i);
printf("\n");
}
printf("\n\n");
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 69


Capítulo 5 Lenguaje “C”

Ejemplo2: Otro ejemplo muy sencillo.

/* ----------------
Programa: for2.c
---------------- */

main()
{
int x;

for(x=1;x<=100;x++)
printf(" - %d",x);
printf(" -\n");

Ejemplo3: No hay dos sin tres.

/* ----------------
Programa: for3.c
---------------- */

main()
{
int x;

for(x=100;x>=0;x--)
printf(" - %d",x);
printf(" -\n");

Ejemplo4: Calculo de los números impares entre 1 y 100.

/* -------------------
Programa: impares.c
------------------- */

#include <stdio.h>

main()
{
int i;

printf(" NUMEROS IMPARES ENTRE 1 Y 100 \n");


printf(" ------------------------------ \n\n\n");

for(i=1;i<=100;i++)
if (i%2) printf("- %d ",i);
printf("\n\n");
}

5.10.-SENTENCIA GOTO Y ETIQUETAS.

70 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

Formato:

goto nombre;
.
.
.
nombre: sentencia(s)

La sentencia "goto" transfiere el control a la sentencias que figuran a continuación de la


etiqueta "nombre".

Una etiqueta es un identificador simple.

Ejemplo:

if (codigo_error>0)
goto salir;
.
.
.
salir:
return(codigo_error);

5.11.-SENTENCIA RETURN.
Formato:

return[expresión];

La sentencia "return" termina la ejecución de la función en la cual aparece y retorna el control


a la siguiente línea a la llamada a dicha función. El valor de "expresión", si esta presente, se retorna a
la llamada a dicha función; Si se omite "expresión" la función retorna un valor no definido.
Ejemplo:

main()
{
.
.
y=cuadrado(x);
dibujar(x,y);
.
.
}
cuadrado(x)
int x;
{
return(x*x);
}
void dibujar(x,y)
int x,y;
{
.
.
return;
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 71


Capítulo 5 Lenguaje “C”

En el ejemplo superior la función main() llama a dos funciones, cuadrado() y dibujar(). La


función cuadrado() retorna el valor "x*x" a la función main() y dicho valor es asignado a la variable
"y". La función dibujar() se declara como una función void y no retorna ningún valor.

Los paréntesis de la expresión de "return" no son obligatorios, se suelen colocar por convenio.

Si una sentencia "return" no aparece en la definición de la función, el control pasa a la


siguiente sentencia a continuación a la llamada. Si no se requiere un valor "return", la función debería
ser declarada con tipo void en el tipo a retornar.

EJEMPLOS RECAPITULATIVOS:

Ejemplo1: En toda división ha de verificarse que el divisor no sea cero.

/* ------------------
Programa: divide.c
------------------ */

#include <stdio.h>

main()
{
int a,b;
printf("\n\n\n");
printf("Introducir dos numeros (separados por un espacio) :");
scanf("%d %d",&a,&b);
printf("\n\n");
if(b)
printf("<%d> division entera <%d> =-> <%d>\n\n",a,b,a/b);
else
printf(" no puedo dividir por cero \n\n");
}

Ejemplo2: Una versión del clásico juego de adivinar un número, en el que el ordenador hace de
adivino. Como práctica modificar el programa para que sea el ordenador el que piense
el número.

/* -------------------
Programa: adivina.c
------------------- */
# include <stdio.h>
# define ALTO 100
# define BAJO 1

main()
{
int sup = (ALTO + BAJO)/2;
int max = ALTO;
int min = BAJO;
char respuesta;
printf(" Escoja un numero entre %d y %d. Tratare",ALTO,BAJO);
printf(" de adivinarlo. \n Responda <s> si he acertado,
<a> si mi");
printf(" numero es demasiado alto. \n <b> si demasiado");
printf(" bajo. \n\n");
printf("(============================================) \n");
printf("( Hmmm ........... su numero es el -> %3d ) \n", sup);
printf("(============================================) \n\n");
while ((respuesta = getchar()) != 's')
{
if (respuesta != '\n')
{

72 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 5

if (respuesta == 'a')
{
max = sup-1;
sup = (max +min)/2;

printf("(============================================) \n");
printf("( Demasiado alto .... Entonces sera -> %3d) \n", sup);
printf("(============================================) \n\n");
}
else if (respuesta == 'b')
{
min = sup + 1;
sup = (max + min)/2;
printf("(============================================) \n");
printf("( Demasiado bajo .... Entonces sera -> %3d )\n", sup);
printf("(============================================) \n\n");
}
else
{
printf("\n");
printf(" ......................................... \n");
printf(" . No comprendo; Utiliza una <s>o una <a>.\n");
printf(" . o una <b> .\n");
printf(" .......................................... \n");
}
}
}
printf("\n\n");
printf(" ..........................................\n");
printf(" . SABIA QUE LO CONSEGUIRIA .\n");
printf(" . FUE FACIL. .\n");
printf(" ..........................................\n");
}

Ejemplo3: Un tercer y último ejemplo.

/* -----------------
Programa: Zenon.c
----------------- */
/*
Este programa refleja el problema que se planteo Zenon
con las distancias.
Por otro lado se puede ver la potencia de la sentencia
for en TURBO C.
*/

# define LIMITE 15

main()
{
int cont;
float suma, x;

printf("\n\n\n");
for (suma=0.0, x=1.0, cont=1; cont <= LIMITE; cont++,x*=2.0)
{
suma += 1.0/x;
printf(" suma = %2.4f en la etapa %3d \n",suma,cont);
}
printf("\n\n\n");
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 73


Capítulo 6 Lenguaje “C”

CAPÍTULO 6
CONSIDERACIONES: ARRAYS, PUNTEROS

6.1.-CONSIDERACIONES SOBRE ARRAYS.


Un conjunto dimensionado o tabla, array, es una estructura de datos que contiene una serie de
variables llamadas 'elementos' que están ordenados, pertenecen al mismo tipo de datos y se referencian
con un nombre común.

Para localizar un elemento determinado dentro del conjunto dimensionado se emplean los
números no negativos 0,1,2, ... llamados 'indices'. Por esta razón se dice que los elementos están
indexados. Dependiendo del número de indices los conjuntos se llaman monodimensionales,
bidimensionales, etc, y en general multidimensionales. El número máximo de índices esta limitado por
el compilador.

Los conjuntos dimensionados se declaran indicando, nombre y tamaño, este último encerrado
entre corchetes. (Repasar capítulo "Variables y Tipos en "C", apartado: Declaración de arrays).

Ejemplo:

int x[6];
float tabla[3][4];

En la sentencia int [6] se declara un conjunto monodimensional para almacenar 6 números


enteros. Si suponemos que se almacenan a partir de la posición de dirección 1000 de memoria.

Nota: Se toma como tamaño del tipo int 2 bytes. Máquina de 16 bits.

5 16 40 - 24 13 8
1000 1002 1004 1006 1008 1010

Como el compilador reserva 2 octetos para cada elemento de tipo int, el total de octetos
reservados es:

6 * sizeof(int) = 6 * 2 = 12 octetos.

Siendo sus direcciones de memoria:

&x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6]

El contenido del cuarto elemento es:

x[3] = -24

y su dirección

74 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

&x[3] = 1006

El compilador reconoce la dirección del primer elemento por el nombre del conjunto, sin
indices ni corchetes:

x == &x[0]

En el segundo ejemplo, la variable tabla se estructura sobre dos índices y permite albergar 12
números de tipo float, agrupados por filas y columnas. Suponiendo que se almacenan a partir de la
dirección 2000.

Nota: El tamaño del tipo float son 4 bytes.

Columna
--------
0 1 2 3

3.8 -0.4 6.1 -3.7


0 └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘
2000 2004 2008 2012

3.2 10.9 -8.6 0.0


Fila 1 └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘
---- 2016 2020 2024 2028

0.0 20.2 18.4 3.0


2 └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘ └──┴──┴──┴──┘
2032 2036 2040 2044

En este caso el compilador reserva cuatro octetos para cada elementos, con un total de:

filas * columnas * sizeof(float) = 3 * 4 * 4 = 48 octetos.

ordenados por filas.

Para referenciar un elemento individual del conjunto bidimensional se emplean dos indices,
uno de fila y otro de columna. Así p. ej. el elemento séptimo del conjunto, que está situado en la fila 1
columna 2 de cuadro se representa mediante la variable:

tabla[1][2]==-8.6

Su dirección se obtiene anteponiéndole el operador de dirección.

&tabla[1][2]==2024

Ahora bien, el lenguaje "C" tiene la capacidad de tratar cada fila del conjunto bidimensional
como un conjunto monodimensional. Esto explica por qué las direcciones de las filas se representan
con el nombre del conjunto seguido del primer índice. Por ej.

tabla[1] es la dirección del primer elemento de la fila 1.

En consecuencia, se puede averiguar la dirección de un elemento cuando se conoce su posición


relativa dentro de la fila. Así por ej. la dirección del tercer elemento de la fila 1 es:

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 75


Capítulo 6 Lenguaje “C”

&tabla[1][2] == tabla[1] + sizeof(float)


= 2016 + 2 * 4
= 2024

Además, como ocurre cono los conjuntos monodimensionados, el compilador también


reconoce la dirección del conjunto bidimensional por el nombre, sin indices ni corchetes. En
consecuencia, la dirección del elemento de la fila 1, columna 2 también puede determinarse a partir
del nombre y el número de columnas del conjuntos bidimensional.

&tabla[1][2]=tabla+1*cols*sizeof(float)+2*sizeof(float)
= 2000 + 1 * 4 * 4 + 2 * 4
= 2024

¿Qué ocurre cuando se pasa la dirección de un conjunto dimensionado como argumento de una
función?. El compilador no crea un nuevo conjunto como hace con las variables locales (llamada a la
función por valor) sino que pasa únicamente la dirección del conjunto (llamada por referencia). En
consecuencia, sólo se guarda una copia del conjunto en memoria, independientemente del número de
funciones a que puedan acceder.

Por otro lado, la función que recibe la dirección de un conjunto dimensionado tiene que definir
todas las dimensiones excepto la primera, que puede omitirse; de otra manera no haría la indexación
correctamente.

6.2.-CADENAS.
En lenguaje "C" una cadena no es un tipo especial de dato, como ocurre con otros lenguajes
(BASIC, PASCAL, etc); es simplemente un conjunto dimensionado de tipo char. Cada carácter de la
cadena ocupa un octeto de la memoria y al final de la misma el compilador añade el carácter nulo '\0'
que, en realidad es la única forma que tiene el compilador de saber dónde se encuentra el final de la
cadena.

Ejemplos:

char cad1[11];
char cad2[4][6];

Supondremos que el compilador almacena los conjuntos cad1[] y cad2[] a partir de las
direcciones 1500 y 2500, respectivamente.

cad1

S a n t i a g o \0
└───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509

cad2
A l a v a \0
0 └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
2500 2501 2502 2503 2504 2505
C a d i z \0
1 └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
2506 2507 2508 2509 2510 2511
L u g o \0

76 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

2 └───┘ └───┘ └───┘ └───┘ └───┘ └───┘


2512 2513 2514 2515 2516 2517
A v i l a \0
3 └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
2518 2519 2520 2521 2522 2523

En el primer caso, se declara que la variable cad1 puede almacenar hasta 10 caracteres más
uno. El nombre cad1 es la dirección del primer carácter.

cad1 == &cad1[0] = 1500

En el segundo, se declara la variable cad2 como un conjunto dimensionado de cadenas, en


realidad es un conjunto bidimensional de caracteres. El orden de los índices es importante: el primero
(4) indica el número de cadenas mientras que el segundo (6) indica la longitud máxima de la cadena,
incluyendo el carácter nulo.

La dirección de una cadena individual, por ej. la de índice 3, se determina especificando


únicamente el primer índice, cad2[3]. También se puede emplear el operador de dirección.

cad2[3] == &cad2[3][0] =2518

El contenido de esta dirección es:

cad2[3][0] = 'A'

A continuación vemos un programa que lee cadenas por teclado y las imprime en pantalla,
carácter a carácter. Utiliza variables registro.

/* -----------------
Programa: texto.c
----------------- */
# define FIL 100
# define COL 81
# define BORRA_P "\x1B[2J"
# define FINAL "\x1B[25;1f"
# define INICIO "\x1B[1;1f"
char text[FIL][COL];

main()
{
register int i,j,k;
printf(BORRA_P);
printf(FINAL);
printf(" Escriba una línea y pulse <INTRO>. \n");
printf(" Para terminar pulse <INTRO> al principio de una línea.");
printf(INICIO);
for(k=0;k<FIL;k++)
{
printf("%d ", k+1);
gets(text[k]);
if (!text[k][0])
break;
}
printf(BORRA_P);
for(i = 0; i<k ; i++)
{
puts(text[i]);
}
}

El lenguaje "C" también acepta las constantes de cadena y las trata como tales. p. ej. este programa:

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 77


Capítulo 6 Lenguaje “C”

/* ------------------
Programa: saludo.c
------------------ */

# define saludo " ! Hola ! Bienvenido a Galicia"


main()
{
printf("%s",saludo);
}

El siguiente programa inicializa una cadena y luego llama a una función para vaciarla.

/* -----------------
Programa: vacia.c
----------------- */

void vacia(char[]);
main()
{
static char cadena[]="De Marin al cielo";
printf("Cadena inicial: %s. \n", cadena);
vacia(cadena);
printf("\nCadena final: %s \n ", cadena);
}

void vacia(c)
char c[];
{
c[0]='\0'; /* Carácter nulo al principio de la cadena. */
}

6.3.-PUNTEROS.
Un puntero es una variable que contiene una dirección de memoria. Se declara indicando el
tipo de dato que apunta y un nombre, precedido de un asterisco (*). (Ver capítulo "Variables y Tipos
en "C", apartado: Declaración de Punteros).

Ejemplos:

char *puntero;
int *punt_entero;
float *punt_flot[10];

Los punteros permiten hacer relación a las variables de forma indirecta, a través de sus
direcciones.
La razones para utilizar punteros son varias:

- Manejo cómodo de los conjuntos dimensionados.


- Paso de conjuntos dimensionados a funciones.
- Creación de estructuras complejas de datos.
- Comunicación con la memoria del Ordenador a través de la función malloc().

A los punteros se les puede sumar o restar constantes. En cambio, no es posible realizar con
ellos operaciones de multiplicación, división ni desplazamiento de bits.

78 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

Ejemplo:

int *ptri = 1000;


ptri += 3;

└─────┴─────┘ └─────┴─────┘ └─────┴─────┘ └─────┴─────┘


1000 1002 1004 1006

La primera sentencia declara un puntero a entero y asigna inicialmente a la variable ptri la


dirección 1000. La segunda es equivalente a:

ptri = ptri +3

y hace que ptri contenga la dirección 1006. Ténganse en cuenta que ptri apunta a elementos de dos
octetos (bytes).

Los punteros también se pueden comparar entre ellos mediante lo operadores de relación. Se
comparan direcciones, no los contenidos de los campos que apuntan. El programa siguiente compara
punteros a enteros largos.

Ejemplo:

/* -----------------
Programa: punte.c
----------------- */

long x,y;
main()
{
long int *ptrx,*ptry;
ptrx = &x+2;
ptry = &y;
printf("direccion de x .... <%d> \n",&x);
printf("direccion de y .... <%d> \n\n",&y);
if (ptrx < ptry)
printf(" ptrx (=%d) direccion mas baja que ptry (= %d)",ptrx,ptry);
else if (ptrx > ptry)
printf(" ptrx (=%d) direccion mas alta que ptry (= %d)",ptrx,ptry);
else
printf(" ptrx (=%d) direccion igual a ptry (= %d)",ptrx,ptry);
}

Antes de utilizar un puntero es preciso asignarle un valor, ya que de lo contrario se corre el


riesgo de bloquear el Sistema. Pero si queremos que no apunte a ninguna posición de memoria,
podemos asignarle el valor NULL (nulo). La constante NULL tiene valor 0 y está definida en el
fichero de cabecera "stdio.h". Se dice entonces que es un puntero nulo. por ej. las sentencias:

float *ptrf;
ptrf=(float *)NULL
proporcionan un puntero nulo a flotante. Nótese que el puntero nulo no contiene una direc-ción, sino el
valor 0. Véase también el uso de un nombre de tipo (float *) para asegurar el tipo de dato, puntero a
float.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 79


Capítulo 6 Lenguaje “C”

6.4.-PUNTEROS A CONJUNTOS DIMENSIONADOS.


Los punteros y los conjuntos dimensionados tienen fundamentos muy similares, y a veces se
confunden en la práctica. Vemos un ejemplo:

int x[10], *ptri;


ptri = x;

A partir de estas sentencias, el acceso a cualquier elemento del conjunto puede hacerse de dos
formas totalmente equivalentes: indexando o con punteros. Así p. ej. el quinto elemento del conjunto
dimensionado se representa:

x[4]; (notación indexada)


*(ptri+4) (notación de punteros)

A pesar de la equivalencia, los programadores se inclinan por una u otra forma en razón de la
optimización en el tiempo. En general, es más rápido el uso de punteros en proceso secuenciales; en
cambio, la indexación es mejor en los procesos de acceso directo.

Cuando se trata con conjuntos bidimensionales, la notación de punteros proporciona una de las
características más interesantes del lenguaje "C": la doble indexación o tratamiento de punteros que
apuntan a punteros. Veamos un ejemplo:

float tabla[3][4];

La dirección del conjunto completo es tabla, que empieza en la dirección 2000. El compilador
interpreta la expresión tabla+1, o su equivalente tabla[1], como la dirección de la segunda fila del
conjunto, y para calcularla hace la suma de tabla con 4*sizeof(float), que es el número de bytes de una
fila completa; por tanto tabla+1 tiene la dirección 2016.

tabla+1 == tabla[1] == &tabla[1][0] == 2016 (notación indexada)


*(tabla+1) == 2016 (notación puntero)

Ahora bien, sumando 1 a la dirección tabla+1 se obtiene tabla+2, que es la dirección de la


tercera fila de conjunto, es decir 2032. En cambio sumando 1 a *(tabla+1) se obtiene la dirección del
siguiente elemento de la segunda fila, esto es 2020.

Por tanto, la dirección del tercer elemento de la segunda fila es:

&tabla[1][2] == 2024 (notación indexada)


*(tabla+1)+2 == 2024 (notación punteros)

Anteponiendo el operador de indirección se obtiene el contenido del elemento *(*(tabla+1)+2).


Esta expresión es un puntero de puntero, que tiene su equivalencia en notación indexada.

tabla[1][2] == -8.6 (notación indexada)


*(*(tabla+1)+2) == -8.6 (notación punteros)

Vemos un ejemplo de estos conceptos:

/* --------------------
Programa: matrices.c

80 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

-------------------- */
# define FIL 4
# define COL 4
# define BORRA_P "\x1B[2J"

main()
{
int i,j;
static int mat1[FIL][COL]=
{{10,15,17,19},
{20,22,24,26},
{31,33,35,37},
{40,42,44,46}};
static int mat2[FIL][COL]=
{{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}};

int mat3[FIL][COL];
printf(BORRA_P);
printf("\n\n\n\n");
for (i=0;i<FIL;i++)
for (j=0;j<COL;j++)
*(*(mat3+i)+j) = *(*(mat1+i)+j) + *(*(mat2+i)+j);
printf(" < MATRIZ 1 >\t < MATRIZ 2 >\t< MATRIZ SUMA > \n");
printf(" ---------- \t --------- \t ------------- \n\n");
for(i=0;i<FIL;i++)
{
for (j=0;j<COL;j++)
printf("%3d",*(*(mat1+i)+j));
printf(" ");
for (j=0;j<COL;j++)
printf("%3d",*(*(mat2+i)+j));
printf(" ");
for (j=0;j<COL;j++)
printf("%3d",*(*(mat3+i)+j));
printf("\n");

/* LA OTRA FORMA DE IMPRESION. */


/* --------------------------- */

printf("\n\n");
for (i=0;i<FIL;i++)
for (j=0;j<COL;j++)
mat3[i][j] = mat1[i][j] + mat2[i][j];
printf(" < MATRIZ 1 >\t < MATRIZ 2 >\t< MATRIZ SUMA > \n");
printf(" ---------- \t --------- \t ------------- \n\n");
for(i=0;i<FIL;i++)
{
for (j=0;j<COL;j++)
printf("%3d",mat1[i][j]
printf(" ");
for (j=0;j<COL;j++)
printf("%3d",mat2[i][j]
printf(" ");
for (j=0;j<COL;j++)
printf("%3d",mat3[i][j]
printf("\n");
}
}

6.5.-PUNTEROS A CADENAS.
El uso de puntero tiene claras ventajas sobre la indexación cuando se inicializan cadenas, como
veremos en seguida.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 81


Capítulo 6 Lenguaje “C”

Comparemos la sentencia

char *capital="Pontevedra";

con la conocida versión indexada

static char capital[]={"Pontevedra"};

El compilador reserva espacio en la memoria para almacenar la palabra "Pontevedra", más un


octeto para el carácter '\0' (nulo). Esto es así en ambas versiones, con punteros y con índices. Pero en
la versión de punteros, el compilador reserva, además, espacio para la variable de puntero "capital".
Esta variable puede incluso cambiarse y así la sentencia

puts(capital+2);

mostraría la subcadena "ntevedra" en la pantalla. En cambio, en la versión indexada, capital es una


constante de puntero cuya dirección no puede cambiarse.

A continuación veremos otra diferencia entre ambas versiones. Con la versión indexada la
sentencia

static char ciudad[4][11]={"Pontevedra",


"Sanxenxo",
"Marín",
"Vigo"};

informa al compilador para que almacene en memoria cuatro cadenas de caracteres, todas de tamaño
11, incluyendo el carácter nulo.

Con la versión de punteros


char *ciudad[4]={"Pontevedra",
"Sanxenxo",
"Marín",
"Vigo"};

también el compilador reserva espacio en memoria para almacenar las cuatro cadenas, sólo que una a
continuación de la otra, sin dejar espacios vacíos, con lo que el ahorro de memoria es de 8 octetos.

versión indexada.

P o n t e v e d r a \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1000 1010

S a n x e n x o \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1011 1021

M a r í n \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1022 1032

V i g o \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1033 1043

82 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

versión con punteros.

P o n t e v e d r a \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1000 1010

S a n x e n x o \0
└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘└───┘
1011 1019

M a r í n \0
└───┘└───┘└───┘└───┘└───┘└───┘
1020 1025

V i g o \0
└───┘└───┘└───┘└───┘└───┘
1026 1030

En la versión de punteros no hay espacio inutilizado y las cadenas no forman un conjunto


dimensionado. Sin embargo, queda definido un conjunto dimensionado de punteros a cadenas.

ciudad[0] es el puntero "Pontevedra"


ciudad[1] es el puntero "Sanxenxo"
ciudad[2] es el puntero "Marín"
ciudad[3] es el puntero "Vigo"

Veamos un ejemplo:

/* ------------------
Programa: vacia2.c
------------------ */

void vacia(char *);


# define BORRA_P "\x1B[2J"
# define FILA10 "\x1B[10;10f"
# define FILA12 "\x1B[12;10f"
main()
{
char *cadena="Ante todo mucha calma. \"Siniestro Total\"";

printf(BORRA_P);
printf(FILA10);
printf("Cadena inicial: <%s>. ", cadena);
vacia(cadena);
printf(FILA12);
printf("Cadena final : <%s>.\n\n\n", cadena);
}

void vacia(cad)
char *cad;
{
*cad='\0'; /* Carácter nulo al principio de la cadena. */
}

El paso de un conjunto dimensionado a una función sigue los mismos criterios que los de
cualquier otro conjunto dimensionado. La función que recibe el conjunto dimensionado tiene que
definir todos los índices excepto el primero, que puede omitirse.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 83


Capítulo 6 Lenguaje “C”

El siguiente programa llama a una función para imprimir un conjunto dimensionado de


números primos y sus direcciones.

/* ---------------
Programa: imp.c
--------------- */
void imp(int *[], int);
# define BORRA_P "\x1B[2J"
main()
{
int i;
static int primos[]={2,3,5,7};
int *ptri[4];
printf(BORRA_P);
for (i=0;i<4;i++)
ptri[i]=primos+i;
imp(ptri,4);
}

void imp(ptri,n)
int *ptri[], n;
{
int i;
printf("Numeros : %d", *ptri[0]);
for (i=1;i<n;i++)
printf(", %d", *ptri[i]);
printf("\n\n");
printf("Direcciones : ");
for (i=0;i<n;i++)
printf(" %u",ptri[i]);
printf("\n\n");
}

6.6.-PUNTEROS A FUNCIONES.
La dirección de una función es el punto de entrada de la misma. Cuando el montador de
enlaces transforma el código objeto en código ejecutable, define los puntos de entrada de cada
función.

Si un puntero contiene el punto de entrada de una función, puede utilizarse para llamar a la
función.

El compilador reconoce la dirección de una función por el nombre de la misma sin paréntesis
ni argumentos. Es muy parecido al criterio que sigue para determinar la dirección de un conjunto
dimensionado, con el nombre sin corchetes ni índices.

El siguiente programa muestra cómo se puede asignar la dirección de una función a un puntero
y hacer la llamada con él, sin utilizar el nombre de la función.

/* ---------------
Programa: max.c
--------------- */
# define BORRA_P "\x1B[2J"
# define FILA10 "\x1B[10;10f"
# define FILA13 "\x1B[13;10f"

int max(int, int);


main()
{
int num1, num2;

84 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 6

int (* ptr)(int, int);


printf(BORRA_P);
ptr=max;
printf(FILA10);
printf("Teclea do numeros enteros separados por un espacio : ");
scanf("%d %d",&num1,&num2);
printf(FILA13);
printf("Valor máximo = < %d > \n\n ", (* ptr)(num1, num2));
}

int max(x,y)
int x, y;
{
int resul;
resul=(x>y)?x:y;
return(resul);
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 85


Capítulo 7 Lenguaje “C”

CAPÍTULO 7
FUNCIONES “C”

7.1.-INTRODUCCIÓN.
Una función es una colección independiente de declaraciones y sentencias. Los programas "C"
tienen al menos una función principal, la función main(). Este capitulo describirá como definir,
declarar y llamar a las funciones C.

Una definición de una función especifica el nombre de la función, sus parametros formales y
las declaraciones y sentencias que definen su acción. La definición de la función puede también llevar
el tipo retornado por esta y su clase de almacenamiento.

Una declaración de una función establece el nombre, el tipo retornado, y la clase de


almacenamiento de la función cuya definición explícita se da en otro punto del programa. El número y
tipos de los argumentos de la función pueden ser especificados en la declaración de la función. Esto
permitirá al compilador comparar los tipos de los argumentos actuales y los parametros formales de la
función. La declaración de funciones es opcional para aquellas funciones que retornan un tipo int. Para
asegurar un correcto comportamiento, las funciones como los otros tipos deberían ser declaradas antes
de ser llamadas.

Una llamada a una función pasa el control de ejecución de la llamada de la función a la función
llamada.

Los argumentos actuales, si existen, se pasan por valor a la función llamada.

La ejecución de alguna sentencia "return" en la función retorna el control y posiblemente un


valor a la llamada de la función. Vemos algunos ejemplos de lo comentado:

Ejemplo1: Uso de una función para calcular el cuadrado de un número.

/* --------------------
Programa: funcion1.c
-------------------- */

# include <stdio.h>
# define BORRA_P "\x1B[2J"

int sqr(int);
main()
{
int t = 10;
printf(BORRA_P);
printf("\n\n\n");
printf(" El valor %d es el cuadrado de %d", sqr(t), t);
printf("\n\n");
}

86 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

/* Función cuadrado */
/* ---------------- */
sqr(x)
int x;
{
x = x*x;
return(x);
}

Ejemplo2: Un caso de múltiples llamadas a dos funciones dentro de una sentencia "for".

/* --------------------
Programa: funcion2.c
-------------------- */
/*
.. ..
Suma los valores desde el 0 al 9
.. .. */

#include <stdio.h>
#define BORRA_P "\x1B[2J"

int sum; /* variable global */

main()
{
int contador; /* variable local */

printf(BORRA_P);
sum=0; /* inicializa la variable global */
for (contador=0;contador<10;contador++)
{
total(contador);
display();
}
}

/* función que añade el total */


/*-----------------------------*/

total(x) /* int x parámetro formal */


int x;
{
sum=sum+x;
}

/* función de visualizacion */
/*---------------------------*/

display()
{
int contador; /* Este contador es distinto al de main() (Es local) */

for(contador=0;contador<10;contador++) printf(".");
printf("La suma actual es %d \n", sum);
}

Ejemplo3: Operación suma dentro de una función.

/* --------------------
Programa: funcion3.c
------------------- */
/*

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 87


Capítulo 7 Lenguaje “C”

.. ..
Suma los numeros del 0 a un numero dado.
.. ..
*/

#include<stdio.h>
#define BORRA_P "\x1B[2J"

main()
{
int t;
printf(BORRA_P);

printf("\n\n\n Introduzca un numero : ");


scanf("%d",&t);
printf(BORRA_P);
total(t);
}

/* Función total */
/*---------------*/

total(x)
int x;
{
int sum=0,i,contador;

for (i=0;i<x;i++)
{
sum=sum+i;
for(contador=0;contador<10;contador++) printf(".");
printf("la suma actual es %d \n",sum);
}
}

7.2.-DEFINICION DE FUNCIONES.
La definición de una función consiste en especificar su nombre, los parametros formales, y el
cuerpo de la función. Se puede definir también el tipo retornado por la función y su clase de
almacenamiento. Sigue el siguiente formato:

[ind_almac.][ind_tipo]declarador([lista_parametros])
[declaración_parametros]
cuerpo_función

Donde:

ind_almac.: Indica la clase de almacenamiento de la función, debe de ser static o extern.


ind_tipo.: Es el tipo retornado a la función.
declarador: Es el nombre de la función.
lista_parametros: Es la lista de parámetros formales que usará la función, puede estar vacia.
declaración_parametros: Establece el tipo de los parametros formales.
cuerpo_función: Es un conjunto de sentencias que contienen la declaración de variables locales
y sentencias.

7.2.1.-CLASES DE ALMACENAMIENTO.

Las clases de almacenamiento especificadas en la definicion de una función pueden ser: static
o extern. Una función con almacenamiento static es visible solo en el fichero fuente en el cual esta

88 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

definida. Todas la demás funciones tanto si se les da almacenamiento extern explícitamente o si lo


toman implícitamente son visibles en todos los ficheros fuente que constituyen el programa.

7.2.2.-TIPOS RETORNADOS.

El tipo retornado por una función define el tamaño y tipo del valor retornado por la función. La
declaración del tipo tiene el formato:

[ind_tipo]declarador

donde ind_tipo y declarador definen el tipo y el nombre del valor retornado por la función. Si
no se indica ind_tipo se toma por defecto tipo int.

El indi_tipo puede especificar cualquier tipo fundamentas, estructura o unión. Las funciones no
pueden retornar arrays o funciones, pero pueden retornar punteros a cualquier tipo, incluyendo arrays
y funciones.

Una función retorna un valor cuando una sentencia return que contiene una expresión se
ejecuta, la expresión se evalúa, se convierte, si es necesario, al tipo de valor a retornar y se retorna al
punto de llamada. Si una sentencia return no es ejecutada o si la sentencia return no contiene una
expresión, el valor retornado por la función esta indefinido.

Ejemplo:

1:
static sumar(x,y)
int x,y;
{
return(x+y);
}
2:
typedef struct
{
char nombre[20];
int id;
long clase;
} ESTUDIANTE;

ESTUDIANTE ordenaestu(a,b)
ESTUDIANTE a,b;
{
return((a.id<b.id)?a:b);
}
3:
char *cadenapeque(s1,s2)
char s1[],s2[];
{
int i;
i=0;
while(s[i]!='\0'&&s2[i]!='\0')
i++;
if(s1[i]!=='\0')
return(s1)
else
return(s2);
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 89


Capítulo 7 Lenguaje “C”

En el primer ejemplo, el tipo retornado por la función sumar es el tipo int por defecto. La
función tiene clase de almacenamiento static.

En el segundo ejemplo se define el tipo ESTUDIANTE con una declaración typedef y se


define la función ordenaestu() con tipo ESTUDIANTE; la función selecciona y retorna uno de sus dos
argumentos de la estructura.

El tercer ejemplo define una función que retorna un puntero a un array de caracteres, la función
lleva dos arrays (strings) como argumentos y retorna un puntero al más corto de las dos cadenas.

7.2.3.-PARÁMETROS FORMALES.

Los parámetros formales son variables que reciben valores pasados a la función por una
llamada a esta. Los parametros formales se declaran como una lista de parametros al comienzo de la
declaración de la función, definiendo el nombre de los parametros y el orden en el cual reciben los
argumentos de la función de llamada.

Formato:

([identificador[,identificador]]...)

donde cada identificador es un nombre de parámetro.

La declaración de los parametros define el tipo y tamaño de los valores que se pueden
almacenar en ellos.

Un parámetro formal puede ser de tipo fundamental, estructura, union, puntero, o array; si no
se indica tipo asume tipo int. Un parámetro formal solo puede tener almacenamientos auto o register,
si este no se indica se asume auto. Los parametros formales pueden ser declarados en cualquier orden.

Los identificadores de los parametros formales se usan en el cuerpo de la función para referirse
a los valores pasados a la función.

Ejemplo:

struct estudiante
{
char nombre[20];
int id;
long clase;
struct estudiante *siguiestu
} estudiante;

main()
{
int compañero(struct estudiante*,char*);
.
.
if (compañero(estudiante.siguiestu,estudiante.nombre)>0)
{
.
.
}
}

compañero(r,n)
struct estudiante *r;
char *n;

90 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

{
int i=0;
while (r->nombre[i]==n[i])
if (r->nombre[i++]=='\0')
return(r->id);
}

Este ejemplo contiene una declaración de un tipo estructura, una declaración hacia delante de
la función compañero, una llamada a dicha función y la definición de esta.

Nótese que el mismo nombre, estudiante, se puede usar sin conflicto para el nombre de
estructura y para un campo.

La función compañero() se declara con dos argumentos, el primero un puntero al tipo de


estructura estudiante y el segundo un puntero al tipo char.

Los dos parámetros formales de compañero son r y n. El parámetro r se declara como un


puntero al tipo de estructura estudiante mientras que el parámetro n se declara como un puntero a un
tipo char. La función se llama con dos argumentos, ambos miembros de la estructura estudiante.

7.2.4.-CUERPO DE LA FUNCIÓN.

El cuerpo de la función esta formado por un conjunto de sentencias que definen las acciones a
realizar por la función y puede también contener la declaración de las variables usadas por estas
sentencias.

Una variable declarada en el cuerpo de una función tiene almacenamiento auto a no ser que se
indique otra cosa. Cuando se llama a la función se crea espacio para las variables locales y se ejecuta
una inicialización local a continuación se pasa el control a la primera sentencia del cuerpo de la
función y se ejecuta el cuerpo de la función hasta que se ejecuta una sentencia return o se encuentra el
final de la función; pasando el control al siguiente punto a la llamada.

7.3.-DECLARACIÓN DE FUNCIONES.
Una declaración de una función define el nombre, el tipo retornado y la clase de
almacenamiento de una función dada y puede establecer el tipo de alguno o todos los argumentos de la
función.

Las funciones pueden ser declaradas implícitamente o con declaración hacia adelante.

Una declaración implícita tiene lugar cuando la función se llama sin ser previamente definida o
declarada. El compilador implícitamente declara la función para retornar un tipo int y para tener clase
de almacenamiento extern.

Una declaración hacia adelante establece los atributos de una función, permitiendo la
declaración de la función a ser llamada antes de su definición o al ser llamada desde otro fichero
fuente. Estas declaraciones tienen varios usos; Establecer el tipo retornado por las funciones que
retornan algún tipo excepto el tipo int. Las funciones que no retornan un tipo int no pueden se
llamadas antes de ser declaradas o definidas, ya que sino el compilador asume que dichas funciones
retornan un tipo int.

Las declaraciones hacia adelante se pueden usar para establecer los tipos de argumentos
esperados de la función de llamada.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 91


Capítulo 7 Lenguaje “C”

La declaración hacia adelante se usa también para declarar punteros a funciones antes de que la
función sea definida.

Ejemplo:

main()
{
int a=0,b=1;
float x=2.0,y=3.0;
double sumareal(double,double);
a=sumaetera(a,b);
x=sumareal(x,y);
}

sumaentera(a,b);
int a,b;
{
return(a+b);
}

double sumareal(x,y)
double x,y;
{
return(x+y);
}

En este ejemplo, la función sumaentera() se declara de forma implícita para retornar un valor
entero, de modo que es llamada antes de su definición.

La función sumareal() retorna un valor double en lugar de un int. La declaración de sumareal()


en la función main() permite a la función sumareal() ser llamada antes de ser definida. La declaración
hacia adelante de sumareal() también establece los tipos de sus dos argumentos.

7.4.-LLAMADAS A FUNCIONES.
7.4.1.-ARGUMENTOS ACTUALES.

Un argumento actual puede ser un valor de tipo fundamental, estructura, union o puntero.
Todos los argumentos actuales son pasados por valor. Una copia de los argumentos actuales se
asigna a los parámetros formales. La función usa esta copia sin tener efecto sobre los argumentos de
llamada.

Los punteros proporcionan un modo de acceder a los valores por referencia desde una función.
Un puntero a una variable almacena la dirección de esta y la función puede utilizar esta dirección para
acceder a arrays y funciones.

Ejemplo:

main()
{
void intercambia(int *, int *);
int x,y;
.
.
intercambia(&x,&y);

92 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

void intercambia(a,b)
int *a,*b;
{
int t;
t=*a;
*a=*b;
*b=t;

En este ejemplo, la función intercambia() se declara en la función main() con dos argumentos,
ambos puntero a enteros. Los parámetros formales a y b se declaran como punteros a variables enteras.

En la función de llamada: intercambia(&x,&y), la dirección de x se almacena en a y la


dirección de y se almacena en y. Hay dos nombres, o alias, para una misma localización. Las
referencias *a y *b dentro de intercambia() cambian el contenido de x e y.

Ejemplo4: Invocación de una función incluida en un fichero fuente aparte.

/* --------------------
Programa: funcion4.c
-------------------- */
/*
.. ..
Contiene el programa principal para
calcular la superficie de una esfera.
Utiliza la función área() del fichero "sup.c".
.. ..
*/

#include "sup.c"
#define BORRA_P "\x1B[2J"

double área(void); /* prototipo */


float radio; /* definicion de la variable externa 'radio' */

main()
{
printf(BORRA_P);
printf("\n\n\n");
printf(" Introduzca el radio de la esfera: ");
scanf("%f",&radio);
printf("\n\n -----------------------------------------");
printf("\n\n El área de la esfera es %.10le",área());
printf("\n\n -----------------------------------------\n");

/* -----------------------
Programa: sup.c
Invocado por funcion5.c
----------------------- */
/*
.. ..
Contiene la función área() para calcular el área de una esfera.
la llama el programa del fichero CALC.C
.. ..
*/

double área() /* definicion de la función */

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 93


Capítulo 7 Lenguaje “C”

{
extern float radio; /* declara la variable externa 'radio' */
return(4*3.1416*radio*radio);
}

Compilamos y ejecutamos este programa:

# cc -compat función.c
funcion5.c
# ./a.out

Introduzca el radio de la esfera: 2 <Return>

-----------------------------------------

El área de al esfera es 5.0265600000e+01

-----------------------------------------

7.4.2.-LLAMADAS CON UN NÚMERO VARIABLE DE ARGUMENTOS.

Para llamar a una función con un número variable de argumentos, el programador simplemente
indica estos en la función de llamada. En la declaración hacia adelante de una función (si hay alguna),
el número variable de argumentos se especifica colocando una coma al final de la lista de tipos. Un
argumento debe de figurar en la función de llamada por cada nombre de tipo especificado en la lista de
tipos de argumentos. Si solo se indica una coma indica que no se requieren argumentos cuando la
función se llama.

Todos los argumentos dados en la función de llamada se almacena en una pila. El número de
parámetros formales declarado por la función determina como algunos de los argumentos se llevan de
la pila y se asignan a los parámetros formales. El programador es el responsable de toda esta
operación.

Ejemplo:

main()
{
int calificacion(int,);
int cont,media,i;
.
.
media=calificacion(cont,14,96,82);
.
.
}

calificacion(numero)
int numero;
{
int *ip,total=0,i;
ip=&numero+1;
for (i=1;i<=numero;i++,ip++)
total+=*ip;
if (numero>0)
return(total/numero);
return(-1);

94 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

En este ejemplo la función calificacion tiene un número variable de argumentos. La


declaración hacia adelante de la función calificacion() dentro de la función main() establece que esta
tiene al menos un argumento, un entero. La coma al final de la lista de tipos de argumentos indica que
pueden figuran más argumentos no declarados.

En la llamada a calificación se pasan cuatro argumentos. El primer argumento es chequeado


por el compilador para comprobar su compatibilidad con la lista de tipos de los argumentos y las lisa
de parámetros formales de la función calificación. En la definicion de la función calificacion() solo se
declara un parámetro formal. Los restantes argumentos son recuperados llevando la direccion del
anterior argumento (número en el primer caso), incrementándolo y recuperando el valor de esa
dirección. Este procedimiento funciona porque los argumentos pasados a la función se almacenan en
orden en la pila.

El parámetro número almacenará el número de argumentos adicionales. Cuando un número de


argumentos es recuperado y añadido a total, la media de calificaciones es devuelta a la función main().
Se retorna un valor -1 si número es cero.

7.4.3.-LLAMADAS RECURSIVAS.

Una función en un programa "C" puede ser llamada de forma recursiva. Por lo tanto una
función podrá llamarse a si misma. El compilador "C" permite cualquier número de llamadas
recursivas a un función. En cada llamada, se reserva espacio de almacenamiento para los parámetros
formales y para las variables auto y register, manteniendo siempre sus valores previos. Las variables
definidas con almacenamiento global se mantienen en cada una de las llamadas recursivas ya que
existen durante todo el tiempo de vida del programa; ya que la referencia a esta variables es a la
misma área de almacenamiento.

Por tanto el limite esta marcado por el entorno ya que al requerirse en cada llamada recursiva
memoria adicional para la pila, demasiadas llamadas recursivas pueden provocar un overflow
(desbordamiento) de la pila.

Ejemplo5: Un caso muy aclaratorio de la recursividad.

/* --------------------
Programa: funcion5.c
-------------------- */
/*
.. ..
Este programa muestra el funcionamiento
de la recursividad.
La función main() se invoca a si misma.
.. ..
*/

# define BORRA_P "\x1B[2J"


# define FILA5 "\x1B[5;7f"
# define FILA10 "\x1B[10;12f"
# include <stdio.h>

main()
{
char ch;
printf(BORRA_P);
printf(FILA5);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 95


Capítulo 7 Lenguaje “C”

printf("Pulse un carácter cualquiera. Se detiene con una Q. : ");


ch = getchar();
printf(FILA10);

if (ch != 'Q')
main();
else
printf(" Aja!. eso era una - %c - \n\n",ch);
}

Ejemplo6: Ahora uno clásico, cálculo del factorial de un número.

/* --------------------
Programa: funcion6.c
-------------------- */
/*
.. ..
Utiliza las funciones 'factorial_1()'
y 'factorial_2()'.
.. ..
*/

unsigned long factorial_1(unsigned long); /* prototipos */


unsigned long factorial_2(unsigned long);
# define BORRA_P "\x1B[2J"
main()
{

unsigned long n, solucion;

printf(BORRA_P);

printf("\n\n\n");
printf(" Introduzca un numero entero (menor que 34):");
scanf("%lu", &n);
solucion=factorial_1(n); /* solucion recursiva */
printf("\n\n\n Solucion recursiva : %lu! -> %lu \n", n, solucion);
solucion=factorial_2(n); /* solucion no recursiva */

printf("\n\n\n Solucion no recursiva : %lu! -> %lu\n", n, solucion);


}

/* Función factorial_1() */

unsigned long factorial_1(n) /* definicion de la función */


unsigned long n;
{
unsigned long solucion;
if (n==0||n==1)
return(1);
solucion = factorial_1(n-1)*n;
return(solucion);
}

/* Función factorial_2() */

unsigned long factorial_2(n) /* definicion de la función */


unsigned long n;
{
unsigned long i, solucion=1;
if(n==0)
return(1);
for (i=1;i<=n;i++)
solucion *= i;

96 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

return(solucion);
}

7.5.-EJEMPLOS RECAPITULATIVOS.
Ejemplo1: Visualización del contenido de un array dentro de dos funciones, cada una de ellas con una
notación distinta.

/* --------------------
Programa: funcion7.c
-------------------- */

#include <stdio.h>
#define BORRA_P "\x1B[2J"
main()
{
int t[10],i;

printf(BORRA_P);

for (i=0;i<10;++i) t[i]=i;


display(t);
display1(t);
printf("\n\n");
}

/* Función display */
/* ---------------- */

display(num)
int num[10];
{

int i;

printf("\n\n\n\n -> ");


for (i=0;i<10;i++) printf(". %d ",num[i]);
printf(". <-");
}

/* Función display1 */
/* ---------------- */

display1(num)
int *num;
{
int i;

printf("\n\n\n\n -> ");


for (i=0;i<10;i++) printf(". %d ",num[i]);
printf(". <-");
}

Ejemplo2: Intercambiamos dos números dentro del cuerpo de una función.

/* --------------------
Programa: funcion8.c
-------------------- */
/*
.. ..
Este programa intercambia dos numeros.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 97


Capítulo 7 Lenguaje “C”

.. .. */

# define BORRA_P "\x1B[2J"


main()
{
int x=5,y=10;
printf(BORRA_P);
printf("\n\n");
printf("=================================\n");
printf("En principio x = %2d y y = %2d. \n",x,y);
printf("=================================\n\n\n");
intercambia(x,y);
printf("\n\n");
}
/* Función intercambia */
/* =================== */
intercambia(u,v)
int u,v;
{
int temp;
temp = u;
u = v;
v = temp;
printf("=================================\n");
printf("Ahora x = %2d y y = %2d. \n",u,v);
printf("=================================\n");
}

Ejemplo3: Intercambiamos dos números pasando a una función sus direcciones.

PASO DE VALORES POR REFERENCIA

/* --------------------
Programa: funcion9.c
-------------------- */
/*
.. ..
En este programa se contempla el funcionamiento de una
función pasando valores por referencia.
.. ..
*/

#define BORRA_P "\x1B[2J"

main()
{
int x,y;

printf(BORRA_P);
x=10;
y=20;
printf("\n\n\n");
printf(" Valores iniciales de x e y : <%2d> <%2d> \n",x,y);
swap(&x,&y);
printf(" Valores intercambiados de x e y: <%2d> <%2d> \n",x,y);

/* Función swap */
/* ------------ */

swap(x,y)
int *x, *y;
{
int temp;
temp = *x; /* Guarda el valor de la variable apuntada por x en temp */

98 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

*x = *y; /* Asigna el contenido de la direccion apuntada por x */


*y = temp; /* en la direccion apuntada por y */
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 99


Capítulo 7 Lenguaje “C”

Ejemplo4: Un último ejemplo.

/* --------------------
Programa: funcio10.c
-------------------- */

#define BORRA_P "\x1B[2J"


#include <stdio.h>

int cont=100;

main()
{
printf(BORRA_P);
func1();
}

/* cuerpo de func1() */
/* ----------------- */

func1()
{
printf("\n\n\n\n\n");
func2();
printf("\n\n");
printf(" cont es %d", cont);
printf("\n\n");
func2();
printf("\n\n\n");
}

/* Cuerpo de func2 */
/* --------------- */

func2()
{
int cont;
printf(" ");
for (cont=1;cont<12;cont++)
printf(".");
}

100 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 7

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 101


Capítulo 8 Lenguaje “C”

CAPÍTULO 8
FUNCIONES PARA MANEJOS DE CADENAS

8.1.-FUNCIONES DE CADENA
El lenguaje "C" no tiene operadores para el manejo de cadenas. Por esta razón mediante una
sentencia de asignación no se puede asignar una cadena a una variable tipo char. El siguiente ejemplo
seria por tanto incorrecto en base a la afirmación anterior.

/* -------------------
Programa: cadena1.c
------------------- */

#define CADENA "\"Xacobeo 93\""


main()
{
char cad;
cad=CADENA;
printf("%s",cad);
}║

Producirá el siguiente error:

3% cc -compat cadena1.c
cadena1.c
cadena1.c(10) : warning 47: `=' : different levels of indirection

Para imprimir la constante CADENA del programa anterior basta especificarla como
argumento en la función printf().

/* --------------------
Programa: cadena2.c
-------------------- */

# define CADENA "\"Xacobeo 93\""


# define DIEZ "\x1B[10;30f"

main()
{
system("clear");
printf(DIEZ);
printf("(%s)\n\n", CADENA);
}

La función utilizada en C para la lectura de cadenas es la función gets(). Vemos un ejemplo de


su uso.

102 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 8

/* -------------------
Programa: cadena3.c
------------------- */
# include <stdio.h>
# define DIEZ "\x1B[10;10f"

main()
{
char cadena[80];

system("clear");
printf("\n\n\n");
printf(" Introduzca una cadena: ");
gets(cadena);
printf("\n\n");
system("clear");
printf(DIEZ);
printf(" La cadena introducida es : <%s> :",cadena);
printf("\n\n\n");

En C no se pueden comparar dos cadenas con los operadores de relación. Por ello el
lenguaje "C" incorpora un amplio conjunto de funciones de programoteca que permiten comparar y
tratar cadenas. La mayoría de ellas serán tratadas en este capítulo.

La mayor parte de las funciones de cadena tienen definidos sus prototipos y constantes en los
ficheros cabecera string.h y ctype.h.

En el fichero ctype figuran, entre otras, las siguientes funciones:

char *strcat(s1, s1) char *strchr(s, c)


char *s1, *s2; char *s;
int c;

char *strncat(s1, s2, n) char *strrchr(s, c);


char *s1, *s2; char *s;
int n; int c;

char strcmp(s1, s2) char *strpbrk(s1, s2)


char *s1, *s2; char *s1, *s2;

int strncmp(s1, s2, n); int strspn(s1, s2)


char *s1, *s2; char *s1, *s2;
int n;

char *strcpy(s1, s2) int strcspn(s1, s2)


char *s1, *s2; char *s1, *s2;

char *strncpy(s1, s2, n) char *strtok(s1, s2)


char *s1, *s2; char *s1, *s2;
int n;

char *strlen(s) char *strdup(s)


char *s; char *s;
En el fichero cabecera ctype.h figuran entre otras las siguientes funciones: isalpha, isupper,
islower, isdigit,isxdigit, isalnum, isspace, sipunct, isprint, isgraph, iscntrl, isascii, tolower, toupper,
toascii.

Vemos parte de estas funciones agrupandolas en base a su finalidad.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 103


Capítulo 8 Lenguaje “C”

8.2.-FUNCIONES DE COPIA DE CADENAS.


- Función strcpy().

Esta función copia una cadena en otra. El primer argumento es un puntero a la cadena
receptora y el segundo otro puntero a la cadena origen. Devuelve un puntero a la cadena receptora.

Ejemplo: Copiamos una cadena en un array de caracteres.

/* -------------------
Programa: cadena4.c
------------------- */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[5;10f"

main()
{
char cadena[80];

system("clear");
strcpy(cadena,"Hola como estas");
printf(CINCO);
printf("La cadena introducida es : <%s> :\n\n",cadena);
}

- Función strncpy().

Copia n caracteres de una cadena en otra. El primer argumento es un puntero a la cadena


receptora; el segundo es otro puntero a la cadena origen y el tercero es el número n, de tipo int.
Devuelve un puntero a la cadena receptora.

- Función strcat().

Copia una cadena al final de otra. El primer argumento es un puntero a la cadena receptora y el
segundo otro puntero a la cadena origen. Devuelve un puntero a la cadena receptora.

Ejemplo: Combinamos las funciones strcpy() y strcat() para dadas dos cadenas concatenarlas.

/* -------------------
Programa: cadena5.c
------------------- */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[5;10f"
main()
{
char cadena1[80],cadena2[80];

system("clear");
strcpy(cadena1,"Hola como estas. ");
strcpy(cadena2,"- Muy bien !");

104 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 8

strcat(cadena1,cadena2);
printf(CINCO);
printf("La cadena resultado es : <%s> :\n\n",cadena1);
}

- Función strncat().

Copia n caracteres de una cadena al final de otra. El primer argumento es un puntero a la


cadena receptora; el segundo otro puntero a la cadena origen y el tercero es el número n, de tipo int.
Devuelve un puntero a la cadena receptora.

- Función strlen().

Esta función determina la longitud de una cadena. El argumento es un puntero a la cadena.


Devuelve la longitud de la cadena.

Ejemplo: Utilizamos la función anterior para obtener la longitud de una cadena.

/* -------------------
Programa: cadena6.c
------------------- */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[5;10f"

main()
{
char cadena1[80];

system("clear");
printf(CINCO);
printf("Introduzca una cadena: ");
gets(cadena1);
system("clear");
printf(CINCO);
printf("La longitud de la cadena <%s> es <%d> " ,cadena1,strlen(cadena1)); ║
printf("\n\n\n");
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 105


Capítulo 8 Lenguaje “C”

8.3.-FUNCIONES DE BUSQUEDA Y COMPARACION DE CADENAS.


- Función strchr() y strrchr().

La función strchr() (strrchr()) retorna un puntero a la primera (última) ocurrencia del carácter
"c" en la cadena "s" (Ver formato). El primer argumento es un puntero a la cadena y el segundo el
código ASCII del carácter. Devuelve un puntero a la posición carácter dentro de la cadena. Si no se
encuentra el carácter devuelve un puntero NULL. Vemos un ejemplo:

/* -------------------
Programa: cadena7.c
------------------- */

# include <stdio.h>
# include <string.h>

main()
{
char *cadena = "esta es una cadena";
char *caracter, *caracter2;
system("clear");
caracter=strchr(cadena, 97);
caracter2=strrchr(cadena, 101);
printf("\n\nCadena = \"%s\"", cadena);
printf("\n\ncadena1 = '%c'", *caracter);
printf("\n\ncadena2 = '%c'\n\n", *caracter2);
}
/* .. ..
a -> ascii(97).
e -> ascii(101).
.. .. */

- Función strcmp().

Compara dos cadenas. Los argumentos son, ambos, punteros a las cadenas. Devuelve 0 si las
cadenas son iguales, un número mayor que 0 si cadena1 es mayor que cadena2 o un número menor
que 0 si cadena1 es menor que cadena2.

Ejemplo: Un ejemplo de como se utiliza esta función en un bucle infinito.

/* -------------------
Programa: cadena8.c
------------------- */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[5;10f"
# define SEIS "\x1B[6;10f"
# define DIEZ "\x1B[10;10f"

main()
{
char cadena1[30];
for(;;)
{
system("clear");
printf(CINCO);
printf("Introduzca una cadena. Acabar -> \"salir\" : ");
gets(cadena1);

106 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 8

if (strcmp("salir",cadena1)) visualiza(cadena1);
if(!strcmp("salir",cadena1)) break;
}
system("clear");
printf(CINCO);
printf("Logicamente para salir ha tecleado : <%s> : ",cadena1);
printf(SEIS);
printf("----------------------------------------------");
printf("\n\n\n");
}
visualiza(cadena)
char cadena[30];
{
printf(DIEZ);
printf("<%s>",cadena);
}

- Función strncmp().

Compara los n primeros caracteres de dos cadenas. Los dos primeros argumentos son punteros
a las cadenas, mientras que el tercero es el número n, de tipo int. Devuelve 0 si los n primeros
caracteres de cadena1 son iguales a los de cadena2, un número mayor que 0 si los caracteres de
cadena1 tienen valor mayor que los de cadena2 y un número menor que 0 en caso contrario.

- Función strpbrk().

Esta función retorna un puntero a la primera ocurrencia de la cadena s2 en la cadena s1 (Ver


formato) o un carácter NULL si la cadena s2 no se encuentra en s1.

8.4.-FUNCIONES DE TRANSFORMACION DE CARACTERES.


- Funciones tolower() y toupper.

La función tolower() convierte a minúsculas cualquier carácter del alfabeto inglés. Cualquier
otro carácter no lo convierte.

La función toupper convierte a mayúsculas cualquier carácter del alfabeto inglés. Cualquier
otro carácter no lo convierte.

Ambas funciones devuelven el código del carácter transformado. Vemos un ejemplo del uso de
ambas funciones:

Ejemplo: Un caso interesante, utilizando una función, del uso de estas funciones.

/* -------------------
Programa: cadena9.c
------------------- */

#include "ctype.h"
int conver(void);

main()
{
int minus,mayus;
char tecla;
system("clear");
printf("\n\n Pulse una tecla alfabetica: ");
minus=conver();
printf("\n\n Minuscula -> <%c>", minus);
mayus=toupper(minus);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 107


Capítulo 8 Lenguaje “C”

printf("\n\n Mayuscula -> <%c>", mayus);

printf("\n\n");
}

/*-----------*/
int conver()
{
int x;
x = getchar();
x = tolower(x);
if (x==165)
x==164;
return(x);
}

8.5.-FUNCIONES DE TRANSFORMACION DE TIPO.


- Funciones atof(), atoi(), atol(), ecvt().

double atof(nptr)
char *nptr;

int atoi(nptr)
char *nptr;

long atol(nptr)
char *nptr;

char *ecvt(numero, longitud, cifras, signo)


double numero;
int longitud, *cifras, *signo;

- Función atof().

Esta función transforma la cadena del argumento en un número de tipo double. El argumento
puede tener signo y utilizar notación exponencial. Si se incluyen caracteres sin interpretación numérica
se da por finalizada la conversión. Esta función devuelve un número transformado o cero a partir de la
subcadena ilegal.

- Función atoi().

Transforma la cadena del argumento en un número de tipo int. El argumento puede tener signo.
Si se incluyen caracteres sin interpretación numérica se da por finalizada la con-versión. Esta función
devuelve el número transformado, o cero si no se puede transformar.

108 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 8

Ejemplo: Convertimos el "12" en 12 e imprimimos su cuadrado.

/* ---------------------
Programa: cadena10.c
-------------------- */

main()
{
int valor;
char *cadena="12";
system("clear");
valor = atoi(cadena);
printf("\n\n Cuadrado de valor -> <%d> \n\n", valor * valor);
}

- Función atol().

Transforma la cadena del argumento en un número de tipo int long. Por lo demás, se comporta
igual que atoi(). Esta función devuelve el número transformado, o cero a partir de la subcadena ilegal.

- Función ecvt().

Transforma un número de los tipos float o double en una cadena, con el carácter 'nulo' al final
de la misma. Tiene cuatro argumentos, los dos primeros de entrada y los dos últimos de salida. El
primero contiene el número que se desea convertir; el segundo contiene la longitud de la cadena; el
tercero es un puntero a un entero donde la función almacena un entero que indica la situación del
punto decimal dentro de la cadena por la izquierda (positivo, negativo o cero, según el punto esté
situado a la derecha, a la izquierda o precediendo justamente al primer dígito de la cadena); el cuarto
es otro puntero a un entero donde la función almacena el signo del número (0 si es positivo y 1 si es
negativo). Devuelve un puntero a la cadena transformada. El ejemplo cadena11 es una muestra del uso
de esta función.

/* --------------------
Programa: cadena11.c
-------------------- */

main()
{
char *cadena;
double valor = 3.141592654;
int num_cifras = 10;
int posicion_punto = 1;
int signo = 0;
system("clear");
printf("\n\nLa cadena es: <%s> ",ecvt(3.141592654, num_cifras,
&posicion_punto, &signo));
printf("\n\nNumero de cifras almacenadas : <%d> ",num_cifras);
printf("\n\nPosicion del punto decimal ( primera es 0) : <%d> ",
posicion_punto);
printf("\n\nSigno (positivo 0, negativo 1) : <%d> \n\n", signo);
}

8.6.-OTROS EJEMPLOS INTERESANTES.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 109


Capítulo 8 Lenguaje “C”

Ejemplo1: Aplicación de la función strlen() en un bucle "for" para inicializar la variable utilizada en
la condición a evaluar.

/* --------------------
Programa: cadena12.c
-------------------- */

/* .. ..
Recordar que las cadenas en C son arrays de caracteres,
terminados con un caracter nulo '\0'.
.. .. */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[5;10f"

main()
{
char cadena1[80];
int i;

system("clear");
printf(CINCO);
printf("Introduzca una cadena: ");
gets(cadena1);
system("clear");
printf(CINCO);
for(i=strlen(cadena1)-1;i>=0;i--)
printf("* %c ",cadena1[i]);
printf("*");
printf("\n\n\n");
}

Ejemplo2: Utilizamos la función strcmp() en la condición de una sentencia "if" para com-parar dos
cadenas, indicando si son iguales; Posteriormente las concatenamos con strcat().

/* --------------------
Programa: cadena13.c
-------------------- */

# include <stdio.h>
# include <string.h>
# define CINCO "\x1B[10;5f"
# define ONCE "\x1B[11;30f"
# define DIEZ "\x1B[10;10f"
# define DOCE "\x1B[12;10f"
# define CATO "\x1B[14;10f"

main()
{
char s1[80],s2[80];

system("clear");
printf(CINCO);
printf("Introduzca dos cadenas : ");
gets(s1);

printf(ONCE);
gets(s2);
system("clear");
printf(DIEZ);
printf("Longitud: %d %d \n", strlen(s1),strlen(s2));
if(!strcmp(s1,s2))
{
printf(DOCE);

110 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 8

printf("Las cadenas son iguales. \n");


}
strcat(s1,s2);
printf(CATO);
printf("La concatenacion es : <%s> : \n",s1);
}

Ejemplo3: Un último ejemplo, con las funciónes strcpy() y toupper() muy interesante dado el uso de
un vector como única componente de la condición a evaluar en un bucle for.

/* --------------------
Programa: cadena14.c
-------------------- */

# include <stdio.h>
# include <string.h>
# define DIEZ "\x1B[10;10f"

main()
{
char s1[80];
int i;

system("clear");
strcpy(s1,"esto es una prueba");
for(i=0;s1[i];i++) s1[i] = toupper(s1[i]);
printf(DIEZ);
printf(": <%s> :",s1);
printf("\n\n");
}

/* .. ..
La condicion de prueba del bucle for es el array que
indexa la variable de control. La razon de que este
programa funcione es que es verdad cualquier valor no 0.
Por tanto el bucle funciona hasta que encuentre el
terminador nulo, que es 0, el cual marca el fin de la
cadena.
.. .. */

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 111


Capítulo 9 Lenguaje “C”

CAPÍTULO 9
DIRECTIVAS PREPROCESADOR

9.1.- INTRODUCCION.
El preprocesador de "C" es un procesador de textos para manipular el texto de un fichero
fuente antes de la compilación.

Las directivas de preprocesador se usan normalmente para conseguir programas fuente fáciles
de modificar y ejecutarlos en diferentes entornos de ejecución. Estas directivas como parte de las
instrucciones de un fichero fuente ejecutarán diferentes acciones; por ejemplo el preprocesador puede
reemplazar palabras clave en el texto, insertar el contenido de otros fi-cheros en el fichero actual,
suprimir la compilación de una parte del fichero fuente, etc.

Las directivas de preprocesador figuran en la siguiente relación:

#define #ifdef
#elif #ifndef
#else #include
#endif #line
#if #undef

El signo numérico "#" tiene que ser el primer carácter no blanco de la línea que contiene la
sentencia directiva. Algunas sentencias directivas pueden ir seguidas por argumentos o valores y
pueden aparecer en cualquier parte de un fichero fuente.

9.2.- CONSTANTES Y MACROS.


La directiva #define se usa normalmente asociado con el significado de constantes,
palabras_clave, y comunmente se usa con sentencias y expresiones. Los identificadores asociados a
#define pueden representar a constantes o sentencias en cuyo caso se llaman "macros".

Una vez definido el identificador, no puede redefinirse con un valor diferente sin borrar antes
su definicion previa. La directiva undef borra la definicion de un identificador. Una vez quitado el
identificador con #undef este se puede redefinir con un valor diferente.

Las macros pueden ser definidas como funciones de llamada, sin embargo pueden crear
problemas si no son definidas y usadas con cuidado; La definición de macros con argumentos puede
requerir el uso de paréntesis para preservar el grado de precedencia en una expresión.

9.2.1.- DIRECTIVA DEFINE.

Formato:

#define identificador texto


#define identificador (lista_parametros)texto

112 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

La directiva #define sustituye el texto dado por las ocurrencias del identificador en el fichero
fuente. Si aparece "lista_parametros" después del identificador la directiva #define reemplaza cada
ocurrencia del identificador (lista de argumentos) con una versión del texto modificado por la
sustitución de los argumentos actuales por parámetros formales.

Si el "texto" es mayor que una línea, este puede continuar en la siguiente incluyendo un
carácter backslash "\". El "texto" puede también estar vacío, su efecto es que quita todas las
ocurrencias del identificador del fichero fuente.

La "lista_parametros", cuando aparece, esta formada por uno o más nombres de parámetros
formales separados por comas; cada nombre en la lista tiene que ser único y la lista debe estar
encerrada entre paréntesis. No se permiten espacios entre el identificador y la apertura del paréntesis.
Los parámetros formales aparecen en el texto para marcar el lugar en donde los valores actuales serán
sustituidos.

Ejemplo:

1. #define ANCHO 80
#define LARGO (ANCHO+10)

2. #define FICHEROMENS "intento de crear \


un fichero erroneo por falta de espacio"

3. #define REG1 registro


#define REG2 registro
#define REG3

4. #define MAX(x,y) ((x)>(y))?(x):(y)

5. #define MULT(a,b) ((a)*(b))

El primer ejemplo define el identificador ANCHO como una constante entera de valor 80, y
define el identificador LARGO como otra de valor ANCHO+10. Cada ocurrencia de LARGO se
reemplaza con "ANCHO+10" que a su vez es reemplazado por "(80+10)". Los paréntesis son
importantes ya que controlan la interpretación en una sentencia, ejemplo:

var=LARGO*20;

Después del preprocesado la sentencia queda

var=(80+10)*20;

Sin los paréntesis quedaría

var=80+10*20;

El segundo ejemplo muestra un caso de una definicion extendida a mas de una línea usando el
carácter "\".

En el tercero se definen los identificadores REG1, REG2, REG3, los dos primeros se definen
con la palabra registro. La definicion de REG3 esta vacia por ello cada ocurrencia de REG3 se borra
del fichero fuente.

El cuarto ejemplo define una macro de nombre MAX. Cada ocurrencia del identificador MAX
se reemplaza por la expresión "((x)>(y))?(x):(y)" donde los valores actuales reemplazan los
parámetros "x" e "y". Por ejemplo la ocurrencia

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 113


Capítulo 9 Lenguaje “C”

MAX(1,2)

se reemplaza por

((1)>(2))?(1):(2)

y la ocurrencia

MAX(I,S[I])

por

((i)>(s[i]))?(i):(s[i])

En ciertos casos las macros pueden producir resultados inesperados, como en la siguiente
ocurrencia

MAX(i,s[i++])

que es reemplazada por

((i)>(s[i++]))?(i):(s[i])

donde la expresión "(s[i++])" se evalúa dos veces, incrementándose i en dos.

El tercer ejemplo define la macro MULT. Una vez definida esta una ocurrencia como

MULT(3,5) se reemplaza por "(3)*(5)".

donde los paréntesis alrededor de los paréntesis tiene su importancia ya que controlan la interpretación
cuando expresiones complejas constituyen los argumentos de una macro. Por ejemplo:

MULT(3+4,5+6)

se reemplaza por

"(3+4)*(5+6)", de valor 77,

sin dos paréntesis el resultado seria

"3+4*5+6" de valor 29.

Ejemplo1: Utilización de la sentencia define para la definicion de constantes.

/* ------------------
Programa: direc1.c
------------------ */

# include <stdio.h>
# define TAMA_MAX 16
# define BORRA_P "\x1B[2J"
unsigned int potencias_de_dos[TAMA_MAX];

/* Visualizacion de las potencias de dos. */

114 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

main()
{
int i;
printf(BORRA_P);
potencias_de_dos[0]= 1;

for(i=1;i<TAMA_MAX;i++)
potencias_de_dos[i] = potencias_de_dos[i-1] * 2;

printf(" Las primeras 16 potencias de 2: \n");


printf(" ------------------------------- \n\n\n");
printf(" ");
for(i=0;i<TAMA_MAX-6;i++)
printf(" <%u> ",potencias_de_dos[i]);
printf("\n\n ");

for(i=10;i<TAMA_MAX;i++)
printf(" <%u> ",potencias_de_dos[i]);
printf("\n\n");
}

Ejemplo2: Definición de una macro, MIN(a,b), que calcula el mínimo de dos números.

/* ------------------
Programa: direc2.c
------------------ */

# include <stdio.h>
# define BORRA_P "\x1B[2J"
# define FILA10 "\x1B[10;10f"
# define FILA12 "\x1B[12;10f"
# define FILA14 "\x1B[14;10f"
# define FILA16 "\x1B[16;10f"
# define MIN(a,b) ((a)<(b)) ? (a) : (b)
main()
{
int x,y;
printf(BORRA_P);
x=10;
y=20;
printf(FILA10);
printf(" ... El mínimo de: <%2d> y <%2d> es <%2d> ", x,y,MIN(x,y));
printf(FILA12);
printf(" ... El mínimo de: <%2d> y <%2d> es <%2d> ", 2,5,MIN(2,5));
printf(FILA14);
printf(" ... El mínimo de: <%2d> y <%2d> es <%2d> ", 2*x,2*y,MIN(2*x,2*y));
printf(FILA16);
printf(" ... El mínimo de: <%2d> y <%2d> es <%2d> ", x+y,y-x,MIN(x+y,y-x));
printf("\n\n");
}

9.2.2.- DIRECTIVAS NO DEFINIDAS.

La directiva #undef quita la definición actual de un identificador. El preprocesador ignora las


subsiguientes ocurrencias del identificador. Para quitar una definicion de macro se usa #undef seguido
solo del identificador.

La directiva #undef va normalmente emparejada con la directiva #define para crear una región
en un programa fuente en el cual un identificador tenga un significado especial. Por ejemplo, una
función especifica del programa fuente puede usar unas constates para definir un entorno especifico de

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 115


Capítulo 9 Lenguaje “C”

valores que no afecten al resto del programa. La directiva #undef también se utiliza conjuntamente con
la directiva #if para controlar partes de la compilación de un programa fuente.

Ejemplo:

#define ANCHO 80
#define SUMAR(X,Y) (X)+(Y)
.
.
#undef ANCHO
#undef SUMAR

En este ejemplo la directiva #undef quita las definiciones de una constante y una macro. Véase
como únicamente se indica el identificador. La directiva #undef se puede apli-car también a un
identificador que no se ha definido previamente para asegurar que el iden-tificador está indefinido.

Ejemplo3: Uso de la sentencia directiva "undef" para quitar el valor actual de un identificador.

/* ------------------
Programa: direc3.c
------------------ */

# include <stdio.h>
# define BORRA_P "\x1B[2J"
# define LONGITUD 100
# define ANCHO 100
int array[LONGITUD][ANCHO];
# undef LONGITUD
# undef ANCHO
# define LONGITUD 3
# define ANCHO 3

main()
{
int i,j;
printf(BORRA_P);
for (i=0;i<3;i++)
for (j=0;j<3;j++)
array[i][j] = i+j;

/* IMPRESION */
/* --------- */

printf(" Imprimimos el vector anterior.\n");


printf(" -----------------------------.\n\n\n");

for (i=0;i<3;i++)
for (j=0;j<3;j++)
printf(" <%2d> ",array[i][j]);
printf("\n\n");
}

9.3.- DIRECTIVA INCLUDE.


Formato:

#include camino
#include <camino>

116 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

La directiva #include añade el contenido de un fichero a otro fichero. Definiciones de macros y


constantes pueden figurar dentro del fichero a incluir. Esta directiva se puede usar también para incluir
ficheros que incorporan declaraciones de variables externas y tipos complejos de datos.

La directiva #include permite al preprocesador tratar el contenido del fichero como si


apareciese en el programa fuente en el punto en el cual esta la directiva. El nuevo texto puede también
contener sentencias directivas.

El "camino" es un nombre de fichero opcionalmente precedido por una especificación de


directorio, tiene que ser el nombre de un fichero existente. La sintaxis de la expecificación depende del
sistema operativo especifico en el cual el programa es compilado.

El procesador usa el concepto de directorio(s) "standard" para buscar los ficheros incluidos. La
localización de estos directorios depende de la implementación y del sistema operativo. El procesador
para la búsqueda tan pronto como encuentre un fichero con el nombre dado.

Si se indica el camino entre comillas ("") o entre ángulos (<>), el preprocesador busca solo en
esos caminos y ignora los directorios standard. Si no se da la especificación completa del "camino" y
la especificación esta encerrada entre comillas , el preprocesador busca el fichero en el directorio de
trabajo actual. Luego busca en los directorios especificados en la línea de comandos del compilador y
finalmente busca en los directorios estándares.

Si la especificación del fichero se encierra entre "<>", el preprocesador no busca en el


directorio actual de trabajo. Comienza la búsqueda del fichero en los directorios especificados en la
línea de comandos del compilador y luego busca en los directorios estándares.

Ejemplo:

1. #include <stdio.h>
2. #include "defs.h"

El primer ejemplo añade el contenido del fichero stdio.h a programa fuente actual. Los
símbolos "<>" provocan que el preprocesador busque en los directorios estándares el fichero stdio.h,
después de buscar en los directorios especificados en la línea de comando.

El segundo ejemplo añade el contenido del fichero defs.h al programa fuente. La comillas
dobles provocan que se busque en primer lugar en el directorio que contiene el programa fuente actual.

9.4.- COMPILACION CONDICIONAL.


Esta sección describe la sintaxis y uso de las directivas que controlan la compilación
condicional. Estas directivas permiten la supresión de la compilación de parte del programa fuente.
Estas directivas evalúan una constante_expresión o un identificador para determinar que bloque(s) del
programa fuente se compilan y cuales no.

9.4.1.- DIRECTIVAS IF, ELIF, ELSE Y ENDIF.

Formato:

#if rest_constante_expresión

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 117


Capítulo 9 Lenguaje “C”

[texto]
[#elif rest_constante_expresión
texto]
[#elif rest_constante_expresión
texto]
.
.
[#else
texto]
#endif

La directiva #if, junto con las directivas #elif,#else, y #endif, controlan la compilación de
partes de un programa fuente. Cada directiva #if tiene que finalizar con una directiva #endif.

Cero o más directivas #elif pueden aparecer entre las directivas #if y #endif, pero solo se
permite una directiva #else. La directiva #else, si esta presente, tiene que figurar antes que #endif.

El preprocesador selecciona uno de los bloques de texto. Normalmente un bloque de texto es


un programa fuente que tiene significado para el compilador o para el procesador.

El texto seleccionado es procesado por el procesador y se pasa al compilador, mientras que el


bloque de texto no seleccionado por el procesador se quita del fichero y no es compilado.

La selección de un bloque de texto resulta de la evaluación de "rest_constante_expresión"


asociado a cada directiva #if o #elif hasta que se encuentra una "rest_constante_expresion" cierta (no
0), en cuyo caso todo el texto entre "rest_constante_expresion" y el siguiente signo # se selecciona.

Si "rest_constante_expresion" no es cierta, o sin no hay directivas #elif, el preprocesador


selecciona el texto después de la directiva #else; si esta directiva (#else) se omite y
"rest_constante_expresion" no es cierta en el bloque #if no se selecciona texto.

Las directivas #if, #elif, #else y #endif pueden anidarse dentro del texto de otra directiva #if.
Cuando se anidan, cada directiva #else, #elif y #endif pertenece al directiva #if que le precede.

Ejemplos:

1. #if definida(CREDITO)
credito();
#elif definida(DEBITO)
debito();
#else
imprimerror();
#endif

2. #if NIVELD>5

#define SIGNO 1
#if PILAUSO==1
#define PILA 200
#else
#define PILA 100
#endif
#else
#define SIGNO 0
#if PILAUSO==1
#define PILA 100
#else
#define PILA 50

118 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

#endif
#endif

3. #if NIVELD==0
#define PILA 0
#if NIVELD==1
#define PILA 100
#elif NIVELD > 5
display(debugptr);
#else
#define PILA 200
#endif

En el primer ejemplo, las directivas #if y #endif controlan la compilación de una de las tres
funciones llamadas. La función crédito() se compilará solo si el identificador CREDITO esta definido.
Si el identificador DEBITO esta definido la función debito se compilara. Si ningún identificador esta
definido se compilará la función imprimerror().

Los siguientes dos ejemplos suponen definidos la constante NIVELD. El segundo ejemplo
muestra dos conjuntos de sentencias directivas #if, #else y #endif anidadas. El primer conjunto de
directivas se ejecuta solo si "NIVELD >5" es cierto, en caso contrario se ejecuta el segundo conjunto.

En el tercer ejemplo, las directivas #elif y #else son usadas para hacer cambios según se el
valor de NIVELD. La constante PILA tomará un valor 0, 100 o 200 dependiendo del valor de
NIVELD, si no esta definido, "display(debugptr);" se compilará y Pila quedará indefinido.

Ejemplo1: Uso de las sentencias directivas "#if", "#else", "endif" en un programa.

/* ------------------
Programa: direc4.c
------------------ */

# include <stdio.h>
# define BORRA_P "\x1B[2J"
# define FILA10 "\x1B[10;10f"
# define FILA20 "\x1B[20;10f"
# define MAX 100
main()
{
int i=0;
printf(BORRA_P);
# if MAX > 99
printf(FILA10);
printf("COMPILANDO PARA UN ARRAY MAYOR \n");

# else
printf(FILA20);
printf("COMPILANDO PARA UN ARRAY PEQUEÑO \n");
# endif
}

Ejemplo2: Anidamiento de sentencias directivas con "#if", "elif", "endif".

/* ------------------
Programa: direc5.c
------------------ */

# include <stdio.h>

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 119


Capítulo 9 Lenguaje “C”

# define BORRA_P "\x1B[2J"


# define FILA10 "\x1B[10;10f"
# define MAX 100
# define AMERICA 0
# define INGLATERRA 1
# define FRANCIA 2
# define PAIS_ACTIVO AMERICA

# if PAIS_ACTIVO == AMERICA
char moneda[]="dolar americano";
# elif PAIS_ACTIVO == INGLATERRA
char moneda[]="libra inglesa";
# else
char moneda[] = "franco frances";
# endif

main()
{
printf(BORRA_P);
printf(FILA10);
printf("La moneda seleccionada es <%s> \n",moneda);
}

9.4.2.- DIRECTIVAS IFDEF Y IFNDEF.

Formato:

#ifdef identificador
#idndef identificador

La directivas #ifdef y #ifndef se usan conjuntamente con las directivas #if y #define. Cuando el
procesador encuentra una directiva #ifdef chequea el identificador para ver si esta definido. Si es así,
#ifdef vale cierto (no 0), en otro caso vale falso (0).

La directiva #ifndef chequea exactamente lo contrario a #ifdef y si el identificador no ha sido


definido o su definición ha sido quitada con #undef vale cierto (no 0); en caso contrario vale falso (0).

Ejemplo: Comprobando en si ciertas constantes están definidas o no con las directivas "ifdef" y
"ifndef".

/* ------------------
Programa: direc6.c
------------------ */

# include <stdio.h>
# define BORRA_P "\x1B[2J"
# define FILA10 "\x1B[10;10f"
# define FILA15 "\x1B[15;10f"
# define JON 100

main()
{
printf(BORRA_P);

# ifdef JON
printf(FILA10);
printf("- HOLA JON ! Te debo <%d> Pts \n",JON);
# else
printf(FILA10);
printf("-HOLA A CUALQUIERA! no debo nada a nadie \n");
# endif
# ifndef RAQUEL
printf(FILA15);

120 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

printf("Creo que la contante RAQUEL no esta definida \n ");


# endif
}

9.5.- CONTROL DE LÍNEA.


Formato:

#line constante ["nombre_fichero"

La directiva #line instruye al preprocesador a cambiar internamente el número de línea


almacenado y el fichero a un número de línea dado y a un nombre de fichero. El compilador usa el
número de línea almacenado internamente y el nombre de fichero para referirse a errores encontrados
durante la compilación. El número de línea normalmente se refiere a la línea de entrada actual; el
nombre de fichero hace referencia al fichero fuente de entrada actual. El número de línea se
incrementa después de que cada línea se procesa.

Cambiar el número de línea y el nombre de fichero hace que el compilador ignore los valores
por defecto y proceda con los nuevos valores. El valor constante de la directiva #line puede ser
cualquier constante entera. El "nombre_fichero" puede estar formado por cualquier combinación de
caracteres y debe estar encerrado entre comillas.

El número de línea actual y el nombre de fichero están siempre disponibles en los


identificadores __LINE__ y __FILE__. Los identificadores __LINE__ Y __FILE__ se pueden usar
para insertar mensajes de error descriptivos propios dentro de un programa de texto.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 121


Capítulo 9 Lenguaje “C”

Ejemplos:

1. #line 151 "copia.c"


2. #define ASSERT(cond)
if (!cond)
{
printf("error de línea %d,file(%s)\n",__LINE__,__FILE__);
}
else;

En el primer ejemplo, el número de línea almacenado internamente es 151 y el nombre_fichero


se cambia a copia.c.

En el segundo ejemplo, la macro ASSERT(con) usa la identificación predefinida "__LINE__"


y "__FILE__" para visualizar un mensaje de error sobre el fuente no cierto.

122 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 9

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 123


Capítulo 10 Lenguaje “C”

CAPÍTULO 10
EJEMPLO

En el siguiente ejemplo se muestra un programa recapitulativo de la mayor parte de los


conceptos vistos hasta el momento en "C". Es interesante pararse en la forma en que se utiliza un
bucle "for" infinito desde el cual se invoca a las dos funciones, una con la que juega la máquina y otra
con la que juega el usuario.

Lógicamente este programa es una idea para un juego mejor;

A. Incluir un océano mayor.


B. Insertar en dicho océano más de un navío por ambas partes, creando un algoritmo,
función(es), que impida que en una misma posición marina puedan figurar a la vez dos
navíos.
C. Mejorar la parte gráfica, etc.

/* -----------------
Programa: Juego.c
JUEGO SUBMARINO..
-----------------
*/
/*
.. ..
El acorazado y el submarino.
( Un simple juego de ordenador )
La computadora controla el submarino
El usuario controla el acorazado.
Tanto el submarino como el acorazado navegan por el océano, ambos se
encuentran en continuo movimiento. Cada uno trata de alcanzar al otro
con uno de sus misiles.
.. ..
*/
/*
.. ..
El océano es un cuadro 3*3. (matriz[3][3])
Por ello los disparos en coordenadas X,Y
deben de figurar en el rango X: 0 - 2
Y: 0 - 2
( Siendo el angulo superior izquierdo de
coordenadas -> 0,0
.. ..
*/

# include <stdio.h>
# include <time.h>
# define BORRA_P "\x1B[2J"
# define FILA15 "\x1B[15;15f"
# define FILA17 "\x1B[17;15f"
# define FILA25 "\x1B[6;27f"
# define ESPACIO ' '
# define SUBMARINO 'S'
# define ACORAZADO 'A'
char matriz[3][3] =
{
ESPACIO,ESPACIO,ESPACIO,

124 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 10

ESPACIO,ESPACIO,ESPACIO,
ESPACIO,ESPACIO,ESPACIO
};

int compX, compY, jugadorX, jugadorY;

main()
{

/* Generacion de numeros aleatorios. */

printf(BORRA_P);
compX=compY=jugadorX=jugadorY=0;
visualizar_océano();
for(;;)
{
if (submarino())
{
visualizar_océano();
printf(FILA15);
printf("(((( El submarino ha hundido al acorazado.))))");
printf(FILA17);
printf("GANA: ! SUBMARINO ! \n");
break;
}
if (jugador())
{
visualizar_océano();
printf(FILA15);
printf("(((( El acorazado ha hundido al submarino. ))))");
printf(FILA17);
printf("GANA: ! ACORAZADO ! \n");
break;
}
visualizar_océano();
}
}

/* Función submarino */
/* ----------------- */
/*
Genera el siguiente movimiento utilizando el generador de numeros aleatorios.
*/

submarino()
{
matriz[compX][compY] = ESPACIO;
compX = rand() % 3;
compY = rand() % 3;
/*--------------------------------*/
if(matriz[compX][compY] == ACORAZADO)/* Si el movimiento generado -*/
return 1; /* GANA SUBMARINO */ /* tiene las mismas coordenadas -*/
else /* que las de la posicion actual -*/
{ /* del acorazado. -*/
matriz[compX][compY] = SUBMARINO; /* GANA SUBMARNO. -*/
return 0; /* PIERDE ACORAZADO */ /*--------------------------------*/
}
}
/* función jugador */
/* --------------- */

jugador()
{
matriz[jugadorX][jugadorY] = ESPACIO;
do
{
printf(FILA25);
printf("Introduzca coordenadas (X,Y): ");
scanf("%d,%d",&jugadorX,&jugadorY);
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 125


Capítulo 10 Lenguaje “C”

while (jugadorX < 0 || jugadorX > 2 || jugadorY < 0 || jugadorY >2);


printf("\n\n se salto la opcion do");
if (matriz[jugadorX][jugadorY] == SUBMARINO)/*---------------------------*/
return 1; /* GANA ACORAZADO */ /* Si el movimiento del */
else /* jugador coincide con las */
{ /* coordenadas del submarino */
matriz[jugadorX][jugadorY] = ACORAZADO; /* GANA ACORAZADO. */
return 0; /* PIERDE SUBMARINO */ /*---------------------------*/
}
}

/* Función visualizar_océano */
/* ------------------------- */
visualizar_océano()
{
int x, y;

printf(BORRA_P);
printf("\n");
printf(" <0> <1> <2> \n");
printf(" ------------------- \n");
printf("<0>. %c . %c . %c . \n",matriz[0][0],matriz[0][1],matriz[0][2]);
printf(" ------------------- \n");
printf("<1>. %c . %c . %c . \n",matriz[1][0],matriz[1][1],matriz[1][2]);
printf(" ------------------- \n");
printf("<2>. %c . %c . %c . \n",matriz[2][0],matriz[2][1],matriz[2][2]);
printf(" ------------------- \n");
}

126 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 10

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 127


Capítulo 11 Lenguaje “C”

CAPÍTULO 11
CORRIENTES Y ARCHIVOS

11.1.- CONCEPTO DE CORRIENTE


En “C”, los dispositivos tradicionales, terminales, unidades de disco o cinta, puertos e
impresoras son tratados como archivos. Esto es posible porque, sea cual sea la naturaleza del
dispositivo externo, el sistema de almacenamiento temporal de archivos lo convierte en un dispositivo
lógico denominado corriente (stream). Debido a que la corriente es casi independiente del dispositivo
que la genera, las funciones de librería que escriben un archivo en disco pueden ser utilizadas, con las
oportunas variaciones, para producir una salida por impresora o por la consola.

Por ello, la entrada estándar de datos (por teclado) se asocia con un archivo de nombre “stdin”
(Standard Input o entrada de datos estándar), y la salida por pantalla con “stdout” (Standard Ouput o
salida de datos estándar).

Fundamentalmente, “C” distingue dos tipos de corrientes:

- Corrientes de texto.
- Corrientes binarias.

Una corriente de texto es una sucesión indefinida de caracteres organizados en líneas de


diversas longitudes o, informáticamente hablando, en registros de longitud indefinida. Para distinguir
el principio y final de cada línea, “C” utiliza como “separador” el carácter de nueva línea (LF o Line
Feed), ASCII número 10 en decimal.

Habitualmente, este tipo de agrupación de líneas o registros (fichero o archivo) termina con el
carácter ASCII decimal 26 (EOF, de End Of File o fin de fichero).

Registro1 LF Registro2 LF Registro3 LF EOF

L1 bytes L2 bytes L3 bytes

- Tres registros de longitud variable.

Aunque LF es el carácter separador que utilizan en general los editores de texto


cuando con ellos creamos los llamados archivos ASCII de lenguaje fuente, aptos para
ser compilados , linkeditados y convertidos en programa ejecutable o código
máquina, hay que tener en cuenta que también un número elevado de estos editores
utilizan como separador dos caracteres seguidos: el CR o Carriage Return (ASCII 13
decimal) y el LF (ASCII 10). El carácter de control CR sitúa al puntero del Sistema
al principio de la línea actual. El LF lo sitúa, conservando esa posición, en la línea o
registro siguiente.

128 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

El actual estándar ANSI propuesto no establece este carácter de separación como obligatorio,
dejándolo como opcional; no obstante, es el utilizado por la mayor parte de las implementaciones de
“C”.

Al tratar una corriente de texto “C” realiza ciertas transformaciones de los caracteres
especiales leídos/escritos. Así, por ejemplo, un carácter LF puede transformarse en un pro (CR y LF)
según las conveniencias del Sistema. Por tanto hay ciertas diferencias o viceversa, cuando se trata el
archivo como archivo de texto.

En una corriente binaria las transformaciones mencionadas no se realizan, y el texto presente


en la memoria y el del disco coinciden unívocamente, carácter a carácter o byte a byte.

En “C”, un archivo es un concepto lógico que en el momento de su apertura se asocia o


conecta a una corriente, y se disocia de ésta en el momento del cierre del archivo.

“C” no escribe, salvo indicación expresa, el carácter EOF al cerrar un archivo, el cual marca el
final de mismo.

Un archivo “C” puede tener diferentes tipos de organización (secuencial, directa e indexada),
formas que están relacionadas con la estructura (longitud constante o variable) de la agrupación den
líneas o registros de los bytes del mismo.

Registro1 LF Registro2 LF Registro3 Registro EOF

L1 bytes L2 bytes L3 bytes

- Cuatro registros de longitud constante.

A diferencia de otros lenguajes, por ejemplo COBOL y FORTRAN VAX, es estándar


ANSI propuesto no incorpora la Organización Indexada ni el acceso por Índice, pero
proporciona las suficientes herramientas para hacer este tipo de implementaciones.

11.2.- ARCHIVOS DE CABECERA (NOMBRE.H)


Cuando se compila y enlaza o linkedita un programa en lenguaje de alto nivel las funciones de
librería se incorporan al código máquina en el momento de enlazarlo. A veces, incluso aunque estas
funciones no sean utilizadas en el programa, produciendo un código máquina de excesiva en
innecesaria longitud. Para evitar este inconveniente, “C” incorpora los archivos de cabecera,
terminación .h.

Un archivo de cabecera en “C” no es más que la agrupación de las definiciones de funciones


de librería o macros, que poseen algunas características comunes. Así, el archivo de cabecera
<stdio.h> incorpora las funciones de entrada/salida del Sistema. Estos archivos se incorporan, como
vimos, al programa mediante la sentencia #include del preprocesador. La razón de la existencia de
estos archivos radica en incorporar al código ejecutable solamente las rutinas que se utilizarán en el
programa y no otras.

Los archivos cabecera agrupan funciones que tienen un cometido común.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 129


Capítulo 11 Lenguaje “C”

11.3.- CORRIENTES ESTANDAR


Cuando se ejecuta un programa, “C” abre de forma automática cinco corrientes estándar
asociadas a dispositivos:

• stdin Entrada estándar de datos (Teclado)


• stdout Salida estándar de datos (Pantalla)
• stderr Errores estándar (Pantalla)
• stdaux Auxiliar estándar (RS-232)
• stdprn Impresora estándar (Impresora)

11.4.- UNIX Y C. E/S ESTANDAR Y E/S A NIVEL DE SISTEMA


La historia de UNIX está ligada a “C” desde su creación, hasta el punto de que
aproximadamente un 90% del Sistema Operativo UNIX está escrito en “C”, y las primeras funciones
de E/S creadas en “C” están orientadas a UNIX. Por ello al se UNIX un S.O. de E/S sin buffer, “C”
incorporo en principio el tipo adecuado de funciones de librería ajustado a esta características. No
obstante el estándar ANSI propuesto actualmente no incorporan las funciones E/S sin buffer, si bien la
mayor parte de las implementaciones de “C” conservan estas funciones por compatibilidad con
versiones anteriores.

Las entradas/salidas Estándar utilizan punteros a corrientes asociadas a dispositivos/archivos.

Las entradas/salidas UNIX a nivel del Sistema utilizan, en forma similar a otros lenguajes de
alto nivel, descriptores de archivos o handles.

11.5.- PUNTEROS A ARCHIVOS


En “C” estándar la posición inical de un archivo se define por un tipo especial de variable
puntero que almacena los datos necesarios para el manejo del mismo. El formato de declaración de
este puntero es el siguiente:

FILE *NomPunteroFichero;

11.6.- APERTURA Y CIERRE DE ARCHIVOS


La sentencia de apertura de un archivo, una vez declarado un puntero FILE al mismo, es de la
forma:

NomPunteroFichero = fopen(NomFichero,ModoApertura);

donde:

NomFichero: es el nombre de un array de caracteres, o un puntero a una cadena de


caracteres, que contiene el nombre del archivo, o el nombre del mismo entre
comillas.

ModoApertura: se refiere al modo de apertura del archivo y puede ser alguno de los
siguientes:

130 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

“r” Abrir archivo de texto sólo para lectura. El archivo debe existir.
“w” Abrir archivo de texto sólo para escritura. Si el archivo existe, se destruye.
“a” Abrir archivo de texto para añadir datos. Si el archivo no existe, se crea.
“rb” Abrir archivo binario para lectura.
“wb” Abrir archivo binario para escritura.
“ab” Abrir archivo binario para añadir datos.
“r+” Abrir archivo de texto para escritura/lectura. El archivo debe existir.
“w+” Crear archivo de texto para lectura/escritura. Si el archivo existe, se destruye.
“a+” Abrir archivo de texto para lectura/escritura. Si el archivo no existe se cra.
“rb+” Abrir archivo binario para lectura/escritura.
“wb+” Crear archivo binario para lectura/escritura.
“ab+” Abrir archivo binario para lectura/escritura.

Si se produce un error y no es posible la apertura del archivo, el valor devuelto (almacenado


en NomPunteroFichero) es el de NULL (valor entero –1 definido como macro en stdio.h) en lugar de
los datos de identificación del archivo.

El valor de NULL no debe confundirse con el carácter nulo (‘\0’).

En la práctica se utiliza el siguiente formato de apertura:

if ((NomPUnteroFichero=fopen(NomFichero,Modo Apertura))= =NULL)


{
printf(“ No es posible abrir el fichero %s ”, NomFichero);
exit(0);
}

que realiza la apertura del archivo si la dirección de almacenamiento del mismo existe, y en caso
contrario (puntero NULL), imprime un mensaje de error y se produce la terminación del programa en
ejecución.

La función:

fclose(NomPunteroFichero)

cierra y desconecta el archivo del dispositivo físico.

Esta función devuelve le valor entero = si la operación ha sido correcta y EOF si se ha


producido un error.

11.7.- CONTROL DEL FIN DEL ARCHIVO. CONSTANTE EOF.


FUNCIÓN FEOF ( )
En forma habitual. los Editores de Texto y algunos compiladores del alto nivel terminal la
grabación de sus registros escribiendo a continuación del último de éstos el signo EOF, abreviatura de
End Of File, o fin de archivo.

En la práctica normal el signo EOF es un carácter especial, no confundible con cualquier otro
símbolo presente en el archvio, concretamente el carácter ASCII 26 (en decimal). En “C”, EOF es una
constante manifiesta, definida en el archivo de cabecera <stdio.h> que, en archivos escritos en modo
texto utilizando “C” o Editores de Texto ASCII, toma valor entero –1 cuando el Sistema detecta el
último carácter del archivo. En el caso de archivos escritos con Editores de Texto, este último carácter
es el ASCII 26.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 131


Capítulo 11 Lenguaje “C”

Si el archivo no es de texto, por ejemplo un archivo ejecutable o binario, el mencionado


carácter ASCII 26 será uno más de los integrantes de archivo. Más aún, no será reconocido como tal
por un archivo abierto en modo binario.

Para solucionar este problema, “C” dispone de la función feof (FILE *PunteroFichero) que
detecta el final del archivo por la situación del puntero del Sistema en el mismo o, más claramente, por
el byte en que éste está situado respecto al principio del archivo (el nombre que utilizaremos en
nuestro programa) y el dispositivo físico en que éste se almacena, en el proceso de apertura del
archivo, “C” toma y retiene, de forma automática (entre otros datos), la posición inicial del archivo
dentro del disco y el número de bytes que tiene. Proceso transparente para el usuario.

11.8.- ACCESO SECUENCIAL


Para situarse de forma secuencial en un determinado dato de un archivo para Lectura/Escritura
es necesario leer/escribir previamente los datos que preceden al mismo. Esta forma de acceso es la
típica de archivos de Organización Secuencial con registros o líneas de datos de longitud variable, con
separadores intercalados entre las líneas y, por supuesto, de los dispositivos (unidades de cinta,
impresoras, ...) que por sus características físicas hacen imposible al dispositivo de lectura/escritura
situarse en un dato concreto sin pasar previamente por los que le preceden en el soporte físico donde
se almacenan o van a ser almacenados.

Hay que resaltar que la forma de acceso a un registro (secuencial, directa o por índice) no está
limitada por la organización con la que se creó el archivo, sino por la estructura de los registros del
mismo. De echo en “C” no es necesario definir la organización a la hora de acceder a los datos de un
archivo.

11.8.1 ESCRITURA/LECTURA CARACTER A CARACTER

- Funciones: fputc ( ) y fgetc( ), putc( ) y getc( ).

- Función fpuct.

Escribe un carácter procedente de variable tipo char (VarCarácter) o el Carácter


especificado, en el archivo/dispositivo apuntado por Corriente (Ver formato).

Formato:

#include <stdio.h>
int fputc(VarCarácter/Carácter,Corriente)
int VarCarácter;
FILE *Corriente;

- Función fgetc.

Lee un carácter de Corriente.

Formato:

#include <stdio.h>
int fgetc(Corriente)
FILE *Corriente;

Si se alcanza el final de un archivo de texto, fgetc( ) devuelve un carácter EOF que permite
controlar el final del archivo en corrientes de texto. No obstante, al ser EOF un carácter más en

132 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

corrientes binarias, para detectar el final del archivo en éstas es necesario utilizar la función feof( )
para controlar el final de estos archivos.

Si por otra parte, se produce un error de lectura, fgetc( ) devuelve también un carácter EOF, y
para localizar el error es necesario utilizar la función ferror( ).

Como norma, al fin de evitar quebraderos de cabeza, utilice feof( ) para localizar el final de
cualquier fichero.

Ejemplo: Creamos el fichero “carácter” y comprobamos el valor EOF.

/* --------------------
Programa: fich1_se.c
--------------------- */
# include <stdio.h>
main( )
{
FILE *Corriente;
Char Carácter=’a’;
If ((Corriente=fopen(“carácter”,”W”))==NULL)
{
system(“clear”);
printf(“Imposible abrir el fichero pepe”);
exit (0);
}
fputc(Carácter,Corriente);
fputc(‘b’,Corriente);
fclose(Corriente);

/* .. ..
Lectura de los dos caracteres del fichero.
.. .. */
if ((Corriente=fopen(“carácter”,”r”))==NULL)
{
system(“clear”);
printf(“Imposible abrir fichero pepe”);
exit(0);
}
system(“clear”);
printf(“\n\n\n”);
Carácter=fgetc(Corriente);
printf(“Primer carácter leido -> < %c > \n\n”, Carácter);
Carácter=fgetc(Corriente);
printf(“Segundo carácter leido -> < %c > \n\n”,
Carácter);
Carácter=fgetc(Corriente);
printf(“Tercer carácter leido -> < %d > \n\n”, Carácter);

fclose(Corriente);
}

En este ejemplo se abre un archivo de nombre “carácter” apuntado por Corriente y se escriben
los caracteres indicados, cerrándolo a continuación. La posterior apertura, lectura e impresión del
archivo dan como resultado por pantalla:

Primer carácter leído -> < a>

Segundo carácter leído -> <b>

Tercer carácter leído -> <-1>

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 133


Capítulo 11 Lenguaje “C”

Nótese que se imprime la constante EOF, que al tratarse de un archivo abierto en


modalidad texto tomo valor –1.

- Funciones putc y getc.

La funciones putc(VarCarácter/Carácter,Corriente) y getc(Corriente) son funcionalmente


equivalentes a fputc( ) y fgetc( ).

Formatos:

#include <stdio.h>
int putc(VarCarácter/Carácter,Corriente)
int VariableCarácter;
FILE *Corriente;

#include <stdio.h>
int getc(Corriente);
FILE *Corriente;

Veamos unos ejemplos:

En el siguiente ejemplo se lee un archivo en modo binario, utilizando para el control del fin
del archivo el valor que tomará la macro EOF al detectarse el final del mismo.

Este programa funcionara con corrientes de texto, pero no con corrientes binarias. Si abre un
archivo ejecutable en el modo binario para lectura

/* --------------------
Programa: fich2_se.c
-------------------- */

# include <stdio.h>

main( )
{
FILE *fp;
int ch;
if ((fp=fopen(“carácter”,”rb”))==NULL)
{
printf(“Imposible abrir archivo”)
exit(-1);
}

system(“clear”);
printf(“\n”);

do
{
ch=getc(fp);
printf(“Carácter : < %d >\n\n”,ch);
}
while (ch != EOF);

printf(“\n\n”);
if (ch == EOF)
printf(“EOF vale < %d > \n\n”, ch);
fclose(fp);
}

134 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

Veamos un segundo no programa que no tiene desperdicio. Realmente el programa utiliza dos
corrientes pero sólo declara una. La corriente de entrada es un archivo existente, cuyo nombre de
entrada es el nombre del archivo al que se quiere acceder de forma secuencial carácter a carácter.

Debería existir una segunda definición de corriente para el archivo de salida (la pantalla), pero
no es necesario. Precisamente (stdout) es la que aquí utilizaremos como segundo archivo par salida por
pantalla.

Nótese en el próximo ejemplo la forma de introducir el nombre del fichero en el array de


caracteres “nombre”, en que no hace falta el “&”, y la forma compacta de lectura y escritura de la
sentencia while.

/* --------------------
Programa: fich3_se.c
-------------------- */

#include <stdio.h>
main( )
{
FILE *Corriente;
char nombre[30];
char carácter;
system(“clear”);
printf(“Introduzca el nombre del archivo de texto : “);
scanf(“%s”,nombre);
system(“clear”);
if ((Corriente = fopne(nombre,”r”)) ==NULL)
{
printf(“\n\nImposible abrir el archivo de texto :
%s”,nombre);
printf(“\n\n”);
exit(0);
}

/* .. ..
Impresión del fichero.
.. .. */

printf(“\n\n Fichero -> %s\n”,nombre);


printf(“ ----------------------- \n\n”);
while (!feof(Corriente))
putc(getc(Corriente),stdout);
printf(“\n\n ........ Se ha encontrado el fin de
archivo. \n”);
fclose(Corriente);
}

Ejemplo: En este tercer ejemplo nos creamos una orden de copia propia.

/* --------------------
Programa: fich4_se.c
-------------------- */
/*
.. ..
Copia un fichero de cualquier tipo.
.. .. */

# include <stdio.h>
main(argc,argv)

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 135


Capítulo 11 Lenguaje “C”

int argc;
char *argv [ ];
{
FILE *in, *out;
char ch,aux;

if (argc != 3)
{
system(“clear”);
printf(“\n\n”);
printf(“Olvido introducir los nombres de los ficheros....\n\n”);
printf(“<-> Formato: ./a.out fich_origen fich_destino <-
>\n\n\n”);
exit(1);
}

if ((in=fopen(argv[1],”rb”)) = = NULL)
{
system(“clear”);
printf(“\n\n No puedo abrir el fichero fuente. \n\n\n”);
}

if ((out=fopen(argv[2],”w”)) = = NULL)
{
system(“clear”);
printf(“\n\n No puedo abrir el fichero destino. \n@);
exit(1);
}

system(“clear”);
printf(“\n\n\n”);
print( --> Proceso de copia <-- \n\n”);
print(“COPIANDO : <%s> <> CREANDO : <%s> \n”,argv[1],argv[2]);
printf(“\n\n Pulse <Return> para comenzar copoia. “);
aux=getchar ( );

/* Esta es la linea del codigo que copia el fichero */


/* ----------------------------------------------------- */

while(!feof(in))
putc(getc(in),out);

fclose(in);
fclose(out);
}

Vemos dos casos de la ejecución de este programa:

$ cat mariscos
Langosta Langostino Gamba
Almeja Vieira Mejillón
Etc.

$. /a.out marisco
copia

 Proceso de copia 

COPIANDO: <marisco> <> CREANDO : <copia>

Pulse <Return> para comenzar copia.

$ cat copia
Langosta Langostino Gamba
Almeja Vieira Mejillón
Etc.

136 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

Un caso erróneo:

$ ./a.out

Olvido introducir los nombres de los ficheros....

<-> Formato: ./a.out fich_origen fich_destino <->

11.8.2 ESCRITURA/LECTURA DE ENTEROS

- Funciones putw ( ) y getw ( )

Las funciones putw( ) y getw( ) no están definidas por el estandar ANSI propuesto y pueden
causar problems de traansportabilidad de los programs entre unos Sistemas Operativos y otros.

La función putw( ) escribe un Entero en la Corriente archivo y avanza un lugar en la posición


actual del archivo.

La función getw( ) lee un entero desde Corriente.

Formatos:

#include <stdio.h>
int putw(Entero, Corriente)
int Entero;
FILE *Corriente;
#include <stdio.h>
int getw(Corriente)
FILE *Corriente;

11.8.3 ESCRITURA/LECTURA DE CADENAS DE CARACTERES

- Funciones fputs( ) y fgest( )

- Función fputs( )

La función fputs( ) escribe en corriente la tira de caracteres procedentes de VarCadena/Cadena


(Ver formato. Si Corriente se abre para escritura en forma de texto (w), el terminador ‘\0’ de Cadena
se cambiará al carácter LF en el momento de la grabación de los datos. Por el contrario, si el archvio
se abre como binario (wb), no se producirá ninguna transformación.

Formato:

#include <stdio.h>
int fputs (VarCadena/Cadena,Corriente)
char *VarCadena;
FILE *Corriente;
- Función fgets( )

Lee Num-1 caracteres de la Corriente y los almacena en VarCadena (Ver Formato).

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 137


Capítulo 11 Lenguaje “C”

Los caracteres se leen hasta que se encuentre un carácter LF o EOF o se llegue al límite
especificado.

Una vez leídos los caracteres, incluido el LF, “C” añade al final de VarCadena el carácter nulo
(‘\0’) como parte integrante de la misma. Si Num = 1, la cadena leída es la cadena vacía (“ “).

Formato:

#include <stdio.h>
char *fgets(VarCadena,Num,Corriente)
char *VarCadena;
int Num;
FILE *Corriente;

Ejemplo:

En el siguiente programa, se abre un archivo existente para lectura en binario. Con la función fgets( ),
se lee línea a línea, y se imprime línea a línea con la función fputs( ) por el dispositivo (archivo)
estándar de salida stdout, en este caso el dispositivo por defecto, la pantalla. El programa finaliza
cuando se llega al final del archivo, lo que se comprueba con la función feof (Corriente).

/* --------------------
Programa: fich5_se.c
-------------------- */

# include <stdio.h>
main( )
{
char archivo[35];
char cadena[75];
FILE *pf;
system(“clear”);
printf(“Introduzca el nombre del fichero a leer : “);
scanf(“%s”,archivo);

if ((pf = fopen(archivo,”rb”))==NULL)
{
printf(“\n\n Imposible abrir archivo: < %s >
\n\n”,archivo);
exit(0);
}

system(“clear”);
printf(“\n\n Fichero -> %s “,archivo);
printf(“\n ------------------------- \n\n”);
while (!feof(pf))
{
fgets(cadena,25,pf);
fputs(caden,stdout);
}
fclose(pf);
printf(“\n\n”);
}

Ejemplo:

Este otro programa es una variación del anterior, en que tanto el nombre del archivo (archivo) como el
de la variable de caracteres (cadena) son punteros a la dirección en que ambos datos se almacenarán.

Observa la forma en que se leen los datos con la condición “if feof(pf)” para evitar un mensaje
de error al intentar efectuar una lectura fuera del archivo. Este error se deberá a que la lectura de los

138 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

datos se realiza desde la posición inicial en memoria de los mismos, en el número de caracteres
especificado, sin tener en cuenta los caracteres especiales de control.

/* --------------------
Programa: fich6_se.c
-------------------- */

#include <stdio.h>
main( )
{
char *archivo;
char *cadena;
FILE *pf;
system(“clear”);
printf(“Introduzca nombre del archivo a leer: “);
scanf(“%s”,archivo);
if ((pf = fopen(archivo,”rb”)) == NULL)
{
printf(“\n\n Imposible abrir el archivo:
%s\n\n”,archivo);
exit(0);
}
system(“clear”);

printf(“\n\n Fichero -> %s \n”,archivo);


printf(“ ----------------------- \n\n”);
for(;;)
{
if ((fgets(caden,25,pf)) == NULL)
if (feof(pf))
{
printf(ª\n\n .......... Final del archivo
CORRECTO. \n\n\n”);
exit(0);
}
fputs(caden,stdout);
}
fclose(pf);
printf(“\n\n”);
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 139


Capítulo 11 Lenguaje “C”

11.8.4 ESCRITURA/LECTURA FORMATEADA

- Funciones fprintf( ) y fscanf( )

- Función fprintf( )

La funcnión printf( ) imprime una cadena de caracteres en Corriente con el formato y


contenido de Lista de Variables (Ver Formato) y devuelve el número de caracteres escritos. Actúa de
forma equivalente a la funcinón printf( ).

Formato:

#include <stdio.h>
int printf(Corriente,”Formatos”,Lista de Variables)
FILE *Corriente;

- Función fscant( )

La función fscanf( ) lee datos desde Corriente almacenándolos en Lista de Variables. Actúa
igual que la funcnión scanf( ), pero tomando los datos desde la corriente especificada.

fscant( ) retorna el número de campos leídos y asignados realmente a la lista de variables,


separadas por comas, que se indique.

Formato:

#include <stdio.h>
int fscanf(Corriente,”Formatos”,Lista de Variables)
FILE *Corriente;

Ejemplos:

Creamos un archivo organizado secuencialmente con registros de longitud indefinida.

/* --------------------
Programa: fich7_se.c
-------------------- */

# include <stdio.h>

# define SIETE “\x1B[7;10f”


# define NUEVE “\x1B[9;10f”
# define ONCE “\x1B[11;10f”
# define TRECE “\x1B[13;10f”

main( )
{
char color[15];
int codigo;
long int precio;
int cantidad;
FILE *pf;

/* .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
._._._._._._._._._._._._._._._._._._._._. */

system(“clear”);
if ((pf = fopen(“rotulador”,”w”)) == NULL)
{
printf(“\n\n Imposible abrir archivo < rotulador > \n\n”);

140 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

exit(0);
}

printf(SIETE);
printf(“Código (fin = 0) :-> “);
scanf(“%d”,&codigo);

while (codigo !=0)


{
printf(NUEVE);
printf(”Color : “);
scanf(“%s”,color);
printf(ONCE);
printf(“Precio : “);
scanf(“1k”,&precio);
printf(TRECE);
printf(“Cantidad : “);
scanf(“%d”,&cantidad);
fprint(pf,”%d %s %1d %d\n”, codigo, color, precio, cantidad);
System(“clear”);
printf(SIETE);
printf(“Codigo (fin = 0) :-> “);
scanf(“%d”,&codigo);
}
fclose(pf);
System(“clear”);
}

Leemos el archivo secuencial creado en el ejemplo anterior.

/* --------------------
Programa: fich8_se.c
-------------------- */

# include <stdio.h>

#define SIETE “\X1b[7;25f”


#define NUEVE “\x1b[9;25f”
#define ONCE “\x1B[11;25f”
#define TRECE “\x1B[13;25f”
#define FINAL “\x1B[23;1f”

main( )
{
char color [15],a;
int codigo;
long int precio;
int cantidad;
FILE *pf;

/* .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
._._._._._._._._._._._._._._._._._._._._. */

system(“clear”);

if ((pf = fopen(“rotulador”,”r”)) == NULL)


{

printf(“Imposible abrir archivo de rotulado “);


exit(0);
}

while (!feof(pf))
{
fscanf(pf,”%d %s %1d %d”,&codigo, color, &precio, &cantidad);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 141


Capítulo 11 Lenguaje “C”

if (feof(pf))
exit(0);
printf(FINAL);
printf(“ < Return > -> Proximo registro”);
printf(SIETE);
printf(“Codigo .... : %12d*”,codigo);
printf(NUEVE);
printf(“Color .... : %12s *”,color);
printf(ONCE);
printf(“Precio .... : %131d *”,precio);
printf(TRECE);
printf(“Cantidad .. :%12d *”,cantidad);
a=getchar( );
System(“clear”);
}
fclose(pf);
}

11.8.5 ESCRITURA/LECTURA DE BLOQUES

- Funciones fwrite( ) y fread( )

- Función fwrite( ).

La función fwrite( ) escribe Num elementos de Long número de bytes (Ver formato) desde la
zona de memoria apuntada por Buffer a Corriente. La función fwrite( )devuelve el número de
caracteres leídos. Si Corriente se abre en forma texto, cada carácter CR de Buffer se sustituye por el
par de caracteres CR LF. El reemplazo de caracteres no tiene efecto sobre el valor devuelto (número
de caracteres escritos). Esta transformación de caracteres no tiene lugar cuando el archivo se abre en
binario.

Formato:

#include <stdio.h>
int fwrite(Buffer, Long, Num, Corriente)
const void *Buffer;
int Long, Num;
FILE *Corriente;

-Función fread( )

La función fread( ) realiza la función inversa a la anterior leyendo desde Corriente y


almacenando los caracteres leídos en la zona de memoria apuntada por Buffer.

Si la corriente es abierta en modo texto, el par de caracteres CR LF son sustituidos por el


carácter LF. Como en la función anterior, la trasformación de caracteres no tiene lugar si el archivo se
abre en modo binario.

Buffer puede ser, y lo es a menudo, una variable de cualquier tipo cuya dirección se indica con
el operador de dirección &. Otras veces, puede asignarse Buffer dinámicamente.

Formato:

#include <stdio.h>
int fread(Buffer, Cadena, Long, Num, Corriente)
const void *Buffer;
int Long, Num;
FILE *Corriente;

142 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

Una de las mejores aplicaciones de fwrite( ) y fread( ) consiste en la escritura y lectura de los
tradicionalmente llamados registros, conjunto de datos escritos/leídos en su archivo, que en “C”
reciben o se tratan como datos de tipo struct (estructura). Su capacidad para tratar cualquier tipo de
datos, ya sean cadenas de caracteres, variables simples o arrays numéricos, puede observarse en los
siguientes ejemplos.

Ejemplos 1,2: En el primer caso escribimos un valor flota en un fichero, y en segundo efectuamos su
lectura.

/* --------------------
Programa: fich9_se.c
-------------------- */
/*
.. ..
Escribe un numero en punto flotante a un fichero
en disco.
.. .. */

# include <stdio.h>
main( )
{
FILE *fp;
float f=12.23;

if ((fp=fopen(“test”,”wp”)) == NULL)
{
printf(“ No puede abrir el fichero <test>. \n”);
}

fwrite(&f,sizeof(float),1,fp);

fclose(fp);
}

/* --------------------
Programa : fich10_se.c
-------------------- */
/*
.. ..
Escribe un numero en punto flotante a un fichero
en disco.
.. .. */

# include <stdio.h>
# define DIEZ “\x1B[10;20f”
main( )
{
FILE *fp;
float f;
system(“clear”);

if ((fp=fopen(“test”,”rb”)) == NULL)
{
printf(“No puede abrir el fichero <test>.\n”);
printf(“<-> <->\n”);
exit(1);
}

fread(&f,sizeof(float),1,fp);
printf(DIEZ);
printf(“Valor del unico float del fichero <test> ->%.2f<-
\n”,f);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 143


Capítulo 11 Lenguaje “C”

fclose(fp);
}

Ejemplos 3,4: Declaramos un vectos, le asignamos contenidos, lo grabamos en el fichero muestra y


posteriormente lo leemos.

/* --------------------
Programa: fich11_se.c
-------------------- */

/* .. ..
Una de las aplicaciones más útiles de fread( ) y fwrite( )
es la lectura y escritura de vectores.
.. .. */
# include <stdio.h>

main( )
{
FILE *fp;
float muestra[10];
int 1;

if ((fp=open(“muestra”,”wb” == NULL)
{
printf(“No puedo abrir el fichero <muestra>.\n”);
printf(“<-> <->\n”);
exit(1);
}

for(i=0;1<10;1++)
muestra[[i] = (float) (i*.5);

fwrite(muestra,sizeof(muestra),1,fp); /* graba en una


operación */

fclose(fp);
}

Lectura del archivo anterior.

/* --------------------
Programa: fich12_se.c
-------------------- */

/* .. ..
Una de las aplicaciones más utiles de fread( ) y fwrite( )
es la lectura y escritura de vectores.
.. .. */
# include <stdio.h>

main( )
{
FILE *fp;
float ejemplo[10];
int i;

if ((fp=fopen(“muestra”,”rb”)) == NULL)
{

144 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

printf(“No puedo abrir el fichero <muestra>.\n”);


printf(“<-> <->\n”);
exit(1);
}

/* lee el vector en una operación. */


fread(ejemplo,sizeof(ejemplo),1,fp);

system(“clear”);
printf(“\n\n”);
printf(“ Este es el contenido del vector leído.\n”);
printf(“ ----------------------------------------- \n”);
printf(“\n”);
for(i=0;i<10;1++)
printf(“ <%.2f> 2,ejemplo[i]);

fclose(fp);
}

11.8.6 ESCRITURA/LECTURA DE DATOS COMO REGISTROS

Hasta ahora hemos escrito/leido los datos como caracteres o cadenas, vemos ahora la
escritura/lectura de datos en la forma tradicional, es decir, utilizando los ya tradicionales registros
compuestos de campos. Hay que pensar que ya disponemos de todas las librerías necesarias para crear
los registros que ahora vamos a utilizar con el tipo de dato estructura (struct).

Ejemplos:

La creación de archivos como registros definidos por el usuario no difiere esencialmente del sistema
seguido en los diferentes lenguajes de alto nivel. En este caso, la función gets( ) lee los caracteres
introducidos por el teclado hasta que se pulse la tecla <RETURN>. Como gets( ) lee caracteres, no
valores numéricos, y lo que escribimos en el archivo es una estructura mixta, es necesario transformar
estos valore previamente a su grabación en el registro, lo que se hace utilizando las funciones atoi( ),
atol( ) y atof( ).

Para finalizar es necesario teclear la cadena “ZZ” en el código del trabajador.

/* --------------------
Programa: fich13_se.c
-------------------- */

#include <tdio.h>

#define NCOD 4
#define NAPE 35
#define NOV 25
#define NCOS 10

#define CINCO “\x1B.[5;10F”


#define SEIS “\x1B.[6;10F”
#define OCHO “\x1B.[8;10F”
#define DIEZ “\x1B.[10;10F”
#define DOCE “\x1B.[12;10F”
#define CATORCE “\x1B.[14;10F”
#define DIECISÉIS “\x1B.[16;10F”

struct NOMINA
{
char CODIGO[NCOD];
char APE[NAME];

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 145


Capítulo 11 Lenguaje “C”

char NOMBRE[NOV];
char CODSS[NCOS];
int IRPF;
int long SUELDO;
};

main( )
{
FILE *PuntFi;
char CIRPF[10];
char CSUELDO[10];
struct NOMINA PAGOS;
system (“clear”);

if ((PuntFi = fopne(“nomi”,”w”)) == NULL)


{
printf(“ No se puede abrir el archivo ... \n”);
exit(0);
}
printf(CINCO);
printf(“Código del trabajador. “);
printf(SEIS);
printf(“Pulse \”ZZ\” para terminar: “);
gets (PAGOS.CODIGO);

while (strncmp(PAGOS.CODIGO,”ZZ”) !=0)


{
printf(OCHO);
printf(“Apellidos : “);
gets(PAGOS.APE);
print(DIEZ);

print(“Nombre : ”);
gets(PAGOS.NOMBRE);
printf(DOCE);
printf(“Código de la S.Social : “);
gets(PAGOS.CODSS);

printf(CATORCE);
printf(“I.R.P.F. : “);
gets(CIRPF);
printf(DIECISÉIS);
printf(“Sueldo : “);
gets(CSUELDO);

PAGOS.IRPF = atoi(IRPF);
PAGOS.SUELDO = ato1(CSUELDO);

if (fwrite(&PAGOS,sizeof(struct NOMINA),1,PuntFi) != 1)
{
printf(“\nError de escritura \n”);
}

system(“clear”);
printf(CINCO),
printf(“Código del trabajador.”);
printf(SEIS);
printf(“Pulse \”ZZ\” para terminar: “);
gets (PAGOS.CODIGO);
}
system(“clear”);
}

Lectura del programa anterior.

146 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

/* --------------------
Programa: fich14_se.c
-------------------- */

#include <stdio.h>

#define NCOD 4
#define NAPE 35
#define NOV 25
#define NCOS 10

#define SEIS “\x1B[6;10f”


#define OCHO “\x1B[8;10f”
#define DIEZ “\x1B[10;10f”
#define DOCE “\x1B[12;10f”
#define CATORCE “\x1B[14;10f”
#define DIECISÉIS “\x1B[16;10f”
#define VEINTITRÉS “\x1B[23;10f”

struct NOMINA
{
char CODIGO[NCOD];
char APE[NAME];
char NOMBRE[NOV];
char CODSS[NCOS];
int IRPF;
int long SUELDO;
};

main( )
{
FILE *PuntFi;
char CIRPF[10];
char CSUELDO[10];
char aux;
struct NOMINA PAGOS;
system(“clear”);

if ((PuntFi = fopen(“nomi”,”r”)) == NULL)


{
printf(“ No se puede abrir el archivo ... \n”);
exit(0);
}

if(fread(&PAGOS, SIZEOF(STRUCT nomina),1,PuntFi) != 1)


{
printf(“\n Error de lectura. \n”);
}

while (!feof(PuntFi))
{
printf(SEIS);
printf(“Código del trabajador : %s “, PAGOS.CODIGO);
printf(OCHO);
printf(Apellidos : %s “, PAGOS.APE);
printf(DIEZ);
printf(“Nombre : %s “, PAGOS NOMBRE);
printf(DOCE);
printf(“Código de la S.Social : %s “,PAGOS CODSS);
printf(CATORCE);
printf(“I.R.P.F. : %d “,PAGOS.IRPF);
printf(DIECISÉIS);
printf(“Sueldo : %1d ”,PAGOS.SUELDO);
if(fread(&PAGOS, sizeof(struct NOMINA),1,PuntFi) != 1)
{
printf(“\n\n\n”);
printf(“ Ultimo registro. (Fin Achivo.)”);
}
printf(VEINTITRES);
printf(“Pulse <Return> para continuar.”);
aux=getchar( );

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 147


Capítulo 11 Lenguaje “C”

system(“clear”);
}
}

11.8.7 ESCRITURA/LECTURA DE OTROS TIPOS DE DATOS

Hasta ahora, hemos escrito tiras de caracteres. Vemos un ejemplo donde se escriben valors
numéricos.

/* --------------------
Programa: fich15_se.c
-------------------- */

#include <stdio.h>
FILE *corri;
int numeros[100];
int numlee, numesc, i;
int n = 50;

main( )
{
system(“clear”);
for (i=0; i<= 99; i++)
numeros[i]=n++;
if ((corri = fopen(“enteros”,”wb”)) != NULL)
{
numesc = fwrite((char *)numeros,sizeof(int),100,corri);
printf(“\n Numero de items escritos : <%d>”,numesc);
}
else
{
printf(“No puede abrirse el archivo \”enteros\””);
exit(-1);
}
fclose(corri);

/* ......................................................................................................
----------------------------------------------------------------------------- */

corri = fopen(“enteros”,”rb”);
numlee = fread((char *)numeros, sizeof(int),100,corri);
print(“\n Numero de items leídos : <%d>”,numlee);
print(“\n El primer item leído es : <%d>”, numeros[0]);
print(“\n\n”);
fclose(corri);
}

Hemos escrito y leido, posteriormente, 100 datos de tipo int en sendas operaciones de
escritura/lectura. Para escribir/leer hemos moldeado el tipo de datos con (char *) numeros, y para
asegurar la portabilidad del programa hemos dado la longitud de cada dato con sizeof(int).

11.9.- ACCESO DIRECTO A LOS DATOS

148 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 11

- Funciones fseek( ) y ftell( )

Al igual que otros lenguajes, “C” permite también el acceso directo a los datos almacenados
en un archivo. Sin embargo, a diferencia de la mayoría de ellos, “C” admite desplazamientos byte a
byte dentro de un archivo.

- Función fseek( )

Formato:

#include <stdio.h>
int feb(Corriente, Desplazamiento, Origen)
FILE *Corriente;
long Desplazamiento;
int Origen;

donde:

Corriente es el nombre del puntero al archivo correspondiente.


Desplazamiento es el número de bytes a contar desde Origen.

El Origen desde el que se efectúa el desplazamiento puede ser alguno de los siguientes:

0 Principio del archivo.


1 Situación del puntero.
2 Final del archivo (hacia atrás).

La función fseek( ) devuelve un valor 0 si se ejecuta correctamente, y otro valor distinto de


cero en caso contrario.

La función fseek( ) elimina, si se sitúa sobre ellos, los indicadores de final de archivo, de esto
se deduce que es responsabilidad del programador no situarse fuera del archivo.

Hay que tener en cuenta que un fseek( ) seguido de un operación de lectura hace que se lea a
partir del carácter siguiente a la posición en que se colocó fseek( ) el puntero del Sistema.

- Función ftell( )

Formato:

#include <stdio.h>
long ftell(Corriente)
FILE *Corriente;

La función ftell(Corriente) devuelve la posición actual del puntero del Sistema tomando como
origen el principio del archivo. En el siguiente ejemplo, se crea un archivo y se graba una cadena
alfanumérica. La gunción fseek( ) con origen en el principio del archivo “0” sitúa al puntero del
archivo en el carácter 7 desde el principio del archivo. La función fgets( ) lee ocho caracteres,
comenzando por el octavo, y los almacena en “cadena” añadiendo el carácter nulo al final de los
caracteres leídos. Ver la salida de este programa al final del código.

/* --------------------
Programa : fich_di.c
-------------------- */
#include <stdio.h>
main( )
{

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 149


Capítulo 11 Lenguaje “C”

char cadena[9];
FILE *fichero;
int numero;
long desplaza;
fichero=fopen(“numeros”,”w+”);
fprintf(fichero,”%s”,”123456789ABCDEFGHIJK”);
system(“clear”);
desplaza = 7;
fseek(fichero, desplaza, 0);
printf(“ Posición : <%2d> \n”,ftell(fichero));
fgets(cadena,8,fichero);
printf(“ Cadena : <%9s> \n”, cadena);
printf(“\n\n”);
printf(“ -> Al leer el puntero pasa a la siguiente posición.\n”);
fclose(fichero);
}

Da como salida:

Posición :< 7>


Cadena :< 89ABCDE>

-> Al leer el puntero pasa a la siguiente posición

150 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

CAPÍTULO 12
ENTRADAS Y SALIDAS A NIVEL DEL SISTEMA

12.1.- DESCRIPTORES DE ARCHIVO (HANDLES) Y PUNTEROS DE


ARCHIVOS
La versión C para UNIX, al igual que los lenguajes clásicos de alto nivel, utiliza descriptores o
handles. La versión estándar ANSI de C punteros tipo FILE a archivos.

Un descriptor de archivo no es más que una serie ordenada de bytes que se crea al abrir el
mismo y que contiene los datos necesarios para el manejo del archivo por el Sistema Operativo.

De forma similar, un puntero tipo FILE es un puntero a una estructura especial, definida en
<stdio.h>, que contiene la información que necesita el Sistema Operativo para utilizar el archivo.

Aunque existen funciones para relacionar handles y punteros tipo FILE es aconsejable que se
utilicen handles si se está trabajando a nivel de Sistema, y punteros FILE si se trabaja en C estándar
ANSI.

En C a nivel del Sistema se consigue una mayor velocidad de ejecución, puesto que sus
funciones están más próximas al Sistema Operativo.

12.2.- APERTURA Y CIERRE DE ARCHIVOS


- Funciones open( ), close( ), creat( )

- Función open( )

Esta función, procedente del UNIX, no se incluye en el estándar ANSI propuesto. Open abre
un descriptor de fichero para el nombre de fichero indicado y configura los flags de estado del fichero
en función de los valores de Modo (Ver formato).

-Formato:

#include <stdio.h>
#include <fcntl.h>
int open(NomFichero, Modo, [Permisos])
char *NomFichero;
int Modo;
int Permisos;

NomFichero: Contendrá el camino en el árbol de directorios al fichero y el nombre de éste, o


directamente el nombre del fichero si este se encuentra en el directorio actual.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 151


Capítulo 12 Lenguaje “C”

Modo: Expresa la forma de apertura del archivo. Esta opción se construye en función de las siguientes
posibilidades.

O_RDONLY: Abre el archivo para sólo lectura. Este Modo no puede utilizarse conjuntamente
con O_RDWR ni O_WRONLY.

O_APPEND: Sitúa el puntero al final del archivo y permite añadir nuevos datos.

O_CREAT: Crea y abre un archivo para escritura. Si el archivo existe esta operación no se
realiza, es decir este flag no tiene efecto.

O_TRUNC: Abre un archivo para escritura, y si éste existe lo borra.


O_EXCL: Devuelve un error si el archivo especificado por NomFichero existe. Solo es
aplicable conjuntamente con O_CREAT y O_TRUNC.

Si se necesita abrir el archivo en más de un modo y, siempre que éstos sean compatibles,
puede hacerse utilizando el operador OR (I).

La función open( ) devuelve un valor entero no negativo, el descriptor del fichero, si este se
abrió correctamente. Un valor devuelto igual a –1 indica que se ha producido un error al abrir el
archivo. Si la función open( ) se ejecutó sin error el puntero del fichero, que marca la posición actual
en el fichero, se coloca al comienzo de éste.

No se permiten más de 60 descriptores de fichero abiertos simultáneamente.

- Función close( )

La función close( ) cierra el archivo asociado a una Handle, es decir, cierra el descriptor de
fichero indicado por Handle (Ver formato inferior).

Formato:

int close(Handle)
int Handle;

Ejemplo:

/* --------------------
Programa: fich1.c
-------------------- */

# include <stdio.h>
# include <fcntl.h>
int handle;

main ( )
{
system(“clear”);
handle = open(“datos”,O_CREAT | O_WRONLY);
printf(“\nNumero de handle asociado al\n”);
printf(“fichero \”datos\”: <%d> \n”, handle);
close (handle);
}

152 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

El programa anterior abre un archivo, de nombre “datos”, para escritura. El número de handle
devuelto es un número entero positivo mayor que 0, en caso contrario, si se produce un error, retorna
el valor –1.

- Función crat( )

int creat(NomFichero, Permisos)


char *NomFichero;
int Permisos;

Esta función UNIX, crea un nuevo archivo o rescribe un fichero existente, indicado por
NomFichero.
La función creat() devuelve un entero no negativo, el descriptor de archivo, si no hay error, y
un –1 si éste se produce.

12.3.- EL BUFFER DE E/S DE DATOS. CONSIDERACIONES


En la entrada/salida a nivel de Sistema no existen facilidades, un dispone de una zona de la
memoria de la computadora que se puede manejar al antojo de uno, pero la computadora, si uno se
descuida un poco, puede volverlo loco.

Los datos se introducen, byte a byte, como el agua en un depósito (el buffer o zona de
memoria) que el programador controla. Si uno se descuida podemos salirnos del depósito (buffer).

Una regla de oro es:

Lea tal como ha escrito

Si no conoce la composición de los registros, no se preocupe demasiado; es muy fácil sacar un


listado de una parte del archivo y averiguar su formato.

Respecto a esto, ha de tenerse en cuenta que es posible que no todos los registros tengan el
mismo formato y que éstos pueden variar en bloques con una cadencia determinada. Tampoco le será
difícil averiguar cómo es la variación y adaptar la longitud del buffer cada vez que sea necesario.

Por otro lado, quizá si se pregunta cómo se tratan los datos numéricos, ya que lo que se escribe
y lee son caracteres ASCII. Sencillamente, escribiendo los números como caracteres (dígitos) y
leyéndolos como tales.

Si se necesita realizar operaciones matemáticas con estos caracteres, transformemos primero


éstos en tipos numéricos, y viceversa, utilizando las funciones de conversión.

12.4.- ACCESO SECUENCIAL A LOS DATOS


- Funciones white( ) y read( )

Formatos:

#include <stdio.h>
int write(Descriptor, Cadena, Num)
int Descriptor;
char *Cadena;
unsigned int Num;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 153


Capítulo 12 Lenguaje “C”

#include <stdio.h>
int read(Descriptor, Cadena, Num)
int Descriptor;
char *Cadena;
unsigned int Num;

Ambas son funciones UNIX y no han sido incorporadas por el estándar ANSI.

La función white( ) escribe Num de bytes en el archivo definido por Descriptor desde la
posición de memoria apuntada por Cadena. Esta función devuelve el número de bytes realmente
escritos o el valor –1 si se produce un error de escritura.

La función read( ) lee Num de bytes desde el archivo definido por Descriptor hasta la zona de
memoria apuntada por Cadena. Esta función devuelve el número de bytes leídos 0 –1 si se produce un
error de lectura.

Ejemplos:

El siguiente programa lee desde el teclado una cadena de caracteres (no mayor de 100, que es el
tamaño de buffer), terminada con la pulsación simultanea de la tecla <Enter>. El número de caracteres
leídos desde el teclado (num) se escribe en el archivo “datos”. Se cierra el archivo y, posteriormente,
se abre para lectura. Se leen los bytes escritos y se imprimen por pantalla.

/* --------------------
Programa: fich2.c
-------------------- */

# include <fcntl.h>
# define DIEZ “\x1B[10;10f”

int handle, num;


char buffer[100];
main( )
{
System(“clear”);
handle = open(“datos”,O_CREAT | O_WRONLY);
printf(“\nNumero de handle asociado al archivo”);
printf(“\”datos\”: <%d> \n\n”,handle);
printf(“Introduce una cadena de caracteres terminada “);
printf(“con <INTRO> \n\n”);
num = read(1,buffer,100);
write(handle, buffer, num);
close(handle);

System(“clear”);
handle = open(“datos”, O_RDONDLY);
read(handle, buffer, num);
printf(DIEZ);
printf(“%s \n\n\n”,buffer);
close(handle);
}

Ejemplo:

Este segundo ejemplo, crea un archivo de nombre “oscar”, en el cual se graban líneas de texto. El
fichero “oscar” se cierra al teclear “quit” en una línea nueva. Posteriormente se abre el fichero para
visualizarlo línea a línea por pantalla.

/* --------------------

154 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

Programa: fich3.c
-------------------- */
/* .. ..
Lectura y escritura usando E/S sin memoria intermedia.

Este programa ilustra algunos aspectos del sistema


de E/S sin almacenamiento intermedio.
Leerá líneas de texto desde el teclado y las escribe en
un fichero de disco.
.. .. */

# include <fcntl.h>
# include <stdio.h>
# include <string.h>

int input(char *, int);


int display(char *, int);

# define BUF_SIZE 128

main( )
{
char buf[BUF_SIZE];
int fd1,fd2;
system(“clear”);

if ((fd1 = creat(“oscar”,O_WRONLY)) == -1)


{
printf(“\n\n\n\n”);
printf(“ No puede abrir el fichero. \n”);
exit(1);
}

input(buf,fd1);
close(fd1);

if ((fd2 = open(“oscar”,O_RDONLY,O)) == -1)


{
printf(“\n\n\n\n”);
printf(“ No puedo abrir el fichero. \n”);
exit(1);
}
display(buf,fd2);
close(fd2);
}

/*---------------------------------------------------------*/
/* Lectura de algunas líneas desde el teclado. */
/* ----------------------------------------- */
input(buf, fd1)
char *bud;
int fd1;
{
register int t;
printf(“Introduce texto, para <Parar> introducir ‘quit’\n”);
printf(“en una nueva línea. \n\n”);
do
{
for (t=0;t<BUF_SIZE,t++)
buf[t]=’\0’;
printf(“: “);
gets(buf); /* Introduce caracteres desde el teclado. */
if (write(fd1,buf, BUF_SIZE) != BUF_SIZE)
{
printf(“Error de escritura. |n”);
exit(1);
}
}
while (strcmp(buf,”quit”));
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 155


Capítulo 12 Lenguaje “C”

/* ----------------------------------------------------*/
/* PRESENTA EL TEXTO. */
/* ____________________ */

display(buf, fd2)
Char *buf;
int fd2;
{
system(“clear”);
printf(“\n\n\n”);
printf(“ TEXTO TECLEADO. \n”);
printf(“ --------------- \n\n”);
for(;;)
{
if (read(fd2,buf,BUF_SIZE) == 0)
return;
printf(“---->:<%s>:\n”,buf);
}
}

12.5.- EL CONTROL DEL FIN DE ARCHIVO


El control del fin de archivo se puede detectar en base al valor 0 que devuelve la función read
cuando se alcanza el final del archivo. Por ello, la siguiente secuencia de instrucciones es suficiente
para leer sucesivamente cualquier archivo hasta encontrar el final del mismo.

while (( num = read(Handle, Buffer, NumeroCaracteres)) > 0)


{
.
.
.
}

Ejemplo:

La función unlink(NombreDeFichero) elimina el archivo indicado por NombreDeFichero.


NombreDeFichero puede ir acompañado de un camino de directorio. Esta función retorna un valor 0 si
esta se ejecuta sin errores, en caso contrario retorna el valor –1.

Formato:

Int unlink(NombreDeFichero)
Char *NombreDeFichero;

Globalmente, antes y fuera de cualquier función se inicializa el buffer de memoria. Hay que
prever, con esta forma de inicialización, un carácter más (el ‘\0’) que se añadirá automáticamente a la
cadena , siempre que haya espacio disponible.

La función write( ) escribe 100 caracteres en el archivo “datos”. Para acceder otra vez al
principio del mismo, se cierra el archivo y se vuelve a abrir para lectura. Los bytes del archivo se leen
ahora de 10 en 10, hasta alcanzar el fin de archivo, y se imprimen en pantalla con write( ). Se utiliza el
número del dispositivo estándar salida, pantalla, el 2.

/*--------------------
Programa: fich4.c
-------------------- */

# include <fcntl.h>

156 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

# include <stdio.h>

int handle, num;


char buffer[100]=”0123456789ABCDEFGHIJKL\
MNOPQRSTUVXYZ0123456789”;

main( )
{
int n;
system(“clear”);
unlink(“datos”);

handle = open(“datos”, O_CREAT | O_WRONLY);


printf(“\nNumero de handle asociado %d”, handle);
num = vrite(handle, buffer, 100);
close (handle);

handle = open(“datos”, O_RDONLY);


puts(“\n\nContenido del Archivo: “);
while ((n=read(handle,buffer,10)) > 0)
{
write(2, buffer, 10);
putchar(‘@’);
printf(“ \n”);
}
}

12.6.- ACCESO DIRECTO A LOS DATOS


- Las funciones lseek( ) y tell( ).

- Función lseek( )

Formato:

#include <stdio.h>
long lseek(Descriptor, Desplazamiento, Origen)
int Descriptor;
long Desplazamiento;
int Origen;

La función lseek( ) es otra función de UNIX original y no está incluida en el estándar ANSI
actual y es similar a la de esta, fseek( ).

Esta función devuelve el valor de Desplazamiento si se ejecuta correctamente, en caso


contrario devuelve –1.

Los valores posibles de Origen son 0, 1, 2.


0: Principio del archivo.
1: Posición actual de puntero.
2: Final del archivo.

- Función tell( )

Formato:

# include <io.h>
long tell(Descriptor)
int Descriptor;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 157


Capítulo 12 Lenguaje “C”

La función tell( ) es otra función UNIX original y es similar a la estándar ANSI ftell( ) y
devuelve la posición actual del puntero al archivo, esta posición se expresa como un número de bytes
desde el comienzo del fichero. Esta función, si se incluye, debe compilarse con el flag –dos.

Ejemplo:

Este programa C, es una utilidad que permite acceder a los distintos bloques de un fichero de forma
directa., para ello utilizamos la función lseek( ) seguida de una función read( ).

/* --------------------
Programa: fich1_di.c
-------------------- */

/* .. ..
Lectura y escritura usando E/S sin memoria
intermedia.
Este programa lee memorias intermedias
usando lseek.
.. .. */

# include <fcntl.h>
# include <stdio.h>
# include <ctype.h> /* Se necesita para isprint( ) */

# define SIZE 128


# define DIEZ “\x1B[10;10f”

char buf[SIZE]
void display(int);

main(argc, argv)
int argc;
char *argv[ ];
{
char s[10]M
int fd,sector,numread;
long pos;

system(“clear”);
if (argc !=2)
{
printf(“\n\n Se olvido introducir el nombre del fichero. \n”);
exit(1);
}

if ((fd = open(argv[1],O_RDONLY, 0)) == -1)


{
printf(“ No puede abrirse el fichero. \n”);
exit(1);
}

do
{

printf(DIEZ);
printf(“\n\n Sector <-1> -> FIN : “);
gets(s);
system(“clear”);
sector = atoi(s); /* coge el numero de sector para leer */
if (sector != -1)
{
pos = (long) (sector*SIZE);
if (lseek(fd,pos,0) != pos)
printf(“\n Error de seek. \n”);
numerad = read(fd, buf, SIZE);

158 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

display(numread);
}
}
while (sector >= 0);
close(fd);
}

/* Funcion */
/* ---------- */

void display(numread)
int numread;
{
int i,j;
for (j=0;i<numread/16;i++)
{
for (j=0;j<16;j++)
printf(“%3x”buf[i*16+j]);
printf(“ “);
for (j = 0;j < 16; j++)
{
if (isprint(buf[i*16+j]))
printf(“%c,buf[i*16+j]);
else printf(“.”);
}
printf(“\n”);
}
}

Dando como salida:

35% ./a.out fich1_di.c

Hexadecimal ascii

20 20 20 20 20 20 20 20 2e 2e a 20 20 20 4c 65 ... Le
63 74 75 72 61 20 79 20 65 73 63 72 69 74 75 72 ctura y escritur
61 20 75 73 61 6e 64 6f 20 45 2f 53 20 73 69 6e a usando E/S sin
20 6d 65 6d 6f 72 69 61 a 20 20 20 69 6e 74 65 Memoria. inte
72 6d 65 64 69 61 2e a a 20 20 20 45 73 74 65 rmedia. Este
20 70 72 6f 67 72 61 6d 61 20 69 6c 75 73 74 72 programa ilustr
61 20 61 6c 67 75 6e 6f 73 20 61 73 70 65 63 74 a algunos aspect
6f 73 20 64 65 6c 20 73 69 73 74 65 6d 61 a 20 os del sistema.
Sector <-1> -1 FIN : 1

12.7.- REASIGNACIÓN DEL HANDLE DE UN ARCHIVO


- Las funciones dup( ) y dup2( )

Formatos:

int dup (Handle)


int Handle;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 159


Capítulo 12 Lenguaje “C”

int dup2(Handle1, Handle2)


int Handle, Handle;

La función fup( ) devuelve el número de hanle que se asocia a la próxima corriente que se
abra. Dup( ) devuelve el más bajo descriptor disponible. El nuevo descriptor retornado por dup( ) tiene
en común con el original:

- El mismo fichero abierto.


- El mismo puntero de fichero.
- El mismo modo de acceso. (lectura, escritura o lectura/escritura.

La función dup2( ) hace que un archivo abierto pueda ser referenciado por un
segundo nombre, sin crear un nuevo handle. La función dup2( ) enlaza a un segundo
Handle2 con el archivo asociado a Handle1.

El acceso a los registros del archivo no se modifica por relacionar a éste con un segundo canal
o Handle.

Si se efectúa una llamada a la función dup 2( ) con un handle de un archivo abierto, se cierra
primero el archivo al que estaba asignado la Handle.

Ambas funciones devuelven un valor entero no negativo si todo va correctamente y el valor –1


si se produce algún error.

Ejemplo1: Un caso de utilización de dup( ).

/* --------------------
Programa: fich2_di.c
-------------------- */

# include <fcntl.h>
# include <stdio.h>

# define TB_MIN 1
# define TB_MAX 12

int hanle, prox_handle;

main( )
{
handle = open(“datos”,O_CREAT | O_WRONLY);
prox_handle = dup(handle);
system(“clear”);
printf(“\n\nNumero de handle asociado a \”datos\” : <%d>”, handle);
printf(“\nNumero del próximo handle asociado : <%d> \n”, prox_handle);
close(handle);
}

Ejemplo2: Un segundo ejemplo con dup2( )

/* --------------------
Programa: fich3_di.c
-------------------- */
/* .. ..
La línea a utilizar para compilar este programa es:
45% cc –compat –lx fich8_di.c
.. .. */

160 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 12

# include <fcntl.h>
# include <stdio.h>

int handle, otro_handle, num;


char buffer [100] = “123456789ABCDEFGHIJKLMNOPQRST”;
char cadena[100];

main( )
{
system(“clear”);
handle = open(“datos”,O_CREAT | O_WRONLY);
printf(“\n Numero de handle asociado <%d> \n”, handle);
num = write(handle, buffer, 100);
close(handle);

handle = open(“datos”,O_RDONLY);
dup2(handle, otro_handle);
read(otro_handle, cadena, num);

printf(“\n La línea escrita en el archivo\n”);


printf(“ es:\n\n <%s>\n\n”, cadena);
printf(“\n El numero del nuevo handle es <%d> \n”, otro_handle);
close(handle);
}

que produce la siguiente salida.

Número de handle asociado <3>


La línea escrita en el archivo
es:
<123456789ABCDEFGHIJKLMNOPQRST>
El número del nuevo handle es <0>

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 161


Capítulo 13 Lenguaje “C”

CAPÍTULO 13
ASIGNACIÓN DINÁMICA DE MEMORIA EN “C”

13.1.- INTRODUCCIÓN
Hasta ahora no nos ha preocupado la forma en la que C guarda la información que se maneja
en la memoria principal de la computadora. Los modelos de memoria en C gestionan ésta de la
siguiente forma.

- Los códigos y los datos inicializado (variables globales y externas) ocupan una zona que se
mantiene fija al principio de la memoria. Esta zona se asigna durante la compilación del
programa.

- Las variables no inicializadas (automáticas y estáticas) ocupan espacio de memoria justo a


partir de la zona de las variables inicializadas y crece hacia la pila. Este espacio, heap, recibe
la denominación de montículo o montón.

- La pila ocupa la zona superior de la memoria asignada, teniendo su crecimiento hacia la zona
del montón.

Esta organización anterior puede presentar un grave problema: es posible que en algunas
aplicaciones la pila, en su crecimiento, llegue a ocupar parte de la zona del montón.

Este problema, planteado en los puntos anteriores, se puede solucionar con la asignación
dinámica.

La asignación dinámica se utiliza también para otros usos por ejemplo, cuando se declaran
arrrays de estructuras, el compilador C asigna el espacio de memoria suficiente para almacenar toda la
posible información del array. Este sistema no es eficiente en el caso de arrays grandes en los que no
se usan todos los elementos, desperdiciándose parte de la memoria que no será utilizada.

13.2.- FUNCIONES DE ASIGNACIÓN DINÁMICA


Las funciones estándares de asignación dinámica de memoria son:

calloc( ), malloc( ), free( ), realloc( ).

Todas estas funciones están contenidas en la biblioteca estándar <malloc.h>. Estas funciones
devuelven punteros tipo void.

La función malloc( ) retorna un puntero al primer byte del bloque de memoria de tamaño
solicitado. En caso de no haber memoria libre (del montón) suficiente para asignarla al bloque,
devuelve n puntero NULL. Tiene el siguiente formato:

void *malloc(unsigned int Tamaño)

162 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

Tamaño: es el número de bytes que se necesitan.

La función free( ) libera un bloque que previamente había sido asignado mediante malloc( ) o
calloc( ). Tiene el siguiente formato:

void free(void *Puntero)

Puntero debe contener la dirección del primer byte del bloque. En el caso de que no sea un
puntero utilizado con alguna de las funciones de asignación, lo más probable es que el sistema quede
bloqueado.

Otro efecto que puede ocurrir al utilizar de forma sucesiva malloc( ), APRA asignaciones de
memoria, y free( ), para liberaciones es el de la fragmentación.

La fragmentación de memoria durante una asignación dinámica sucede cuando se libera


bloques de memoria en medio de memoria (previamente) asignada.

Veamos un ejemplo de fragmentación: Dados cuatro bloques asignados A,B.C,D de tamaño


20, 45, 10 y 35 bytes y que ocupan espacios continuos, como se ve gráficamente.

A B C D

20 Bytes 45 Bytes 10 Bytes 35 Bytes


*___________*_____________________________*_________*___________________*

Si con la orden free( ) liberamos los bloques B y D, el tamaño de la memoria libre es de 80;
sin embargo, no podrá ser asignado ningún bloque mayor de 45, puesto que no se podrá distribuir al
estar el bloque C asignado en medio. De ello se deduce que no es conveniente hacer asignaciones de
bloques de memoria pequeños, sino agruparlos en uno grande.

Otra posibilidad es la de almacenar temporalmente la información en un fichero de la unidad


de disco, liberar toda la memoria y recuperar posteriormente dicha información.

La función calloc( ), de forma parecida a la función malloc( ), devuelve un puntero al primer


byte del bloque de memoria, excepto que el bloque almacena n_elementos ocupando cada uno
m_bytes. Sigue el siguiente formato:

void *calloc(unsigned n_elementos, unsignded m_bytes)

En el caso de no haber memoria libre suficiente para asignarla la bloque, se devuelve un


puntero NULL.

La función realloc( ) ajusta el tamaño del bloque localizado por el puntero al especificado por
nuevo_tamaño. Tiene el siguiente formato:

void *realloc(void *Puntero, unsigned int nuevo_tamaño)

Esta función retorna la dirección del nuevo bloque. Puede ser diferente de la dirección del
bloque inicial. Si el bloque no puede ser ajustado retorna NULL.

Todas las funciones vistas se aplican a estructuras dinámicas de datos, tales como colas, pilas,
listas enlazada y árboles binarios, que proporcionan diversas formas de almacenamiento y
recuperación de la información, como veremos a partir de ahora.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 163


Capítulo 13 Lenguaje “C”

13.3.- COLAS
13.3.1 COLA LINEAL

La cola lineal es una técnica de almacenar información organizada de forma que el primer
dato que se introduce es el primero que se recupera (responde a la estructura FIFO, First Input First
Output), de manera que para recuperar un elemento determinado es preciso recuperar los datos
anteriores. Todos los datos que se recuperan se pierden.

Esta estructura tiene su utilidad en los buffers de entrada/salida rápidos y planificados con
organización secuencial.

Vemos un ejemplo de gestión de una cola lineal. Donde una función permitirá introducir los
datos, según el orden en que estos son recibidos (introduce_comerciante( )), otra función permite
extraer los datos próximo_comerciante( )).

La función strcpy( ), copia el contenido de comerciante en la dirección de memoria gestionada


por malloc( ) y apuntada pro p_comerciante[numero].

La estructura de cola del programa se refleja en el siguiente gráfico.

Entrada p_comerciante[0] p_comenciante[2]


/
Salida

...
p_comerciante[1]
*___________________*_____________________*_________________
__*
bloque1 bloque2 bloque3

bloquen = strlen(comerciante)

Vemos el programa ejemplo:

/*----------------
Programa: cola.c
----------------- */

/*----------------
Ficheros cabecera
----------------- */

#include <malloc.h>
#include <string.h>
#include <stdio.h>

/*--------------------
Sentencias directivas.
--------------------- */

#define MAXIMO 10
#define TRUE 1
#define DOS “\x1B[2;20f”
#define TRES “\x1B[3;20f”
#define CUATRO “\x1B[4;20f”
#define CINCO “\x1B[5;20f”
#define SEIS “\x1B[6;20f”

164 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

#define SIETE “\x1B[7;20f”


#define DIEZ “\x1B[10;25f”
#define DIEZ2 “\x1B[10;49f”
#define ULTIMA “\x1B[22;20f”

char *p_comerciante[MÁXIMO];
int numero=0, orden=0;

void introduce_comerciante( );

void proximo_comerciante( );

/* --------- */ /*--------- */
void introduce_comerciante( )
{
char comerciante[10], *puntero_bloque_memoria;
while (numero < MÁXIMO)
{
fflush(stdin);
printf(DIEZ2);
printf(“ “);
printf(ULTIMA);
printf(“<Return> FINALIZAR”);
printf(DIEZ);
printf(“Nombre a añadir a la cola <%d > =-> “, numero +1);
gets(comerciante);
if (*comerciante == 0)
break;
puntero_bloque_memoria=malloc(strlen(comerciante));
if (puntero_bloque_memoria == NULL)
{
printf(“\nEspacio de memoria insuficiente”);
return;
}
else
{
strcpy(puntero_bloque_memoria, comerciante);
p_comerciante[numero] = puntero_bloque_memoria;
numero++;
}
}
}

/* ------- */ /* ------- */
void proximo_comerciante( )
{
if (orden == numero)
{
printf(DIEZ);
printf(“(.)(.)(.) No hay más nombres. (.)(.)(.)”);
numero=0;
orden=0;
return;
}
if (p_comerciante[numero-1] == NULL)
return;
printf(DIEZ);
printf(“Siguiente comerciante[%d]: <%s> :”,orden+1,p_comerciante[orden]);
orden++;
}

/*-----------------
Programa principal
------------------ */

main( )
{
char aux;
while (TRUE)
{

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 165


Capítulo 13 Lenguaje “C”

system(“clear”);
printf(DOS);

printf(“* MENU DE GESTION DE UNA COLA. *”);


printf(TRES);
printf(“---------------------------------“);
printf(CUATRO);
printf(”- P – INSERTAR NOMBRES A LA COLA.”);
printf(CINCO);
printf(“ - S – OBTENER NOMBRE DE LA COLA.”);
printf(SEIS);
printf(“ - X- SALIR DEL PROGRAMA ........”);
printf(SIETE);
printf(“\t Opción -> “);
fflush(stdion);
switch (toupper(aux=getchar()))
{
case ‘P’:
introduce_comerciante( );
break;
case ‘S’:
proximo_comerciante( );
break;
case ‘X’;
system(“clear”);
exit(‘);
}
}
}

En este programa primero se deben introducir todos los datos de la cola y posteriormente se
van obteniendo en el mismo orden en que fueron introducidos.

No se permite introducir más datos si se a llegado a introducir el máximo, incluso si se han


sacado datos de la cola.

13.3.2 COLA CIRCULAR

La cola circular evita el problema que presenta la cola lineal de tener que interrumpir el
programa cuando se introduce el máximo de datos permitidos. La solución es hacer que el índice de
dato a introducir (ind_intro_comerciante) se coloque otra vez al principio de la cola, una vez que se
llega al final de ésta. El mismo tratamiento sufrirá el índice del comerciante al extraer los datos.

Con este método se podrá introducir y extraer comerciantes de forma ilimitada (la cola sólo se
llenará cuando ambos índices sean iguales).

/*--------------------
Programa: cola_cir.c
-------------------- */

/*----------------
Fichero cabecera
---------------- */

#include <string.h>
#include <stdio.h>
#include <malloc.h>

166 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

/*-------------------

Sentencias directivas
--------------------- */

#define MAXIMO 10
#define TRUE 1
#define DOS “\x1B[2;20f”
#define TRES “\x1B[3;20f”
#define CUATRO “\x1B[4;20f”
#define CINCO “\x1B[5;20f”
#define SEIS “\x1B[6;20f”
#define SIETE “\x1B[7;20f”
#define DIEZ “\x1B[10;25f”
#define DIEZ2 “\x1B[10;49f”

char *p_comerciante[MAXIMO];
Intind_prox_comerciante=0,int_intro_comerciante=0;

Void introduce_comerciante();
Void proximo_comerciante();

/* ------- */ /* ------- */

void introduce_comerciante()
{
char comerciante[30], *puntero_bloque_memoria;
If (ind_intro_comerciante==MAXIMO)
{
if (ind_prox_comerciante != 0)
ind_intro_comerciante = 0;
else
{
fflush(stdin);
printf(DIEZ);
printf(“***---La cola esta llena---***”);
getchar( );
return;
}
}
if(ind_intro_comerciante+1==ind_prox_comerciante)
{
fflush(stdin);
printf(DIEZ);
printf(“***---La cola esta llena---***”);
getchar( );
return;
}
fflush(stdin);
printf(DIEZ2);
printf(“ “);
printf(DIEZ);
printf(“Nombre a añadir a la cola : “);
gets(comerciante);
if (*comerciante == 0)
return;
puntero_bloque_memoria= malloc(strlen(comerciante));
if (puntero_bloque_memoria == NULL)
{
printf(“\nEspacio de memoria insuficiente”);
return;
}
else
{

strcpy(puntero_bloque_memoria, comerciante);
p_comerciante[ind_intro_comerciante] = puntero_bloque_memoria;
ind_intro_comerciante++;

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 167


Capítulo 13 Lenguaje “C”

}
}

/* ------- */ /* ------- */

void proximo_comerciante( )
{
fflush(stdin);
if (ind_prox_comerciante == ind_intro_comerciante)
{
printf(DIEZ);
printf(“(.)(.)(.) No hay más nombres. (.)(.)(.)”);
getchar( );
return;
}
if (ind_prox_comerciante == MAXIMO)
{
Ind_prox-comerciante=0;
}
if (p_comerciante[ind_prox_comerciante] == NULL)
return;
printf(DIEZ);
printf(“Siguiente comerciante <%s>“,p_comerciante[ind_prox_comerciante]);
Getchar( );
ind_prox_comerciante++;
}

/*------------------
Programa principal
------------------ */

main( )
{
char aux;
while (TRUE)
{
system(“clear”);
printf(DOS);
printf(“MENU DE GESTION DE UNA COLA CIRCULAR.”);
printf(TRES);
printf(“-----------------------------------“);
printf(CUATRO);
printf(“P – INSERTAR NOMBRES A LA COLA .”);
printf(CINCO);
printf(“S – OBTENER NOMBRES DE LA COLA.”);
printf(SEIS);
printf(“X – SALIR DEL PROGRAMA .......”);
printf(SIETE);
printf(“\t Opción -> “);
fflush(stdin);
switch (toupper(aux=getchar( )))
{
case ‘P’:
introduce_comerciante( );
break;
case ‘S’:
proximo_comerciante( );
break;
case ‘X’:

system(“clear”);
exit(0);
}
}
}

168 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

13.4.- PILAS
Las pilas al contrario de las colas, son secuencias de datos en las que la información que se
recupera es justamente la última que se ha almacenado (responden a la estructura LIFO, Las Input
Fiirst Ottput). Un uso de las pilas es almacenar las direcciones de retorno de los programas que, con C,
BASIC, FORTRAN, COBOL, etc. realizan llamadas a subrutinas (funciones en C).

Asignaremos dinámicamente una zona libre de memoria para la pila, según el tamaño máximo
que se prevea. Los datos se almacenarán a partir de la dirección inferior a la superior, sin llegar a
superar el máximo. En el siguiente ejemplo que gestiona una pila la función introduce_datos( ) se
encarga de introducir un dato en la pila. Previamente se verifica que queda zona libre, incrementando
la dirección que podrá ser ocupada por otro dato.

Por otro lado la función recupera_datos( ), primero decrementa el valor de la dirección de la


pila, comprueba que no es el límite inferior y recupera el dato.

/*----------------
Programa: pila.c
---------------- */

/*-----------------
Ficheros cabecera.
------------------ */

# include <string.h>
# include <stdio.h>

/*---------------------
Sentencias directivas.
--------------------- */

#define DOS “\x1B[2;20f”


#define TRES “\x1B[3;20f”
#define CUATRO “\x1B[4;20f”
#define CINCO “\x1B[5;20f”
#define SEIS “\x1B[6;20f”
#define SIETE “\x1B[7;20f”
#define DIEZ “\x1B[10;25f”
#define VEINTE “\x1B[20;20f”
#define MÁXIMO 10
#define TRUE 1

/*------------------
Variables Externas.
------------------- */

int *dirección_pila;
int *dirección_inferior;
int *dirección_superior;
int dato;

/*--------------------------------------
Prototipos de las funciones utilizadas.
--------------------------------------- */

Void introduce-dato(void);
void recupera_dato(void);

/*---------
Funciones.
---------- */

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 169


Capítulo 13 Lenguaje “C”

void introduce_dato( )
{
If (dirección_pila > dirección_superior)
{
printf(VEINTE);
printf(“(.) LA PILA ESTA LLENA (.)”);
return;
}
printf(DIEZ);
printf(“DATO A INTRODUCIR : “);
scanf(“%d”,&dato);
*dirección_pila=dato;
dirección_pila++;
}

/*------ */ /* ------ */
void recupera_dato( )
{
dirección_pila--;
if (dirección_pila < dirección_inferior)
{
printf(VEINTE);
printf(“(.) LA PILA ESTA VACIA. (.)”);
return;
}
printf(DIEZ);
printf(“ El dato es = <%d> “, *dirección_pila);
}

/*----------------
Programa principal
----------------- */

main( )
{
char aux;
dirección_pila=(int *)malloc(MÁXIMO * sizeof(int));
If (dirección_pila == NULL)
{
printf(VEINTE);
printf(“ESPACIO DE MEMORIA INSUFICIENTE”);
return;
}
dirección_inferior = dirección_pila;
dirección_superior = dirección_pila + MÁXIMO –1;
while (TRUE)
{
system(“clear”);
printf(DOS);
printf(“MENU DE GESTIÓN DE UNA PILA”);
printf(TRES);
printf(“---------------------------“);
printf(CUATRO);
printf(“- I – Introducir dato. “);
printf(CINCO);
printf(“- R – Recuperar dato.. “);

printf(SEIS);
printf(“- T – Terminar........ “);
printf(SIETE);
printf(“\t Opcion -> “);
fflush(stdin);
switch(toupper(aux=getchar( )))
{
case ‘I’:
introduce_dato( );
break;

170 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

case ‘R’:
recupera_dato( );
break;
case ‘T’:
system(“clear”);
exit(0);
}
}
}

13.5.- LISTAS ENLAZADAS


Las técnicas de almacenamiento y recuperación de datos como colas y pilas carecen de las
prestaciones que son imprescindibles para algunas aplicaciones, como ocurre en las bases de datos. La
simplicidad de aquéllas hace necesario una zona contigua de memoria para el almacenamiento de los
datos y la destrucción de la información almacenada en el momento de ser recuperada.

Para obtener un dato que ocupe un orden determinado, previamente se deben leer los datos
anteriores (colar) o los posteriores (pilas); es decir, no se pueden realizar accesos aleatorios.

Una lista enlazada es una estructura dinámica de información en la que los datos están
relacionados entre si mediante punteros. Se denomina lista simplemente enlazada si cada elemento
está conectado con el siguiente y lista doblemente encadenada si cada elemento está conectado con el
siguiente y con el anterior.

Son dos las ventajas de las listas doblemente enlazadas:

1. Se puede realizar la exploración de los datos en cualquier dirección de la lista.


2. Es más segura ante fallos de los punteros.

Tanto en la lista simplemente enlazada como en la lista doblemente enlazada debe existir un
puntero al primer elemento de la lista (Ver figuras inferiores).

Las listas son estructuras complejas de datos. Permiten acceder a un elemento determinado de
forma aleatoria y sin que signifique su destrucción. Como segunda ventaja de las listas es la de poder
operar con datos complejos (listas de estructuras), permitiendo la inserción de nuevos elementos y la
recuperación o el borrado de un elemento especifico.

Figura1: lista simplemente enlazada.

Puntero_al_primer elemento_de_la_lista

P. a dato2
Dato 1 Dato 2 P. a dato3 Dato 3 P. a dato4

Daton NULL

Figura2: Lista doblemente enlazada.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 171


Capítulo 13 Lenguaje “C”

Puntero_al_primer_elemento_de_la_lista

NULL Dato 1 P.a Dato2 P.a Dato1 Dato 2 P.a Dato3 P.a Dato2 Dato 3 P.a Dato4

P.a Dato n-1 Dato n NULL

13.5.1 ESTRUCTURA BASE Y ASIGNACIÓN DE MEMORIA

Definimos una estructura con las variables necesarias para crear una agenda (Nombre,
Dirección, Teléfono) y con una variable tipo puntero que contenga la dirección del primer byte de la
estructura siguiente.

struct agenda
{
char nombre[30];
char dirección[50];
char teléfono[10];
struct agenda *siguiente;
};

También es necesario definir las variables puntero a esta estructura mediante la instrucción
struct agenda *primero, *nuevo, *índice, dado que vamos a trabajara con las direcciones de los
elementos de la lista.

Cuando se asigne una zona de memoria a cada elemento de la lista, malloc( ) devolverá un
puntero a la dirección del primer byte de la estructura. *Siguiente es el puntero al siguiente elemento
de la lista. La variable puntero primero contendrá la dirección del primer elemento de la lista.
Igualmente las variables puntero nuevo e índice hacen referencia al primer byte de una estructura.

Para hacer referencia a un miembro de la estructura debemos utilizar el operador “.”, que
conecta una variable estructura con uno de sus miembros, pero, como hemos definido variables
puntero a estructura, es necesario redireccionar mediante la expresión (*puntero). Para facilitar este
proceso C proporciona el operador ->, que contenga un puntero con un miembro de la estructura. Por
ejemplo, nuevo -> dirección hace referencia a la variable dirección a través del puntero nuevo.

La asignación de memoria se realiza cuando se introduce un nuevo elemento en la lista. La


función asigna_memoria( ) utiliza la función malloc( ) mediante la expresión nuevo = (struct agenda
*) malloc (sizeof(struct agenda)); se asigna un puntero a la estructura agenda, que es del tamaño
determinado por sizeof.

Como la función malloc( ) retorna una dirección, pero sin informar sobre el tipo de dato, para
asegurarse de que es un puntero a estruct agenda se utiliza el typecast o modelado (struct agenda *).

Una vez obtenido el puntero, se hace preciso enlazar el nuevo elemento de la lista con los
existentes. Primero se comprueba la existencia de algún elemento en la lista; si no lo hay, se asigna el
valor del puntero nuevo al puntero primero. En caso contrario, el nuevo elemento se añade al final de
la lista. El enlace se consigue con índice->siguiente=nuevo.

172 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

En el ejemplo que presentamos a continuación no se incluye el caso de insertar un nuevo


elemento entre los de la lista; caso que queda para que sea resuelto por el alumno. (Se puede tomar un
criterio de ordenamiento de las estructura por orden alfabético.).

13.5.2 LIBERACIÓN DE MEMORIA Y LISTADO DEL PROGRAMA

La liberación de memoria se produce solamente en el caso de querer borrar algún elemento de


la lista. Para realizar esta operación sólo es necesario enlazar el puntero siguiente del elemento anterior
con el elemento posterior al que se desea borrar. La función borra_ficha( ) define la variable borra,
puntero a la estructura agenda. Pregunta que elemento se quiere borrar, obteniendo su puntero, y tras
la confirmación, si el elemento a borrar es el primero de la lista, se asigna primero=primero-
>siguiente; en caso contrario, se asignan los enlaces de la siguiente forma:

índice->siguiente = borra->siguiente

Y por último la liberación se realiza mediante la función free( ).

Incluimos a continuación el listado del programa “lista.c” que abarca lo comentado en los
punteros anteriores.

/*-----------------
Programa: lista.c
----------------- */

/*-----------------
Ficheros cabecera
----------------- */

#include <string.h>
#include <stdio.h>

/*---------------------
Sentencias directivas
--------------------- */

#define VERDAD 1
#define CUATRO “\x1B[4;10f”
#define CINCO “\x1B[5;10f”
#define SIETE “\x1B[7;10f”
#define OCHO “\x1B[8;10f”

#define NUEVE “\x1B[9;10f”


#define DIEZ “\x1B[10;10f”
#define ONCE “\x1B[11;10f”
#define DOCE “\x1B[12;10f”
#define TRECE “\x1B[13;10f”
#define QUINCE “\x1B[15;10f”
#define DIECISIETE “\x1B[17;10f”

/*---------------------------------------
Prototipos de las funciones utilizadas.
---------------------------------------- */

void intro_fihca( );
void lista_fichas( );
void muestra_ficha( );
void borra_ficha( );
void alamacena_datos( );
void lee_datos( );
void asigna_memoria( );

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 173


Capítulo 13 Lenguaje “C”

struct agenda *display_ficha( );


struct agenda *busca( );
void visu_ficha( );

/*-------------------------------------
Estructura de registro de un cliente.
------------------------------------- */

struct agenda
{
char nombre[30];
char dirección[35];
char teléfono[10];
struct agenda *siguiente;
};

/*--------------------------------------------
Variables puntero al tipo estructura anterior.
------------------------------------------- */

struct agenda *primero, *nuevo, *índice;

/*----------
Funciones.
---------- */

void intro_ficha( )
{
system(“clear”);
asigna_memoria( );
fflush(stdin);
printf(OCHO);
printf(“ALTA DE UN NUEVO CLIENTE”);
printf(NUEVE);
printf(“------------------------“);
printf(ONCE);
printf(“Nombre del cliente: “);
gets(índice->nombre);
printf(TRECE);
printf(“Dirección de trabajo : “);
gets(índice->dirección);
printf(QUINCE);
printf(“Teléfono de trabajo : “);
gets(índice->teléfono);
índice->siguiente = (struct agenda *) NULL;

/* ------ */ /* ------ */

void lista_fichas( )
{
if (primero == (struct agenda *) NULL)
{
flush(stdion);
system(“clear”);
printf(DIEZ);
printf(“* La lista de clientes esta vacía.”);
printf(ONCE);
prinft(“* Pulsa <Return> *”);
getchar( );
return;
}
índice = primero;
do
{
visu_ficha(índice);

174 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

índice = índice -> siguiente;


}
while (índice != (struct agenda *) NULL);
}

/* ------ */ /* ------ */

viod muestra_ficha( )
{
char inbuf[30]
fflush(stdion);
system(“clear”);
printf(CUATRO);
printf(“ Indroduzca el nombre del cliente: “);
agets(infub);
diplay_ficha(inbuf);
}

/* ------ */ /* ------ */

void borra_ficha( )
{
struct agenda *borra;
char infub[30];
fflush(stdin);
system(“clear”);
printf(CUATRO);
printf(“Nombre del cliente a dar de baja: “);
gets(inbuf);
borra = (struct agenda *) display_ficha(inbuf);
if (borra == (struct agneda *) NULL)
return;
printf(DIECISIETE);
printf(“Dar de baja a este cliente. (s/n) : “);
switch(toupper(getchar( )))
{
case ‘N’:
return;
case ‘S’:
break;
}
if (borra == primero)
primero = primero -> siguiente;
else
{
índice=primero;
while (índice->siguiente != borra)

índice = índice->siguiente;
índice->siguiente = borra->siguiente;
}
free(borra);
}

/* ------ */ /* ------ */

void almacena_datos( )
{
FILE *almacena;
char inbuf[20];
fflush(stdin);
system(“clear”);
printf(DIEZ);
printf(“Grabación de la lista de clientes en disco. “);
printf(ONCE);
printf(“Introduzca el nombre del fichero : “);
gets(inbuf);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 175


Capítulo 13 Lenguaje “C”

if ((almacena = fopen(inbuf, “wb”)) == NULL)


{
system(“clear”);
printf(DIEZ);
perror(“* No puedo abrir el fichero +”);
getchar( );
return;
}
índice = primero;
do
{
fwrite(índice,sezeof(struct agenda),1, almacena);
índice = índice->siguiente;
}
while (índice != (struct agenda *) NULL);
fclose(almacena);
}

/* ------ */ /* ------ */

/* .. ..
Lee una lista de estructura de un fichero en disco,
si ya existe una lista en memoria, la lista leida del
disco se añade al final.
.. .. */

void lee_datos( )
{
FILE *lee;
char inbuf[20];
system(“clear”);
fflush(stdin);
prinft(DIEZ);
printf(“Recuperación de datos de los clientes del disco.”);
printf(ONCE);
printf(“Introduzca el nombre del fichero: “);
gets(inbuf);
if ((lee = fopen(inbuf,”rb”)) == NULL)
{
system(“clear”);
printf(DIEZ);
perror(“Error de acceso en el fichero indicado”);
getchar( );
return;
}
asigna_memoria( );
do

{
índice->siguiente = nuevo;
índice = nuevo;
fread(índice, sizeof(struct agenda); 1, lee);
nuevo = (struct agenda *) malloc (sizeof(struct agenda));
}
while (índice->siguiente != NULL);
fclose(lee);
}

/* ------ */ /* ------ */

void asigna_memoria( )
{
nuevo = (struct agenda *) malloc(sizeof(struct agenda));
if (nuevo == NULL)
{
system(“clear”);
printf(DIEZ);

176 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

printf(“Espacio insuficiente en la memoria.”);


getchar( );
return;
}
if (primero == (struct agenda *) NULL)
primero = índice = nuevo;
else
{
índice = primero;
while (índice->siguiente != (struct agenda *) NULL)
índice = índice->siguiente;
índice->siguiente = nuevo;
índice = nuevo;
}
}

/* ------ */ /* ------ */

struct agenda *busca(inbuf)


char *inbuf;
{
índice = primero;
while(índice)
{
if (!strcmp(inbuf, índice->nombre))
return índice;
índice = índice -> siguiente;
}
return NULL;
}

/* ------ */ /* ------ */

struct agenda *display_ficha(n_ficha)


char *n_ficha;
{
struct agenda *ficha;
printf(SIETE);
if(!(ficha=(struct agenda *) busca(n_ficha)))
{
char aux;
printf(DIEZ);
printf(“* El cliente indicado no existe *”);
printf(ONCE);
printf(“* Pulsa <Return> *”);
aux = getchar( );
return NULL;

}
else
visu_ficha(ficha);
return ficha;
}

/* ------ */ /* ------ */

void visu_ficha(visu)
struct agenda *visu;
{
int aux;
fflush(stdin);
system(“clear”);
printf(SIETE);
printf(“Nombre del cliente : <%-35s>”, visu->nombre);
printf(NUEVE);
printf(“Dirección de trabajo : <%-35s>”, visu->dirección);
printf(ONCE);

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 177


Capítulo 13 Lenguaje “C”

printf(“Teléfono de trabajo : <%-35s>”, visu->teléfono);


printf(TRECE);
printf(“.. ..”);
printf(QUINCE);
printf(“<><> Return -> Continuar <><>”);
aux=getchar( );
}

/*------------------
Programa principal.
------------------ */

void main( )
{
char aux;

primero=(struct agenda *) NULL;

while(VERDAD)
{
system(“clear”);
printf(CUATRO);
printf(“ * Menu clientes *”);
printf(CINCO);
printf(“ --------------------“);
printf(SIETE);
printf(“- I - Introducir un nuevo cliente. -“);
printf(OCHO);
printf(“- L - Listado de todos los clientes. -“);
printf(NUEVE);
printf(“- V - Visualizar datos de un cliente. -“);
printf(DIEZ);
printf(“- B - Baja de un cliente. -“);
printf(ONCE);
printf(“- G - Grabar los datos en disco. -“);
printf(DOCE);
printf(“- R - Leer datos del disco. -“);
printf(TRECE);
printf(“- F - ** FINALIZAR ** -“);
printf(QUINCE);
printf(“ Opción:-> “);
switch(toupper(aux=getchar( )))
{
case ‘I’:
intro_ficha( );
break;
case ‘L’:

lista_fichas( );
break;
case ‘V’:
muestra_ficha( );
break;
case ‘B’:
borra_ficha( );
break;
case ‘G’:
almacena_datos( );
break;
case ‘R’:
lee_datos( );
break;
case ‘F’:
system(“clear”);
exit(0);
}
}

178 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

Los elementos de la lista se almacenan en un fichero uno a continuación de otro. Las


direcciones que almacenan el primer byte de la estructura se han almacenado gracias a la variable
siguiente, excepto para el primer elemento de la lista.

Para realizar la lectura de una lista almacenada en un fichero se ha preferido asignar


nuevamente la memoria a todos los elementos e ir enlazando sus direcciones. De esta manera se puede
evitar la fragmentación.

Otra observación sobre esta función es que realiza el enlace del primer elemento almacenado
en el archivo con el último elemento de la lista presente en la memoria (caso de estar trabajando con
una lista). Si no se desea esto, es preciso añadir la sentencia siguiente en la función lee_datos( );

Primero = (struct agenda *) NULL;

que borra todas las fichas presentes en memoria.

13.6.- ÁRBOLES BINARIOS


Un árbol binario es una estructura jerarquizada muy utilizada en aquellas aplicaciones que
modifican su tamaño en tiempo de ejecución. Los árboles se utilizan en la programación de hojas de
cálculo o en Inteligencia Artificial. Son, además, una alternativa a las listas doblemente enlazadas.

En estas estructuras, los elementos reciben la denominación de nodos.

Cada nodo se relaciona (ramifica) con otros nodos siguiendo algún criterio de relación. Estos,
a su vez, se relacionan con otros nodos formando verdaderos subárboles. Cada vez que se ramifica se
dice que se cambia el nivel; el nivel 1 está formado por el nodo raíz y la altura del árbol son las capas
que crecen a partir de la raíz.

Los nodos que no tienen subárboles se denominan terminales.

Los árboles binarios son aquellos en los que cada nodo sólo puede tener dos ramificaciones:
derecha e izquierda. Tienen especiales aplicaciones porque permiten búsquedas, inserciones y
borrados rápidos. Vemos un ejemplo gráfico de un árbol binario de 3 niveles.

DATO M

P. IZDA P. DCHA

DATO M DATO M

P. IZDA P. DCHA P. IZDA P. DCHA

DATO M DATO M DATO M DATO M

P. IZDA P. DCHA P. IZDA P. DCHA P. IZDA P. DCHA P. IZDA P. DCHA

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 179


Capítulo 13 Lenguaje “C”

Aunque la representación gráfica de un árbol muestre la estructura que tienen los datos en la
memoria, no hay que olvidar que el almacenamiento real de los elementos se realiza en la memoria
lineal de la computadora.

Por su forma podemos distinguir diversos tipos de árboles. Se denominan degenerados


cuando cada nodo tiene como máximo un subárbol (se trata en realidad de listas), y equilibrados
cuando la diferencia de altura de los subárboles es como máximo uno.

13.6.1 ESTRUCTURA BASE

Cada nodo del árbol se define como una variable mediante la estructura dinámica siguiente:

struct árbol
{
int clave;
.
.
.
struct árbol *izquierda;
struct árbol *derecha;
};

donde clave es la variable que nos indica el valor del nodo, mientras que las variables estructura
izquierda y derecha enlazan con los nodos que tienen un valor de clave menor o mayor,
respectivamente, que la raíz.

La mayoría de las funciones utilizadas pro estas estructuras de datos son recursivas, puesto
que el árbol es una estructura recursiva (cada subárbol es un árbol).

13.6.2 ORDENACIÓN DE ÁRBOLES

La mayoría de las aplicaciones utilizan árboles ordenados. La ordenación de un árbol binario


consiste en que cada subárbol de la izquierda contiene nodos que son menores o iguales que su raíz, y
dos nodos de los subárboles de la derecha son mayores.

Las operaciones que tengamos que realizar sobre los árboles necesitarán recorrer
secuencialmente cada nodo del mismo siguiendo un orden previamente establecido. Hay tres posibles
ordenaciones, según se efectúe el recorrido de los nodos.

Preorden: Se considera la raíz antes que los subárboles, que se recorren de izquierda a
derecha. El árbol del gráfico se recorrería en el siguiente orden:

MDAKRPX

Orden Central: Se considera primero el árbol más a la izquierda, seguido de la raíz y los
demás subárboles ordenados de izquierda a derecha.

ADKMPRX

Postorden: Se consideran los subárboles ordenados de izquierda a derecha seguidos de la raíz.

AKDPXRM

13.6.3 BÚSQUEDA EN EL ÁRBOL

180 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

En la búsqueda de los datos almacenados en árboles binarios se sigue un camino único desde
la raíz al nodo. La función siguiente puede ser utilizada para este fin.

/* ---------- */ /* --------- */
struct árbol *busca(raíz, nodo)
{
struct árbol *raíz;
int nodo;
if (!raíz) /* Árbol vacío */
return raíz;
while (raíz->clave != nodo)
{
if (nodo < raíz->clave)
raíz = raíz->izquierda;
else
raíz = raíz->derecha;
if (raíz == NULL)
break; /* Termina la ejecución. */

}
return raíz;
}

/* -------- */ /* --------- */

Hasta que no se encuentre el nodo buscado se realiza el bucle while recorriendo la izquierda
mientras el valor buscado sea inferior a los pesos que encuentra, o bifurcando hacia la derecha en caso
contrario; una vez localizado retorna el puntero al nodo en cuestión termina la ejecución de la función
en caso de llegar al final de árbol, raíz == NULL. Cuando se utiliza una marca de final de árbol la
condición del bucle while será sustituida por:

while (raíz->peso != marca)

La búsqueda se combina en muchas ocasiones con la inserción.

13.6.4 BORRADO EN EL ÁRBOL BINARIO

El borrado en los árboles binarios consiste en la eliminación del nodo que tenga una clave
determinada. Se pueden presentar varios casos según la posición que ocupe el nodo. Puede tratarse del
nodo raíz o de un nodo terminal. El nodo a borrar puede tener uno o dos subárboles. La función
borrado puede ser la siguiente:

/* --------- */ /* --------- */

struct árbol *borra(raíz, nodo)


struct árbol *raíz;
char nodo;
{
struct árbol *d1, *d2;
if (raíz->clave == nodo) /* Encuentra en nodo a borrar */
{
if (raíz->izquierda == raíz->derecha) /* Último nodo */
{
free(raíz);
return(NULL);
}
else

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 181


Capítulo 13 Lenguaje “C”

if (raíz->izquierda == NULL) /* Subárbol a la derecha. */


{
d1 = raíz->izquierda;
free(raíz);
return d1;
}
else
if (raíz ->derecha = NULL) /* Subárbol a la izquierda */
{
d1 = raíz->derecha;
free(raíz);
return d1;
}
else /* Subárbol en los dos lados */
{
d2 = raíz->derecha;
d1 = raíz->izquierda;
while (d1->izquierda)
d1 = d1->izquierda;
d1->izquierda = raíz->izquierda;
free(raíz);
return d2;
}
}
if (raíz->clave < nodo)
/* Recorre por la derecha */
raíz->derecha = (struct árbol *) borra(raíz->derecha,nodo);
else
/* Recorre por la izquierda */
raíz->izquierda = (struct árbol *) borra(raíz->izquierda,nodo);
return raíz;
}

/* ---------- */ /* ------------ */

Si borra la raíz, y ésta tiene un solo subárbol, izquierdo o derecho, se elimina el nodo y se
retorna al puntero del subárbol. Si son dos los subárboles, izquierdo y derecho, se recorre la rama más
a la izquierda del subárbol derecho del nodo a borrar, sustituyendo el puntero izquierdo por el derecho,
borrando el nodo y retornando el puntero.

Cuando no es la raíz el nodo buscado, se recorre el subárbol de la izquierda o derecha según


clase, realizando llamadas recursivas a la función borra( ).

13.6.5 CREACIÓN DE UN ÁRBOL BINARIO.

La creación de un árbol binario debe basarse en una función que permita la ordenación según
el valor de una clave, introducida como dato desde la función main( ). La función main( ) tendrá que
contener:

182 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

/* --------- */ /* --------- */
main( )
{
struct árbol *crea_árbol( );
raíz = NULL;
char dato;
do
{
printf(“ Entre un dato : “);
gets(&dato);
if (!raíz)
raíz = crea_árbol(raíz, raíz, dato);
else
cra_árbol(raíz, raíz, dato);
}
while (dato);
}
/* -------- */ /* ------- */

El bucle do-while recoge los datos, llamando la primera vez a la función crea_árbol( ) que
retornará un puntero al nodo raíz, el cual permanecerá constante durante toda la ejecución del
programa. La función que va recogiendo los valores de los datos y ejecuta la ordenación es la
siguiente:

/* ----------- */ /* ----------- */
struct árbol *crea_árbo(raíz, aux, dato)
struct árbol *raíz;
struct árbol *aux;
char dato;
{
if (!aux)
{
aux = (struct árbol *) malloc(sizeof(struct árbol));
aux->izquierda = (struct árbol *) NULL;
aux->derecha = (struct árbol *) NULL;
aux->clave = dato;
if (!raíz)
return aux; /* Primer nodo = raíz */
if (dato < raíz->clave)
raíz->izquierda = aux;
else
raíz->derecha = aux;
return aux;
}
if (dato < aux->clave)
crea_árbol(aux,aux->izquierda, dato);
else
if (dato > aux->clave)
crea_árbol(aux,aux->derecha, dato);
}
/* ---------- */ /* ----------- */

La primera vez que se ejecuta esta función será para almacenar la raíz, con lo que los valores
iniciales de raíz y de aux son NULL. En este caso se asigna memoria dinámicamente son malloc( ), se
almacena el dato y los punteros de la izquierda y de la derecha a NULL y se retorna el valor del
puntero de la primera entrada (puntero aux).

La segunda y posteriores entradas de datos harán que la función crea_árbol( ) se ejecute y


siendo (!aux) falso en la primera recursión, se compara el dato introducido con el dato almacenado en
la raíz y se llama a la función crea_árbol( ), pero pasando como argumentos los punteros aux y de aux-
>izquierda o aux->derecha según el dato sea mayor o mayor que la clave.

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 183


Capítulo 13 Lenguaje “C”

13.6.6 LISTADO COMPLETO DEL PROGRAMA

El siguiente programa recoge todo lo comentado en los puntos anteriores. Almacena los datos,
ordenándolos según van entrando, en una estructura de árbol. El programa permite borrar un nodo y
visualiza el listado de los datos en función del sistema de ordenación que se elija, Preorden, Central,
Postorden.

/*-----------------
Programa: árbol.c
----------------- */

/*-----------------
Ficheros cabecera
----------------- */

# include <string.h>
# include <stdio.h>

/*---------------------
Sentencias directivas
--------------------- */

# define VERDAD 1
# define SEIS “\x1B[6;10f”
# define SIETE “\x1B[7;10f”
# define NUEVE “\x1B[9;10f”
# define DIEZ “\x1B[10;10f”
# define ONCE “\x1B[11;10f”
# define DOCE “\x1B[12;10f”
# define TRECE “\x1B[13;10f”
# define CATORCE “\x1B[14;10f”

/*--------------------------------------
Prototipos de las funciones utilizadas
-------------------------------------- */

void intro_nodo ( );
void lista_nodo ( );
void borra_nodo ( );
void lista_preorden ( );
void lista_central ( );
void lista_postorden ( );
struct árbol *borra ( );
struct árbol *crea_arbol ( );

/*---------------------
Estructura de un nodo
--------------------- */

struct árbol
{

char clave;
struct árbol *izquierda;
struct árbol *derecha;
};

/*----------------------------
Puntero a la estructura raíz
---------------------------- */

struct árbol *raíz;

184 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

/* --------- */ /* ---------- */

struct árbol *borra(raíz, nodo)


struct árbol *raíz;
char nodo;
{
struct árbol *d1, *d2;
if (raíz->clave == nodo)
{
if (raíz->izquierda == raíz->derecha)
{
free(raíz);
return(NULL);
}
else
if (raíz->izquierda == NULL)
{
d1 = raíz->izquierda;
free(raíz);
return d1;
}
else
if (raíz->derecha = NULL)
{
d1 = raíz->derecha;
free(raíz);
return d1;
}
else
{
d2 = raíz->derecha;
d1 = raíz->derecha;
while (d1->izquierda)
d1 = d1->izquierda;
d1->izquierda = raíz->izquierda;
free(raíz);
return d2;
}
}
if (raíz->clave < nodo)
raíz->derecha = (struct árbol *) borra(raíz->derecha,nodo);
else
raíz->izquierda = (struct árbol *) borra(raíz->izquierda,nodo);
return raíz;
}

/* ----------- */ /* ----------- */

struct árbol *crea_árbol(raíz, aux, dato)


struct árbol *raíz;
struct árbol *aux;
char dato;
{
if (!aux)
{
aux = (struct árbol *) malloc(sizeof(struct árbol));

aux->izquierda = (struct árbol *) NULL;


aux->derecha = (struct árbol *) NULL;
aux->clave = dato;
if (!raíz)
return aux;
if (dato < raíz->clave)
raíz->izquierda = aux;
else
raíz->derecha = aux;
return aux;
}

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 185


Capítulo 13 Lenguaje “C”

if (dato < aux->clave)


(struct árbol *) crea_árbol (aux,aux->iquierda, dato);
else
if (dato > aux->clave)
(struct árbol *) crea_árbol(aux,aux->derecha,dato);
}

/* -------- */ /* --------- */

void intro_nodo( )
{
char dato;
fflush(stdin);
do
{
system(“clear”);
printf(DIEZ);
printf(“Tecleame un carácter. “);
printf(ONCE);
printf(“<Return> finalizar-> “);
gets(&dato);
if (!raíz)
raíz = (struct árbol *) crea_árbol(raíz, raíz, dato);
else
(struct árbol *) crea_árbol(raíz, raíz, dato);
}
while (dato);
}

/* -------- */ /* --------- */

void borra_nodo( )
{
char dato;
system(“clear”);
fflush(stdin);
printf(DIEZ);
printf(“Carácter a borrar :-> “);
gets(&dato);
(struct árbol *) borra(raíz, dato);
}

/* ----------*/ /* ---------- */

void lista_preorden(aux)
struct árbol *aux;
{
int orden;
system(“clear”);
if (!aux)
return;
printf(“ %c “, aux->clave);
lista_preorden(aux->izquierda);
lista_preorden(aux->derecha);
}

/* -------- */ /* ---------- */

void lista_central (aux)


struct árbol *aux;
{
int orden;
system(“clear”);
if (!aux)
return;
lista_central (aux->izquierda);
printf(“ %c “,aux->clave);

186 Este manual ha sido desarrollado por el departamento técnico de DELTA PC


Lenguaje “C” Capítulo 13

lsita_central (aux->derecha);
}

/* ----------- */ /* ------------ */

void lista_postorden(aux)
struct árbol *aux;
{
int orden;
system(“clear”);
if (!aux)
return;
lista_postorden(aux->izquierda);
lista_postorden(aux->derecha);
printf(“ %c “,aux->clave);
}

/* -------- */ /* ---------- */

void lista_nodos( )
{
fflush(stdin);
system(“clear”);
printf(SEIS);
printf(“LISTADO DE LOS NODOS.”);
printf(SIETE);
printf(“---------------------“);
printf(NUEVE);
printf(“ – P – Preorden. -“);
printf(DIEZ);
printf(“ – C – Orden Central. -“);
printf(ONCE);
printf(“ – S – Postorden. -“);
printf(TRECE);
printf(“ Opcion -> “);
switch(toupper(getchar( )))
{
case ‘P’:
lista_preorden(raíz);
fflush(stdin);
printf(CATORCE);
printf(“Pulsa <Return> para continuar”);
getchar( );
break;
case ‘C’:
lista_central(raíz);
fflush(stdin);
printf(CATORCE);
printf(“Pulsa <Return> para continuar”);
getchar( );
break;
case ‘S’:
lista_postorden(raíz);
fflush(stdin);
printf(CATORCE);
printf(“Pulsa <Return> para continuar”);

getchar( );
break;
}
}

/*------------------
Programa principal
------------------ */

void main( )
{

Este manual ha sido desarrollado por el departamento técnico de DELTA PC 187


Capítulo 13 Lenguaje “C”

raíz = (struct árbol *) NULL;

while (VERDAD)
{
system(“clear”);
printf(SEIS);
printf(“ MENU DEL PROGRAMA ÁRBOL “);
printf(SIETE);
printf(“ ----------------------- “);
printf(NUEVE);
printf(“ –A – Añadir nodo. “);
printf(DIEZ);
printf(“ – L – Listado de nodos. “);
printf(ONCE);
printf(“ – B – Borrar nodo. “);
printf(DOCE);
printf(“ – T – Terminar. “);
printf(CATORCE);
printf(“ Opción -> “);
switch(toupper(getchar( )))
{
case ‘A’:
intro_nodo ( );
break;
case ‘L’:
lista_nodos ( );
break;
case ‘B’:
borra_nodo( );
break;
case ‘T’:
system(“clear”);
exit(0);
}
}
}

188 Este manual ha sido desarrollado por el departamento técnico de DELTA PC

Вам также может понравиться