|
|
Apendice A. El lenguaje de programación
C. |
A.1 IntroducciónC es un lenguaje de programación de alto nivel desarrollado por Dennis Ritchie para codificar el sistema operativo UNIX. Las primeras versiones de UNIX se escribieron en ensamblador, pero a partir de 1973 comenzaron a escribirse en C. Solo la pequeña parte del núcleo de UNIX relacionada directamente con el hardware sigue escrita en ensamblador, todas las ordenes y aplicaciones estándar que acompañan al sistema están escritas en C. Por lo cual este lenguaje es la forma natural de comunicarse con el sistema. El lenguaje se puede clasificar dentro de los lenguajes de alto nivel, sin embargo, también le ofrece al programados posibilidades que solo están presentes en los lenguajes de bajo nivel. Así, por ejemplo, en C se pueden manipular bits y aritmética de direcciones. C también permite el desarrollo de la programación estructurada y modular. A.2 Ciclo de creación de un programaA la hora de crear un programa empezaremos por la edición de un fichero que contendrá el código fuente. Este fichero, se nombra por convenio añadiéndole la extensión .c. Si nos valemos del editor vi, la forma de editar el programa será: $ vi programa.cEl compilador cc es el encargado de generar el fichero ejecutable a partir del fichero fuente. Para invocarlo debemos escribir: $ cc programa.cEsta línea de ordenes genera el fichero a.out, que ya es ejecutable. Si queremos que nuestro ejecutable tenga otro nombre, compilaremos con la orden: $ cc -o programa programa.clo que haria que el fichero ejecutable se llame programa en lugar de a.out. También podemos compilar un programa utilizando la orden make. Si queremos crear el programa ejemplo a partir del fichero fuente ejemplo.c, bastara con escribir: $ make ejemploA.2.1 Componentes léxicos del lenguajeExisten 6 clases de componentes léxicos: identificadores, palabras reservadas, constantes, cadenas de caracteres, operadores y otros separadores.
A.2.2 Estructura de un programa CAunque las formas de un programa C pueden ser muy variadas y no hay formas fijas, suele ser común estructurar un programa como sigue:
Las directrices del preprocesador son órdenes que ejecuta el preprocesador -cpp- para generar el fichero con extensión .i con el que posteriormente trabajará el compilador. Hay dos directrices que se emplean masivamente: include y define.
A.3 Tipos de datosEn C se consideran dos bloques de datos: los que suministra el lenguaje -tipos fundamentales- y los que define el programador -tipos derivados-. A.3.1 Tipos fundamentalesTipos enterosPara declarar variables de alguno de los tipos enteros emplearemos las palabras reservadas char, short, int, long y enum.
Estos cuatro tipos pueden ir precedidos del modificador unsigned para indicar que el tipo solo representa números positivos o el cero. enum se utiliza para definir un subconjunto dentro del conjunto de los numeros enteros. Tipos realesPara declarar variables de alguno de los tipos reales emplearemos las palabras reservadas float y double.
A.3.2 Operador sizeofPara determinar el tamaño en bytes tanto de un tipo fundamental como derivado, podemos usar el operador sizeof. A.3.3 Tipos derivadosLos arrays son bloques de elementos del mismo tipo. El tipo base puede ser fundamental o derivado. Los elementos individuales del array son accesibles mediante una secuencia de índices. Los índices deben ser variables o constantes de tipo entero. Se define la dimensión de un array como el total de índices que necesitamos para acceder a un elemento en particular. A los arrays unidimensionales se les llama vectores, y los bidimensionales matrices. Los arrays unidimensionales de tipo char se llaman cadenas de caracteres, y los arrays de cadenas de caracteres - matrices de caracteres- se llaman tablas. A.3.4 PunterosLos punteros son variables que almacenan direcciones de memoria. Se definen también en base a un tipo fundamental o a un tipo derivado. La declaración de un puntero es como sigue: tipo_base *puntero;Para trabajar con punteros hay definidos dos operadores unarios: & y *. El operador & da la dirección de memoria asociada a una variable y se utiliza para inicializar un puntero. El operador * se utiliza para referirse al contenido de una dirección de memoria. Los punteros admiten las operaciones de incremento, decremento, suma de una constante entera y diferencia de punteros. Al realizar estas operaciones, realmente estamos modificando la dirección a la que referencia el puntero. Un array no es lo mismo que un puntero. Mientras que un array es una constante, un puntero es una variable. Unos de los inconvenientes que se plantea en el uso de arrays es que sus dimensiones deben ser conocidas por el compilador. los punteros nos ayudan a solucionar este problema, ya que posibilita el uso de arrays dinámicos -arrays que se dimensionan en tiempo de ejecución-. EstructurasUna estructura es un agregado de tipos fundamentales o derivados y se compone de varios campos. Puesto que el tipo de cada campo de una estructura puede ser un tipo fundamental o derivado, también puede ser otra estructura. Tendremos así declaradas estructuras dentro de estructuras. Uniones Las uniones se definen de forma parecida a las estructuras y se emplean para almacenar en un mismo espacio de memoria variables de distintos tipos. El tamaño de una unión no es igual a la suma de cada uno de sus campos, como ocurre con las estructuras, sino que es igual al tamaño del mayor de sus campos. Campos de bitsC da la posibilidad de definir variables cuyo campo en bits puede no coincidir con un múltiplo de 8. A.3.5 Alias para los nombres de tipo.Para hacer que un identificador sea considerado el nombre de un nuevo tipo, tenemos que emplear la palabra clave typedef. A.4 Expresiones y operadoresUna expresión esta formada por operadores y operandos. Los operadores establecen la relación entre los operandos, y los operandos pueden ser variables, constantes u otras expresiones. Los paréntesis también pueden formar parte de una expresión y se emplean para modificar la precedencia de los operadores. A.4.1 Operadores aritméticos.Hay 5 operadores aritméticos:
Las expresiones aritméticas se evalúan de izquierda a derecha. Si en una expresión aritmética intervienen variables o constantes de diferentes tipos, el tipo del resultado coincidirá con el tipo mayor que aparezca en la expresión. A.4.2 Operadores de relación y lógicosLos operadores de relación y los lógicos se emplean para formar expresiones booleanas. Una expresión booleana solo puede tomar dos valores: VERDADERO o FALSO. En C se considera una expresión booleana FALSA cuando su resultado es 0 y VERDAD cuando es distinto de 0. Los operadores de relación son:
Los operadores lógicos son:
A.4.3 Operadores para el manejo de bitsC también implementa operadores para manipular los bits de las variables o contantes enteras. Estos operadores son:
A.4.4 Expresiones abreviadasEn esta tabla se muestra una relación entre expresiones abreviadas y sus equivalentes:
A.5 Sentencias de control de flujoA.5.1 Proposiciones y bloquesUna proposición es una expresión seguida de ;. Una proposición compuesta o bloque es un cojunto de declaraciones y proposiciones agrupadas entre llaves { }. A.5.2 Selección (if - else)Su sintaxis es:if (expresión) proposición1; else proposición2; Si expresión es verdadera se ejecuta proposición1; en caso contrario, se ejecuta proposicion2. A.5.3 Selección múltiple (else-if)Su sintaxis es:if (expr1) (proposicion1); else if (expr2); (proposicion2); ... else if (expr n) (proposicion n) else (proposicion m) A.5.4 Selección por casos (switch) La proposición switch es una decisión múltiple que prueba si una expresión coincide con alguno de entre un número de valores constantes enteros y traslada el control adecuadamente. Su sintaxis es: switch (expresión) {case exp_const1: proposiciones1; case expr_const2: proposiciones2; ... case exp_const n: proposiciones n; default: proposiciones; } A.5.5 Bucle forSu sintaxis es: for (inicialización; expresión; progresión)proposición; Mientras la expresión sea verdadera, se estará ejecutando proposición; inicialización es una expresión, o conjunto de expresiones, para inicializar las variables de control que intervienen en expresión; y progresión es una o conjunto de expresiones que indica como evolucionan las variables de control. A.5.6 Bucle (while)Su sintaxis es; while (expresion)proposición; donde proposición se estará ejecutando mientras expresión sea verdadera. A.5.7 Bucle (do-while)Su sintaxis es: do {proposición; } while (expresión); donde proposición se estará ejecutando mientras expresión sea verdadera, la primera vez siempre se ejecuta. A.5.8 break y continueLa proposición break produce una salida anticipada de un bucle for, while o do - while, y también produce la salida de la sentencia switch. La proposición continue provoca que se inicie la siguiente iteración del bucle for, while o do - while que la contiene. A.6 FuncionesLas funciones se utilizan para agrupar bajo un identificador una serie de proposiciones concebidas para realizar alguna tarea específica. La organización de un programa grande en funciones sencillas hará que el programa sea estructurado además de fácil de depurar y mantener. Una función puede ser de cualquier tipo, excepto del tipo función o array. Si no se especifica nada, el tipo es int. Las variables locales y los parámetros de la función se reservan en la pila de usuario del programa, por lo que al entrar en la función se reserva espacio para ellos, pero al salir de la función desaparecen. Este tipo de almacenamiento se conoce como automático, en contraposicion al estático. Si una variable local va precedida del calificador static, su almacenamiento será estático y ocupara una zona de memoria reservada a las variables globales; además, esa variable existirá durante todo el tiempo de ejecución del programa. A.6.1 Paso de parámetros por valor y por referenciaEs importante tener en cuenta que los parámetros de una función sólo se pueden modificar a nivel local. Para modificar un parámetro de forma permanente hay que trabajar con un puntero al mismo. El paso de arrays como parámetros de funciones siempre se realiza por referencia. Las estructuras se pueden pasar por valor o por referencia. Si pasamos una estructura por valor, las modificaciones de sus campos serán locales mientras que pasándolas por referencia, las modificaciones serán permanentes. El hecho de que los parámetros pasados por valor sólo sufran modificaciones a nivel local se debe a que el parámetro es una copia en la pila de usuario de la variable a que se refiere. Así, las modificaciones se hacen sobre la copia de la variable que hay en la pila y no sobre la propia variable. Bajate esta documentación en un archivo Acrobat Reader |