Está en la página 1de 14

Programación en C

1. Tipos de datos y operadores básicos

1.1 Tipos atómicos *1 byte = 8 bits

• short (2 bytes): número entero corto con signo. [-215, 215-1], esto es [-32768,
32767]
• int (4 bytes): número entero con signo. El estándar ANSI C especifica que este
tipo nunca ocupa menos que short ni más que long, que aparece más adelante. [-
231, 231-1], esto es [-2147483648, 2147483647]
• long (8 bytes): número entero largo con signo. [-263, 263-1], esto es [-
9223372036854775808, 9223372036854775807]
• float (4 bytes): número con parte decimal representado en punto flotante con
precisión simple (hasta 8 cifras decimales y 4 bytes). [1.17549e–38, 3.40282e+38]
• double (8 bytes): número con parte decimal representado en punto flotante con
precisión doble (hasta 16 cifras decimales y 8 bytes). [2.22507e-308,
1.79769e+308]
• char (1 byte): carácter del juego estándar de caracteres que puede ser con signo
o sin signo, dependiendo de la implementación. Consulte la macro CHAR_MIN del
archivo de cabecera . [-128, 127]
• void: del que no se pueden declarar variables. Este tipo se utiliza, por ejemplo,
para especificar que una función no acepta argumentos.

1.2 Formatos de escritura


Una especificación de formato de escritura general presenta el siguiente aspecto,
donde todo lo encerrado entre corchetes es opcional:

%[alineacion][anchura][.precision][longitud]tipo

La especificación más elemental consiste en %tipo. Veamos el significado de cada


una de las componentes:

• Alineacion: uno o más caracteres que especifican características opcionales como


la alineación, la escritura del signo + de los enteros positivos, la escritura en octal,
hexadecimal, etc. Por ejemplo, el signo + de la siguiente instrucción hace que los
enteros positivos vengan acompañados por un signo + a su izquierda:
printf ("%+d\n", 23);

• Anchura: número entero no negativo que especifica el número mínimo de


caracteres mostrados en la salida. Por ejemplo, la siguiente instrucción escribe 23
con tres espacios en blanco a su izquierda:
printf ("%5d\n", 23);
• Precisión: número entero no negativo que especifica, por ejemplo, el número
mínimo de dígitos que se escriben de un número entero.

• Longitud: un carácter o más que indican que el valor que se representa tiene un
tipo más largo o más corto que el especificado por el tipo (%hd, %ld, %lld). Algunos
modificadores son: hh, h, l, ll y L.

Tipos más comunes:


EJEMPLO DE
ESPECIFICADOR TIPO FORMATO VALOR
MOSTRADO
Entero decimal
%d Int -26824
con signo
Punto flotante con
%f Float 3.750000
signo
Punto flotante con
%lf Double 3.750000
precisión doble
%c Char Un único carácter ‘g’
Una cadena de
%s char “Hola mundo”
caracteres

NOTA: Los especificadores %f y %lf admiten la notación %.nf y %.nlf donde n es el


número de decimales que se ha de mostrar. Por ejemplo %.3f mostrará 3.750 en
lugar de 3.750000

1.3 Operadores en C

Símbolo Significado
++ Incremento sufijo
-- Decremento sufijo
() Llamada a función
[] Elemento de tabla
-> Acceso a miembro desde un puntero
. Acceso a miembro
++ Incremento prefijo
-- Decremento prefijo
! Negación lógica
~ Complemento a uno
- Cambio de signo (operador unario)
+ Identidad (operador unario)
& Dirección
* Seguir un puntero (indirection en inglés)
Sizeof() Tamaño en bytes
(tipo) Conversión explícita de tipos
* Multiplicación
/ División
% Resto de la división entera
+ Suma
- Resta
<< Desplazamiento de bits a la izquierda
>> Desplazamiento de bits a la derecha
< Menor que
<= Menor o igual
> Mayor que
>= Mayor o igual
== Igual
!= Desigual
& Conjunción (Y) de bits
^ O exclusivo de bits
| O inclusivo de bits
&& Conjunción (Y) lógica
|| Disyunción (O) lógica
= Asignación simple
*=, /=, %=, +=, -=, <<=, >>=, &=, Asignaciones compuestas
^=, |=
?: Expresión condicional
, Separador de expresiones

