Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Tema 12-Lenguaje C
Tema 12-Lenguaje C
Curso 2012/13
Versin: 1.0.4
1.
Introduccin ................................................................................................................................................ 1
2.
Elementos en C............................................................................................................................................ 2
2.1.
2.2.
2.3.
Punteros ............................................................................................................................................ 12
3.
4.
5.
5.2.
5.3.
6.
7.
8.
1. Introduccin
El lenguaje C es un lenguaje de programacin creado en 1972 por Dennis M. Ritchie en los Laboratorios Bell.
Es un lenguaje cuyo origen es la implementacin del sistema operativo Unix. C es valorado por la eficiencia
del cdigo que produce y es el lenguaje de programacin ms popular para crear software de sistemas,
aunque tambin se utiliza para crear aplicaciones. Se trata de un lenguaje de medio nivel pero con muchas
caractersticas de bajo nivel. Dispone de las estructuras de control tpicas de los lenguajes de alto nivel pero,
a su vez, dispone de sentencias y operadores que permiten un control a muy bajo nivel, esto es, cercano a la
mquina. La compilacin de un programa C se realiza en varias fases que normalmente son automatizadas y
ocultadas por el IDE:
1. Precompilacin, consistente en modificar el cdigo fuente en C segn una serie de instrucciones
(denominadas directivas de precompilacin), simplificando de esta forma el trabajo del compilador.
Algunas de las tareas de las que se encarga el precompilador son la inclusin de ficheros y la
expansin de macros.
2. Compilacin, que genera el cdigo objeto a partir del cdigo ya preprocesado.
Fundamentos de Programacin
3. Enlazado, que une los cdigos objeto de los distintos mdulos y bibliotecas externas (como las
bibliotecas del sistema) para generar el programa ejecutable final.
El fichero fuente de un programa en C tiene la extensin .c, el cdigo objeto despus de la fase de
compilacin mantiene el mismo nombre y la extensin .obj y, finalmente, el fichero ejecutable despus de la
fase de enlazado tiene la extensin .exe. La Figura 1 muestra un esquema de las fases del proceso de
compilacin en C y los archivos que se generan en cada una de ellas. Hay que mencionar que la fase de
precompilacin no genera ningn archivo fsicamente en disco, y que el archivo con extensin .c que
aparece en la figura no es ms que una modificacin del archivo .c que se hace en memoria.
C es un lenguaje no orientado a objetos, por lo que las tcnicas y el estilo de programacin son muy
diferentes a las que hemos usado en Java. En C seguimos una filosofa de programacin denominada
programacin estructurada. Introduciremos los elementos de C, y cuando sea necesario, veremos los
elementos que se comparten con Java y los que no. En general decimos que C es un lenguaje de ms bajo
nivel que Java.
2. Elementos en C
2.1. Estructura de un programa en C
El cdigo de un programa en C se compone de un conjunto de ficheros con extensiones .h y .c. Los ficheros .h
contienen declaraciones y definiciones de tipos mientras que los ficheros .c contienen el cdigo de los
mtodos (ahora llamados funciones y procedimientos).
La estructura de un programa en C no es nica, habiendo cierta libertad; sin embargo vamos a considerar
una nica estructura posible en la asignatura. Aunque el cdigo se suele estructurar en ms de un fichero, de
momento vamos a ver la estructura de un programa en C en un nico fichero fuente .c que ser de la forma
siguiente:
Importaciones
Definicin de constantes y macros
Definicin de tipos
Declaracin de funciones y procedimientos
void main () { // Definicin de la funcin principal
declaracin de variables;
instrucciones;
}
Definicin de las funciones declaradas anteriormente
Cuando un programa en C tiene pocas funciones (tres o cuatro) suele ponerse todo el cdigo en un solo
fichero .c. Sin embargo, cuando el programa se hace ms complejo, suele organizarse el cdigo en varios
ficheros. La organizacin mnima es un fichero de cabecera o .h, que normalmente tiene todas las
declaraciones de constantes, tipos y funciones; un fichero .c con nicamente el programa principal, y otro
fichero .c con la definicin o cdigo de las funciones. Dependiendo de la complejidad del programa y de las
relaciones entre funciones, se puede organizar el cdigo con varios ficheros .h y varios .c con las funciones
agrupadas por su funcionalidad.
Importaciones
En C es habitual usar un conjunto de funciones ya predefinidas por el compilador y situadas en libreras.
Estas funciones estn declaradas en ficheros que tienen la extensin .h (de header o fichero de cabecera). En
la declaracin de importaciones el programador indica al compilador qu libreras va a necesitar. Hay
numerosas libreras de funciones y adems cada programador y cada entorno puede aadir funciones
propias.
Tres ejemplos de archivos de cabecera muy utilizados son stdio.h, math.h y string.h. El archivo de cabecera
stdio.h (standard input/output) contiene declaraciones de funciones de lectura (tanto desde teclado como
desde fichero) y de escritura (tanto en pantalla como en fichero). El archivo de cabecera math.h contiene
declaraciones de funciones matemticas (raz cuadrada, potencia, trigonomtricas, etc.) y, finalmente,
string.h contiene declaraciones de funciones para tratamiento de cadenas de caracteres.
Cada declaracin de importacin debe ir en una sola lnea y comienza con #include. Cada lnea de este tipo
indica al preprocesador que incluya en ese punto el fichero que le sigue. Si el nombre de fichero est entre
mayor y menor (< >), el archivo de cabecera declara funciones de una librera estndar de C o una librera
proporcionada por el entorno de programacin. En este caso, el compilador busca el cdigo de las funciones
declaradas en estos archivos de cabecera en una ruta definida por el entorno. Si el fichero est entre , el
archivo de cabecera declara funciones implementadas por el programador, y entonces el compilador busca
el cdigo de las funciones declaradas en la ubicacin del cdigo fuente del programador. Una forma genrica
de definir estos dos tipos de importaciones es la siguiente:
#include <fichero1.h>
#include fichero2.h
Fundamentos de Programacin
#include <stdio.h>
#include <math.h>
#include punto.h
Estas lneas son similares a las declaraciones import de JAVA, y en ellas se indica que en el fichero se usan las
declaraciones de algunas funciones de entrada/salida (stdio.h), de algunas funciones matemticas (math.h),
y algunas funciones definidas por el programador relacionadas con un tipo punto (punto.h).
Definicin de constantes
Una constante es un identificador que se asocia a un valor que no cambia durante toda la ejecucin del
programa. Habitualmente se usa para una mejor documentacin del programa o para hacer ms fcil su
mantenimiento y cambio. La forma habitual de declarar una constante es una lnea que comienza por la
declaracin #define. Con esta lnea se indica al preprocesador que en lo que sigue sustituya el nombre de la
constante por su valor. Hay dos tipos segn el nombre vaya seguido de parntesis o no.
#define identificador1 expresion1
Ejemplos:
#define
#define
#define
#define
PI 3.14159
MAXIMO 999
ULTIMA_LETRA 'Z'
MENSAJE "Introduzca su edad: "
Ntese que entre el identificador y el valor NO HAY carcter =, que la sentencia NO termina en punto y coma
y que la constante no necesita declararse de un determinado tipo, sino que el valor determina el tipo. Las
constantes es usual escribirlas con letras maysculas.
Declaracin de tipos
El C proporciona un conjunto de tipos bsicos ya definidos (int, float, char, etc.) y tambin permite que el
programador defina sus propios tipos. Los tipos ms habituales que se definen en C son los tipos
enumerados, los tipos tabla o array y los tipos registro o struct (apartado 5).
Con la clusula typedef se pueden declarar nuevos tipos. La sintaxis de la clusula typedef es la siguiente:
typedef especificacin_tipo nombre_tipo;
Ejemplos:
typedef enum {R, G, B} Color;
typedef enum {falso, cierto} boolean;
En el ejemplo anterior se ha declarado el tipo Color como un enumerado que puede tomar tres posibles
valores (R, G y B), y el tipo boolean como otro enumerado que puede tomar los valores falso y cierto. Hay
que tener en cuenta que, realmente, con la sentencia typedef no se crean nuevos tipos como tales, sino que
se crean sinnimos para tipos que ya existen o nombres para tipos que podran especificarse de otra forma.
El usar typedef nos permite utilizar estos nombres a la hora de declarar variables. Por ejemplo, en las dos
siguientes lneas se crean una variable de tipo Color llamada c, y otra de tipo boolean llamada a. Adems,
esta ltima se inicializa con el valor cierto.
Color c;
boolean a = cierto;
Declaracin de funciones
Las funciones en C deben declararse mediante su prototipo. ste debe escribirse antes de su invocacin por
el programa principal. El prototipo de una funcin consiste en definir cul va a ser su nombre, qu tipos de
datos tiene como entrada (argumentos) y qu tipo de dato devuelve.
Para explicar estos conceptos hay que introducir los conceptos de valor de retorno y de argumentos. Toda
funcin bien definida debe recibir uno o varios valores a partir de los cuales calcular un valor de salida. El
ejemplo ms cercano son las funciones matemticas, como la potencia, que puede recibir dos argumentos,
uno real (base) y otro entero (exponente) y devuelve un real con el resultado de la base elevada al
exponente. Por tanto, los argumentos son base y exponente y el resultado o valor de retorno la potencia.
Para declarar una funcin basta con darle un nombre y declarar los tipos de los argumentos y del valor de
retorno de sta usando la siguiente sintaxis:
tipo_retorno nombre_funcion (tipo_arg1, tipo_arg2,, tipo_argn);
En la primera lnea se define el prototipo de una funcin llamada potencia, que tiene dos argumentos, uno
de tipo float, y otro de tipo int, y cuyo resultado es de tipo float. Luego se declara una funcin factorial que
tiene un argumento de tipo int, y cuyo resultado es de tipo long. Le sigue una funcin aMayuscula que tiene
un argumento de tipo char y un resultado de tipo char. A continuacin se declara una funcin mcd con dos
argumentos de tipo int y con resultado de tipo int. Por ltimo, mostrarColor es una funcin que tiene un
argumento de tipo Color, y no devuelve nada (es de tipo void).
Ntese que un prototipo siempre termina en el separador punto y coma (;) y que los tipos de los argumentos
estn entre parntesis y separados por comas.
Funcin principal
Todo programa en C debe tener una funcin principal que debe denominarse main. Aunque puede tener
distintos formatos de cabecera lo usual es void main(void). A continuacin, entre llaves estar el cdigo que
resuelve el problema para el que se crea el programa. Lo usual es que la mayora del cdigo de la funcin
main consista en invocaciones a las funciones en las que se habr dividido el problema. La estructura del
cdigo que va entre llaves es similar a la de cualquier otra funcin y se ver con ms detalle en el siguiente
apartado (Definicin de funciones).
Fundamentos de Programacin
Definicin de funciones
Finalmente el fichero fuente en C termina con la definicin de las funciones que se usan en el programa, que
sern invocadas desde el main, o bien desde otras funciones. Las funciones se definen una a continuacin de
otra.
La definicin de una funcin es el conjunto de sentencias o instrucciones necesarias para que la funcin
pueda realizar su tarea cuando sea llamada. En otras palabras, la definicin es el cdigo correspondiente a la
funcin. Adems del cdigo, la definicin de la funcin debe incluir una primera lnea que es una cabecera
similar a la declaracin o prototipo que hemos visto. Esta cabecera debe incluir, de forma obligatoria, los
nombres de las variables que van a ser los argumentos de la funcin. Estas variables se llaman argumentos o
parmetros formales, a diferencia de los argumentos que estn en la invocacin a las funciones, que se
llaman argumentos o parmetros reales. Veamos un ejemplo con la funcin potencia:
float potencia (float base, int exponente) {
float resultado = 1.0;
int i = 1;
while (i <= exponente) {
resultado = base * resultado;
i = i+1;
}
return resultado;
}
Las variables base y exponente son los argumentos formales y, a su vez, son variables locales en el cdigo de
la funcin.
Sin detenernos, de momento, en qu hace esta funcin, s podemos entender qu significan los parmetros
formales. Las variables base y exponente sirven de comunicacin entre la invocacin y el cdigo de la
funcin; de esta manera, la invocacin:
pot = potencia (3.4f, 2);
hace que la variable base tome el valor 3.4 y exponente el valor 2, que son denominados parmetros reales.
Si la invocacin se hiciera en vez de con constantes con valores de variables, por ejemplo:
pot = potencia (a, b); // a de tipo float y b de tipo int
Los parmetros reales seran a y b. Cuando se invoca a la funcin potencia, el valor de a se transfiere a base,
y el de b, a exponente. La funcin potencia realiza una serie de clculos con los valores recibidos de base y
exponente y obtiene un valor que se guarda en la variable local resultado. Esta variable es devuelta en la
ltima sentencia de la funcin, y su valor se transfiere a la invocacin de la funcin y es asignada a su vez a la
variable pot.
Es importante repetir que los parmetros formales en la cabecera de la funcin son variables locales a la
funcin, es decir, slo tienen sentido dentro del cdigo de la funcin, y es un error muy comn querer
definirlos otra vez dentro de la funcin. Las variables i y resultado tambin son variables locales de la funcin
potencia.
Si una funcin no devuelve ningn valor su tipo de retorno ser void. Por razones histricas estas funciones
suelen llamarse procedimientos. Por ejemplo, se usan habitualmente para imprimir resultados en pantalla,
ya que son funciones que estructuran bien el cdigo y son reutilizables pero no devuelven ningn valor.
2.2. Tipos, declaraciones, operadores, expresiones
Los tipos bsicos son:
char, que representa un carcter.
int / long, que representa un nmero entero.
float / double, que representa un nmero real.
void, que representa un tipo sin valores.
Los tipos bsicos de C equivalen a los tipos predefinidos de Java, aunque no existen ni el tipo boolean, ni el
tipo String. En ANSI C, un booleano se modela mediante un valor entero. Si el valor es 0, se considera falso, y
si es distinto de cero se considera cierto. En nuestra asignatura, definiremos un tipo boolean a partir de un
enumerado con los valores falso y cierto, de la siguiente forma:
typedef enum {falso, cierto} boolean;
Ejemplos:
char c;
int i, j;
long potencia;
double radio, longitud;
Expresiones
Como en Java, las expresiones son combinaciones de constantes, variables operadores y funciones. Todas las
expresiones, si tienen una sintaxis correcta, tienen un tipo.
Los operadores disponibles en C son similares a los de Java. Se clasifican en aritmticos, relacionales, lgicos
y de asignacin. Ms adelante veremos otros tipos de operadores.
Operadores Aritmticos: operan sobre datos de tipo numrico. Forman expresiones cuyo tipo es real
(float/double) o entero (int/long) y son los siguientes:
Fundamentos de Programacin
+
*
/
%
++
--
Suma
Resta
Multiplicacin
Divisin
Resto
Cambio de signo
Incremento
Decremento
Todos estos operadores se pueden aplicar a constantes, variables y expresiones. El resultado es el que se
obtiene de aplicar la operacin correspondiente entre los dos operandos. El nico operador que requiere
una explicacin adicional es el operador resto %. En realidad su nombre completo es resto de la divisin
entera. Este operador se aplica solamente a constantes, variables o expresiones de tipo int. Por ejemplo,
17%3 es 2, puesto que el resto de dividir 17 por 3 es 2. Por tanto, si a%b es cero, a es mltiplo de b.
Ejemplos de expresiones aritmticas son:
(votos/electores)*100
a*x*x + b*x + c
horas*3600 + minutos*60 + segundos
(alto-bajo)/2
Las expresiones pueden contener parntesis (...) que agrupan algunos de sus trminos, como ocurre en el
primer ejemplo. Puede haber parntesis contenidos dentro de otros parntesis. El significado de los
parntesis coincide con el habitual en las expresiones matemticas. Es habitual la inclusin de espacios en
blanco para mejorar la legibilidad de las expresiones.
Operadores relacionales: operan sobre elementos de diferentes tipos construyendo expresiones de tipo
lgico. El tipo boolean no existe en C, como ya se han indicado anteriormente. En su lugar se suele usar un
entero que si es cero indica falso y en otro caso verdadero. Aunque nosotros usaremos el tipo boolean
definido como un enumerado, no debemos olvidar que en C se usa normalmente un entero en su lugar. Los
operadores relacionales en C son los siguientes:
>
<
>=
<=
==
!=
Mayor que
Menor que
Mayor o igual que
Menor o igual que
Igual que
Distinto
donde op es uno de los operadores (==, <, >, <=, >=, !=). El funcionamiento de estos operadores es el
siguiente: se evalan expresion1 y expresion2, y se comparan los valores resultantes. El resultado de la
comparacin es un valor booleano, que en C se representa por un entero. Si el valor de este entero es cero,
se considera falso y si es distinto de cero se considera cierto.
Ejemplos de expresiones relacionales son:
x >= (y+z)
contador < 10
numero%2 == 0
ordenado != cierto
Operadores Lgicos. Estos operadores operan sobre valores lgicos construyendo expresiones de tipo lgico:
Los operadores lgicos son operadores que permiten combinar los resultados de los operadores
relacionales, comprobando que se cumplen simultneamente varias condiciones, que se cumple una u otra,
etc. El lenguaje C tiene tres operadores lgicos:
&& Y
|| O
!
Negacin
Su forma general de uso es la siguiente:
expresion1 && expresion2
expresion1 || expresion2
! expresion
El operador && devuelve un valor cierto si expresion1 y expresion2 son ciertas (o distintas de 0), y falso (o 0),
en caso contrario, es decir, si una de las dos expresiones o las dos son falsas (iguales a 0). Por otra parte, el
operador || devuelve cierto si al menos una de las expresiones es cierta. Finalmente, el operador ! cambia el
valor de expresion de cierto a falso o viceversa.
Ejemplos de expresiones lgicas son:
(opcion < 5) || (opcion > 20)
(opcion >= 5) && (opcion <= 20)
! encontrado
Operadores de Asignacin. Son los operadores =, +=, -=, etc. con una semntica similar a la de Java. Los
operadores de asignacin sirven para dar valor a una variable a partir del resultado de una expresin o el
valor de otra variable (en realidad, una variable es un caso particular de una expresin).
El operador de asignacin ms utilizado es el smbolo =. Su forma general es:
nombre_de_variable = expresion;
La semntica de esta expresin es la siguiente: en primer lugar, se evala la expresion y, en segundo lugar, se
almacena el valor obtenido en la variable cuyo identificador aparece a la izquierda del smbolo igual
10
Fundamentos de Programacin
(realizndose la correspondiente conversin de tipo si es necesario). Por ejemplo, dadas dos variables x e y,
donde x vale 7 e y vale 3,
x
y
7
3
despus de la asignacin
y = x + 1;
7
8
Como se ve, el efecto de la asignacin es destructivo: cuando se asigna un nuevo valor a una variable se
pierde el valor anterior de la variable.
Hay dos errores relacionados y que son tpicos en programadores principiantes. Uno es intentar poner a la
izquierda del = una expresin en vez de una variable, y el segundo es no entender bien una asignacin
cuando intervienen slo dos variables, por ejemplo:
x = y;
En este caso, el valor que tuviera y se almacena en x, haciendo que el anterior valor de x desaparezca.
Por supuesto, la variable y no sufre ninguna modificacin.
En C todos estos operadores son operadores binarios bsicos con la misma prioridad. Son legales, aunque no
recomendables, expresiones del tipo:
a = b = c = 18;
Una expresin construida con un operador de asignacin tiene el tipo del operando izquierdo y devuelve el
valor asignado a ese operando izquierdo.
Adems de los operadores vistos hasta ahora, el lenguaje C dispone de otros operadores que veremos ms
adelante, en especial los operadores que trabajan con direcciones de memoria.
Existen otros cuatro operadores de asignacin (+=, -=, *= y /=) formados por los cuatro operadores
aritmticos seguidos por la asignacin. Estos operadores simplifican algunas operaciones recurrentes sobre
una misma variable. Su forma general es:
variable op= expresion;
donde op representa cualquiera de los operadores (+, - , *, /). La expresin anterior es equivalente a:
Precedencia y asociatividad. El resultado de una expresin depende del orden en el que se ejecutan las
operaciones. Por ejemplo, veamos la expresin 3 + 4 * 2. Si evaluamos primero la suma (3+4), y despus el
producto (7*2), el resultado es 14; sin embargo, si primero se evala el producto (4*2), y luego la suma
(3+8), el resultado es 11. Para saber en qu orden se realizan las operaciones es necesario definir unas reglas
de precedencia y asociatividad. La tabla siguiente resume las reglas de precedencia y asociatividad de los
principales operadores en el lenguaje C. Los operadores que estn en la misma lnea tienen la misma
precedencia, y las filas estn en orden de precedencia decreciente.
Operador
Asociatividad
! ++ -- - (cambio de signo)
derecha a izquierda
* / %
izquierda a derecha
+ - (diferencia)
izquierda a derecha
izquierda a derecha
== !=
izquierda a derecha
&&
izquierda a derecha
||
izquierda a derecha
Por supuesto, el orden de evaluacin puede modificarse por medio de parntesis, pues siempre se evalan
primero las operaciones encerradas en los parntesis ms interiores. En general, es recomendable el uso de
los parntesis para remarcar y documentar el orden de las operaciones.
Conversiones explcitas e implcitas de tipo. Existen conversiones automticas (implcitas) de cada uno de los
tipos al siguiente en la sucesin: char, int, long, float, double. Como en Java, una operacin que involucre
dos tipos diferentes de los anteriores se hace en dos pasos: en primer lugar se convierten ambos tipos al
mayor (en el orden anterior) y posteriormente se hace la operacin correspondiente sobre los valores de los
operandos.
Si se quiere hacer una conversin de tipo de uno mayor a otro menor hace falta usar el operador de casting
que tiene la misma forma que en Java y las mismas consecuencias.
Funciones predefinidas. En las expresiones tambin se pueden incluir llamadas a funciones, bien definidas
por el programador, bien existentes en las libreras de funciones predefinidas proporcionadas con el
11
12
Fundamentos de Programacin
compilador de C. Ejemplos de funciones proporcionadas por el compilador son las funciones matemticas o
funciones de conversin de tipos.
Las funciones matemticas usuales vienen proporcionadas en la librera math.h. Algunas de ellas son:
int abs(int num);
int rand(void);
double log(double x);
double log10(double x);
double sin(double x);
double cos(double x);
double pow(double x, double y);
double exp(double x);
double sqrt(double x);
//
//
//
//
//
//
//
//
//
valor absoluto
nmero entero aleatorio entre 0 y RAND_MAX
logaritmo neperiano
logaritmo decimal
seno, el ngulo debe estar expresado en radianes
coseno, el ngulo debe estar expresado en radianes
x elevado a y
e elevado a x
raz cuadrada
Adems se proporcionan un conjunto de funciones para convertir cadenas de caracteres con un formato
dado a valores de los tipos bsicos. Vienen en stdlib.h y algunas de ellas son:
int atoi(const char * s);
long atol(const char * s);
double atof(const char * s);
2.3. Punteros
El concepto de puntero no existe directamente en Java aunque todos los objetos (de Java) estn
implementados usndolos. Un puntero es una variable capaz de almacenar direcciones de memoria y se usa
para sealar dnde hay guardada una informacin. Una variable puede ser declarada de tipo puntero a un
tipo T. Esto se consigue posponiendo * al tipo T. El tipo puntero a T se declara como T *.
Ejemplos:
int *pint;
float *pf;
En el ejemplo anterior hemos declarado la variable pint de tipo puntero a int y pf de tipo puntero a float. Eso
quiere decir que los valores de pint sern direcciones de memoria adecuadas para sealar la direccin donde
se ubica un entero. Igualmente ocurre con pf pero respecto a un float.
En general, si tenemos un tipo T, entonces T* ser el tipo puntero a T.
Para los punteros los operadores disponibles son: * y &.
El operador & se aplica a una variable de tipo T y devuelve un valor de tipo T * (la direccin donde se
encuentra ubicada en la memoria la variable).
El operador * se aplica sobre una variable de tipo T * y devuelve un valor de tipo T (el valor contenido en la
memoria sealada por el puntero).
Al operador & se le denomina obtencin de la direccin y al operador * obtencin del contenido.
Ejemplo:
Sean dos variables enteras i y j, un puntero a entero p y un puntero a entero p2 inicializado con la direccin
de j:
int
int
int
i =
p =
j =
i, j;
* p;
// p puede guardar la direccin de una variable entera como i,j
* p2 = &j; // p2 se inicializa guardando la direccin de j
15;
&i;
// p guarda la direccin de la variable i, apunta a i
*p +7;
// j pasa a valer 7 + lo que guarda lo apuntado por p, esto es, 22
La sentencia switch evala la expresion, que debe ser obligatoriamente de tipo int o char. Si el valor de la
expresin coincide con el valor de una de las expresiones constantes de los case, se empiezan a ejecutar las
sentencias que haya hasta el final del bloque switch o hasta que se encuentre un break. Si el valor de la
13
14
Fundamentos de Programacin
expresion no coincide con ninguno de los valores de las expresiones constantes de los bloques case,
entonces se ejecuta el bloque default, si es que hay. Note que tanto el bloque default como las sentencias
break al final de cada case son opcionales.
switch (expresion) {
case expresion_constante_1:
sentencias_1;
[break;]
case expresion_constante_2:
sentencias_2;
[break;]
case expresion_constante_n:
sentencias_n;
[break;]
[default:
sentencias_m;]
}
C no incluye el for-extendido, pero se puede simular con un for clsico. A su vez cualquier sentencia for
podemos convertirla en una sentencia while. La transformacin es de la forma:
for (inicializacin; condicin; incremento) {
sentencias
}
inicializacin;
while (condicin) {
sentencias;
incremento;
}
carro. La funcin putchar escribe un carcter en la pantalla. Por ejemplo, siendo ch una variable de tipo char,
el siguiente fragmento de cdigo
ch = getchar ();
putchar (ch);
lee un carcter de la entrada estndar y lo asigna a la variable ch. Despus escribe el mismo carcter en la
salida estndar.
Entrada y salida con formato
La funcin printf. La funcin printf permite escribir una lista de datos de acuerdo con un formato
preestablecido. Acepta diferentes tipos de argumentos: carcter, valor numrico entero o real, o una cadena
de caracteres, y los escribe segn un formato especificado sobre la salida estndar. El formato de la funcin
printf es:
printf ("formato", arg1, arg2, ..., argn);
donde los argi pueden ser constantes, variables o expresiones en general, y formato es una cadena de
caracteres en la cual se pueden encontrar dos clases de datos: un mensaje o texto a escribir literalmente, y
los smbolos especificadores del formato con el cual se van a escribir las variables dadas como argumentos.
Por ejemplo,
printf ("Esto es un mensaje\n");
Escribe la cadena Esto es un mensaje seguida de un retorno de lnea (debido al '\n'). Los especificadores de
formato deben ir precedidos del carcter %. Algunos de ellos son:
%c
%d
%ld
%f
%lf
%s
Carcter (char)
Nmero entero (int)
Nmero entero largo (long)
Nmero real (float)
Nmero real (double)
Cadena de caracteres
Por cada argumento a imprimir debe existir un especificador de formato, que indica el formato en el cual se
va a mostrar el dato. Por ejemplo, siendo letra una variable de tipo char con el valor 'A', la instruccin
printf ("El cdigo ASCII de %c es %d", letra, letra);
mostrndose el mismo dato con dos formatos distintos: como carcter (%c) y como entero (%d). Obsrvese
que hay dos especificadores de formato y dos argumentos (aunque en este caso sean el mismo).
15
16
Fundamentos de Programacin
La funcin printf puede emplearse sin argumentos, en cuyo caso slo se imprimen los caracteres presentes
en la cadena de formato. Entre el smbolo % y el carcter que especifica la notacin a emplear se pueden
insertar ciertos caracteres opcionales. Son los siguientes:
El signo menos (-) para que el dato se ajuste por la izquierda, en lugar de hacerlo por la derecha, que
es lo establecido a falta de especificacin explcita.
Un nmero que indica la longitud mnima en caracteres que tiene el campo donde se imprimir el
dato correspondiente. Los espacios juegan el papel de caracteres de relleno.
Un punto decimal (.) seguido de una cifra que indica el nmero de dgitos despus del punto decimal
de un dato flotante, o el nmero mnimo de dgitos para un entero, o el nmero mximo de
caracteres de una cadena que sern impresos.
Por ejemplo:
%6d imprime un nmero entero (int) alineado por la derecha y en un campo de al menos seis
caracteres.
%-10s imprime una serie de caracteres alineados por la izquierda y asegurando una longitud mnima
de 10 caracteres.
%.4f imprime un nmero real (float) en coma flotante con un mximo de 4 cifras significativas en la
parte fraccionaria.
La funcin scanf. La funcin scanf permite leer valores desde la entrada estndar (teclado) y almacenarlos en
las variables que se especifican como argumentos. stas deben ir normalmente precedidas por el smbolo &.
El smbolo & es un operador unario que significa direccin de. La sintaxis de scanf es:
scanf ("formato", arg1, arg2, ..., argn);
donde debe haber tantos especificadores en la cadena de formato como variables en la lista de argumentos.
Por ejemplo,
scanf ("%d%lf", &n, &x);
para leer un valor n de tipo int y un valor x de tipo double. Estos valores se introducirn desde la entrada
estndar separndolos por espacios o retornos de carro.
En la cadena de formato aparecen especificadores de formato, que son los mismos que se han visto para
printf pero, a diferencia de lo que ocurre con ste, nunca deben aparecer caracteres que no sean formatos.
La variable v es una tabla de 10 enteros. La variable palabra es una tabla que tiene tantos caracteres como
indique la constante TAM_CADENA. La variable a es una tabla de 3 enteros.
Asociado a una tabla vamos a definir dos conceptos que no existen en C, pero que es conveniente usarlos
para hacer una programacin ms segura.
Tamao de una tabla: es el nmero de elementos que tiene. El tamao de una tabla no se puede
modificar. Cada vez que declaremos una tabla declararemos una constante que guardar el tamao de la
tabla. La constante tendr como identificador TAM_NOMBRE, donde NOMBRE es el identificador de la
tabla.
Longitud: es el nmero de elementos tiles de la tabla. Esta longitud s puede variar. En muchos
programas en C usaremos tablas que no estn completamente llenas. En este caso declararemos una
variable con identificador nombreLength que guarde el nmero de elementos tiles. Debe cumplirse que
nombreLength <= TAM_NOMBRE y ambas son mayores o iguales a cero.
Declaracin e inicializacin de los elementos de una tabla: cuando declaramos una tabla se reserva memoria
para ubicar cada uno de sus elementos. Pero estos elementos no estn inicializados y por lo tanto si los
consultamos obtenemos, en general, un valor no definido. Igual que para las dems variables, los elementos
de una tabla deben de ser inicializados antes de ser consultados. Mediante la sentencia T b[] = {v1,v2,,vn}
17
18
Fundamentos de Programacin
es posible declarar la tabla b, de tipo T y tamao n, e inicializar sus elementos con los valores indicados entre
llaves {}. El primer valor, v1, quedar ubicado en la casilla de ndice 0 y as sucesivamente.
Acceso a los elementos de una tabla: se realiza mediante el operador corchete ([]), y un nmero entero que
representa un ndice. Si i es un ndice, entonces se debe cumplir 0 <= i < TAM_NOMBRE y si la tabla no est
completamente llena entonces 0 <= i < nombreLength. Si no se cumplen esas restricciones los programas
que construyamos funcionarn mal y el error ser difcil de detectar.
Modificacin de los elementos de una tabla: se realiza usando nmeros enteros como ndices dentro del
operador [], como para la consulta, pero ubicando la expresin en la parte izquierda de un operador de
asignacin.
Ejemplos:
#define TAM_V 10
int v[TAM_V];
int a[] = {2, -3, 7};
int aTam = 3;
a[2] = a[1] + 2;
// la tabla a ha quedado modificada a {2, -3, -1}
v[4] = v[3];
// incorrecto: la casilla v[3] se est usando antes de inicializarse
v[14] = 12;
// incorrecto pero no es detectado por el compilador
En el ejemplo anterior se declara la tabla v pero no se inicializan sus elementos. La tabla a es declarada e
inicializada.
Un buen programa (y por tanto un buen programador) debe definir previamente la constante de la
dimensin del array y un tipo que permita trabajar con la tabla, de forma que, cada vez que vayamos a
trabajar con una tabla, procederemos declarando la constante y el tipo para manejar el array en el
correspondiente archivo .h, de la siguiente forma:
#define TAM_VECTOR3D 3
Una variable de tipo Vector3D se puede inicializar igual que cualquier tabla, por ejemplo:
void main (void){
Vector3D punto3D = {1.1, 3.2, -4.5};
#define TAM_V 20
T v[TAM_V];
T * p = v;
&v[0]
v+i
v+i
v[i]
*(p+i)
v[i]
*(v+i)
Como vemos, el nombre de una tabla es esencialmente un puntero, pero constante. Es decir, no puede ser
colocado a la izquierda de un operador de asignacin. Por otra parte, a cualquier puntero es posible sumarle
un entero i (tambin restarle). Esa operacin nos da otro puntero que seala a la casilla i de una tabla. Estas
operaciones se denominan aritmtica de punteros.
El operador [], denominado operador de indexacin, puede colocarse detrs de un nombre de tabla o de un
puntero. En general si p es un puntero, lo que incluye al identificador de una tabla, entonces p[i] es
equivalente a *(p+i) para cualquier i. Ambas expresiones pueden ser usadas indistintamente a la derecha de
un operador de asignacin, para la consulta del contenido del elemento i, o a la izquierda del mismo, para la
modificacin del elemento i.
5.2. Cadenas de caracteres
El lenguaje C no tiene definido un tipo equivalente al tipo String de Java para almacenar cadenas de
caracteres. En C, una cadena de caracteres es una tabla de char donde los elementos tiles van desde la
casilla 0 hasta la que contenga el carcter `\0`. Este carcter se conoce como carcter de fin de cadena. Las
funciones sobre cadenas de caracteres funcionan mal si usamos cadenas sin el carcter final `\0`. Las
constantes de tipo cadena son secuencias de caracteres entre comillas. En el fichero string.h se definen las
funciones sobre cadenas proporcionadas en la librera estndar de C.
Definimos los tipos Cadena y PChar tal como vemos abajo. El tipo PChar es equivalente a char * y, por lo
tanto, al ser declarado no reserva espacio para los caracteres. El tipo Cadena, por el contrario, es una tabla
de caracteres de tamao TAM_CADENA.
#define TAM_CADENA 256
typedef char Cadena[TAM_CADENA];
typedef char * PChar;
Estas declaraciones las incluimos en el fichero tipos.h junto con otros tipos que puedan ser de inters como
el tipo boolean visto ms arriba.
19
20
Fundamentos de Programacin
Los prototipos de las funciones de manejo de cadenas de caracteres estn en string.h. Las ms importantes
son las siguientes:
int strlen (const char * s);
char * strchr(const char * s, char c);
char * strstr(const char * s1, const char * s2);
char * strcpy (char * s1, const char * s2);
int strcmp(const char * s1, const char * s2) ;
char * strcat(char * s1, const char * s2);
Como puede observar, algunos de los parmetros de estas funciones van precedidos por la palabra
reservada const. Esto indica que estos parmetros son parmetros de entrada, mientras que los que no
llevan son de salida. Una explicacin ms detallada de estas cuestiones se ve en el apartado Funciones y
procedimientos: paso de parmetros. La semntica de las funciones anteriores es la siguiente:
strlen
strchr
strstr
strcpy
strcmp
strcat
A la hora de usar estas funciones hay que hacer algunas consideraciones. En las funciones anteriores los
parmetros formales con la etiqueta const son parmetros de entrada y no van a ser modificados. Los que
no tienen la etiqueta const s sern modificados. As ocurre con s1 en strcpy y strcat. Como s1 va a ser
modificado, hay que tener en cuenta que debe apuntar a una zona de memoria, previamente reservada, con
tamao suficiente. En el caso de strcpy para ubicar la cadena s2 (junto con el carcter final \0) y en el caso
de strcat para ubicar a s1 seguida de s2.
Finalmente, recordemos algunos detalles importantes de implementacina la hora de trabajar con cadenas:
Las cadenas en C acaban siempre con el carcter \0.
El nmero de caracteres tiles de la cadena es el devuelto por strlen, pero la cadena necesita, al
menos, un carcter ms de memoria para ubicar el \0 final. Por lo tanto, para una cadena s dada, el
tamao de la tabla de caracteres donde se ubique la cadena debe cumplir que TAM_CADENA >
strlen(s).
Algunos ejemplos del uso de cadenas de caracteres son:
int i1, i2;
Cadena s1 = Hola;
Cadena s2 = Mundo;
PChar s3;
i1 = strlen(s1);
s3 = strcat(s1,s2);
i2 = strlen(s3);
El resultado para i1 es 4 y para i2 es 10. Hemos tenido en cuenta que los tamaos de las tablas de caracteres
(256) donde se ubican s1, s2 (antes y despus de la concatenacin) cumplen la restriccin anterior.
Por otra parte, hemos de tener en cuenta que el tipo Cadena define una tabla de caracteres cuyo
identificador, como en todas las tablas, es un puntero constante y, por lo tanto, no puede estar en la parte
izquierda de una asignacin. As, la sentencia siguiente sera incorrecta:
s1 = strcat(s1,s2);
Tambin hemos de tener en cuenta que una variable de tipo PChar puede ser inicializada con la direccin de
una cadena constante pero no puede modificarla.
PChar nn1 = "Nuevo Mundo";
Cadena s1 = Hola; // La cadena constante se copia en la memoria de s1
nn1[3] = '5';
// produce un error porque intenta modificar la cadena constante
s1[3] = '5';
// correcto
Una variable de un tipo construido mediante un struct puede inicializarse con un conjunto de valores para
los campos, separados por comas y delimitados por llaves:
Tr r = {v1, v2, ..., vn};
PTr pr = &r;
21
22
Fundamentos de Programacin
} Punto;
typedef Punto * PPunto;
Punto p = {2.0,3.1};
PPunto pp = &p;
Arriba definimos el tipo Punto a partir de un struct y el tipo PPunto como un puntero a la estructura Punto.
Tambin se muestra la forma de inicializar variables de estos tipos.
Operadores de acceso a campos: hay disponibles dos operadores para acceder a los campos de un tipo struct
o puntero a struct. Son los operadores punto ( . ) y flecha (->), respectivamente. El primero se usa para
acceder a los campos de una variable de tipo struct. El segundo se usa para acceder a los campos de una
variable de tipo puntero a struct. Si tenemos la declaracin de los tipos Punto y PPunto vista arriba y las
declaraciones de las variables p y pp, el acceso a los campos se har tal como sigue:
Es decir, si tenemos una variable de tipo puntero a struct y queremos acceder a un campo, usamos el
operador flecha (->). Si la variable es de tipo struct usamos el operador punto (.).
Las expresiones construidas con estos operadores (. y ->) tienen el tipo del campo correspondiente y pueden
ser colocadas a la derecha o a la izquierda de un operador de asignacin. Si se colocan a la derecha, estamos
consultando el valor del campo. Si se colocan a la izquierda, estamos modificando su valor.
Ejemplos de uso de estos operadores son:
pp->x = pp->y+18.0;
(*pp).x = (*pp).y+18.0;
o equivalentemente
extensin .c. Usualmente, dentro de la cultura del C, a las funciones que devuelven void se les llama
procedimientos.
Las funciones permiten la reutilizacin de cdigo. Las funciones junto con su agrupacin en ficheros son los
mecanismos de modularizacin del cdigo en C. Son mecanismos primitivos, si los comparamos con los
disponibles en Java: interfaces, clases y paquetes.
Ejemplos de prototipos de funciones son:
double getXPunto(const Punto p);
void setXPunto(Punto p, double x);
double getYPunto(const Punto p);
void setYPunto(Punto p, double y);
La estructura del cuerpo de una funcin EXIGE declarar las variables al principio como sigue:
Tr nombre (T1 e1, ..., Tn en) {
Declaracin de variables locales
Sentencias
return r;
// no hay return si la funcin devuelve void.
}
Un programa en C se compone de un conjunto de ficheros con extensin .h y otro conjunto con extensin .c.
Los primeros son ficheros de declaraciones e incluyen lneas de #include (para incluir otros ficheros .h), lneas
#define (que definen macros en general y constantes en particular), declaraciones de tipos (mediante
typedef) y prototipos de funciones. Los ficheros .c contienen lneas #include (para inclusin de ficheros .h) y
cdigo de funciones (cabecera ms cdigo).
Los ficheros con extensin .h suelen tener una estructura particular para facilitar su uso. As el fichero tipos.h
tiene la estructura:
#ifndef TIPOS_H_
#define TIPOS_H_
typedef enum{falso,cierto} boolean;
#endif /* TIPOS_H_ */
El objetivo es definir una variable similar al nombre del fichero, en este caso TIPOS_H_, que nos sirve para
conseguir que el fichero se incluya una sola vez. Esto se consigue por el funcionamiento de las clusulas del
preprocesador #ifndef, #define, #endif. La primera decide si se ha definido antes la variable. Si no se ha
definido, expande el fichero hasta #endif. La primera lnea define la variable que no estaba definida. Si ya
est definida, porque el fichero se haba incluido previamente en otro lugar, el preprocesador se salta todo
el contenido del fichero hasta #endif.
Un programa en C es, por lo tanto, un conjunto de funciones. Como no existe sobrecarga todas deben tener
nombres diferentes. De todas ellas, hay una nica funcin distinguida cuyo nombre es main. La llamada a
esta funcin es el comienzo del programa.
23
24
Fundamentos de Programacin
La llamada a una funcin es como en Java salvo que en C no se invoca sobre ningn objeto. Igualmente la
invocacin de funcin puede formar una sentencia bsica (si la funcin devuelve void) o formar parte de una
expresin si devuelve un valor. El concepto de parmetros formales y parmetros reales es igual que en Java.
Ejemplos:
double x;
Punto pd = {3.0,4.5};
PPunto p = &pd;
x = getXPunto(p);
setYPunto(p,3.0);
int x = 5, y = 8, z = 10;
double r;
intercambio (&x, &y);
printf(%d %d\n,x,y);
r = mediaAritmetica(x,y,z);
printf(%d %f\n,x,r);
}
En este caso, el compilador nos avisara de que estamos intentando modificar un valor declarado constante
cuando compila la sentencia t[0]++ en en cuerpo del mtodo prueba_array.
25
26
Fundamentos de Programacin
La funcin fopen conecta una variable de tipo fichero con el archivo correspondiente en el sistema
operativo. Asimismo, debe indicar si el fichero se abre en modo lectura (para obtener datos de l) o en modo
escritura (para guardar datos). Por tanto, la funcin fopen recibe dos argumentos de tipo cadena de
caracteres. El primero es el nombre del archivo en el sistema de archivos, incluyendo si fuera necesario la
ruta. El segundo argumento es el modo de apertura. En este tema slo vamos a estudiar los tipos de lectura
r, escritura w o aadir a1. La funcin fopen devuelve una variable de tipo FILE * que es la variable que
se conecta con el archivo cuyo nombre es el primer argumento. Si el fichero nombreFichero no existe en el
sistema de fichero la funcin anterior devuelve NULL. Veamos algunos ejemplos de apertura de ficheros:
fich
fich
fich
fich
=
=
=
=
fopen("datos.txt","r");
fopen("resultados.dat", "w");
fopen(".\\Debug\\datos.dat","r");
fopen("..\\..\\datos.dat","a");
En el primer ejemplo la variable fich representa al archivo datos.txt, que se abre en modo lectura, es decir,
para extraer informacin de l. Cuando se abre un fichero en modo r el fichero debe existir previamente
en el sistema de archivos; si no la variable fich tomar el valor NULL. En este primer caso, el argumento con
el nombre del archivo no lleva ruta; por tanto, el archivo deber estar en la misma carpeta (o directorio) que
el fichero fuente .c que contiene la sentencia.
En el segundo ejemplo, el archivo resultados.dat se abre para escritura, de forma que si no existe, se crea, y
si existiera, se borra y se crea nuevo. Igual que en el caso anterior, al no contener ruta la cadena del primer
argumento, el archivo se crea en la misma carpeta en la que est el archivo fuente.
En el tercer ejemplo, el archivo datos.dat debe estar en una carpeta de nombre Debug situada en el
directorio de trabajo. Como la apertura es en modo lectura, el archivo debe existir.
Finalmente, en el ltimo ejemplo, el archivo datos.dat se sita dos carpetas por arriba del directorio de
trabajo. El archivo se abre para aadir, por tanto, se escribir a continuacin de la ltima lnea. Si el archivo
no existiera se creara en blanco.
Ntese que aunque el modo de apertura slo es un carcter (a, w r), se escribe entre dobles comillas
porque el compilador espera una cadena de caracteres, no un tipo char.
Una vez que se termina de trabajar con un fichero, es importante cerrarlo. Para cerrar un fichero la
sentencia es nica independientemente del modo de apertura.
int fclose(FILE *fp);
La funcin fclose recibe como argumento la variable de tipo FILE *, y desconecta el archivo fsico de la
variable. Es importante cerrar el fichero, sobre todo cuando se ha abierto en modo escritura o aadir porque
Estos no son los nicos modos de apertura. En C tambin estn los modos r+, w+ y a+ que permiten realizar en
un mismo fichero lectura y escritura. Adems de que son muy engorrosos de utilizacin, tienen poca utilidad en
ficheros de texto.
Igualmente se puede enviar el resultado a un fichero mediante la funcin fprintf, cuyo formato es el
siguiente:
fprintf (f,formato, arg1, arg2, , argn);
Ambas escriben una lnea (en la consola o en el fichero f) y retornan un valor positivo si no ha habido error.
Entrada con formato: scanf, sscanf, fscanf
Las funciones de entrada hacen el trabajo inverso a las de salida: convertir una cadena, procedente de la
consola, de una cadena dada o de un fichero, en una secuencia de valores cuyos tipos y estructura est
especificada por un formato. La funcin scanf permite leer valores desde la entrada estndar y almacenarlos
en las variables que se especifican como argumentos, y se vi en el apartado Funciones para trabajar con la
entrada y salida estndar. Las funciones sscanf y fscanf hacen lo mismo, pero en lugar de tomar como origen
la consola, lo toman de una cadena de caracteres dada o de un fichero. El formato de estas funciones es el
siguiente:
scanf ("formato", arg1, arg2, ..., argn);
sscanf(s, "formato", arg1, arg2, ..., argn);
fscanf(f, "formato", arg1, arg2, ..., argn);
27
28
Fundamentos de Programacin
La funcin fgets lee un mximo de n-1 caracteres o hasta que encuentra un fin de lnea del fichero f,
poniendo el resultado en la cadena s. El puntero s debe sealar a una zona con suficiente memoria reservada
para ubicar la lnea leda. Regresa un puntero a la cadena leda o NULL si ha encontrado el fin de fichero.
Similarmente funciona gets pero leyendo una lnea de la consola.
Comprobacin de apertura de un fichero
Una vez invocada la funcin fopen con el argumento r, lo primero que se debe hacer es comprobar si la
apertura se ha realizado correctamente, es decir, si la variable fichero tiene valor null o no. Un valor de null
en una variable de tipo FILE * despus de la apertura para lectura indica que el fichero no existe en la
carpeta donde se le esperaba. Si es en modo escritura, el valor de null indicara disco lleno o protegido
contra escritura.
Por tanto, cada vez que trabajemos con un fichero, es necesario comprobar que la variable fichero tiene un
valor vlido, mediante una sentencia if, de la siguiente forma:
f=fopen(nomfich,"r"); //nomfich de tipo array de char o Cadena
if (f==NULL) {
printf("El fichero %s no existe", nomfich);
}else {
// tratamiento del fichero
}
Es decir, en primer lugar, hay un nmero entero y despus, tantos valores reales como indique el primer
nmero. Se desea construir una funcin que reciba como argumento de entrada una cadena de caracteres
con el nombre de un fichero y devuelva como salida un array de reales con los valores contenidos en la
segunda lnea del fichero de texto, y en el nombre de la funcin debe devolver el tamao real del array, esto
es el valor de la primera lnea. Su cdigo sera:
int lecturaFichero(const Cadena nomfich, TablaReales v, int tamTablaReales) {
FILE* f;
int numelem = -1, i;
f = fopen(nomfich,"r");
if(f==NULL) {
printf("El fichero %s no existe", nomfich);
}else {
fscanf(f,"%d",&numelem);
if (numelem > tamTablaReales) {
printf(El numero de elementos es mayor que el tamao de la tabla);
} else {
for(i=0; i<numelem && ! feof(f); i++){
fscanf(f,"%f",&v[i]);
}//end-for
}//end-else
}//end-else
return numelem;
}
Ntese cmo las invocaciones a fscanf son similares a si quisiramos leer desde teclado con la funcin scanf.
Esto es, el formato de las variables a leer es %d para int y %f para float. Igualmente es necesario un &
delante de las variables a leer y por eso se escribe &numelem y &v[i].
Ejemplo 2:
Supongamos tenemos un fichero de texto como el del Ejemplo 1, pero sin el primer nmero entero, sino
solamente con un nmero indeterminado de valores reales. Queremos construir una funcin que reciba una
cadena de caracteres con el nombre de un fichero y devuelva un array con todos los valores reales ledos del
fichero. Como es habitual, la funcin deber devolver el nmero de elementos que hay en el array.
int lectura (const Cadena nomFich, TablaReales v, int tamTablaReales) {
FILE *f;
int i = 0;
float x;
f = fopen(nomFich,"r");
if(f==NULL)
printf("El fichero no existe");
else{
fscanf(f,"%f",&x);
while (!feof(f) && i < tamTablaReales){
v[i] = x;
i++;
fscanf(f,"%f",&x);
}
29
30
Fundamentos de Programacin
fclose(f);
}
return i;
}
Ntese como hay una lectura antes del bucle while. Si el fichero estuviera vaco, esta lectura activara la
funcin feof, que devolvera cierto y, entonces, el bucle no se ejecutara ni una sola vez, devolviendo i con un
valor cero. Si el fichero contiene algn elemento, entonces la primera lectura guarda en x el valor ledo, feof
devuelve falso y, como la condicin del while est puesta con el operador lgico No, entonces !feof(f)
devuelve cierto y el flujo del programa entrara en el bucle (ya que i es menor que el tamao de la tabla).
Note que en la condicin de while, adems de comprobar que no se ha llegado al final del fichero, tambin
se comprueba que hay espacio reservado para almacenar el elemento ledo del fichero, gracias a la
condicin i<tamTablaReales. Dentro del bucle el valor x ledo fuera se asignara a la primera posicin del
vector v (i est inicializada a 0), se incrementara el contador i en uno y se volvera a leer la misma variable x,
de forma que si el fichero tena ms de un valor, volvera a la condicin del while que seguira siendo cierta,
la guardara en la siguiente posicin del array y as sucesivamente. Cuando se lee el ltimo dato, todo
continua igual, esto es, la condicin sigue siendo cierta, se guarda en v, se incrementa la i, pero cuando se va
a volver a leer, se lee la marca de fin de fichero. En este caso, feof devuelve cierto.
8. Ejercicios propuestos
BLOQUE 1 Definiciones de constantes, tipos y variables. Funciones. Estructura de un programa. Punteros
Ejercicio 1
Defina las siguientes constantes:
a)
b)
c)
d)
e)
f)
Ejercicio 2
Defina los siguientes tipos de datos:
a)
b)
c)
d)
e)
f)
g)
h)
Ejercicio 3
Defina las siguientes variables:
a)
b)
c)
d)
e)
f)
g)
h)
i)
j)
k)
l)
Ejercicio 4
Implemente las siguientes funciones:
a)
Una funcin que dado un nmero entero, devuelva cierto si es par. Para probarla escriba una funcin main que
muestre por la salida estndar un mensaje indicando si un nmero es par o impar.
b) Una funcin que dado un nmero real que representa el radio del crculo, devuelva su rea. Pruebe que la funcin
est bien implementada usando una funcin main. Defina el valor de como una constante.
c)
Una funcin que calcule el factorial de un nmero. Pruebe la funcin factorial en una funcin main.
d) Una funcin que dados tres enteros que representan las horas, minutos y segundos de un instante de tiempo,
devuelva el nmero de segundos transcurridos desde las cero horas hasta dicho instante de tiempo. Utilice la
frmula:
segundos_transcurridos = horas * 3600 + minutos * 60 + segundos
Pruebe que la funcin est bien implementada en una funcin main.
e)
Una funcin que dada una nota expresada en forma numrica devuelva uno de los siguientes valores, definidos en
un enumerado: SUSPENSO, APROBADO, NOTABLE, SOBRESALIENTE, VALOR_INCORRECTO. La correspondencia de
los valores numricos es la siguiente: 0 nota < 5 = SUSPENSO; 5 nota < 7 = APROBADO; 7 nota < 9 = NOTABLE;
y 9 nota 10 = SOBRESALIENTE. Cualquier otro valor numrico se corresponder con VALOR INCORRECTO.
Pruebe la funcin en un main.
f)
Una funcin que dado un carcter, indique su tipo mediante un enumerado. El enumerado podr tomar los
valores: MAYUSCULA (de la 'A' a la 'Z'), MINUSCULA (de la 'a' a la 'z'), DIGITO (del '0' al '9'), OTRO. Pruebe la funcin
en un main.
g)
Una funcin que devuelva cierto si un nmero es mltiplo de otro. Pruebe la funcin en una funcin main.
31
32
Fundamentos de Programacin
Ejercicio 6
En un archivo cadenas.c escriba las siguientes funciones:
a) Una funcin que dado un carcter devuelva cierto si dicho carcter es una vocal.
b) Una funcin que dada una cadena de caracteres devuelva el nmero de vocales que hay en la cadena.
c)
Una funcin que devuelva cierto si una palabra es un palndromo. Las palabras palndromos son aquellas que
se leen igual de izquierda a derecha que de derecha a izquierda. Por ejemplo, ana o reconocer seran
palndromos.
d) Una funcin que dado un vector de cadenas de caracteres y una cadena, devuelva cierto si la cadena est
incluida en el vector.
Escriba tambin los ficheros de cabecera (.h) que considere necesarios, as como, un fichero main.c en el que incluya
una funcin main para probar las funciones anteriores.
Ejercicio 7
Implemente una funcin que dado un array de enteros y la longitud del mismo, permita mediante parmetros de
entrada/salida el clculo a la vez de la suma, el mximo, el mnimo y la media de los valores de un array de enteros
dado. El prototipo de la funcin es:
void calculaSumMaxMinMed(const Vector, int, IntP, IntP, IntP, IntP, DoubleP);
Realice tambin las definiciones de los tipos Vector, IntP y DoubleP.
Ejercicio 8
Implemente la funcin con prototipo:
void leerDatos(Vector a, int aTam, IntP aLength);
donde a es el array a inicializar, aTam es el tamao mximo del array y aLength es el nmero real de valores que tiene
el array. Realice tambin un programa donde se utilice la funcin anterior para leer los valores del array desde la
entrada estndar.
Aada la definicin de un tipo enumerado que contenga los nombres de los cuatro cuadrantes y la palabra Ejes
para aquellos puntos que estn sobre los ejes.
iv.
v.
vi.
Escriba un fichero pruebaPunto.c que contenga un mtodo main donde se invoquen todos los mtodos
anteriores y se impriman los resultados en pantalla.
g)
Intente imprimir por pantalla el resultado de invocar el mtodo cuadrante. Qu especificador de formato
habr que usar?
h) Implemente un nuevo mtodo que devuelva una cadena de texto a partir de un parmetro de tipo Cuadrante,
con el siguiente prototipo:
void obtenerCadenaCuadrante (Cuadrante c, Cadena cad);
Ejercicio 10
Realice los siguientes apartados para poder trabajar con el tipo Album en C:
a)
Escriba en un fichero album.h las definiciones de tipos y estructuras necesarias para trabajar con los siguientes
tipos:
i. Cancion: ttulo, intrprete, duracin, ao
ii. Album: nombre, gnero musical, array de canciones
Teniendo en cuenta que:
iii. Un lbum tendr un mximo de 100 canciones.
iv. El gnero musical de un lbum debe ser uno de los siguientes: pop, rock, jazz, salsa, flamenco.
b) Defina tambin el tipo AlbumP como un tipo puntero del tipo Album.
c)
Escriba un fichero pruebaAlbum.c que contenga un mtodo main donde se invoquen todos los mtodos anteriores
y se impriman los resultados en pantalla.
33
34
Fundamentos de Programacin
Defina un tipo para almacenar los puntos que conforman los vrtices de un polgono de a lo sumo 20 vrtices.
d) Escriba slo el prototipo de dos funciones distintas tal que cada una de ellas modifique las dos coordenadas de
un punto con dos valores que se pasan como argumentos. En una versin el punto modificado se devolver
como valor de retorno y en la otra versin el punto ser pasado como argumento o parmetro de
entrada/salida. Suponiendo definido el punto p, haga una invocacin a cada una de las dos posibilidades
cambiando el punto p al origen de coordenadas.
e) Escriba el cdigo de una funcin leePunto para leer desde teclado las coordenadas de un punto y devolverlo
como argumento de salida.
f)
Usando la funcin anterior escriba el cdigo de una funcin leePoligono que lea los vrtices de un polgono. El
nmero de vrtices se leer tambin por teclado en la misma funcin.
g)
Escriba un programa principal para probar leePoligono, mostrando el conjunto de vrtices ledos por pantalla
con el siguiente formato:
Las coordenadas del vrtice 1 son (3.2,4.7)
Las coordenadas del vrtice 2 son (-1.2,3.6)
Ejercicio 12
a) Defina los siguientes tipos:
i.
Un tipo que represente un valor lgico.
ii.
Un tipo que represente un jugador de baloncesto, cuyas propiedades son:
- Nombre, de tipo cadena de caracteres.
- Dorsal, de tipo entero.
- Posicin, de tipo enumerado con los valores BASE, ESCOLTA, ALERO, PIVOT, ALA-PIVOT.
- Promedio de Rebotes, de tipo real.
- Promedio de Puntos, de tipo real.
Si para el tipo jugador de baloncesto necesita algn tipo auxiliar, defnalo tambin.
iii.
Un tipo que represente un puntero a un jugador
iv.
Un tipo que represente un array de jugadores que tenga como mximo 50 jugadores.
b) Escriba una funcin que pida los datos de un jugador por la consola, y devuelva el jugador en un parmetro de
salida.
c)
Escriba slo el prototipo de un mtodo que sirva para leer un array de Jugadores por consola.
d) Un mtodo que dado un array de jugadores y un entero que representa la longitud de ese array, muestre por
consola el contenido de ese array con el siguiente formato:
Pau Gasol, 16, ALA-PIVOT, 11.3 Rebotes, 18.3 Puntos
Marc Gasol, 33, PIVOT, 9.3 Rebotes, 14.6 Puntos
e) Escriba una funcin main en la que declare un array de jugadores, y usando las funciones definidas en los
apartados anteriores pida por consola los datos de los jugadores, los almacene en el array y los muestre por
consola.
Ejercicio 13
a)
c)
Escriba la cabecera de un mtodo que permita modificar los goles a favor, goles en contra y puntos de un
equipo. Realice una invocacin a dicho mtodo a partir de una variable de tipo Equipo y tres variables de tipo
int.
d) Implemente un mtodo que permita leer desde la entrada estndar un array de Cadena con los nombres de
los jugadores y devolverlo como parmetro de salida. Su cabecera debe ser: void leeJugadores(...). Utilice el
tipo ArrayCad del apartado primero, y tenga en cuenta que el mximo nmero de jugadores para un equipo
ser MAXJUG.
e) Implemente un mtodo que permita leer desde la entrada estndar un Equipo y lo devuelva como parmetro
de salida. Su cabecera debe ser: void leeEquipo(). Utilice para su implementacin el mtodo anterior y el tipo
Equipo.
Ejercicio 14
a) Defina un tipo Producto con las siguientes propiedades: identificador, de tipo cadena; nombre, de tipo cadena;
precio, de tipo real; unidades, de tipo entero; stock_minimo, de tipo entero. Defina tambin los siguientes
tipos: un tipo puntero a Producto y un tipo vector de Producto capaz de almacenar un nmero de productos
determinado por una constante. Defina los tipos auxiliares que sean necesarios.
b) Implemente una funcin que lea desde teclado los datos necesarios para un Producto.Una funcin que, a partir
del nombre de un fichero de texto, lea los datos necesarios para cargarlos en un vector de productos. El
formato de las lneas del fichero ser similar al del ejercicio 1 (sustituyendo las comas por blancos). La funcin
devolver un entero con el nmero de productos ledos o -1 si no pudo abrir el fichero.
c)
Implemente una funcin que devuelva el valor comercial (nmero de unidades multiplicado por el precio) de
todos los productos de un array.
d) Escriba slo el prototipo de una funcin que, dado un vector de productos, devuelva otro con todos los
productos cuyo nmero de unidades es menor al stock mnimo.
e) Escriba slo el prototipo de una funcin que, dado un array de productos, devuelva el de mayor precio y su
posicin.
f)
Para cada una de las funciones anteriores (tanto si se solicita el cdigo completo como slo el prototipo)
escriba una invocacin a la misma en un programa principal suponiendo definidas las siguientes variables:
VectorProducto v1, v2, v3;
Producto p1, p2, p3;
int a, b, c;
double x, y, z;
Cadena s1, s2, s3;
Ejercicio 15
a)
35
36
Fundamentos de Programacin
b) Escriba una funcin que reciba el nombre de un fichero que contiene los datos de una persona en cada lnea
separados por espacios (suponemos que en los nombres y apellidos no hay espacios) y que devuelva un vector
con los datos de esas personas y el nmero de lneas ledas, o -1 si no se pudo abrir el fichero.
c)
d) Escriba una funcin que dado un VectorPersonas y un entero d, devuelva las personas de edad mayor o igual a
d.
e) Escriba una funcin que dado un VectorPersonas devuelva la Persona de mayor edad. Si hay varias empatadas
a mayor edad deber devolver la primera de ellas.
Ejercicio 16
a)
Escriba el contenido de un fichero episodio.h en el que se incluya la definicin de los siguientes tipos:
- Un tipo Logico.
- Un tipo Cadena, de tamao determinado por una constante.
- El tipo Episodio con las siguientes propiedades: nomenclatura, de tipo Cadena; ttulo, de tipo Cadena;
subtitulado, de tipo Logico.
- El tipo EpisodioP como un puntero a Episodio.
- El tipo VectorEpisodio como un array de Episodio de un tamao determinado por una constante.
b) Escriba una funcin que reciba un VectorEpisodio, el nmero real de episodios que tiene el vector y un nombre
de fichero y escriba en ese fichero el contenido del vector con el formato nomenclatura-titulo-carcter, donde
carcter ser V si el episodio est subtitulado, y F en caso contrario.
c)
Escriba una funcin que lea por teclado los datos de un episodio y devuelva el episodio ledo.
d) Suponiendo definida una funcin que lee los datos de un conjunto de episodios por teclado y los almacena en
un vector de episodios, escriba el cdigo de una funcin main para leer por teclado un conjunto de episodios y
almacenarlos en un fichero llamado episodios.txt utilizando una de las funciones creadas anteriormente. La
cabecera de la funcin para leer los datos es: void leeEpisodios(VectorEpisodio , int *);
e) Escriba una funcin que dado un VectorEpisodio, devuelva cuntos episodios tienen subttulos.
Ejercicio 17
a)
Escriba el contenido de un fichero servicio.h en el que se incluya la definicin de los siguientes tipos:
- Un tipo Cadena, de tamao determinado por una constante previamente definida con valor 256.
- El tipo Servicio, con las siguientes propiedades:nombre, de tipo Cadena; categora, de tipo Cadena;
minAsistentes de tipo enterp; temporada, array de dos dimensiones de tipo entero
- El tipo ServicioP como un puntero a Servicio.
- El tipo VectorServicio como un array de Servicio de un tamao determinado por una constante
previamente definida con valor 100.
- El tipo Menu, con las siguientes propiedades: nombre, de tipo Cadena; numServicios, de tipo entero;
servicios, de tipo VectorServicio.
- El tipo MenuP como un puntero a Menu.
b) Escriba una funcin que lea por teclado los datos de un servicio y lo devuelva como parmetro de salida.
c)
Escriba una funcin que lea por teclado los datos de un men y lo devuelva como parmetro de salida.
d) Escriba una funcin main que lea por teclado un men y muestre en pantalla el nombre de cada servicio.
e) Escriba una funcin que dado un men devuelva el servicio ms caro como parmetro de salida.