Abraham Zamudio 1 PYTHON Universidad Nacional de Ingenieria Centro de Tecnologias de Informacion y Comunicaciones Facultad de Ciencias Abraham Zamudio Ch.
Abraham Zamudio 1
PYTHON Universidad Nacional de
Ingenieria
Centro de Tecnologias de Informacion y
Comunicaciones
Facultad de Ciencias
Abraham Zamudio Ch.
Abraham Zamudio 3
Lenguaje C
Una introduccion
Abraham Zamudio 4
Abraham Zamudio 5
C: Etapas de Compilacion
Abraham Zamudio 6
El Lenguaje C
● Es un lenguaje de programación de alto nivel, de propósito general, sintácticamente económico.
● Proximidad a la máquina.● Potente.● Su estructura sintáctica y semántica está edificada sobre conceptos tales como:
● estructuras sencillas,● jerarquía de bloques y● control de flujo de datos.
● Instrucciones similares a expresiones algebraicas.● Buen conjunto de operadores.● Fácil de aprender.
Abraham Zamudio 7
Genealogia de C
Abraham Zamudio 8
Estructura de un programa en C
Generalmente, un programa en C tiene la siguiente estructura:● Comandos para el preprocesador (CPP).● Definiciones de tipo de datos de las variables (definición de variables globales).
● Prototipos de funciones (define el nombre de la función, el tipo que retorna y los parámetros que recibe, aunque pueden aparecer sólo los tipos de éstos).
● Funciones (incluyendo la función main()).
Abraham Zamudio 9
Estructura de un programa en C/*Programa de EjemploFecha_Autor_*/#include ____#define ____typedef ____[Prototipos]int main(void){[variables] /* descripcion */[instrucciones]return 0;}
Abraham Zamudio 10
Estructura de una función en C
Cada función debe contener:● Un encabezado de la función, que está compuesta por el nombre de ésta, seguido de una lista opcional de argumentos encerrados entre paréntesis y el tipo de dato que retorna.
● Una lista de declaración de argumentos.● Una o más instrucciones, que conforman el cuerpo de la función.
Abraham Zamudio 11
Estructura de una función en C
Abraham Zamudio 12
El Preprocesador de C
El preprocesador tiene más o menos su propio lenguaje el cual puede ser una herrramienta muy poderosa para el programador. Todas las directivas de preprocesador o comandos inician con un #.
Las ventajas que tiene usar el preprocesador son:
● los programas son más fáciles de desarrollar,● son más fáciles de leer,● son más fáciles de modificar● y el código de C es más transportable entre diferentes arquitecturas de máquinas.
Abraham Zamudio 13
El Preprocesador de C #define
El preprocesador también permite configurar el lenguaje. Por ejemplo, para cambiar a las sentencias de bloque de código { ... } delimitadores que haya inventado el programador como inicio ... fin se puede hacer:
#define inicio {#define fin }Durante la compilación todas las ocurrencias de inicio y fin serán reemplazadas por su correspondiente { o } delimitador y las siguientes étapas de compilación de C no encontrarán ninguna diferencia.
Abraham Zamudio 14
El Preprocesador de C #define
La directiva #define se usa para definir constantes o cualquier sustitución de macro. Su formato es el siguiente:
#define <nombre de macro> <nombre de reemplazo>
Por ejemplo:
#define FALSO 0#define VERDADERO !FALSO
Abraham Zamudio 15
El Preprocesador de C #define
La directiva #define tiene otra poderosa característica: el nombre de macro puede tener argumentos. Cada vez que el compilador encuentra el nombre de macro, los argumentos reales encontrados en el programa reemplazan los argumentos asociados con el nombre de la macro. Por ejemplo:
#include <stdio.h>#define MIN(a,b) (a < b) ? a : b
main(){
int x=125, y=20;printf("EL minimo es %d\n", MIN(x,y) );
}
Cambia este pequeño programa, usa argc y argv para ingresar los argumentos de MIN por teclado
Abraham Zamudio 16
El Preprocesador de C #define
Otros ejemplos usando #define pueden ser:
#define Deg_a_Rad(X) (X*M_PI/180.0)/*Convierte grados sexagesimales a radianes, M_PI es el valor de pi y esta definida en la biblioteca math.h */
Abraham Zamudio 17
El Preprocesador de C #undef
Se usa #undef para quitar una definición de nombre de macro que se haya definido previamente. El formato general es:
#undef <nombre de macro>
El uso principal de #undef es permitir localizar los nombres de macros sólo en las secciones de código que los necesiten.
Abraham Zamudio 18
El Preprocesador de C #include
La directiva del preprocesador #include instruye al compilador para incluir otro archivo fuente que esta dado con esta directiva y de esta forma compilar otro archivo fuente. El archivo fuente que se leerá se debe encerrar entre comillas dobles o paréntesis de ángulo. Por ejemplo:
#include <archivo>
#include "archivo"
Abraham Zamudio 19
El Preprocesador de C #include
Cuando se indica <archivo> se le dice al compilador que busque donde estan los archivos incluidos o ``include'' del sistema. Usualmente los sistemas con UNIX guardan los archivos en el directorio /usr/include.
Si se usa la forma "archivo" es buscado en el directorio actual, es decir, donde el programa esta siendo ejecutado.
Los archivos incluidos usualmente contienen los prototipos de las funciones y las declaraciones de los archivos cabecera (header files) y no tienen código de C (algoritmos).
Abraham Zamudio 20
El Preprocesador de C #if (Inclusión condicional)
La directiva #if evalua una expresión constante entera. Siempre se debe terminar con #endif para delimitir el fin de esta sentencia.
Se pueden así mismo evaluar otro código en caso se cumpla otra condición, o bien, cuando no se cumple ninguna usando #elif o #else respectivamente.
Abraham Zamudio 21
El Preprocesador de C #if (Inclusión condicional)
#if k-expresion-1 <seccion-1>
#elif k-expresion-2 <seccion-2>
... #elif k-expresion-n
<seccion-n>#else
<seccion-final>#endif
Abraham Zamudio 22
El Preprocesador de C #if (Inclusión condicional)
Por ejemplo :
#define MEX 0#define EUA 1#define FRAN 2
#define PAIS_ACTIVO MEX
#if PAIS_ACTIVO == MEXchar moneda[]="pesos";
#elif PAIS_ACTIVO == EUAchar moneda[]="dolar";
#else char moneda[]="franco";
#endif
Abraham Zamudio 23
Lectura de Datos desde la consola
● El prototipo de la función main que se utiliza comúnmente es:int main( void )
● La rutina main no admite ningún parámetro.● Sin embargo existe otro posible prototipo para esta función:
● int main( int argc, char **argv ) o int main( int argc, char *argv[] ) Donde:
● int argc: Este parámetro contiene el número de argumentos con los que el programa ha sido llamado, incluyendo mismo nombre del programa (binario).
● char **argv: Array de punteros a punteros de cadenas de caracteres.
● Ejemplo:● ps –eo pid,ppid,stat,command
● La función main es llamada por el Sistema Operativo (específicamentepor el kernel).
Abraham Zamudio 24
Lecura de Datos desde la consola
Abraham Zamudio 25
Primer Ejemplo (C)
#include <stdio.h>
main(int argc, char *argv[]){
int i,n;
/*Control de numero de argumentos. Es un control habitual en todo programa Unix que debe aceptar un numero determinado de argumentos. */
if (argc < 1) { fprintf(stderr,"argv: utilización argv arg1 arg2 arg3 arg4 ...\n"); return 0;}
/* Visualiza todos los argumentos. Cada componente de argv es una tira. El primer componente es el nombre del programa que se ejecuta */
for (i=0;i<argc;i++) printf("El argumento %d es %s\n",i,argv[i]);
printf("\n");}
Abraham Zamudio 26
Segundo Ejemplo (C)#include <stdio.h> #include <stdlib.h>
int main(int argc, char **argv) { int i; char **p; puts("-------------"); puts("forma 1: \n"); for (i = 0; i < argc; i++) printf("argv[%d]=%s\n", i, argv[i]); puts("-------------"); puts("forma 2: \n"); for (p = argv; *p != NULL; p++) printf("argv[%ld]=%s\n", (long) (p - argv), *p); puts("-------------"); return EXIT_SUCCESS; }
Abraham Zamudio 27
Tercer Ejemplo (C)#include <fcntl.h>main(argc,argv)int argc;char * argv[];{char buffer[16],c;int l,f1,f2,i;f1=0;f2=1;switch(argc){ case 3: f2=open(argv[2],O_WRONLY|O_CREAT); case 2: f1=open(argv[1],O_RDONLY); break; case 1: break; default: perror("Error: demasiados argumentos"); return 0; }while((l=read(f1,buffer,sizeof(buffer)))>0){ for(i=0;i<l;i++){ c = (char)buffer[i]; if (islower(c)) buffer[i]=(char)toupper((int)buffer[i]); else if (isupper(c)) buffer[i]=(char)tolower((int)buffer[i]); } write(f2,buffer,l); }}
Abraham Zamudio 28
Elimina printf y scanf usando argc y argv
#include <stdio.h>#include <stdlib.h>#include <math.h>
int calcula ( float, float );
int main(){float a, b;int mcd;printf("Programa que calcula el MCD de dos numeros:\n\n");printf("Inserta a: ");scanf("%f", &a);printf("Inserta b: ");scanf("%f", &b);mcd= calcula (a , b);printf("El mcd de a y b es %d\n\n", mcd);getchar ();return 0;
}
int calcula ( float a, float b ){
float r, rmcd = 0;do{
r = fmod ( a, b );if ( r == 0 )
b = rmcd;else{
a = b;b = r;rmcd = b;
}} while ( r != 0 );
return ( rmcd );}
Abraham Zamudio 29
Memoria dinámica: malloc y free
Los variables y vectores en C ocupan un tamaño prefijado, no pueden variarlo durante la ejecución del programa. Por medio de punteros se puede reservar o liberar memoria dinámicamente, es decir, según se necesite. Para ello existen varias funciones estándares, de la biblioteca <stdlib.h>x
La función malloc sirve para solicitar un bloque de memoria del tamaño suministrado como parámetro. Devuelve un puntero a la zona de memoria concedida:
Abraham Zamudio 30
Memoria dinámica: malloc y free
void* malloc ( unsigned numero_de_bytes );
El tamaño se especifica en bytes. Se garantiza que la zona de memoria concedida no está ocupada por ninguna otra variable ni otra zona devuelta por malloc.
Si malloc es incapaz de conceder el bloque (p.ej. no hay memoria suficiente), devuelve un puntero nulo.
Abraham Zamudio 31
Memoria dinámica: malloc y free
void *malloc(size_t size);Lo anterior indica que regresará un apuntador del tipo void *, el cual es el inicio en memoria de la porción reservada de tamaño size. Si no puede reservar esa cantidad de memoria la función regresa un apuntador nulo o NULL
Dado que void * es regresado, C asume que el apuntador puede ser convertido a cualquier tipo. El tipo de argumento size_t esta definido en la cabecera stddef.h y es un tipo entero sin signo.
Abraham Zamudio 32
Memoria dinámica: malloc y free
char *cp;cp = (char *) malloc(100);
Intenta obtener 100 bytes y asignarlos a la dirección de inicio a cp.
Es usual usar la función sizeof() para indicar el número de bytes, por ejemplo:
int *ip;ip = (int *) malloc(100 * sizeof(int) );
Abraham Zamudio 33
Memoria dinámica: malloc y freeEl compilador de C requiere hacer una conversión del tipo. La forma de lograr la coerción (cast) es usando (char *) y (int *), que permite convertir un apuntador void a un apuntador tipo char e int respectivamente. Hacer la conversión al tipo de apuntador correcto asegura que la aritmética con el apuntador funcionará de forma correcta.
s una buena práctica usar sizeof() aún si se conoce el tamaño actual del dato que se requiere, -- ya que de esta forma el código se hace independiente del dispositivo (portabilidad).
Abraham Zamudio 34
Memoria dinámica: malloc y free(sizeof)
La función sizeof() puede ser usada para encontrar el tamaño de cualquier tipo de dato, variable o estructura. Simplemente se debe proporcionar uno de los anteriores como argumento a la función.
Por lo tanto:
int i;struct COORD {float x,y,z};struct COORD *pt;
sizeof(int), sizeof(i), sizeof(struct COORD) son tambien sentencias correctas.
Abraham Zamudio 35
Memoria dinámica: malloc(sizeof)
En el siguiente ejemplo se reserva memoria para la variable ip, en donde se emplea la relación que existe entre apuntadores y arreglos, para manejar la memoria reservada como un arreglo. Por ejemplo, se pueden hacer cosas como:
main(){ int *ip, i; ip = (int *) malloc(100 * sizeof(int) ); ip[0] = 1000;
for (i=0; i<100; ++i) scanf("%d",ip++);}
Abraham Zamudio 36
Malloc rulez !!!!!#include <stdio.h> int main(void){float *bufferFloat;int num, i; /*Lee el numero de datos que se van a introducir y despues pide los valores*/printf ("\nCuántos valores va a introducir? ");scanf ("%d", &num);bufferFloat = (float *) malloc (sizeof(float) * num);for (i=0;i<num;i++){printf ("Introduzca el valor numero %d: ", i+1);scanf ("%f", (bufferFloat + i));} for (i=0;i<num;i++){printf ("El valor numero %d que introdujo fue ----> %f\n", i+1, *(bufferFloat+i));}
free(bufferFloat); return 0;}
Abraham Zamudio 37
Librerias (C)
Las librerías son archivos que contienen código objeto agrupado en funciones o subrutinas que pueden ser utilizadas por otros programas.
Estos archivos tienen una estructura especial dependiendo del tipo de librería que se trate.
Abraham Zamudio 38
Librerias (C)
El ejemplo mas popular de librerías en Unix es la libc, que es la librería estándar de funciones C, y contiene subrutinas utilizables para operaciones de entrada y salida.
La notación estándar de la librería es:
● libpalabra.extensión
● palabra: diferencia unas librerías de otras, dando una idea de qué tipo de funciones contiene.
● extensión: indica el tipo de librería, la cual puede ser estática (.a) o dinámica o compartida (.so).
Abraham Zamudio 39
Librerias (C)
Se pueden destacar dos formas de utilización de laslibrerías:● Para utilización local, es decir, como un elemento mas del desarrollo de una aplicación.
● Son de fácil mantención.● Como un paquete de funciones desarrolladas por un fabricante.
● No es posible modificarlas.● En general, el sistema conoce su ubicación.
Abraham Zamudio 40
Librerias (C)
Las librerías de utilización local deben ser registradas en alguna variable de ambiente del sistema para indicar su ubicación, por ejemplo,LD_LIBRARY_PATH.
$echo $ LD_LIBRARY_PATH
Abraham Zamudio 41
Librerias del Sistema
● libc. Librería estándar de C.● Entrada y salida estándar: scanf, printf, getchar, que utiliza el
archivo cabecera stdio.h.● Funciones de manipulación de caracteres y string: strcpy,
strlen, etc.● Fechas: ctime, etc.● Llamadas al sistema: como fork().● Linux utiliza Glic, la librería C GNU.
● libm. Librería de funciones matemáticas: cos, sin, exp, etc.● libX11. Librería interfaz gráfica para el sistema X, con funciones como xterm, xload, etc.
● libGL: librería de OpenGL.● libpthread: librería de POSIX threads.
Abraham Zamudio 42
Makefiles (1º Acto)
El comando de linux make nos ayuda a compilar nuestros programas. Presenta muchas ventajas para programas grandes, en los que hay muchos ficheros fuente (muchos .c y muchos .h) repartidos por varios directorios. Principalmente aporta dos ventajas:
Abraham Zamudio 43
Makefiles (1º Acto)
●Es capaz de saber qué cosas hay que recompilar. Si cuandoestamos depurando nuestro programa tocamos un fichero fuente,al compilar con make sólo se recompilaran aquellos ficheros quedependan del que hemos tocado. Si compilamos a mano con gcc,(o el compilador que sea), o tenemos en la cabeza esas
dependencias para compilar sólo lo que hace falta, o locompilamos todo. Si el proyecto es grande, se nos olvidaráalguna dependencia o nos pasaremos horas compilando.
●Nos guarda los comandos de compilación con todos susparámetros para encontrar librerías, ficheros de cabecera (.h),etc, etc. No tendremos que escribir largas líneas de compilacióncon montones de opciones que debemos saber de memoria o, almenos, sólo tendremos que hacerlo una vez.
Abraham Zamudio 44
Makefiles (1º Acto)
HolaMundo.c
#include <stdio.h> main() { printf ("Hola mundo\n"); }
Abraham Zamudio 45
Makefiles (1º Acto)
Lo compilaremos de la forma habitual.
$ gcc HolaMundo.c -o HolaMundo
Abraham Zamudio 46
Makefiles (1º Acto)
Lo probamos y ¡funciona!. Nada espectacular hasta ahora. Vuelve a compilarlo con el mismo comando. Se vuelve a compilar y sigue funcionando.
Veamos ahora con make. Si haces
$ make HolaMundo
Abraham Zamudio 47
Makefiles (1º Acto)
Pues make te dirá que no hay nada que hacer. Primera diferencia con compilar a mano. Como el programa ya está hecho, make no hace nada. Esto, en un programa de muchas líneas de código que tarda varios minutos en compilar, es una gran ventaja.
Abraham Zamudio 48
Makefiles (1º Acto)
Borra el ejecutable y vuelve a hacer make
$ rm HolaMundo $ make HolaMundo
Abraham Zamudio 49
Makefiles (2º Acto)
Crear dos directorios :
● PRINCIPAL : HolaMundo.c
● FUNCION1 : texto1.h
Abraham Zamudio 50
Makefiles (2º Acto)● HolaMundo.c
#include <stdio.h> #include <texto.h> main() { printf ("%s\n", TEXTO); }
● texto1.h#define TEXTO "Hola Mundo"
Abraham Zamudio 51
Makefiles (2º Acto)
Ahora nos metemos en el directorio PRINCIPAL y hacemos, como antes make HolaMundo. Como es de esperar, obtenemos un error. No sabe encontrar el fichero texto.h, puesto que no está en los directorios por defecto de búsqueda de ficheros .h.
Abraham Zamudio 52
Makefiles (2º Acto)
Una variable bastante interesante es CFLAGS (CPPFLAGS para el compilador de C++). Esta variable puede contener las opciones que queramos que se pasen al compilador. Por ejemplo, si hacermos
$ CFLAGS=-g; export CFLAGS $ make HolaMundo
veremos cómo al compilar el fichero HolaMundo se le pasa al compilador la opción -g (para poder meter luego el debugger).
Abraham Zamudio 53
Makefiles (2º Acto)
Una de las opciones que se puede pasar al compilador es la opción -I, que nos permite poner paths de busqueda para ficheros de cabecera (.h). En nuestro ejemplo, y usando un path relativo, deberíamos poner algo así como -I../FUNCION1. Vamos a ello:
$ CFLAGS=-I../FUNCION1; export CFLAGS $ make HolaMundo
Abraham Zamudio 54
Makefiles (3º Acto)Pongamos ahora en el directorio FUNCION1 dos ficheros. Un funcion1.h y un funcion1.c. El contenido de estos ficheros sería:funcion1.hvoid escribeHolaMundo();
funcion1.c#include <stdio.h>void escribeHolaMundo() { printf ("Hola Mundo\n"); }
El fichero texto.h podemos borrarlo porque ya no nos servirá más.
Abraham Zamudio 55
Makefiles (3º Acto)
En cuanto al programa en el directorio PRINCIPAL, lo modificamos para que ponga esto:HolaMundo.c
#include <funcion1.h> main() { escribeHolaMundo (); }
Abraham Zamudio 56
Makefiles (3º Acto)
Desde el directorio PRINCIPAL ponemos nuestra variable CFLAGS como antes y hacemos el make.
$ CFLAGS=-I../FUNCION1; export CLFAGS $ make HolaMundo
Obtenemos otra vez un error. make compila el fichero HolaMundo.c, no compila el fichero funcion1.c y obtenemos un error. Esto ya es demasiado para resolverlo con variables de entorno. En el momento que tenemos dos ficheros .c para construir un único ejecutable, necesitamos decirle a make cuáles son los ficheros que debe compilar.
Abraham Zamudio 57
Creando Makefiles
objetivo: dependencia1 dependencia2 ... <tab>comando1 <tab>comando2 <tab>...
Abraham Zamudio 58
Creando Makefiles
●objetivo es lo que queremos construir. Puede ser el nombre de un ejecutable,el nombre de una librería o cualquier palabra que nos inventemos. Paranuestro ejemplo, podemos poner que nuestro objetivo es el nombre de nuestroejecutable, es decir HolaMundo.
●dependencia<i> es el nombre de otro objetivo que debe hacerse antes que elnuestro o bien ficheros de los que depende nuestro objetivo. En nuestroejemplo, las dependencias serían nuestros ficheros fuente HolaMundo.c,../FUNCION1/funcion1.h y ../FUNCION1/funcion1.c ya que para hacer elejecutable necesitamos todos esos fuentes y si los tocamos, posiblementedebamos rehacer el ejecutable.
●<tab> es un tabulador. Es importante que ahi pongamos un tabulador, porque si no el fichero no se lee correctamente.
●comando<i> es lo que se tiene que ejecutar para construir nuestro objetivo. Seirán ejecutando estos comandos en secuencia. Puede ser cualquier comandode shell válido. (cc, rm, cp, ls, o incluso un script que nos hayamos hecho). Ennuestro ejemplo, sería cc HolaMundo.c -o HolaMundo.
Abraham Zamudio 59
Creando Makefiles
HolaMundo: HolaMundo.c ../FUNCION1/funcion1.c ../FUNCION1/funcion1.h gcc -I../FUNCION1 HolaMundo.c ../FUNCION1/funcion1.c -o HolaMundo
Ahora, después de borrar el ejecutable, si hacemos make (a secas, sin parámetro), se volverá a compilar nuestro programa. Si a make no le ponemos parámetro, buscará un fichero Makefile y dentro de él hará el primer objetivo que encuentre. En nuestro caso, el único que hay es HolaMundo.