2. Funciones

2.1 Main
Todo programa comienza por la función main, Ejem:

Int main(){
/*funciones a ejecutar*/
Return 0;
}

2.2 Función printf()


Imprime en pantalla lo del interior de los paréntesis. Ejem:

Double var1=2.5, var2=2,399;


Printf(“Las variables son %lf y %.1lf”, var1, var2);

El ejemplo escribiría: Las variables son 2.5 y 2.3


2.3 Función scanf()

Escanea el valor de una o varias variables. Ejem:

Int aa;
Scanf(“%d”, &aa);

Nota: a las variables tipo char, se les puede asignar caracteres directamente o en
ASCI. Ejem:
Char car;
Car=‘A’;
Car=65;

3. tablas cadenas y estructuras

3.1 Macros
Se utiliza para establecer constantes, y para declararlas hay que utilizar #define.
Ejem:

#define pi 3,14
Int main(){
Int num;
Num= 5 * pi;
Return 0;
}

3.2 Introducción a las tablas o arrays


Son como vectores, se pueden inicializar cuando se declaran. Ejem:
Int array[]={0,0,1,2,0,99}
O int array[6]={[2]=1,[3]=2,[5]=99}
En este ejemplo, array[2]=1.
Nunca se debe ocupar la última posición, pues esta está reservada para el fin de
cadena.

También podemos realizar tablas en 2 o mas dimensiones, ejem:


Int matriz[2][3]={{0,4,5},{1,2,3}}
En este ejemplo, matriz[0][1]=4

3.3 Introducción a las cadenas


Las cadenas de caracteres (strings en inglés) son un tipo especial de tabla con
algunas peculiaridades.
Son tablas de tipo char, ejem:
Int main(){
Char nombre[12];
Printf(“Por favor introduzca su nombre: ”);
Scanf(“%s”,nombre);
Return 0;}

Para introducir espacios en blanco en nuestra cadena, se utiliza la función gets:


Gets(nombre);

Si queremos limitar el espacio de la cadena, podemos utilizar el formato %ns,


poniendo en “n” el numero de caracteres máximo:
Scanf(“%11s”,nombre);

3.4 Introducción a las estructuras


Las estructuras son colecciones de datos que, a diferencia de las tablas, pueden ser
de tipos diferentes.
Estas se definen mediante struct, y para que puedas crear mas de una se acompaña
de typedef, ejem:

Typedef struct{
Char autor[30];
Char titulo[80];
Int fecha;
}Libro;

Int main(){
Libro novela;
Printf(“Introduzca la fecha del libro: “);
scanf(“%d”, &novela.fecha);
Return 0;
}

Dentro de una estructura podemos declarar no solo variables de tipos elementales,


tablas y cadenas, sino también otras estructuras. Cuando ocurre esto último se dice
que trabajamos con estructuras anidadas.

Nota: Igual que declaramos tablas de int, double o char, podemos declarar tablas de
tipos estructurados.
4. Instrucciones de control
4.1 Operadores relacionales o de igualdad
*Mirar tabla.

4.2 La instrucción if-else


If(condicion){
Instrucciones si ocurre}
Else{
Instrucciones si no ocurre}

4.3 La instrucción switch y las enumeraciones


Enumeraciones:
Typedef enum{ dolares=1, yenes, libras} Monedas;

