Professional Documents
Culture Documents
Sistemas Operativos
INTRODUCCIÓN A LA
PROGRAMACIÓN EN
LENGUAJE C
GUÍA BÁSICA PARA PRINCIPIANTES V. 1.0
Universidad de Valladolid
1 de noviembre de 2010
Introducción a la programación en C by CTG © Página 1
ÍNDICE DE CONTENIDOS
0. NOTA DEL AUTOR ................................................ 4
1. INTRODUCCIÓN .................................................... 6
2. VARIABLES .......................................................... 8
3. OPERADORES ..................................................... 12
4. OPERACIONES E/S.............................................. 18
5. CONTROL DE FLUJO ........................................... 22
6. FUNCIONES ........................................................ 28
7. VECTORES ......................................................... 34
8. PUNTEROS .......................................................... 42
9. ESTRUCTURA DE DATOS .................................... 46
10. PREPROCESADOR................................................ 52
11. FICHEROS ........................................................... 54
12. HERRAMIENTAS ................................................. 58
APÉNDICE A ............................................................. 60
APÉNDICE B.............................................................. 62
APÉNDICE C ............................................................. 64
APÉNDICE D ............................................................. 66
APÉNDICE E.............................................................. 68
APÉNDICE F .............................................................. 70
APÉNDICE G ............................................................. 72
APÉNDICE H ............................................................. 76
BIBLIOGRAFÍA .......................................................... 82
# man fprintf
Realice los guiones propuestos, y no pase al siguiente apartado sin haber respondido
las preguntas y realizado los ejercicios.
1983, ANSI (American National Standars Institute) estableció un comité para crear
una definición no ambigua del lenguaje C e independiente de la máquina que pudiera
utilizarse en todos los tipos de C.
El primer programa
Una opción de uso frecuente es la –l que seguida por una letra indica al compilador aquellas
bibliotecas de funciones que deben ser incluidas, cuando estás no son la estándar. Es habitual
usar en programas funciones matemáticas como seno, coseno, potencias, etc. Estas funciones
están definidas en la biblioteca matemática, que para que sea incluida en la compilación debe
añadirse a la línea de compilación la opción –lm.
1. ¿Qué hace la función printf?. Cambia el valor del argumento a esa función (valor
dentro del paréntesis) y examina qué efectos tiene al compilar y volver a ejecutar el
programa.
2. Cambia el valor del argumento del comando return por otro entero. Vuelve a
compilar el programa y ejecutarlo. Tras esto, Y SIN EJECUTAR NINGÚN OTRO
COMANDO ENTRE MEDIAS, ejecuta el siguiente comando UNIX: “echo $?”.
¿Cuál es el significado del argumento de la función return.
3. Cambia el nombre a la función main e intenta compilar el programa. ¿Por qué da
error?
El lenguaje C maneja datos tanto numéricos (enteros y reales) como de tipo carácter. En este
último caso hay que diferenciar entre los caracteres tratados de manera individual, y las
cadenas de estos. En la siguiente tabla podemos ver un ejemplo de lo expuesto (las comillas
que aparecen en los tipo carácter y cadenas son necesarias al usar este tipo de datos en C).
Enteros 19 -152
Tipos de datos
Ejercicio. Calcular con lápiz y papel el rango de cada tipo de dato entero.
En la siguiente tabla aparecen los distintos formatos de variables de tipo real. Estos datos se
almacenan en punto flotante, siguiendo, generalmente, la norma IEEE 754
/* Declaración y asignación */
int d=-52;
unsigned char UnaLetra=„a‟;
double Grande=-141.255e+7;
return 0;
}
1. ¿Cuál es la función de la letra que está a continuación del símbolo “%” en printf?
2. Viendo lo mostrado por el segundo printf (el que muestra la variable “UnaLetra”,
¿Cuál es el valor ASCII en decimal del carácter „a‟?
3. Añade una línea que, usando la función printf, muestra por pantalla el carácter
correspondiente al valor ASCII en hexadecimal 50 (último valor de la variable a).
Declaración de variables
C es (en general) un lenguaje compilado, y todas las variables han de ser declaradas antes de
ser usadas. A la hora de identificar a una variable (darle nombre) hay que tener en cuenta las
siguientes reglas muy simples:
Se puede usar cualquier combinación de letras (A-Z, a-z) y números (0-9). También se
pueden usar los siguientes caracteres “-“ y “_”
Los dígitos numéricos nunca debe estar al principio del nombre.
No se deben usar acentos ni ñ.
Se distingue entre mayúsculas y minúsculas. Por ejemplo dos variable llamadas Pepe
y pepe, serán diferentes.
De asignación: Asigna un valor a una variable. Forma del operador: “=”. Ejemplos de
uso:
Int a=45;
b = „a‟;
Aritméticos: Sirven para crear expresiones aritméticas. operadores: suma (+), resta (-
), producto (*), cociente (/) y módulo (%). Ejemplos de uso:
var1 = 3.5 * a;
#include <stdio.h>
printf (“Valor devuelto si falso: %d, valor devuelto si verdadero: %d\n”, 6<5,
6!=5);
return 0;
a <= 5 || a > 3 Esta expresión será verdad si la variable a (suponiendo que sea
entera) vale 4 ó 5.
o Operación lógico AND: &. Recordar que la operación AND sobre dos
combinaciones binarias implica la realización de la operación AND sobre los
valores de cada posición.
#include <stdio.h>
int a=-50, b;
b=8;
if ( (b & a) == 0) {
return 0;
Otros operadores
max = a>b ? a : b ;
int < unsigned < long < unsigned long < float < double.
Por ejemplo si multiplicamos un entero por un real, el compilador realizará una conversión de
tipo entero a real, y la multiplicación se realizará entre dos cantidades reales. Lo mismo
ocurre si el resultado de una expresión es de un tipo, y la asignación ser realiza a una variable
de tipo distinto. Sin embargo, el resultado de estas conversiones implícitas no siempre es el
deseado, por eso es aconsejable que sea el propio programador el que realice, en casos como
los comentados, la conversión de manera explícita. Para esto existe el operador de cambio de
tipo. La sintaxis es la siguiente:
(nuevo_tipo) expresión/variable.
Ejercicio/Ejemplo 9. El siguiente programa sirve para ver el problema que puede surgir si se
deja la conversión de tipo al compilador:
#include <stdio.h>
return 0;
() [] -> .
! ~ ++ -- - (type) * & sizeof (todos son unarios)
*/%
+-
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= %= ^= &= |=
,
Las funciones que veremos en esta sección permiten la entrada/salida de caracteres. Ambas
están definidas en el fichero de cabecera stdio.h, por lo que este fichero deberá ser incluido en
el programa.
· Función de salida de carácter: putchar (char c). Muestra el carácter c por la salida
estándar.
Ejercicio/Ejemplo. El siguiente programa sirve para ver el problema que puede surgir si se
deja la conversión de tipo al compilador:
printf (“La palabra introducida es: %c%c%c%c\n”, char1, char2, char3, char4);
return 0;
}
Las funciones que veremos en esta sección permiten la entrada/salida de cualquier tipo de
variable. El tipo es especificado como argumento de la función. Ambas están definidas en el
fichero de cabecera stdio.h, por lo que este fichero deberá ser incluido en el programa.
Función int printf(const char *format). Imprime por la salida estándar una secuencia
de caracteres cuya estructura está definida en format. Se permite dar salida a cualquier
tipo de dato predefinido. format tiene la estructura de una cadena de caracteres normal
pero admite además caracteres de conversión (%letra) para indicar qué tipo de dato se
ha de imprimir. La estructura de una cadena de formato es:
%[flags][.width][.prec][F|N|h|l]type
Donde type es un carácter de conversión. El campo flag afecta al tipo de justificación; width
es la anchura utilizada para imprimir, .prec es la precisión (número de decimales) si el dato
a imprimir es un número en coma flotante. El último campo opcional afecta a la
interpretación del argumento (se deja al estudiante buscar el significado de este campo). Los
tipos de datos a imprimir (campo type) son los siguientes:
%c Caracter
%d Entero decimal
%g Menor entre %e y %f
int scanf(const char *format, ...). Permite la entrada de datos de forma estructurada
por teclado. En la cadena de formato se pueden poner todos los caracteres de
conversión (%) de la tabla anterior. En el ejemplo se explicará más detenidamente la
sintaxis de esta función.
#include <stdio.h>
int main( void )
{
int i;
double f;
if (condición)
Sentencia simple/bloque_1
else
Sentencia simple/bloque_2
Si condición es verdad (recordemos: valor devuelto por la expresión lógica distinto de 0), se
ejecuta Sentencia simple/ bloque_1, si es falso (recordemos: valor devuelto por la expresión
lógica 0), se ejecuta Sentencia simple/ bloque_2. La sentencia else es opcional.
#include <stdio.h>
else
return 0;
while (condición)
Sentencia simple/bloque
Similar al bucle while pero el conjunto de sentencias se ejecuta al menos una vez. Su sintaxis
es:
do
Sentencia simple/bloque
while (condición)
Sentencia simple/bloque
Donde:
· inic es una expresión que sólo se ejecuta una vez al principio del bucle. El
bucle for suele utilizarse en combinación con un contador. Un contador es una
· actualiz exp3 es una expresión que se ejecuta al final de cada iteración. Puesto que
como ya se ha indicado el bucle for se utiliza junto a un contador, actualiz,
en general, contiene una instrucción que actualiza la variable contador.
#include <stdio.h>
int cont;
#include <stdio.h>
int cont;
cont = 0;
cont ++; }
return 0; }
Ejemplo. Ejemplo de uso de la función continue. Este programa lee desde teclado 10
números cuyo valor tiene que estar entre 1 y 100, si no es así se le vuelve a pedir al usuario
que introduzca un valor, descartando el introducido no válido.
#include <stdio.h>
int main( void )
{
int n;
int cont=0;
do
{
printf ("\nIntroduce un número entre 1 y 100:");
scanf ("%d",&n);
if ((n<1)||(numero>100))
continue;
cont++;
} while (cont<50);
return 0;
}
Ejemplo. Ejemplo de uso de la función break. Este programa lee desde teclado valores
numéricos y los muestra por pantalla, salvo que el número introducido sea el 2.
#include <stdio.h>
int main( void )
{
int n;
do
{
printf ("\nIntroduce un número entero:");
scanf ("%d",&n);
return 0;
}
Es una sentencia de selección múltiple, que compara sucesivamente el valor de una expresión
con una lista de constantes enteras o de caracteres. Cuando se encuentra una correspondencia,
se ejecutan las sentencias asociadas con la constante. La sintaxis de esta sentencia es la
siguiente:
switch (expresión)
{
case constante1:
Sentencia/s
break;
···
case costanten:
Sentencia/s
break;
···
[default:
Sentencia/s]
}
No puede haber dos constantes en el mismo switch que tengan los mismos valores. Por
supuesto que una sentencia switch contenida en otra sentencia switch pude tener constantes
que sean iguales.
do {
printf (“introduzca un valor entero entre 1 y 3:”);
scanf ("%d",&n);
switch (n)
{
case 1:
printf(“Ha escogido la opción 1\n”);
break;
case 2:
printf(“Ha escogido la opción 2\n”);
break;
case 3:
printf(“Ha escogido la opción 3\n”);
exit (3);
break;
default:
printf(“Opción no válida\n”);
}
} while (1)
return 0;
}
1. ¿Qué hace la función exit? ¿Qué significa su argumento (valor a su derecha entre
paréntesis? ¿Se podría eliminar la sentencia break que sigue a “exit (3)”?
2. ¿Qué pasa si se quita la sentencia break asociada a “case 1:”?
Donde:
tipo es tipo del dato que devuelve. Si no devuelve nada se declarará de tipo “void”.
Sólo se pueden devolver tipos asignables: enteros, reales, punteros, estructuras y void.
Si no se especifica el tipo, se supone que es entero.
nombre es el nombre con que la función es identificada (mayúscula primera letra).
Parámetros de la función. Es una lista de nombres de variables separados por comas
con sus tipos asociados, que recibirán valor en el momento de llamar a la función. Los
paréntesis siempre deben aparecer, aunque la función no tenga argumentos. Estas
variables sólo estarán definidas dentro de la función, es decir, son locales a la función,
salvo que hayan sido definidas como globales (ver al final de este apartado), creándose
al comienzo de la ejecución de la función y destruyéndose al finalizar ésta. Esto es
aplicable a cualquier otra variable declarada dentro de la función, excepto si se
declaran como static (esta sentencia debe aparecer antes del tipo de la variable). En
este caso el compilador no las destruye y guarda su valor para la próxima llamada,
aunque la variable sigue teniendo limitado el ámbito al interior de la función.
La sentencia return es opcional, sólo debe aparecer si la función retorna un valor.
Puede aparecer en cualquier punto del cuerpo de una función.
Una variable es global cuando se declara fuera de una función, y se puede utilizar en todo el
programa desde el punto de la declaración. Todas las variables usadas en los ejemplos
mostrados en estos apuntes son locales, es decir, declaradas dentro de un bloque de
sentencias, y su ámbito de aplicación es dentro del bloque en que son declaradas. No es
aconsejable el uso de variables globales.
Toda función debe ser declarada antes de ser definida. La forma de declarar una función es la
siguiente:
Las funciones son siempre globales, de manera que su declaración debe ser realizada al
principio del programa; si la primera función definida en éste es la main(), antes de esta
definición. La declaración puede evitarse si la función es definida antes de ser invocada, sin
embargo, es aconsejable declarar al comienzo del programa los prototipos de las funciones
que vamos a definir, incluyendo comentarios sobre su finalidad.
Llamadas a funciones
#include <stdio.h>
/* Prototipo de la función */
/* Función principal */
int i = 3, j = 5;
/* Definición de la función */
int a,b; */
if (a < b)
return (b);
else
return (a);
#include <stdio.h>
/* Definición de la función */
int a,b; */ {
if (a < b)
return (b);
else
return (a);
/* Función principal */
int i = 3, j = 5;
#include <stdio.h>
#include <stdlib.h>
/* Declaración de la función */
/* Función principal */
int i;
suerte();
return 0;
/* Definición de la función */
void suerte () {
int i;
#include <stdio.h>
/* Prototipo */
int main () {
return 0;
/* Definición */
int tmp;
tmp=i;
i = j;
j = tmp;
1. ¿Por qué no se han intercambiado los valores de las variables i y j tras la ejecución
de la función Cambia() en main()?
Un vector es una asignación contigua de memoria donde se almacena una colección de datos
todos del mismo tipo.
#include <stdio.h>
int main () {
int x[5], i;
x[0] = 1;
return 0;
En la siguiente tabla se muestra la relación entre elemento del vector y posición de memoria
que ocupa.
m x[0]
m+4 x[1]
m+8 x[2]
m+12 x[3]
m+16 x[4]
#include <stdio.h>
main () {
int a[3];
a[10000] = 41;
Las cadenas de caracteres son vectores de datos tipo char, con la característica adicional de
que acaban en el carácter nulo „\0‟. Si se da valor al número de elementos de la cadena en su
declaración habrá que tener en cuenta este carácter adicional. Por ejemplo para guardar una
cadena de 10 caracteres, se debe reservar espacio para 11.
#include <stdio.h>
int main () {
char cad1[]=”hola”, cad2[20];
return 0;
}
cad2="hola mundo";
Hay muchas funciones de biblioteca estándar para tratarlas. Por ejemplo, para asignar valor a
una cadena tras su declaración se puede usar la función strcpy (para su uso hay que incluir en
el programa el fichero de cabecera string.h):
También la función strcmp que compara elementos sucesivos de dos cadenas, hasta que
encuentra dos elementos distintos, o hasta que se acaban las cadenas. Devuelve: valor
negativo si primera menor que la segunda (valor lexicográfico), un cero si son iguales o
un valor positivo si la primera es mayor que la segunda.
strcmp (info.cadena,”hola”);
Ya hemos visto funciones que permiten la captura desde entrada estándar (sscanf) o muestran
en la salida estándar (printf) cadenas de caracteres. Aquí vamos a añadir dos que pueden
resultar útiles:
Vectores y punteros
El nombre del vector es un puntero al primer elemento del vector. Esta propiedad ya
ha sido usada en varios de los ejemplos anteriores. Ya que determinadas
funciones necesitan que se especifique el vector mediante el puntero a su primer
elemento. Un ejemplo de esto es printf cuando el dato a imprimir es una cadena (%s).
Ejemplo. Vamos a modificar el ejemplo del primer apartado de esta sección
(características generales) usando en scanf la relación entre punteros y vectores
indicada.
#include <stdio.h>
int main () {
int x[5], i;
x[0] = 1;
return 0;
Si se quiere pasar todo un vector a una función habrá que hacerlo por referencia, es
decir, se pasa el puntero al inicio del vector, es decir, el nombre del vector, como acabamos de
ver en el aparatdo anterior. En este caso hay que tener en cuenta que la función desconoce el
tamaño del vector, por lo que será el programador el responsable de que no se acceda más allá
del límite, por ejemplo, pasando también como argumento a la función el tamaño del vector.
Argumentos a un programa c
El primer argumento se suele llamar argc, pero se le puede asignar cualquier otro
nombre, y es un entero cuyo valor es el número de argumentos pasados al programa
incluyendo como argumento el nombre del propio programa. Al segundo argumento se suele
llamar argv, pero, como antes, se le puede asignar cualquier otro nombre. Este segundo
argumento es un vector de punteros a carácter. Dada la dificultad conceptual de este tipo de
vectores vamos a abandonar momentáneamente la explicación de los argumentos al programa
C, para centrarnos en los vectores de puntero.
Un puntero es un dato, y como para cualquier otro tipo de dato se permite declarar un
vector de punteros. La forma de hacerlo será:
Cada elemento del vector será, en este caso, un puntero a un dato de tipo tipo, es decir,
cada elemento del vector contiene la dirección de memoria de un dato de tipo tipo.
#include <stdio.h>
#include <math.h>
int main ()
{
double *x[2], dist;
int tam_vec, i;
// Reservamos memoria
if ( (x[0]=(double *)malloc(tam_vec * sizeof(double))) == NULL)
{
printf(“ERROR al reservar memoria para el primer vector\n”);
return 1;
}
if ( (x[1]=(double *)malloc(tam_vec * sizeof(double))) == NULL)
{
printf(“ERROR al reservar memoria para el segundo vector\n”);
return 1;
}
// Pedimos datos
for (i=0; i<tam_vec; i++)
{
printf (“Dame el valor %i de primer vector:”,i);
scanf (“%lf”, x[0]+i); //x[0] es un puntero al primer elemento del vector 1
}
for (i=0; i<tam_vec; i++)
{
printf (“Dame el valor %i de segundo vector:”,i);
scanf (“%lf”, x[1]+i); //x[1] es un puntero al primer elemento del vector 2
}
// Calculamos distancia
dist = 0.0;
for (i=0; i< tam_vec; i++)
dist += fabs((x[0][i] – x[1][i]));
return 0;
Vemos esto mediante un ejemplo. Supongamos que ejecutamos el programa a.out con
los siguientes argumentos: a.out uno 2 3.89 “cua tro”. Pues bien, argv[0] será un puntero
(dirección de memoria del primer carácter) al nombre del programa (a.out), argv[1] será un
puntero a la cadena de caracteres introducida como primer argumento (uno), argv[2] será un
puntero a la cadena de caracteres introducida como segundo argumento (2), argv[3] será un
puntero a la cadena de caracteres introducida como tercer argumento (3.89) y argv[4] será un
puntero a la cadena de caracteres introducida como cuarto argumento (cua tro).
#include<stdio.h>
return 0;
}
Es importante tener en cuenta que todos los argumentos son pasados a main() como cadenas
de caracteres. Es el programador el que tendrá que, usando las funciones adecuadas, pasar los
datos de tipo cadena al tipo que desee. Una de estas funciones es, por ejemplo, sscanf().
#include <stdio.h>
Los punteros son una de las herramientas más útiles, pero a la vez, complejas y
peligrosas de C. Un puntero es una variable entera sin signo que almacena la
dirección de memoria de otra variable o dato. Para ser más exactos, almacena la
dirección del primer byte que la variable ocupa en memoria. Esto es importante
tenerlo en cuenta, a la hora de entender la aritmética de punteros.
La declaración de un puntero se realiza de la siguiente manera:
[cualificador] tipo *nombre;
Donde cualificador recordemos que puede ser extern o static y es opcional, sólo será
necesario si la variable queremos que se comporte de la forma indicada para esos
cualificadores. Tipo es cualquiera de los vistos ahora y alguno que añadiremos más
adelante, e indica el tipo de la variable almacenada en la dirección de memoria
apuntada por el puntero. El carácter “*” es el que indica que la variable que estamos
definiendo es un puntero. Y, por último, nombre es el identificador de la variable
puntero.
Es importante tener en cuenta que el hecho de declarar un puntero implica que se
reservará espacio en memoria para guardar un puntero, no para el tipo de datos al que
apunta.
Ejemplo.
La declaración:
int *i;
implica que se está declarando un puntero que vamos a llamar ”i” que apunta a un
entero, es decir, la variable i contendrá una dirección de memoria central donde se
almacenará un entero;, más exactamente, contiene la dirección de memoria del primer
byte del entero almacenado. Con esta declaración se reserva espacio para almacenar “i”,
es decir, el puntero, no para almacenar el entero. Esto último se tendrá que hacer con la
declaración correspondiente, como veremos en siguientes ejemplos.
De igual manera se declararían punteros a tipos float, long, char, etc.
Existen sólo dos operaciones que se puedan usar con punteros: la suma y la resta. Más
concretamente las operaciones permitidas son el incremento/decremento, la suma/resta
de valores enteros y la suma/resta de punteros.
Las operaciones más útiles son el incremento/decremento, como veremos al hablar de
vectores. Cada vez que se incrementa un puntero, apunta a la posición de memoria del
siguiente elemento de su tipo base. Cada vez que se decrementa, apunta a la posición
del elemento anterior. Los punteros aumentan o decrecen la longitud del tipo de datos
a los que apuntan.
El valor NULL
Operadores
Ejemplo. De uso de los operadores “&” y “*” (a partir de aquí en numerosos ejemplos sólo
mostraremos la parte de código que nos interesa, se deja para el alumno añadir las líneas
necesarias para crear un programa que pueda ser compilado).
int *i, j;
j=5;
i=&j; // Asignamos a j la dirección de i
printf (“dirección de j (en hexadecimal)=%x\n”,i);
// Acceso a valor mediante dirección
printf (“valor de j=%d\n”,*i);
// Acceso a valor mediante variable, como lo hemos venido haciendo hasta ahora
printf (“valor de j=%d\n”, j);
Vimos al hablar de funciones que los parámetros se podías pasar por valor o por
referencia usando punteros. En este segundo caso, pasamos a la función la dirección
de la variable, por lo que si modificamos el contenido de esa dirección (no el
contenido del puntero, si no el contenido de la dirección apuntada por el puntero), esa
#include <stdio.h>
/* Prototipo */
int main () {
return 0;
/* Definición */
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
Memoria Dinámica
Otra forma de dar valor a un puntero es asignarle memoria dinámica mediante una
función de asignación de memoria. Recordar que al declarar un puntero simplemente
se está reservando memoria para almacenar una dirección de memoria, no se le está
asignando una dirección correcta, esto habrá que hacerlo posteriormente, como ya
hemos visto en ejemplos anteriores, y veremos también en esta sección.
#include <stdio.h>
int main () {
return 1;
return 0;
Es una colección de variables del mismo o distinto tipo que se referencia bajo un
único nombre, proporcionando un medio eficaz de mantener junta la información
relacionada.
La definición de una estructura se realiza de la siguiente manera:
struct nombre {
lista de variables
};
Donde struct es la palabra clave que indica al compilador que se va a definir una
estructura, nombre es el identificador de la estructura, y en lista de variables es donde
se declaran las variables que van a componer esa estructura.
Ejemplo. De definición de una estructura.
struct complejo {
double real;
double imag;
};
Definida la estructura se pueden declarar variables de este tipo. Hay varias maneras
de realizar esto.
1. Directamente en la definición:
struct nombre {
lista de variables
} nombre_variable1, nombre_variable2, … ;
double real;
double imag;
} complejo;
complejo z, *t;
t=&z;
t->real = 1.0;
t->imag = 2.1;
Uniones
Las uniones son similares a las estructuras, con la diferencia de que en las
uniones se almacenan en los campos solapándose unos con otros en la misma
disposición; al contrario que en las estructuras, donde los campos se almacenan unos a
continuación de otros. En esencia, las uniones sirven para ahorrar espacio en memoria.
Para almacenar los miembros de una unión, se requiere una zona de memoria
igual a la que ocupa el miembro más largo de la unión. Todos los miembros son
almacenados en el mismo espacio de memoria y comienzan en la misma dirección. El
valor almacenado es sobrescrito cada vez que se asigna un valor al mismo miembro o
a un miembro diferente, aquí radica la diferencia con las estructuras.
Después de definir un tipo union, se puede declarar una o más variables de ese tipo de
la siguiente forma: union tipo_union [variables];
Dicho de una forma simple, una unión le permite manejar los mismos datos
con diferentes tipos, o utilizar el mismo dato con diferente nombre, a continuación le
presento un ejemplo:
Tipos enumerados
dias_semana dia;
dia = LUNES;
dia = 1; /* Ambas asignaciones son equivalentes */
Las sentencias del preprocesador pueden aparecer en cualquier punto del programa
(aunque la mayoría suelen estar al principio) y se caracterizan porque empiezan por el
caracter “#”. Las más importantes son:
· #define. Define una macro o una constante. Su sintaxis es #define x y, y lo que hace el
preprocesador es sustituir en el resto del programa donde encuentre el texto x por y.
Ejemplo.
Ejemplo.
y usarla como
x=max(p+q,r+s);
Ejemplo.
#ifndef PI
#define PI 3.1416
#endif
· #include. Permite incluir un fichero dentro del programa C. Este fichero puede ser de
cabecera (.h) u otro programa C. Los ficheros de cabecera contienen:
#include <stdio.h>
#include <math.h>
/* #include “mifichero.h” */
#define TAMMAX 10
#define MES_DESP “finalizada la ejecucion del programa\n”
main ()
{
double a[TAMMAX];
int i;
for (i=0; i<TAMMAX; i++)
{
scanf (“%lf”, &(a[i]));
printf (“%lf”, sin(a[i]));
}
printf(MES_DESP);
return 0;
}
FILE *nombre;
Donde nombre es el identificador con el que haremos referencia al fichero en las distintas
funciones que usemos para operar con él. Cada vez que operamos sobre un fichero se
modifica su estado, con lo que las funciones que lo manipulan siempre modifican la variable
(por eso es un puntero).
Lo primero que hay que hacer para operar con un fichero es “abrirle” mediante la función
fopen(), cuya sintaxis es:
“r”, “w”, “a”. Se va a realizar una operación de lectura, escritura posicionando el flujo
al principio del fichero o escritura posicionando el flujo al final del fichero, en modo
ASCII.
“r+”, “w+”, “a+”. Se va a realizar una operación de lectura y escritura posicionando el
flujo al principio del fichero, escritura posicionando el flujo al principio del fichero y
lectura o escritura posicionando el flujo al final del fichero y lectura, en modo ASCII.
Si se añade al final de cualquiera de las opciones anteriores el caracter b la operación
del lectura y escritura se realizarán en modo binario.
Para cerrar un fichero que ya no se vaya a usar se utiliza la función flose(nombre). Con
nombre el nombre del puntero a FILE con que hayamos identificado al fichero que queremos
cerrar.
En caso de que todo haya ido correcto en la operación esta función devuelve un 0, y si ha
habido error un -1.
#include<stdio.h>
if (argc < 3)
{
fprintf (stderr,”ERROR numero de argumentos insuficiente\n”);
exit (1);
}
fscanf (fpin,”%f”,&dat);
while (!feof(fpin)) {
if ( fwrite(&dat, sizeof(float), 1, fpout_fich) != 1)
{
fprintf(stderr, “ERROR al escribir dato %d en %s\n”, cont, arg[2]);
exit(3);
}
fprintf (fpout_std,"Dato escrito %.2f\n",dat); // se muestra con 2 decimales
fscanf (fpin,”%f”,&dat);
cont++;
}
fclose (fpin);
fclose (fpout_fich);
return 0;
}
Depuración de un programa
Una opción a seguir para detectar la zona que genera el error al compilar es la de ir
comentando partes del programa, hasta detectar la zona con error.
Añadir mensajes para seguir el hilo de la ejecución y vigilar los valores de variables.
Acotar la sección de código que contiene el error.
Evaluar los parámetros que provocan el error.
Make
Esta utilidad es tanto más conveniente cuanto más complejo sea el programa
ejecutable que se está construyendo. La situación ideal es cuando el fichero ejecutable consta
de muchos módulos que pueden ser compilados separadamente.
El comando indent para hacer su trabajo hace un pequeño análisis sintáctico, y por lo
tanto puede indicar ciertos errores, como son un número no balanceado de apertura de
paréntesis y cierre de paréntesis.
Otros
Brian Kernighan & Denis Ritchie. “El lenguaje de programación C”. Prentice Hall.