CursC

Preview:

Citation preview

  • 8/3/2019 CursC

    1/23

    Introduccin al lenguaje C 1

    Curso de programacin en C.Miquel A Garcies. Servei de Clcul i Informatitzaci. UIB.

    Introduccin

    Programas

    El desarrollo de un programa

    Tipos bsicos y variables

    Funciones

    Expresiones y operadores

    Conversin de tipos

    Control de flujo

    Definicin y prototipos de funciones

    Construccin de tipos

    Ambito de funciones y variables

    Punteros

    El preprocesador

    Funciones de entrada y salida por pantalla

    Funciones de asignacin de memoria

    Funciones matemticas

    Operaciones con fic heros

    Bibliografa y referencias

    Introduccin.

    C es un lenguaje de programacin de propsito general que ofrece economa sintctica, control de flujo yestructuras sencillas y un buen conjunto de operadores. No es un lenguaje de muy alto nivel y ms bienun lenguaje pequeo, sencillo y no est especializado en ningn tipo de aplicacin. Esto lo hace unlenguaje potente, con un campo de aplicacin ilimitado y sobre todo, se aprende rpidamente. En pocotiempo, un programador puede utilizar la totalidad del lenguaje.

    Este lenguaje ha sido estrechamente ligado al sistema operativo UNIX, puesto que fueron desarrolladosconjuntamente. Sin embargo, este lenguaje no est ligado a ningn sistema operativo ni a ningunamquina concreta. Se le suele llamar lenguaje de programacin de sistemas debido a su utilidad paraescribir compiladores y sistemas operativos, aunque de igual forma se pueden desarrollar cualquier tipode aplicacin.

    La base del C proviene del BCPL, escrito por Martin Richards, y del B escrito por Ken Thompson en

    1970 para el primer sistema UNIX en un DEC PDP-7. Estos son lenguajes sin tipos, al contrario que elC que proporciona varios tipos de datos. Los tipos que ofrece son caracteres, nmeros enteros y en comaflotante, de varios tamaos. Adems se pueden crear tipos derivados mediante la utilizacin de punteros,

  • 8/3/2019 CursC

    2/23

    Introduccin al lenguaje C 2

    vectores, registros y uniones. El primer compilador de C fue escrito por Dennis Ritchie para un DECPDP-11 y escribi el propio sistema operativo en C.

    C trabaja con tipos de datos que son directamente tratables por el hardware de la mayora decomputadoras actuales, como son los caracteres, nmeros y direcciones. Estos tipos de datos pueden sermanipulados por las operaciones aritmticas que proporcionan las computadoras. No proporciona

    mecanismos para tratar tipos de datos que no sean los bsicos, debiendo ser el programador el que losdesarrolle. Esto permite que el cdigo generado sea muy eficiente y de ah el xito que ha tenido comolenguaje de desarrollo de sistemas. No proporciona otros mecanismos de almacenamiento de datos que nosea el esttico y no proporciona mecanismos de entrada ni salida. Ello permite que el lenguaje seareducido y los compiladores de fcil implementacin en distintos sistemas. Por contra, estas carencias secompensan mediante la inclusin de funciones de librera para realizar todas estas tareas, quenormalmente dependen del sistema operativo.

    Originariamente, el manual de referencia del lenguaje para el gran pblico fue el libro [1] de Kernighan y

    Ritchie, escrito en 1977. Es un libro que explica y justifica totalmente el desarrollo de aplicaciones en C,aunque en l se utilizaban construcciones, en la definicin de funciones, que podan provocar confusin yerrores de programacin que no eran detectados por el compilador. Como los tiempos cambian y lasnecesidades tambin, en 1983 ANSI establece el comit X3J11 para que desarrolle una definicin

    moderna y comprensible del C. El estndar est basado en el manual de referencia original de 1972 y sedesarrolla con el mismo espritu de sus creadores originales. La primera versin de estndar se public en1988 y actualmente todos los compiladores utilizan la nueva definicin. Una aportacin muy importantede ANSI consiste en la definicin de un conjunto de libreras que acompaan al compilador y de lasfunciones contenidas en ellas. Muchas de las operaciones comunes con el sistema operativo se realizan atravs de estas funciones. Una coleccin de ficheros de encabezamiento, headers, en los que se definenlos tipos de datos y funciones incluidas en cada librera. Los programas que utilizan estas bibliotecas parainteractuar con el sistema operativo obtendrn un comportamiento equivalente en otro sistema.

    Programas.

    La mejor forma de aprender un lenguaje es programando con l. El programa ms sencillo que se puedeescribir en C es el siguiente:

    main(){}

    Como nos podemos imaginar, este programa no hace nada, pero contiene la parte ms importante decualquier programa C y adems, es el ms pequeo que se puede escribir y que se compile correctamente.En el se define la funcin main, que es la que ejecuta el sistema operativo al llamar a un programa C. Elnombre de una funcin C siempre va seguida de parntesis, tanto si tiene argumentos como si no. Ladefinicin de la funcin est formada por un bloque de sentencias, que esta encerrado entre llaves {}.

    Un programa algo ms complicado es el siguiente:

    #include

    main(){

    printf("Hola amigos!\n");}

    Con el visualizamos el mensaje Hola amigos! en el terminal. En la primera lnea indica que setengan en cuenta las funciones y tipos definidos en la librera stdio ( standard input/output). Estasdefiniciones se encuentran en el fichero headerstdio.h. Ahora, en la funcin main se incluye unanica sentencia que llama a la funcin printf. Esta toma como argumento una cadena de caracteres,que se imprimen van encerradas entre dobles comillas " ". El smbolo \n indica un cambio de lnea.Hay un grupo de smbolos, que son tratados como caracteres individuales, que especifican algunoscaracteres especiales del cdigo ASCII. Los ms importantes son:

    \a alert\b backspace

  • 8/3/2019 CursC

    3/23

    Introduccin al lenguaje C 3

    \f formfeed\n newline\r carriage return\t horizontal tab\v vertical tab\\ backslash

    \' single quote\" double quote\OOO visualiza un carcter cuyo cdigo ASCII es OOO en octal.\xHHH visualiza un carcter cuyo cdigo ASCII es HHH en hexadecimal.

    Las funciones de entrada y salida y los formatos utilizados los explicaremos con ms detalle en otrocaptulo.

    El desarrollo de un programa.

    Un programa C puede estar formado por diferentes mdulos o fuentes. Es conveniente mantener losfuentes de un tamao no muy grande, para que la compilacin sea rpida. Tambin, al dividirse un

    programa en partes, puede facilitar la legibilidad del programa y su estructuracin. Los diferentes fuentes

    son compilados de forma separada, nicamente los fuentes que han sido modificados desde la ltimacompilacin, y despus combinados con las libreras necesarias para formar el programa en su versinejecutable.

    Los comandos necesarios para compilar, linkary ejecutar un programa dependen del sistema operativo ydebemos dirigirnos a los manuales correspondientes para conocer la sintaxis exacta. Como forma mscomn podemos dar la siguiente:

    cc progcc modulo1, modulo2link prog, modulo1, modulo2prog

  • 8/3/2019 CursC

    4/23

    Introduccin al lenguaje C 4

    fuente

    x.c

    fuente

    y.c

    fuente

    z.c

    objeto

    x.obj

    objeto

    y.obj

    objeto

    z.obj

    ejecutable

    prog.exe

    libreraC

    link x,y,z

    cc x cc y cc z

    Creacin de un programa en C.

    Tipos bsicos y variables.

    Los tipos de datos bsicos definidos por C son caracteres, nmeros enteros y nmeros en coma flotante.Los caracteres son representados porchar, los enteros porshort, int, long y los nmeros en comaflotante porfloat y double. Los tipos bsicos disponibles y su tamao son:

    char Carcter (normalmente 8 bits)short Entero corto con signo (normalmente 16 bits)int Entero con signo (depende de la implementacin)unsigned Entero sin signo (depende de la implementacin)

    long Entero largo con signo (normalmente 32 bits)float Flotante simple (normalmente 32 bits)double Flotante doble (normalmente 64 bits)

    La palabra unsigned en realidad es un modificador aplicable a tipos enteros, aunque si no se especificaun tipo se supone int. Un modificador es una palabra clave de C que indica que una variable, o funcin,no se comporta de la forma normal. Hay tambin un modificadorsigned, pero como los tipos son pordefecto con signo, casi no se utiliza.

    Las variables son definidas utilizando un identificador de tipo seguido del nombre de la variable. Veamosel siguiente programa:

    #include main()

    {float cels, farh;

  • 8/3/2019 CursC

    5/23

    Introduccin al lenguaje C 5

    farh = 35.0;cels = 5.0 * ( farh - 32.0 ) / 9.0;printf("-> %f F son %f C\n", farh, cels );

    }

    En el programa anterior se definen dos variables float, se asigna un valor a la primera y se calcula lasegunda mediante una expresin aritmtica. Las asignaciones en C tambin son una expresin, por lo quese pueden utilizar como parte de otra expresin, pero segn que prcticas de este tipo no son muyrecomendables ya que reducen la legibilidad del programa. En la instruccin printf, el smbolo %findica que se imprime un nmero en coma flotante.

    Hay un tipo muy importante que se representa porvoid que puede significar dos cosas distintas, segnsu utilizacin. Puede significar nada, o sea que si una funcin devuelve un valor de tipo void nodevuelve ningn resultado, o puede significar cualquier cosa, como puede ser un puntero a void es un

    puntero genrico a cualquier tipo de dato. Ms adelante veremos su utilizacin.

    Funciones.

    Un programa C est formado por un conjunto de funciones que al menos contiene la funcinmain

    . Unafuncin se declara con el nombre de la funcin precedido del tipo de valor que retorna y una lista deargumentos encerrados entre parntesis. El cuerpo de la funcin est formado por un conjunto dedeclaraciones y de sentencias comprendidas entre llaves. Veamos un ejemplo de utilizacin de funciones:

    #include #define VALOR 5#define FACT 120

    int fact_i ( int v ){

    int r = 1, i = 0;

    while ( i

  • 8/3/2019 CursC

    6/23

    Introduccin al lenguaje C 6

    Todas las lneas que comienzan con el smbolo # indican una directiva del precompilador. Antes derealizar la compilacin en C se llama a un precompilador cuya misin es procesar el texto y realizarciertas sustituciones textuales. Hemos visto que la directiva #include incluye el texto contenido en unfichero en el fuente que estamos compilando. De forma parecida, #define nombre texto sustituyetodas las apariciones de nombre por texto. As, en el fuente, la palabra VALOR se sustituye por elnmero 5.

    El valor que debe devolver una funcin se indica con la palabra return. La evaluacin de la expresindebe dar una valor del mismo tipo de dato que el que se ha definido como resultado. La declaracin deuna variable puede incluir una inicializacin en la misma declaracin. Se debe tener muy en cuenta que enC todos los argumentos son pasados 'por valor'. No existe el concepto de paso de parmetros 'porvariable' o 'por referencia'. Veamos un ejemplo:

    int incr ( int v ){

    return v + 1;}

    main(){

    int a, b;

    b = 3;a = incr(b);

    /*a = 4 mientras queb = 3. No ha cambiado despus de la llamada.

    */}

    En el ejemplo anterior el valor del parmetro de la funcin incr, aunque se modifique dentro de lafuncin, no cambia el valor de la variable b de la funcin main. Todo el texto comprendido entre loscaracteres /* y */ son comentarios al programa y son ignorados por el compilador. En un fuente C loscomentarios no se pueden anidar.

    Expresiones y operadores.

    Los distintos operadores permiten formar expresiones tanto aritmticas como lgicas. Los operadoresaritmticos y lgicos son:

    +, - suma, resta++, -- incremento, decremento*, /, % multiplicacin, divisin, mdulo>>,

  • 8/3/2019 CursC

    7/23

    Introduccin al lenguaje C 7

    implementacin del compilador o de la optimizacin utilizada en una compilacin o en otra. Estos sonerrores que se pueden cometer fcilmente en C ya que una asignacin es tambin una expresin.

    Debemos evitar:

    if (( x++ > 3 ) || ( x < y ))

    y escribir en su lugar:

    x++;if (( x > 3 ) || ( x < y ))

    Hay un tipo especial de expresin en C que se denomina expresin condicional y est representada porlos operadores ? : . Su utilizacin es como sigue: ? : . Se evala si e entoncesx; si no,y.

    int mayor ( int a, int b ){

    return ( a > b ) ? TRUE : FALSE;}

    waste_time (){

    float a, b = 0.0;

    ( b > 0.0 ) ? sin(M_PI / 8) : cos(M_PI / 4);}

    Conversin de tipos.

    Cuando escribimos una expresin aritmtica a+b, en la cual hay variables o valores de distintos tipos, elcompilador realiza determinadas conversiones antes de que evale la expresin. Estas conversiones

    pueden ser para 'aumentar' o 'disminuir' la precisin del tipo al que se convierten los elementos de laexpresin. Un ejemplo claro, es la comparacin de una variable de tipo int con una variable de tipodouble. En este caso, la de tipo int es convertida a double para poder realizar la comparacin.

    Los tipos pequeos son convertidos de la forma siguiente: un tipo char se convierte a int, con elmodificadorsigned si los caracteres son con signo, o unsigned si los caracteres son sin signo. Ununsigned char es convertido a int con los bits ms altos puestos a cero. Un signed char esconvertido a int con los bits ms altos puestos a uno o cero, dependiendo del valor de la variable.

    Para los tipos de mayor tamao: si un operando es de tipo double, el otro es convertido a double. Siun operando es de tipo float, el otro es convertido a float. Si un operando es de tipo unsignedlong, el otro es convertido a unsigned long. Si un operando es de tipo long, el otro es convertidoa long. Si un operando es de tipo unsigned, el otro es convertido a unsigned. Si no, losoperandos son te tipo int.

    Una variable o expresin de un tipo se puede convertir explcitamente a otro tipo, anteponindole el tipoentre parntesis.

    void cambio_tipo (void){

    float a;int b;

    b = 10;a = 0.5;

    if ( a

  • 8/3/2019 CursC

    8/23

    Introduccin al lenguaje C 8

    Control de flujo.

    La sentencia de control bsica es if () then else . En ella se evala una expresincondicional y si se cumple, se ejecuta la sentencia s; si no, se ejecuta la sentencia t. La segunda parte dela condicin, else , es opcional.

    int cero ( double a ){

    if ( a == 0.0 )return (TRUE);

    elsereturn (FALSE);

    }

    En el caso que no sea una expresin condicional y sea aritmtica, se considera falso si vale 0; y sino, verdadero. Hay casos en los que se deben evaluar mltiples condiciones y nicamente se debe evaluaruna de ellas. Se puede programar con un grupo de sentencias if then else anidadas, aunque ello

    puede ser farragoso y de complicada lectura. Para evitarlo nos puede ayudar la sentencia switch. Suutilizacin es:

    switch (valor) {

    case valor1:

    case valor2: ...

    default: }

    Cuando se encuentra una sentencia case que concuerda con el valor del switch se ejecutan lassentencias que le siguen y todas las dems a partir de ah, a no ser que se introduzca una sentenciabreak para salir de la sentencia switch. Por ejemplo,

    ver_opcion ( char c )

    {switch(c){

    case 'a': printf("Op A\n"); break;case 'b': printf("Op B\n"); break;case 'c':case 'd': printf("Op C o D\n");break;default: printf("Op ?\n");

    }}

    Otras sentencias de control de flujo son las que nos permiten realizar iteraciones sobre un conjunto desentencias. En C tenemos tres formas principales de realizar iteraciones. La sentenciawhile () es seguramente la ms utilizada. La sentencia, o grupo de sentencias se ejecuta mientras laevaluacin de la expresin sea verdadera.

    long raiz ( long valor ){

    long r = 1;

    while ( r * r

  • 8/3/2019 CursC

    9/23

  • 8/3/2019 CursC

    10/23

    Introduccin al lenguaje C 10

    Definicin y prototipos de funciones.

    Los programas sencillos, como los ejemplo planteados hasta ahora, normalmente no necesitan un nivelde estructuracin elevado. Pero cuando stos crecen un poco necesitamos estructurarlos adecuadamente

    para mantenerlos legibles, facilitar su mantenimiento y para poder reutilizar ciertas porciones de cdigo.El mecanismo C que nos permite esto son las funciones. Con los compiladores, los fabricantes nos

    proporcionan un conjunto importante de funciones de librera. A veces, nos puede interesar construirnuestras propias libreras. Ya hemos utilizado funciones, pero veamos cmo debemos definirlas.

    Los prototipos de funciones son una caracterstica clave de la recomendacin ANSI del C. Un prototipoes una declaracin que toma la forma:

    tipo_resultado nombre_funcin ( tipo_parmetro nombre_parmetro ... );

    Aqu tenemos varios ejemplos:

    int fact_i ( int v );int mayor ( int a, int b );int cero ( double a );

    long raiz ( long valor );void final_countdown ( void );int main ( int argc, char **argv );

    Observando el prototipo de una funcin podemos decir exactamente que tipo de parmetros necesita y queresultado devuelve. Si una funcin tiene como argumento void, quiere decir que no tiene argumentos, aligual que si el resultado es void, no devuelve ningn valor.

    En la vieja definicin de Kernighan y Ritchie el tipo que devolva una funcin se declaraba nicamente s iera distinto de int. Similarmente, los parmetros eran declarados en el cuerpo de la funcin, en lugar deutilizar la lista de parmetros. Por ejemplo:

    mayor ( a, b )int a;int b;{...}

    Las funciones al viejo estilo se compilan correctamente en muchos compiladores actuales. Por contra, proporcionan menos informacin sobre sus parmetros y errores que afecten al tipo de parmetros dellamada a las funciones no pueden ser detectados automticamente. Por tanto, la declaracin de unafuncin debe escribirse igual que su prototipo pero sin el punto y coma final. El cuerpo de la funcin lesigue encerrado entre llaves.

    En un programa que est formado por distintas partes bien diferenciadas es conveniente utilizar mltiples

    ficheros fuente. Cada fuente agrupa las funciones semejantes, como por ejemplo en un compilador podramos tener un fuente para el anlisis lxico, otro para el sintctico y otro para la generacin decdigo. Pero en un fuente necesitaremos funciones que se han definido en otro. Para ello, escribiremos unfichero de cabecera (header), que contendr las declaraciones que podemos necesitar en otros fuente. As,en el fuente que implementa el analizador sintctico pondremos una lnea #include "lexic.h". Deesta forma al compilar el mdulo sintctico tendremos todos los prototipos de las funciones del lxico yel compilador podr detectar malas utilizaciones de las funciones all definidas.

    Construccin de tipos.

    Los datos del mundo real, normalmente no estn formados por variables escalares de tipos los tiposbsicos. Por ejemplo, nos puede interesar saber cuntos mdulos en C hemos escrito cada semana, a lolargo del ao. O tambin nos interesa tener los datos de cada planeta del Sistema Solar, masa, posicin,

    velocidad y aceleracin, para un programa de simulacin de la ley de gravitacin de Newton. Pararesolver el primer caso, C nos permite declarar una variable que sea de tipo vector. Para el segundo,

    podemos definir un registro para cada elemento.

  • 8/3/2019 CursC

    11/23

    Introduccin al lenguaje C 11

    Un vector es una porcin de memoria que es utilizada para almacenar un grupo de elementos del mismotipo Un vector se declara: tiponombre[tamao];. Por ejemplo, int modulo[52];. Aqu 'modulo'es un vector de 52 elementos enteros.

    main(){

    int f, modulo[52];

    for ( f = 0; f < 52; f++ )modulo[f] = 0;

    ...}

    Cada elemento de un vector es accedido mediante un nmero de ndice y se comporta como una variabledel tipo base del vector. Los elementos de un vector son accedidos por ndices que van desde 0 hasta N-1

    para un vector de N elementos. Los elementos de un vector pueden ser inicializados en la mismadeclaracin:

    char vocal[5] = { 'a', 'e', 'i', 'o', 'u' };float n_Bode[5] = { 0.4, 0.7, 1, 1.6, 2.8 };

    Tambin podemos definir vectores multidimensionales. C no impone ninguna limitacin al nmero dedimensiones de un vector. Existe, en cambio, la limitacin del tamao de memoria que podamos utilizaren nuestro ordenador. Por ejemplo, para la declaracin de un vector multidimensional podemos escribir:

    int video[25][80][2];

    El tamao de la variable video es proporcional al tamao del tipo int y al tamao de cada dimensin.Existe un operador C que nos permite obtener el tamao de un tipo o de una variable. Este essizeof() y nos proporciona el tamao en bytes.

    if ( sizeof(video) == 80 * 25 * 2 * sizeof(int) )printf("OK!\n");

    elseprintf("Algo no funciona.\n");

    Un tipo vector muy utilizado es la cadena de caracteres (string). Si queremos asignar espacio para unstring podemos hacer:

    char nombre[60], direccion[80];

    Es un vector C pero con la particularidad de que el propio lenguaje utiliza un carcter especial comomarca de final de string. As en un vector de caracteres de tamao N podremos almacenar una cadena de

    N-1 caracteres, cuyo ltimo carcter estar en la posicin N-2 y la marca de final de string en la N-1.

    Veamos un ejemplo:

    char servei[6] = "SCI";

    La posicin 0 contiene el carcter 'S'; la 1 el 'C'; la 2 el 'I'; la 3 el '\0', marca de final de string. El restode componentes no estn definidas. En la inicializacin de strings no se debe indicar el final; ya lo haceel compilador. Para la manipulacin de cadenas de caracteres ANSI proporciona el fichero string.hque contiene las declaraciones de un conjunto de funciones proporcionadas con la librera del compilador.

    Un registro agrupa distintos tipos de datos en una misma estructura. Los registros son definidos de laforma:

    struct nombre { lista de declaraciones };

  • 8/3/2019 CursC

    12/23

    Introduccin al lenguaje C 12

    Los campos de cada registro pueden ser tipos bsicos u otros registros. Por ejemplo:

    struct planeta {struct 3D r, v, a;double masa;char nom[10];

    };

    struct 3D {double x,y,z;

    };

    Los campos de cada registro son accesibles mediante el nombre del registro seguido de punto y elnombre del campo, como por ejemplo venus.r.x = 1.0;. Cada campo se comporta como lo hacesu tipo bsico. C no proporciona mecanismos de inicializacin ni copia de registros, por lo que debe serel programador el que los implemente.

    A veces los datos se ajustan a series ordenadas en las cuales un elemento sigue, o precede, a otro. Uncaso tpico son los das de la semana. Si se desea realizar iteraciones con los das de la semana una formaes, por ejemplo, asignar un nmero a cada da con #define. C proporciona un mecanismo compacto

    para realizar esto; son las enumeraciones. Una enumeracin toma la forma: enumnombre { lista deelementos };. Veamos un ejemplo:

    void planning ( void ){

    enum diasemana { lunes, martes, miercoles,jueves, viernes, sabado, domingo };

    int dia;

    for ( dia = lunes; dia

  • 8/3/2019 CursC

    13/23

    Introduccin al lenguaje C 13

    funciones. Estas variables se crean en la memoria del ordenador cuando se llama a la funcin y sedestruyen cuando la funcin termina de ejecutarse. Es necesario a veces, que una variable tenga un valorque pueda ser accesible desde todas las funciones de un mismo fuente, e incluso desde otros fuentes.

    En C, el mbito de las variables depende de dnde han sido declaradas y si se les ha aplicado algnmodificador. Una variable definida en una funcin es, por defecto, una variable local. Esto es, que slo

    existe y puede ser accedida dentro de la funcin. Para que una variable sea visible desde una funcincualquiera del mismo fuente debe declararse fuera de cualquier funcin. Esta variable slo ser visible enlas funciones definidas despus de su declaracin. Por esto, el lugar ms comn para definir las variablesglobales es antes de la definicin de ninguna funcin. Por defecto, una variable global es visible desdeotro fuente. Para definir que existe una variable global que est definida en otro fuente tenemos queanteponer la palabra extern a su declaracin. Esta declaracin nicamente indica al compilador que sehar referencia a una variable declarada en un mdulo distinto al que se compila.

    Las variables locales llevan implcito el modificador auto. Este indica que se crean al inicio de laejecucin de la funcin y se destruyen al final. En un programa sera muy ineficiente en trminos dealmacenamiento que se crearan todas las variables al inicio de la ejecucin. Por contra, en algunos casoses deseable. Esto se consigue anteponiendo el modificadorstatic a una variable local. Si una funcinnecesita una variable que nicamente sea accedida por la misma funcin y que conserve su valor a travsde sucesivas llamadas, es el caso adecuado para que sea declarada local a la funcin con el modificador

    static. El modificadorstatic se puede aplicar tambin a variables globales. Una variable global espor defecto accesible desde cualquier fuente del programa. Si, por cualquier motivo, se desea que una deestas variables no se visible desde otro fuente se le debe aplicar el modificador static. Lo mismoocurre con las funciones. Las funciones definidas en un fuente son utilizables desde cualquier otro. Eneste caso conviene incluir los prototipos de las funciones del otro fuente. Si no se desea que algunafuncin pueda ser llamada desde fuera del fuente en la que est definida se le debe anteponer elmodificadorstatic.

    void contar ( void ){

    static longcuenta = 0;

    cuenta++;printf("Llamada %ld veces\n", cuenta );

    }

    Un modificador muy importante es const. Con l se pueden definir variables cuyo valor debepermanecer constante durante toda la ejecucin del programa. Tambin se puede utilizar con argumentosde funciones. En esta caso se indica que el argumento en cuestin es un parmetro y su valor no debe sermodificado. Si al programar la funcin, modificamos ese parmetro, el compilador nos indicar el error.

    #define EULER 2.71828const double pi = 3.14159;

    double lcercle ( const double r ){

    return 2.0 * pi * r;

    }

    double EXP ( const double x ){

    return pow ( EULER, x );}

    double sinh ( const double x ){

    return (exp(x) - exp(-x)) / 2.0;}

    Debemos fijarnos que en el ejemplo anteriorpi es una variable, la cual no podemos modificar. Por ellopi slo puede aparecer en un nico fuente. Si la definimos en varios, al linkar el programa se nosgenerar un error por tener una variable duplicada. En el caso en que queramos acceder a ella desde otrofuente, debemos declararla con el modificadorextern.

  • 8/3/2019 CursC

    14/23

    Introduccin al lenguaje C 14

    Otro modificador utilizado algunas veces es el register. Este modificador es aplicable nicamente avariables locales e indica al compilador que esta variable debe ser almacenada permanentemente en unregistro del procesador del ordenador. Este modificador es herencia de los viejos tiempos, cuando lastecnologas de optimizacin de cdigo no estaban muy desarrolladas y se deba indicar qu variable eramuy utilizada en la funcin. Hoy en da casi todos los compiladores realizan un estudio de qu variableslocales son las ms adecuadas para ser almacenadas en registros, y las asignan automticamente. Con loscompiladores modernos se puede dar el caso de que una declaracin register inadecuada disminuya lavelocidad de ejecucin de la funcin, en lugar de aumentarla. Por ello, hoy en da, la utilizacin de estemodificador est en desuso, hasta el punto de que algunos compiladores lo ignoran. Se debe tener encuenta que de una variable declarada como register no se puede obtener su direccin, ya que estalmacenada en un registro y no en memoria.

    Punteros.

    Cada variable de un programa tiene una direccin en la memoria del ordenador. Esta direccin indica laposicin del primer byte que la variable ocupa. En el caso de una estructura es la direccin del primercampo. En los ordenadores actuales la direccin de inicio se considera la direccin baja de memoria.Como en cualquier caso las variables son almacenadas ordenadamente y de una forma predecible, es

    posible acceder a estas y manipularlas mediante otra variables que contenga su direccin. A este tipo devariables se les denomina punteros.

    Los punteros C son el tipo ms potente y seguramente la otra clave del xito del lenguaje. La primeraventaja que obtenemos de los punteros es la posibilidad que nos dan de poder tratar con datos de untamao arbitrario sin tener que moverlos por la memoria. Esto puede ahorrar un tiempo de computacinmuy importante en algunos tipos de aplicaciones. Tambin permiten que una funcin reciba y cambie elvalor de una variable. Recordemos que todas las funciones C nicamente aceptan parmetros por valor.Mediante un puntero a una variable podemos modificarla indirectamente desde una funcin cualquiera.

    Un puntero se declara de la forma: tipo*nombre;

    float *pf;PLANETA *pp;char *pc;

    Para manipular un puntero, como variable que es, se utiliza su nombre; pero para acceder a la variable a laque apunta se le debe preceder de *. A este proceso se le llama indireccin. Accedemos indirectamente auna variable. Para trabajar con punteros existe un operador, &, que indica 'direccin de'. Con l se puedeasignar a un puntero la direccin de una variable, o pasar como parmetro a una funcin.

    void prova_punter ( void ){

    long edat;long *p;

    p = &edat;edad = 50;

    printf("La edat es %ld\n", edat );*p = *p / 2;printf("La edat es %ld\n", edat );

    }

    void imprimir_string ( char string[] ){

    char *p;

    for ( p = string; *p != '\0'; p++ )imprimir_char(*p);

    }

    Los punteros tambin se pueden utilizar con los registros. Para ello se utiliza la notacin -> en lugar del

    punto que utilizbamos anteriormente. Si p es un puntero a PLANETA, y queremos conocer su masa,debemos escribirp->masa. Un puntero se puede utilizar para almacenar la direccin de cualquier tipo dedatos, tanto simple como un vector, como un registro. De cmo lo definimos y lo utilizamos depende su

  • 8/3/2019 CursC

    15/23

    Introduccin al lenguaje C 15

    comportamiento. Las componentes de un vector, por ejemplo pueden ser referenciadas por un puntero altipo de cada componente. Veamos un ejemplo:

    #define N_PLA 9

    static PLANETA SSolar[N_PLA];

    void init_SistemaSolar ( void ){

    PLANETA *p;

    for ( p = SSolar; p < SSolar[N_PLA]; p++ )init_planeta(p);

    }

    void init_planeta ( PLANETA *p ){

    p->masa = 0;p->nom = "";init_co(&(p->r));init_co(&(p->v));

    init_co(&(p->a));}

    void init_co ( struct co *c ){

    c->x = c->y = c->z = 0;}

    Definimos un vector de N_PLA componentes de tipo PLANETA. Este tipo est formado por un registro.Vemos que en la funcin de inicializacin del vector el puntero a la primera componente se inicializa conel nombre del vector. Esto es una caracterstica importante de C. La direccin de la primera componentede un vector se puede direccionar con el nombre del vector. Esto es debido a que en la memoria delordenador, los distintos elementos estn ordenados de forma ascendente. As, SSolar se puede utilizarcomo &SSolar[0]. A cada iteracin llamamos a una funcin que nos inicializar los datos de cada

    planeta. A esta funcin le pasamos como argumento el puntero a la componente en curso para que,utilizando la notacin ->, pueda asignar los valores adecuados a cada campo del registro. Debemosfijarnos en el incremento del puntero de control de la iteracin, p++. Con los punteros se pueden realizardeterminadas operaciones aritmticas aunque, a parte del incremento y decremento, no son muyfrecuentes. Cuando incrementamos un puntero el compilador le suma la cantidad necesaria para queapunte al siguiente elemento de la memoria. Debemos fijarnos que esto es aplicable slo siempre quehaya distintas variables o elementos situados consecutivamente en la memoria, como ocurre con losvectores.

    De forma similar se pueden utilizar funciones que tengan como parmetros punteros, para cambiar elvalor de una variable. Veamos:

    void intercambio ( void )

    {int a, b;

    a = 1;b = 2;swap( &a, &b );printf(" a = %d b = %d\n", a, b );

    }

    void swap ( int *x, int *y ){

    int tmp;

    tmp = *x;*x = *y;

    *y = tmp;}

  • 8/3/2019 CursC

    16/23

    Introduccin al lenguaje C 16

    La sintaxis de C puede, a veces, provocar confusin. Se debe distinguir lo que es un prototipo de unafuncin de lo que es una declaracin de una variable. As mismo, un puntero a un vector de punteros,etc...

    int f1(); funcin que devuelve un enteroint *p1; puntero a entero

    int *f2(); funcin que devuelve un puntero a enteroint (*pf)(int); puntero a funcin que toma y devuelve un enteroint (*pf2)(int *pi); puntero a funcin que toma un puntero a entero y

    devuelve un enteroint a[3]; vector de tres enterosint *ap[3]; vector de tres punteros a enteroint *(ap[3]); vector de tres punteros a enteroint (*pa)[3]; puntero a vector de tres enterosint (*apf[5])(int*pi);

    vector de 5 punteros a funcin que toman un puntero aentero y devuelven un entero

    En los programas que se escriban se debe intentar evitar declaraciones complejas que dificulten lalegibilidad del programa. Una forma de conseguirlo es utilizando typedef para redefinir/renombrartipos.

    typedef int *intptr;typedef intptr (*fptr) ( intptr );fptr f1, f2;

    El preprocesador.

    El preprocesador es una parte del compilador que se ejecuta en primer lugar, cuando se compila un fuenteC y que realiza unas determinadas operaciones, independientes del propio lenguaje C. Estas operacionesse realizan a nivel lxico y son la inclusin de otros textos en un punto del fuente, realizar sustitucioneso eliminar ciertas partes del fuente. Debemos tener en cuenta que el preprocesador trabaja nicamente conel texto del fuente y no tiene en cuenta ningn aspecto sintctico ni semntico del lenguaje.

    El control del preprocesador se realiza mediante determinadas directivas incluidas en el fuente. Unadirectiva es una palabra que interpreta el preprocesador, que siempre va precedida por el smbolo # y queest situada a principio de lnea.

    La directiva #define se utiliza para definir una macro. Las macros proporcionan principalmente unmecanismo para la sustitucin lxica. Una macro se define de la forma #define idsecuencia. Cadaocurrencia de iden el fuente es sustituida porsecuencia. Puede definirse una macro sin una secuencia decaracteres. Una macro se puede indefinir mediante la directiva #undef.

    #define MSG01 "SCI-I-START: Starting system kernel\n"#define MSG02 "SCI-I-STOP: Stopping system kernel\n"

    void print_msg ( void ){

    if ( check_state() == START )printf(MSG01);

    elseprintf(MSG02);

    }

    El estado de una macro, si est definida o no, se puede comprobar mediante las directivas #ifdef y#ifndef. Estas dos directivas se deben completar con una #endif y, el texto comprendido entreambas es procesado si la macro est definida. Todas las directivas deben ser completadas en el mismofuente y pueden ser anidadas.

    #ifndef M_PI#define M_PI 3.1415927

    #endif

  • 8/3/2019 CursC

    17/23

    Introduccin al lenguaje C 17

    El preprocesador nos permite tambin incluir tambin otros ficheros en un fuente C. Esto se consigue conla directiva #include. Esta puede tomar tres formas: #include , #include "fichero"y #includemacro. La diferencia entre la primera y la segunda est en el lugar dnde se buscar elfichero en cuestin. Normalmente se utiliza la primera para ficheros proporcionados por la librera delcompilador y la segunda para ficheros creados por el programador.

    Funciones de entrada y salida por pantalla.

    En este apartado y los siguientes vamos a ver algunas de las funciones ms importantes que nos proporcionan las libreras definidas por ANSI y su utilizacin. Como hemos visto hasta ahora, ellenguaje C no proporciona ningn mecanismo de comunicacin ni con el usuario ni con el sistemaoperativo. Ello es realizado a travs de las funciones de librera proporcionadas por el compilador.

    El fichero de declaraciones que normalmente ms se utiliza es el stdio.h. Vamos a ver algunasfunciones definidas en l.

    Una funcin que ya hemos utilizado y que, ella y sus variantes, es la ms utilizadas para la salida deinformacin es printf. Esta permite dar formato y enviar datos a la salida estndar del sistemaoperativo.

    #include int printf ( const char *format [, argumentos, ...] );

    Acepta un string de formato y cualquier nmero de argumentos. Estos argumentos se aplican a cada unode los especificadores de formato contenidos en format. Un especificador de formato toma la forma%[flags][width][.prec][h|l] type. El tipo puede ser:

    d, i entero decimal con signoo entero octal sin signou entero decimal sin signox entero hexadecimal sin signo (en minsculas)

    X entero hexadecimal sin signo (en maysculas)f coma flotante en la forma [-]dddd.dddde coma flotante en la forma [-]d.dddd e[+/-]dddg coma flotante segn el valorE como e pero en maysculasG como g pero en maysculasc un carcters cadena de caracteres terminada en '\0'% imprime el carcter %p puntero

    Losflags pueden ser los caracteres:

    + siempre se imprime el signo, tanto + como -- justifica a la izquierda el resultado, aadiendo espacios al finalblank si es positivo, imprime un espacio en lugar de un signo +

    # especifica la forma alternativa

    En el campo width se especifica la anchura mnima de la forma:

    n se imprimen al menos n caracteres.

    0n se imprimen al menos n caracteres y si la salida es menor, seanteponen ceros

    * la lista de parmetros proporciona el valor

    Hay dos modificadores de tamao, para los tipos enteros:

    l imprime un entero long

  • 8/3/2019 CursC

    18/23

    Introduccin al lenguaje C 18

    h imprime un entero short

    Otra funcin similar a printf pero para la entrada de datos es scanf. Esta toma los datos de laentrada estndar del sistema operativo. En este caso, la lista de argumentos debe estar formada por

    punteros, que indican dnde depositar los valores.

    #include int scanf ( const char *format [, argumentos, ...] );

    Hay dos funciones que trabajan con strings. La primera lee un string de la entrada estndar y la segundalo imprime en el dispositivo de salida estndar.

    #include char *gets ( char *s );int puts ( char *s );

    Tambin hay funciones de lectura y escritura de caracteres individuales.

    #include int getchar ( void );int putchar ( int c );

    Veamos, por ejemplo, un programa que copia la entrada estndar a la salida estndar del sistemaoperativo, carcter a carcter.

    #include

    main(){

    int c;

    while ( (c = getchar()) != EOF )

    putchar(c);}

    Funciones de asignacin de memoria.

    Hemos visto que en C, las variables estticas pueden estar creadas al inicio de la ejecucin del programa,o bien son variables locales automticas que se crean al iniciarse la ejecucin de una funcin. En muchasaplicaciones en necesario la utilizacin de estructuras de datos dinmicas, o bien variables que debenexistir durante un tiempo no determinado a priori. Para asignar memoria de forma dinmica, C utilizavarias funciones definidas en stdlib.h. Vamos a ver la utilizacin de estas funciones.

    La funcin de asignacin de memoria ms utilizada es malloc. Esta toma como parmetro el tamao enbytes y devuelve un puntero al bloque de memoria asignado. Si no hay memoria suficiente para asignar

    el bloque devuelve NULL.

    #include void *malloc ( size_t size );

    El tipo size_t est definido normalmente como unsigned y se utiliza en todas las funciones quenecesitan un tamao en bytes. Otra funcin similar es:

    #include void *calloc ( size_t nitems, size_t size );

    En este caso se le pasa como parmetro el nmero de elementos consecutivos que se desean. A diferencia

    de malloc, calloc inicializa el contenido de la memoria asignada al valor 0. La funcin que se utiliza

  • 8/3/2019 CursC

    19/23

    Introduccin al lenguaje C 19

    para devolver memoria dinmica previamente asignada es free. Esta toma como parmetro un punteropreviamente obtenido con malloc o calloc

    #include void free ( void *block );

    Hay que tener en cuenta, que la funcin free no cambia el valor del parmetro. El puntero al cual sehaba asignado memoria y ahora se ha liberado sigue almacenando la direccin de memoria, pero esta yano existe despus de llamar a free. Es misin del programador actualizar el valor del puntero, si esnecesario. Como ejemplo mostraremos una funcin que asigna memoria para un vector, lo inicializa y lolibera.

    void alloc_array ( void ){

    int *v, f;

    if ((v = (int *) calloc ( 10, sizeof(int) )) == NULL)printf("No hay memoria\n");

    else {for ( f = 0; f < 10; f++ )

    v[f] = 0;

    free(v);v = NULL;

    }}

    Debemos observar la conversin de tipo realizada al valor que devuelve calloc. Todas las funcionesdevuelven punteros a void, por lo que se deben convertir al tipo de nuestra variable.

    Funciones matemticas.

    La utilizacin de las funciones matemticas definidas en el ANSI C requieren la inclusin del fichero

    math.h. Todas ellas trabajan con el tipo double, por lo que si los argumentos o resultados son deltipo float el propio compilador se encarga de convertirlos al formato adecuado. En ANSI se esttrabajando para proporcionar funciones con argumentos de tipo float e introducir el tipo longfloat. Casi todas la funciones tienen la forma double nombre ( double x );.

    atan2 toma dos argumentos x e y y devuelve la arcotangente de y/x en radianes.

    exp devuelve el valor e elevado a x.

    acos retorna el arco coseno del parmetro x.

    asin retorna el arco seno del parmetro x.

    atanretorna el valor de la arco tangente del parmetro x.

    cos retorna el coseno del ngulo x.

    cosh retorna el coseno hiperblico del parmetro x.

    sin retorna el seno del ngulo x.

    sinh retorna el seno hiperblico del parmetro x.

    tan retorna la tangente del ngulo x.

    tanh retorna la tangente hiperblica del parmetro x.

    log retorna el logaritmo natural del parmetro x.

    log10 retorna el logaritmo en base 10 del parmetro x.

    pow toma dos parmetrosx ey y devuelve el valorxy

  • 8/3/2019 CursC

    20/23

    Introduccin al lenguaje C 20

    sqrt retorna la raiz cuadrada del parmetro x.

    Operaciones con ficheros.

    La entrada y salida a ficheros es uno de los aspectos ms delicados de cualquier lenguaje de programacin, pues suelen estar estrechamente integradas con el sistema operativo. Los servicios

    ofrecidos por los sistemas operativos varan enormemente de un sistema a otro. Las libreras del Cproporcionan un gran conjunto de funciones, muchas de ellas descritas en el libro de Kernighan y Ritchie

    y otras derivadas de los servicios que ofrece el Unix.

    En C hay dos tipos de funciones de entrada/salida a ficheros. Las primeras son derivadas del SO Unix ytrabajan sin buffer. Las segundas son las que fueron estandarizadas por ANSI y utilizan un bufferintermedio. Adems, hacen distinciones si trabajan con ficheros binarios o de texto. Veremos lassegundas, que son las ms utilizadas.

    Las funciones del C no hacen distincin si trabajan con un terminal, cinta o ficheros situados en undisco. Todas las operaciones se realizan a travs de streams. Un stream est formado por una serieordenada de bytes. Leer o escribir de un fichero implica leer o escribir del stream. Para realizaroperaciones se debe asociar un stream con un fichero, mediante la declaracin de un puntero a una

    estructura FILE. En esta estructura se almacena toda la informacin para interactuar con el SO. Estepuntero es inicializado mediante la llamada a la funcin fopen(), para abrir un fichero.

    Cuando se ejecuta todo programa desarrollado en C hay tresstreams abiertos automticamente. Estos sonstdin,stdoutystderr. Normalmente estosstreams trabajan con el terminal, aunque el sistema operativopermite redireccionarlos a otros dispositivos. Las funciones printf() y scanf() que hemos visto,utilizanstdoutystdin respectivamente.

    Los datos de los ficheros pueden ser accedidos en uno de los dos formatos: texto o binario. Un textstream consiste en una serie de lineas de texto acabadas con un carcter newline. En modo binario unfichero es una coleccin de bytes sin ninguna estructura especial.

    Hay que tener muy en cuenta que respecto a la velocidad de la memoria y de la CPU, los dispositivos de

    entrada y salida con muy lentos. Puede haber cinco o seis rdenes de magnitud entre la velocidad de laCPU y la de un disco duro. Adems una operacin de entrada y salida puede consumir una cantidadimportante de recursos del sistema. Por ello, conviene reducir en nmero de lecturas y escrituras a disco.La mejor forma de realizar esto es mediante un buffer. Un buffer es una rea de memoria en la cual losdatos son almacenados temporalmente, antes de ser enviados a su destino. Por ejemplo, las operacionesde escritura de caracteres a un fichero se realizan sobre el bufferdelstream. Unicamente cuando se llena elbuffer se escriben todos los caracteres sobre el disco de una vez. Esto ahorra un buen nmero deoperaciones sobre el disco. Las funciones del C nos permiten modificar el tamao y comportamiento delbufferde unstream.

    Para utilizar las funciones de ficheros se debe incluir el fichero stdio.h. Este define los prototipos detodas las funciones, la declaracin de la estructura FILE y algunas macros y definiciones. Una definicinimportante es EOF, que es el valor devuelto por muchas funciones cuando se llega al final de fichero.

    Los pasos a seguir para operar con un fichero son: abrir, realizar el tratamiento y cerrar. Para abrir unfichero se utiliza la funcin fopen. Esta toma dos strings como parmetros. El primero indica elnombre del fichero que deseamos abrir y el segundo indica el modo de acceso.

    #include FILE *fopen ( const char *filename, const char *mode );

    Los modos de acceso parastreams de texto son los siguientes:

    "r" Abre un fichero que ya existe para lectura. La lectura se realiza al inicio del fichero.

    "w" Se crea un nuevo fichero para escribir. Si el fichero existe se inicializa y

    sobreescribe.

  • 8/3/2019 CursC

    21/23

  • 8/3/2019 CursC

    22/23

    Introduccin al lenguaje C 22

    La funcin fgets lee caracteres delstream hasta que encuentra un final de lnea o se lee el carcter n-1.Mantiene el carcter\n en el string y aade el carcter\0. Devuelve la direccin del string o NULL si se

    produce algn error. La funcin fputs copia el string alstream, no aade ni elimina caracteres \n y nocopia la marca de final de string \0.

    Hay dos funciones que nos permiten trabajan en bloques. Podemos considerar un bloque como un array.

    Debemos especificar el tamao de cada elemento y el nmero de elementos.

    #include size_t fread ( void *p, size_t s, size_t n, FILE *f );size_t fwrite ( void *p, size_t s, size_t n, FILE *f );

    A las anteriores funciones se les pasa un puntero genrico p con la direccin del rea de datos que sedesea leer o escribir, el tamao de cada elemento s, y el nmero de elementos n, adems del stream.Ambas devuelven el nmero de elementos ledos o escritos, que debe ser el mismo que le hemosindicado, en el caso en que no se haya producido ningn error.

    Hasta ahora hemos visto funciones de tratamiento de ficheros que nos van bien para realizar untratamiento secuencial de stos. Debemos tener en cuenta que cuando debemos realizar operaciones de

    entrada y de salida alternadas, se deben vaciar los buffers de los streams para que las operaciones serealicen correctamente sobre los ficheros. Esto se consigue con las funciones fflush y flushall. La

    primera es aplicable alstream que deseemos y la segunda vaca los buffers de todos losstreams abiertos.

    #include int fflush ( FILE *stream );int flushall ( void );

    Si deseamos hacer un acceso aleatorio a un fichero disponemos de las funciones fseek y ftell. Laprimera mueve el cursor, indicador de posicin, a un lugar determinado dentro del fichero. Este lugarviene determinado por el parmetrowhence. Este puede ser SEEK_SET, si es desde el inicio delfichero, SEEK_CUR si es desde la posicin actual y SEEK_END si es desde el final del fichero. Devuelve0 si la operacin se realiza correctamente.

    #include int fseek ( FILE *stream, long offset, int whence );long ftell ( FILE *stream );

    La segunda funcin devuelve la posicin del cursor dentro del fichero. Este valor indica el nmero debytes desde el inicio del fichero, si el fichero es binario.

    int escribir_planetas ( char *nombre ){

    PLANETA *p;FILE *f;

    if ( ( f = fopen ( "w+", nombre )) == NULL )return ERROR;

    if (N_PLA != fwrite ( p, sizeof(PLANETA), N_PLA, f ))return ERROR;

    if ( fclose(f) != 0 )return ERROR;

    elsereturn OK;

    }

    Bibliografa y referencias.

    [1] El lenguaje de programacin C.Brian W. Kernighan, Dennis M. Ritchie.

  • 8/3/2019 CursC

    23/23

    Prentice-Hall Hispanoamericana, 1985.ISBN 968-880-024-4

    [2] American National Standard for Information Systems -- Programming Language C.American National Standards Institute.1430 Broadway

    New York, NY 10018

    [3] El lenguaje de programacin C, segunda edicin.Brian W. Kernighan, Dennis M. Ritchie.Prentice-Hall Hispanoamericana, 1991.ISBN 968-880-205-0

    [4] C. A software engineering approach.Peter A Darnell, Philip E. Margolis.Springer-Verlag, 1991ISBN 3-540-97389-3