Switch:
Switch (variable){
Case dolares: /* o “case 1:” */
Instruciones;
Break;
Case yenes: /* o “case 2:” */
Instruciones;
Break;
Case libras: /* o “case 3:” */
Instruciones;
Break;
Default: /* es opcional */
Instrucciones si no es ninguna de las opciones;

4.4 El bucle while


Los bucles nos permiten repetir un conjunto de instrucciones dependiendo de una
condición lógica. El bucle while es probablemente el que tiene una sintaxis más
sencilla.

While (condición){
Instrucciones;}

4.5 El bucle do-while


El bulce do while es un bucle en el que la condición se evalúa después de la
ejecución del cuerpo.
Do{
Instrucciones;
}while (condicion);
4.6 El bucle for

La sintaxis del bucle for es más complicada que la del bucle while pero podemos
traducirlo fácilmente a un bucle while equivalente.

For (inicio; condición; instrucción final){


Instrucciones;}

Ejemplo: for(x=0, y=1;x+y==0;x++){


Y= y-0,5;}

4.7 bucles anidados


Un bucle dentro de otro.

5. Funciones y punteros

5.1 Funciones
Cuando una función necesita información para ejecutar sus instrucciones, esta
información se ha de pasar a través de sus argumentos, estos se guardan en
variables de nuestra función, no modifican las de main.
Ejem:
Double media (double x, double y){
Return (x+y)/2;}
Int main(){
double m;
m=media( 4, 6)
printf(“la media es: %.0lf”, m);
return 0;}

5.2 Punteros
Los punteros son un tipo de dato que va a jugar un papel clave en el paso de
información de las funciones. Apuntan a la dirección de memoria donde esta una
variable. Se definen de la siguiente manera: Int *p, double *p….
& es el operador que devuelve la dirección de una variable, y * el que devuelve el
valor al que apunta un puntero. Por tanto, si “p” apunta a la dirección de “numero”,
podemos decir que: *p= *&numero= numero y p= &numero.

Los punteros permiten que una función altere el valor de variables declaradas dentro
de otras funciones. Pasando la dirección donde esta la variable en vez de su valor.
Ejem:
Void incrementar( int *p){
(*p)++;}

Int main(){
Int num=2;
Incrementar(&num);
Printf(“%d%”, num); /*por pantalla se imprimiría un 3 */
Return 0;}

Los punteros también se pueden aumentar para que apunten a la siguiente dirección
de memoria, por ejemplo en tablas o cadenas:

Int main(){
Int tabla[3]={54,4,9};
Int *p;
P=&tabla[0];
P++;
Printf(“%d”, *p); /*estaríamos imprimiendo tabla[1]*/
Reuturn 0;}

Además, en las tablas y cadenas, para pasar el primer elemento, basta con pasarel
nombre sin especificar el elemento, ya que es un puntero al primero, por ejemplo, en
el ejemplo anterior seria: p=tabla.

Por último, si queremos acceder al valor de un dato estructurado, basta con poner
“flechitas” en vez de puntos. Por ejemplo si “p” es un puntero a fecha y queremos
acceder al valor de dia: fecha->dia.

5.3 Reserva dinámica de memoria


La memoria reservada desde un programa puede ser estática o dinámica. La
memoria estática se reserva durante la compilación, mientras que la memoria
dinámica se reserva durante la ejecución del programa. Esto se realiza mediante una
llamada a la función malloc, la cual devuelve un puntero de la memoria reservada.
Para poder utilizar esta función, devemos incluir el archivo stdlib.h al principio de
nuestro programa ( #include <stdlib.h> ).

Malloc tiene la siguiente estructura: (/* tipo de puntero*/) malloc (/*tamaño*/)

Si a habido algun error en la reserva de memoria, malloc devuelve un puntero a


NULL.

Por último es muy importante liberar la memoria reservada cuando ya no se


necesite, o al final del programa, esto se realiza con free(/*puntero*/).
Para aclararlo un poco pongamos un ejemplo, queremos reservar una tabla de tres
enteros:

#include <stdlib.h>

Int main(){
Int *tabla;
Tabla = (int*)malloc (3*sizeof(int));
If (tabla == NULL){
Printf(“Error al reservar memoria”);
Return 1;}
/*progama*/
Free(tabla);
Return 0;}

5.3.2 Función realloc


Realloc redimensiona un bloque de memoria reservada previamente con malloc o
realloc.
Prototipo: /*puntero deseado*/ * realloc( void *P, size_t size )
Recibe: void * P ----> puntero genérico a ampliar
size_t t_size ----> el nuevo tamaño del bloque
Devuelve: el puntero genérico al nuevo bloque (análogo al que devuelve malloc)

Efecto:  Si el argumento es correcto (dirección del heap previamente reservada por


malloc o funciones afines) modifica el tamaño del bloque reservado y
devuelve un puntero al mismo. Lo normal es asignar un tamaño mayor.
Para otros casos consultar manuales de C.
 Mantiene los valores del bloque de memoria original
 El nuevo espacio se dispone a continuación del bloque anterior (en el uso
normal de asignar un tamaño mayor)
Por ejemplo, para aumentar la tabla anterior a 4 entero y dar el valor “69” al cuarto:

Tabla = (int*) realloc ((void*)tabla, 4*sizeof(int));


Tabla[3]=69;

6. Archivos de texto

6.1 Lectura de archivos de texto


Para trabajar con ficheros es necesario declarar una variable especifica de FILE*, es
decir puntero a FILE. Antes de leer los datos del fichero, tenemos que abrirlo y
asociar esta variable con el archivo real que se encuentra en alguna carpeta de
nuestro disco duro. Para ello la instrucción fopen recibe dos argumentos: nombre de
archivo (“archivo.txt” o una cadena de char) o ruta(C://programa//archivo.txt) y el
modo de apertura de el archivo (“r” para solo lectura)
Para la lectura del archivo se utiliza la función fscanf, fcanf devuelve el número de
variables que ha podido leer. Para leer una variable necesita leer todas las
anteriores. Fscanf detiene su lectura en espacios o saltos de línea. Si hay algun error
al abrir un archivo, devuelve un puntero a NULL.
Cuando dejemos de utilizar el archivo es importante cerrarlo con fclose().

Por ejemplo, imaginemos un archivo de texto donde aparece en una columna el


nombre del alumno y en la otra su nota:
Celia 4,5
Miguel 9,9
Andres 2
…..

Y hacemos un programa para leerlos todos y hacer la media:

#include <stdlib.h>

Int main(){
FILE* f;
Int i;
Char nombre[20];
Double nota, media;
f=fopenf(“archivo.txt”, ”r”);
if (f==NULL){
printf(“Error abriendo el archivo”);
return 1;}
for (i=0; fscanf(f, “%s”, nombre); i++){
fscanf(f, “%lf”, &nota);
media +=nota;}
printf(“La nota media es %.2lf”, media/i);
fclose (f);
return 0;

6.2 Escritura de archivos


Muy similar a su lectura: declarar un puntero a FILE, abrir el archivo en modo de
apertura escritura (“w”), comprobar que no ha habido ningún error al abrirlo y por
último cerrarlo, también con fclose().
Para escribir en nuestro archivo utilizamos la funcion fprintf():
fprintf (/*puntero a file*/, “texto”, variables);
Ejemplo: (en el ejemplo anterior, si quisiesemos escribir la media en un archivo)

FILE *salida;
Salida=fopenf(“salida.txt”, “w”);
If (salida==NULL){
printf(“Error abriendo el archivo”);
return 1;}
fprintf (salida, “la nota media es %lf \n”, media/i);
fclose (salida);
7. Estructura de un programa

7.1 Archivos de cabecera


Todas las funciones se dividen en cabecera y definición. La cabecera es donde se
dice que tipo de función es y cuales son sus argumentos.
Para escribir el prototipo de la función, basta con poner la cabecera terminada en
punto y coma. Este prototipo se coloca en un archivo cabecera para que el programa
sepa con antelación como es la función.
Los ficheros de cabecera pueden contener: prototipos de funciones, definiciones de
macros (#define), enumeraciones (typedef enum) y estructuras (typedef struct).
Los ficheros cabecera tienen la extensión “.h”, y se llaman al inicio del programa
mediante #include. Los archivos cabecera que creemos nosotros deben estar entre
comillas, para que el programa lo busque primero en el directorio del proyecto.

Para evitar algunos errores, no entremos en detalles, debemos incluir al comienzo de


nuestro archivo cabecera:
#ifndef ARCHIVO_H
#define ARCHIVO_H
.
.
.
.
#endif

Por ejemplo, si tenemos la función circunferencia en un programa, y hacemos un


archivo de cabecera llamado circunferencia.h:

#ifndef CIRCUNFERENCIA_H /*por convenio se suele poner el mismo nombre


#define CIRCUNFERENCIA_H en mayúsculas y con barra*/
#define pi 3,1416
Double circunferencia (double radio);
Double area (double radio);
#endif

*Nota: recuerda que los archivos cabecera no necesitan ser compilados.

7.2 Proyectos con más de un archivo .c


Para la claridad de nuestros programas, se suelen crear más de un archivo .c, por
ejemplo, un archivo para main y otro para las funciones. Estos archivos se compilan
por separado, generando sus .o, y posteriormente se enlazan generando el
ejecutable.
8. Ordenes y comandos en Linux
Algunos comandos básicos son los siguientes:
 ls : lista los archivos y directorios que cuelgan directorio actual
 cd nombre-directorio: cambia al directorio que se indica, que pasa a ser
el directorio actual.
 mkdir nombre-directorio: crea un nuevo directorio, que cuelga del
directorio actual.
 pwd: muestra la ruta del directorio actual (igual que aparece en el
prompt, pero en lugar de “~” aparece la ruta absoluta al directorio
home).
 rm nombre-fichero: borra el fichero cuyo nombre se proporciona y que
cuelga del directorio actual.
 rmdir nombre-directorio: borra el directorio cuyo nombra se
proporciona y que cuelga del directorio actual.

Compilación de programas en C:

gcc -c fichero1.c : Comando básico de compilación cada fichero “.c” a


código objeto, fichero “.o”.
Se recomienda una forma más avanzada del comando:
gcc –c -g -Wall -ansi –pedantic fichero1.c
Estos argumentos adicionales opcionales, aunque se recomienda utilizarlos
siempre:
o -g indica que debe incluirse información de depuración, que será útil
para usar herramientas como valgrind y los depuradores gdb y
ddd.
o -Wall activa la comprobación de todos los warnings del compilador.
o -ansi –pedantic asegura que sólo se utilicen las construcciones de C
estándar

gcc -o nombre_ejecutable fichero1.o fichero2.o : Enlazado de todos los


ficheros .o para obtener un ejecutable.

La ejecución de un programa así compilado se realiza con ./nombre_ejecutable.

Valgrind
Ayuda a la identificación de los errores más comunes en la gestión de memoria
dinámica como son: lagunas de memoria, errores en la liberación de punteros,
acceso a memoria del heap no reservada previamente o no inicializada.
Para analizar este programa bajo la herramienta Valgrind hay que seguir los
siguientes pasos:
1- Compilar con la opción –g: gcc –g programa.c –o programa

2-Ejecutar el programa dentro de Valgrind como:


valgrind --leak-check=full programa
Recordar incluir cabeceras:
#include <stdio.h> Entre otros tiene los prototipos de printf y scanf
#include <string.h>
#include <stdlib.h> Reserva dinámica de memoria

Otras funciones:
Strcmp (cadena caracteres 1, cadena caracteres 2 ): devuelve 0 si ambas cadenas
son iguales.

Strcpy (cadena, cadena a copiar): copia una cadena.

Atoi(): recive como argumento una cadena de caracteres y devuelve una variable
de tipo int. Ejemplo:
Char cadena []= “123” ;
Int num;
Num = atoi (cadena);

La función fgets
Se trata de una función que exige información sobre la longitud de la cadena en la
que queremos guardar los datos leídos. Los argumentos de fgets() son los
siguientes:
• El primer argumento es el nombre de la cadena de caracteres.
• La longitud de la cadena se pasa como segundo argumento.
• Para leer del teclado, el tercer argumento debe ser stdin.
fgets() lee caracteres hasta que encuentra un salto de línea o DIM – 1 caracteres (4
caracteres), lo que ocurra primero (puede leer los espacios). De esta forma se
asegura de que queda espacio para el carácter final de cadena.
Ejemplo:
#include <stdio.h>
#define DIM 5
int main () {
char nombre [DIM];
char * p;
printf ("Teclea tu nombre, por favor: ");
fgets(nombre, DIM, stdin);
printf ("Buenos días, %s.", nombre);
return 0; }
Si introduces Francisco Soria, te imprimiría: “Buenos días Fran.”
Función strtok
La primera vez que se llama a strtok hay que pasarle como primer argumento el
nombre de la cadena de caracteres que queremos partir. Las siguientes veces hay
que pasarle NULL. El segundo argumento de la función es siempre la lista de
delimitadores.
Si ya no quedan más subcadenas, strtok() devuelve un puntero nulo, es decir, NULL.
Ejemplo:
#include <stdio.h>
#include <string.h>
int main() {
char cadena[] = "subcadena 1;subcadena 2|subcadena tres";
char delimitadores[] = "|;";
char * p;
p = strtok(cadena, delimitadores);
while (p!=NULL){
printf("%s\n", p);
p = strtok(NULL, delimitadores);}
return 0;}