Informática


Lenguaje C


Introducción.

El lenguaje C se define como un lenguaje de programación de alto nivel, desarrollado a principios de los años 70 por Dennis Ritchie, e implementado por primera vez en un

DEC PDP-11, bajo el sistema operativo UNIX. El siguiente trabajo de investigación estudia los orígenes, características, desarrollo e importancia del lenguaje C además de su estrecha relación con el sistema UNIX. Primero que nada, es vital comentar una breve reseña histórica del lenguaje C, desde su diseño a creación para luego llegar a su consolidación como uno de los lenguajes favoritos de programadores alrededor de todo el mundo. Luego, de manera sistemática y objetiva, se definen los tipos de datos existentes en el lenguaje C para después estudiar las principales características y procedimientos que se deben seguir al programar en esta poderosa herramienta. Es decir, el saber como compilar, depurar, ejecutar, editar y enlazar un programa. De manera anexada se incluyen ejemplos básicos de estas rutinas, los que facilitan la comprensión y estudio de este lenguaje. Dentro de las características de este lenguaje, se describe la inmensa gama de operadores y sentencias, además de los tipos de datos, con lo que se hace un detallado análisis de C, su relación con UNIX y las razones de su permanencia en el tiempo.

Historia y características principales.

En un principio, C nació en los laboratorios Bell de AT&Tcomo un lenguaje de programación de sistemas, y más específicamente de sistemas operativos. En efecto, las primeras versiones de UNIX se implementaron en ensamblador, pero Dennis Ritchie reescribió el código de UNIX ya existente en C, para facilitar su mantenimiento y transportabilidad. Hoy en día sólo una pequeña parte del núcleo de UNIX se sigue escribiendo en ensamblador, estas son, las partes que se comunican íntimamente con el hardware. Este lenguaje está inspirado en el lenguaje B escrito por Ken Thompson en 1970 con intención de decodificar el UNIX, que en la fase de arranque esta escrito en ensamblador, en vistas a su transportabilidad a otras máquinas. B era un lenguaje evolucionado e independiente de la máquina, inspirado en el lenguaje BCPL concedido por Martin Richard en 1967. Luego, en 1972, Dennis Ritchie toma el relevo y modifica el lenguaje B, creando el lenguaje C y reescribiendo el UNIX basándose en dicho lenguaje. La novedad y ventaja comparativa que proporcionó el lenguaje C sobre B en el momento de su creación y desarrollo fue el diseño de tipos y estructuras de datos. Los tipos básicos de datos eran char (carácter), int (entero), float (reales en simple precisión) y double (reales en doble precisión). Posteriormente se le añadieron los tipos short (enteros de longitud menor a la del int), long (enteros de longitud mayor a la del int) y enumeraciones. Los tipos estructurados básicos de C son las estructuras, las uniones y los arrays. Estos permiten la definición y declaración de tipos derivados de mayor complejidad.

Las instrucciones de control de flujo de C son las habituales de la programación estructurada: if, for, while, swith-case, todas incluidas en su predecesor BCPL. El lenguaje C incluye también punteros y funciones. Los argumentos de las funciones se pasan por valor, esto es copiando su valor, lo cual hace que no se modifiquen los valores de los argumentos en la llamada.

Cuando se desea modificar los argumentos en la llamada, éstos se pasan por referencia, es decir, se pasan las direcciones de los argumentos. Por otra parte, cualquier función puede ser llamada recursivamente.

Una de las peculiaridades y más grandes ventajas de C es su riqueza de operadores. Puede decirse que prácticamente dispone de un operador para cada una de las posibles operaciones en código máquina. Sin embargo, hay además toda una serie de operaciones posibles de realizar con el lenguaje C pero que no están incluidas en el compilador propiamente dicho, si no que C las realiza a través de un preprocesador el cual actúa justo antes de cada compilación. Las dos operaciones más importantes en este ámbito son #define (directriz de sustitución simbólica o de definición) e #include (directriz de inclusión en el fichero fuente).

Sin embargo, el lenguaje C, que ha sido pensado para ser altamente transportable y para programar lo improgramable, presenta inconvenientes al igual que todos los otros lenguajes:

  • Carece de instrucciones de entrada/salida de instrucciones para manejo de cadenas de caracteres, con lo que este trabajo queda para la librería de rutinas, con la consiguiente pérdida de transportabilidad.

  • La excesiva libertad en la escritura de los programas puede llevar a errores en la programación que, por ser correctos sintácticamente no se detectan a simple vista.

  • Por otra parte, las precedencias de los operadores convierten a veces las expresiones en pequeños rompecabezas.

Los detractores de C resaltan que este lenguaje es de nivel medio. Esto no significa que sea más complejo que uno de bajo nivel como el ensamblador, ni tampoco menos evolucionado y en definitiva peor diseñado que uno de alto nivel, como Pascal o su derivado Ada. El “nivel” de un lenguaje no viene dado por su dificultad o su utilidad, si no que por las operaciones que se puedan especificar en él. Se dice que C es un lenguaje de nivel medio porque “aúna” elementos de los lenguajes de alto nivel con las posibilidades que ofrece el lenguaje ensamblador.

En estos lenguajes de alto nivel tiene especial importancia el concepto de tipo de dato, que define el conjunto de valores que puede tomar una variable, y el conjunto de operaciones que se pueden efectuar sobre ella. Algunos tipos son fácilmente comprensibles intuitivamente, como los enteros o los reales, pero otros son más oscuros, como el tipo de datos vacío (void) de C. Todos los lenguajes de alto nivel controlan el uso de los distintos tipos de datos; o sea, ciertas operaciones sólo se pueden realizar sobre ciertos tipos; esto se denomina tipificación. Sin embargo, este control puede efectuarse rígidamente, como en Pascal o Ada, o ser aplicado menos estrictamente, como en C; por esto, se dice que C es un lenguaje débilmente tipificado. Esto conlleva que en C se pueda realizar cualquier transformación de datos, la mayoría incluso de forma implícita. Por ejemplo, el paso del tipo carácter al tipo entero se realiza implícitamente en cualquier expresión de C. Otra característica que C comparte con los otros lenguajes de alto nivel es la posibilidad de crear nuevos tipos de datos a partir de los ya existentes.

Por otro lado, C hereda capacidades del lenguaje ensamblador como la manipulación directa de mapas de bits, punteros, y otros tipos que usa el hardware internamente. Es por este motivo que C está particularmente bien adaptado a la programación de sistemas.

Sin embargo, a pesar de combinar muchas de las ventajas de los lenguajes de alto y bajo nivel, C hereda también algunos de sus problemas. La mayoría de estas dificultades provienen del hecho de que C es un lenguaje pensado por y para programadores; su diseño considera la eficiencia del programa final más que la facilidad de desarrollo. De hecho, es un lenguaje que se lee difícilmente, al permitir “expresiones idiomáticas” particulares, que fomentan la escritura de código difícilmente comprensible para los demás. Además, este lenguaje no implementa ningún tipo de comprobación de errores en tiempo de ejecución; o sea, el compilador asume que aquello que se le pide, aunque parezca extraño, procede de un programador experimentado, que sabe exactamente lo que quiere; esto conlleva que ciertas tareas que en otros lenguajes de más alto nivel son largas se puedan realizar rápidamente en C, pero también que ciertos errores comunes que son localizados en otros lenguajes en tiempo de compilación puedan quedar ocultos durante mucho tiempo en programa en C. En definitiva, se puede decir que C proporciona un gran control al desarrollador sobre cada aspecto de su programa, pero éste debe ser manejado cuidadosamente.

Un lenguaje estructurado

En un lenguaje estructurado como C, el programa se organiza en una serie de bloques de código independientes que se llaman los unos a los otros. Este método de programación soluciona algunos de los problemas más recurrentes que se le pueden plantear al programador: en vez de tener que seguir atentamente el flujo de ejecución a lo largo de un inmenso programa, se puede controlar la ejecución en el bloque principal, que va llamando a los otros bloques para tareas secundarias o repetitivas. En este aspecto, C es similar a otros muchos lenguajes como Pascal, Ada, Modula, etc. sin embargo, estrictamente hablando, C no es un lenguaje estructurado en bloques. El motivo es que no se puede declarar una función local a otra función, es decir que no se puede definir una función que sólo se pueda llamar desde el interior de otra. Por otro lado, los lenguajes de alto nivel ofrecen un gran número de construcciones condicionales y bucles. En estos lenguajes se limita o se elimina el uso de goto y de las etiquetas correspondientes, lo que era posible en programas anteriores, como por ejemplo, en Pascal. De hecho, en aquellos lenguajes que lo permiten, como C, el uso de goto está muy mal visto; éste es uno de los criterios que se usan habitualmente para juzgar si un código está bien estructurado o no. En efecto, el uso de saltos con goto tiene tendencia a producir un “código espaghetti”, que es difícil de leer y de mantener. Por el contrario, en los lenguajes estructurados se usan construcciones más funcionales como if y bucles for y while, que clarifican la estructura del programa.

El Lenguaje C y el Sistema UNIX.

Como fue mencionado al principio, el lenguaje C se desarrolló para tener un lenguaje de alto nivel en el que se pudiera programar el Sistema Operativo UNIX. Por lo mismo, los programas en C constan de uno o más ficheros con procedimientos y declaraciones. Estos ficheros se pueden compilar por separado, generándose ficheros objeto independientes, que el montador (o linker) de enlaces ha de combinar para generar el programa ejecutable. La automatización de este proceso se realiza mediante una herramienta que dependerá del sistema en el que se realice la aplicación.

En UNIX, esta herramienta es (make). Los ficheros de las aplicaciones codificadas en C se dividen en dos tipos, según su nombre acabe en: c o en .h. Los ficheros .c contienen la implementación del módulo y los .h (cabeceras o header) contienen la información sintáctica y semántica sobre el uso de la interfase del módulo. Los ficheros .h pueden ser dependientes de la aplicación o propios del sistema operativo. Contienen un bloque de comentarios, sentencias de inclusión de otros ficheros .h, definiciones de constantes y tipos, y declaraciones de funciones. Debe recordarse eso si de que no se permite declarar variables en un fichero .h.

En toda aplicación existe además un módulo principal, que es el que define la función main. En este sistema existen varias funciones main, debido a que cada una se corresponde con la ejecución de un programa, es decir, un proceso del sistema. Ligado a lo anterior, se corresponderá ahora a explicar el funcionamiento del compilador de C en UNIX, el cual es utilizado para la obtención de los ficheros ejecutables necesarios para el funcionamiento de la parte práctica del sistema. La función de este compilador no es sólo traducir programas en lenguajes de alto nivel a código máquina para que sean ejecutados en una computadora específica. Este compilador además proporciona un entorno de ejecución adecuado en el que se pueden llevar acabo operaciones de entrada/salida, acceso a ficheros y a otras interfaces del sistema operativo. En el sistema operativo UNIX, el comando utilizado para compilar programas en C es cc. Este proceso de compilación consta de 5 fases:

1)Preprocesamiento: es realizado por el programa cpp (preprocesador de C). Maneja definiciones de constantes, inclusión de ficheros, gestión de macros, compilación condicional, etc.

2)Compilación: tomando como entrada los resultados de la fase anterior, el programa ccom realiza chequeos sintácticos, el parsing, y la generación del código ensamblador.

3)Optimización: es opcional. Proporciona al generador de código una mayor velocidad.

4)Ensamblado: el programa ensamblador asm se utiliza para crear un fichero objeto que contiene código binario y genera la información que va a ser utilizada por el linker/cargador.

5)Cargado: el programa de cargado constituye un linker/cargador. Combina todos los ficheros objetos y enlaza (o linca) éste a todas las subrutinas de librerías necesarias para producir un programa ejecutable.

El comando cc se encarga de tomar un fichero .c como entrada, generando el correspondiente fichero .o (objeto). A partir de un fichero .s o ensamblado, se produce este fichero .o. luego, este fichero .o se convierte en un fichero ejecutable. Si esta compilación se realiza en un solo paso, entonces el fichero .o se borra automáticamente.

Tipos de Datos.

En C hay dos clases de tipos de datos:

1)Tipos de Datos Básicos.

Hay varios tipos básicos de datos. Los ficheros de cabeceras limits.h y float.h especifican los valores máximos y mínimos para cada tipo. Se pueden clasificar en:

Tipos Enteros:

char, short, int, long y enum.

Tipos Reales:

float, double y long double

Otros:

void

El tipo booleano no existe, para representarlo se utilizan los enteros, asignándole 0 a falso y cualquier otro valor a verdadero.

(a)El Tipo Entero

Cada tipo int pude ser calificado por las palabras claves signed o unsigned, lo que da lugar a tener disponibles los siguientes tipos extras:

signed char, unsigned char
signed short, unsigned short
signed int, unsigned int
signed long, unsigned long

Un entero calificado signed es un entero con signo, esto es, un número entero positivo o negativo. Un número entero calificado unsigned es un número entero sin signo, el cual es manipulado como un número entero positivo. Si los calificadores signed y unsigned se utilizan sin un tipo específico, se asume el tipo int. Por este motivo son equivalentes son:

signed x; y signed int x;
unsigned x; y unsigned int x;

Tipo char

El tipo char se define generalmente en unidades de memoria de 1 byte (8 bits), que resultan más que suficiente para trabajar con el código ASCII estándar. El tipo char es utilizado para almacenar un valor entero en el rango -128 a 127, correspondiente a un carácter del código ASCII. Solamente los valores 0 a 127 son equivalentes a un carácter. De forma similar el tipo unsigned char puede almacenar valores en el rango de 0 a 255. Valores correspondientes a los números ordinales de los 256 caracteres ASCII.

Ejemplo:

char letra; /* Declara una variable de tipo char*/
letra = A; /* Asigna el valor A a la variable letra */
letra = 65; /* Código correspondiente al caracter Á'*/
letra = A; /* Error. Piensa que A es una variable */
letra = "A "; /* Error. Piensa que "A " es una tira de caracteres */

Tipo int

El tipo int puede calificarse con los adjetivos:

int = 2 bytes. Su rango es de -32768 a + 32767.

unsigned int = Los enteros sin signo van de 0 a 65535.

short int = 2 bytes. Rango es de -32768 a 32767.

unsigned short = Rango es de 0 a 65535.

long int = 4 bytes. Rango es de -2147483648 a 2147483647.

unsigned long = Rango es de 0 a 4294967295.

Tipo enum

Crear una enumeración es definir un nuevo tipo de dato, denominado tipo enumerado y declarar una variable de este tipo. La sintaxis es la siguiente:

enum tipo_enumerado
{

definición de nombres de constantes enteras

};

tipo_enumerado es un identificador que nombra al número tipo definido. Después de definir un tipo enumerado, se puede declarar una o más variables de ese tipo, de la forma: enum tipo_enumerado [variable[, variable]...];

Ejemplo:

enum colores;
{
azul, amarillo, rojo, verde, blanco, negro;
};
enum colores color;

Este ejemplo declara una variable color del tipo enumerado colores, la cual puede tomar cualquier valor de los especificados en la lista. Por ejemplo: color = azul;

Cada identificador, de la lista de constantes enteras en una enumeración, tiene un valor. Por defecto, el primer identificador tiene asociado el valor 0, el siguiente es el valor 1, y así sucesivamente. color= verde; es equivalente a color = 3. A cualquier identificador de la lista, se le puede asignar un valor inicial por medio de una expresión constante. Los identificadores sucesivos tomarán valores correlativos a partir de éste. Por ejemplo:

enum colores
{ azul, amarillo, rojo, verde = 0, blanco, negro

} color;

Este ejemplo define un tipo enumerado llamado colores y declara una variable color de ese tipo. Los valores asociados a los identificadores son los siguientes:

azul = 0, amarillo = 1, rojo = 2, verde = 0, blanco = 1 y negro = 2.

A los miembros de una enumeración se les aplica las siguientes reglas:

  • Dos o más miembros pueden tener un mismo valor.

  • Un identificador no puede tener más de un tipo.

  • Desafortunadamente, no es posible leer o escribir directamente un valor de un tipo enumerado.

  • (b)Tipo Real

    Las variables de tipo real son diferentes de las variables de tipo entero ya que se almacenan en memoria divididas en dos partes, en lugar de una. Las dos partes son:

    mantisa : la mantisa es el valor del número.

    exponente : la potencia a la que está elevado el número real en cuestión.

    Tipo float

    El tipo float necesita 4 bytes para ser almacenado: uno para el exponente y tres para el valor del número. Un real en simple precisión es un número que puede tener un punto decimal y que puede estar comprendido en el rango de:

    -3.402823E+38 a -1.175494E-38 para números negativos.
    1.175494E-38 a 3.402823E+38 para números positivos.

    Un número real en simple precisión no tiene más de 7 dígitos significativos.

    Ejemplo:

    float a = 3.14159;

    Tipo double

    El tipo double utiliza 32 bytes para almacenar un número en punto flotante. De ellos, se usan 8 bits para expresar el valor del exponente y su signo, y 24 bits para representar la parte no exponencial.

    Un real en doble precisión es un número que puede tener un punto decimal y que puede estar comprendido en el rango de:

    -1.79769E+308 a -2.22507E-308 para números negativos
    2.22507E-308 a 1.79769E+308 para números positivos

    Un número real en double precisión tiene hasta 16 dígitos significativos. Esto da lugar a cálculos más exactos que en simple precisión.

    Ejemplo:

    double a = 3.1415926;

    Tipo long double

    El tipo long double utiliza 10 bytes para almacenar un número en punto flotante.

    Los valores para este tipo están comprendidos en el rango de:

    -1.189731E+4932 a -3.362103E-4932 para números negativos
    3.362103E-4932 a 1.189731E+4932 para números positivos

    Un número real en double precisión formato largo no tiene más 19 dígitos significativos. Esto da lugar a cálculos más exactos que en double precisión.

    Ejemplo:

    double a = 3.17e+425;

    (c)Tipo void

    El tipo void se utiliza para declarar funciones que no retornan un valor o para declarar un puntero a un tipo no especificado. Si void aparece entre paréntesis a continuación del nombre de una función, no es interpretado como un tipo. En este caso indica que la función no acepta argumentos.

    Ejemplo:

    double fx(void);
    void fy(void);
    *p;

    Este ejemplo declara la función denominada fx como una función sin argumentos que devuelve un valor de tipo real de doble precisión; la función fy, como una función sin argumentos que no devuelve valor alguno; y un puntero p a un objeto de un tipo no especificado.

    2)Tipo de Datos Compuestos

    Este apartado describe los mecanismos de formación de tipos de datos más complejos: vectores, registros (struct), registros variantes (union) y punteros.

    (a)Vectores

    Los vectores son conjuntos de elementos del mismo tipo. En C, todos los vectores empiezan en el elemento 0. La declaración:

    int vt[MAX_PROCESS];

    Declara un vector, vt, de enteros, con MAX_PROCESS (constante) posiciones. A estas posiciones se hace referencia como vt[0] a vt[MAX_PROCESS-1]. Existen vectores de dos, tres y más dimensiones. Además pueden ser de cualquier tipo.

    (b)Registros

    Los registros son conjuntos de variables, generalmente de tipos diferentes. La declaración:

    typedef struct
    {

    char hostname[LONG_HOST];
    int port;

    } type_address;

    Declara a type_address como un registro con dos campos, el vector de caracteres hostname y el entero port.

    Para asignar el valor 1001 al campo port, se indica:

    type_address.port = 1001;

    Donde el operador "." indica que se ha seleccionado un miembro del registro.

    (c)Registros Variantes (unión)

    Los registros variantes están formados también por varios miembros, pero sólo pueden contener el valor de uno de ellos en cada momento. La declaración:

    union type_mess
    {

    int m3;
    int m4[MAX_PROCESS];

    };

    Indica que type_mess puede contener un entero o un vector de enteros, pero no ambos. Los compiladores deben reservar espacio suficiente para que los registros variantes puedan contener la variante que ocupe más espacio en memoria.

    (d)Punteros

    En C, los punteros se utilizan para almacenar direcciones de máquina. Su uso es muy frecuente. La declaración:

    int i, *pi, a[10], *b[10], **ppi;

    Declara un entero i; un puntero a un entero, pi; un vector de 10 elementos, a; un vector de 10 punteros a enteros, b; y un puntero a un puntero a un entero, ppi. Las reglas exactas de sintaxis de declaraciones complejas que combinen vectores, punteros y otros tipos son bastante complicadas.

    Por ejemplo, la declaración:

    typedef struct ptr_group
    {

    int group;
    int components;
    int view;
    struct ptr_group *p_group;

    ptr_process *p_process;

    } ptr_group;

    Las siguientes expresiones: ptr_group *p;ptr_group g; declaran que p es un puntero a un registro de tipo ptr_group, y que g es un registro de ese tipo. Para hacer que p apunte a g, se haría: p = & g; donde el símbolo "\&" representa el operador unario que hace referencia a la dirección de la variable que le sigue. Para copiar la variable entera group_num, el valor del miembro group, se escribe:

    group_num = p-group;

    Nótese el empleo de la flecha para acceder a los miembros de un registro por medio de un puntero. Una forma equivalente es la siguiente:

    group_num = *(p.group);

    Donde "*" es el operador unario de indirección, mediante el cual se accede al contenido de la dirección que se pone a continuación.

    Conversión de Tipos.

    En C se pueden realizar variadas conversiones entre tipos. En muchas ocasiones es necesario forzar explícitamente el tipo resultante de la conversión. Esto puede conseguirse especificando el tipo deseado entre paréntesis delante de la expresión que se ha de convertir, por ejemplo:

    marshall_long(packet, (long)i);

    Convertiría el entero i a tipo long antes de pasarlo como parámetro al procedimiento marshall_long, que esperará a su entrada una variable de tipo long.

    Sentencias de Control.

    En C los procedimientos contienen declaraciones e instrucciones. Ya hemos examinado las declaraciones, así que es el momento de centrar la atención en las instrucciones. Las instrucciones if, while y las de asignación son igual de importantes que las de otros lenguajes.Lo único que vale la pena destacar es que se utilizan llaves para agrupar instrucciones compuestas, y que la instrucción while tiene dos formas, la normal, y el do while, equivalente al repeat de Pascal.

    1)Sentencia if

    Toma una decisión referente a la acción a ejecutar en un programa, basándose en el resultado (verdadero o falso) de una expresión.

    if (expresión)

    sentencia1;

    [ else

    sentencia2];

    expresión:debe ser una expresión numérica, relacional o lógica. El resultado que se obtiene al evaluar la expresión es verdadero (no cero) o falso (cero).

    sentencia1/2: Representan una sentencia simple o compuesta. Cada sentencia simple debe estar separada de la anterior por un punto y coma.

    La ejecución de la sentencia if sucede así:

  • Si el resultado de la expresión es verdadero, se ejecutará lo indicado por la sentencia1.

  • Si el resultado de la expresión es falso, se ejecutará lo indicado por la sentencia2.

  • Si el resultado de la expresión es falso, y la cláusula else se ha omitido, la sentencia1 se ignora.

  • 2)Sentencia while

    Ejecuta una sentencia, simple o compuesta, cero o más veces, dependiendo del valor de una expresión.

    while (expresión)

    sentencia;

    Expresión: es cualquier expresión numérica, relacional o lógica.

    Sentencia: es una sentencia simple o compuesta.

    La ejecución de la sentencia while sucede así:

  • Se evalúa la expresión.

  • Si el resultado de la expresión es cero (falso), la sentencia no se ejecuta y se pasa a ejecutar la siguiente sentencia en el programa.

  • Si el resultado de la expresión es distinto de cero (verdadero), se ejecuta la sentencia y el proceso se repite comenzando en el punto 1.

  • 3)Sentencia do-while

    Ejecuta una sentencia, simple o compuesta, una o más veces, dependiendo del valor de una expresión.

    do

    sentencia;

    while (expresión);

    Expresión: es cualquier expresión numérica, relacional o lógica.

    Sentencia: es una sentencia simple o compuesta.

    La ejecución de la sentencia do-while sucede de la siguiente forma:

  • Se ejecuta la sentencia o cuerpo de la sentencia do.

  • Se evalúa la expresión.

  • Si el resultado de la expresión es cero (falso), se pasa a ejecutar la siguiente sentencia en el programa.

  • Si el resultado de la expresión es distinto de cero (verdadero), el proceso se repite comenzando en el punto 1.

  • 4)Sentencia for

    Cuando se desea ejecutar una sentencia simple o compuesta, repetidamente un número de veces conocido, la construcción adecuada es la sentencia for. Su forma es la siguiente:

    for (inicializador; condición; progresión)

    sentencia;

    Inicializador: representa la variable o las variables que serán inicializadas a un valor determinado.

    Condición: es una expresión Boole (operados unidos por operadores relacionales y/o lógicos). Si se omite, se supone siempre que es verdadera.

    Progresión: es una expresión cuyo valor evoluciona en el sentido de que se dé la condición para finalizar la ejecución de la sentencia for.

    Sentencia: es una sentencia simple o compuesta.

    La ejecución de la sentencia for sucede de la siguiente forma:

  • Se inicializan la o las variables de inicialización.

  • Se evalúa la condición.

  • Si el resultado es distinto de cero (verdadero), se ejecuta la sentencia, se evalúa la expresión que da lugar a la progresión de la condición y se vuelve al punto 2.

  • Si el resultado de 2 es cero (falso), la ejecución de la sentencia for se da por finalizada y se continúa en la siguiente sentencia del programa.

  • 5)Sentencia switch

    La sentencia switch permite ejecutar una de varias acciones, en función del valor de una expresión.

    La ejecución de la sentencia switch sucede de la siguiente forma:

  • La sentencia switch evalúa la expresión entre paréntesis y compara su valor con las constantes de cada case.

  • La ejecución de las sentencias del cuerpo de la sentencia switch, comienza en el case cuya constante coincida con el valor de la expr-test y continúa hasta el final del cuerpo o hasta una sentencia que transfiera el control fuera del cuerpo (por ejemplo break).

  • Si no existe un valor igual al valor de la expr-test, entonces se ejecutan las sentencias a continuación de default, si esta cláusula ha sido especificada. La cláusula puede colocarse en cualquier parte del cuerpo y no necesariamente al final.

  • 6)Sentencia break

    La sentencia break finaliza la ejecución de una sentencia do, for, switch, o while en la cual aparece. Cuando estas sentencias están anidadas, la sentencia break solamente finaliza la ejecución de la sentencia donde está incluida.

    Expresiones.

    1)Expresiones Numéricas

    Las expresiones son secuencias de operandos y operadores. Los operadores más comunes son:

    Operador

    Función

    ( )

    llamada a función

    [ ]

    Subíndice de un arreglo

    .

    Punto . acceso a miembro de una estructura.

    ->

    Flecha. Apunta a miembro " " "

    !

    Inversor Lógico

    -

    Resta

    --

    Decrementar en uno

    ++

    Incrementar en uno

    &

    Obtener la dirección de memoria

    *

    Obtiene la indirección (contenido de)

    /

    División

    %

    modulo (resto de la división)

    +

    suma

    <

    menor que

    >

    mayor que

    <=

    menor igual

    >=

    mayor igual

    ==

    igualdad de comparación

    !=

    desigualdad

    &&

    operador lógico AND

    ||

    operador lógico OR

    ?:

    condicional evalúa dos expresiones

    =

    asignación simple

    ,

    separador de variables, constantes y expresiones dentro de funciones, estructuras de control, etc.

    sizeof

    Determina el tamaño de una variable o una estructura.

    Operadores de Asignación:

    Operador

    Significado

    *=

    multiplicado por

    /=

    dividido por

    %=

    modulo de

    +=

    añadir a

    -=

    sustraer desde

    2)Expresiones Condicionales

    C Tiene un operador ternario (?:), que se utiliza en expresiones condicionales, las cuales tienen la forma: operando 1 ? operando2 : operando3

    La expresión operando1 debe ser de tipo entero, real o puntero. La evaluación se realiza de la siguiente forma:

    • Si el resultado de la evaluación de operando1 es distinta de 0, el resultado de la expresión condicional es operando2.

    • Si el resultado de la evaluación de operando1 es 0, el resultado de la expresión condicional es operando3.

    Realización de un programa en C.

    Un programa en C está compuesto por un conjunto de procedimientos (que aunque no devuelvan un valor suelen denominarse funciones). Estos procedimientos contienen declaraciones, instrucciones y otros elementos que indican al ordenador sobre el que se trabaja, que es lo que tiene que hacer.

    Los pasos a seguir en la realización de un programa en lenguaje C son:

  • Se editan los ficheros fuentes ( fichero1.c, fichero2.c).

  • Se compilan los ficheros fuentes obteniéndose los ficheros objeto (fichero1.obj, fichero2.obj.)

  • Los ficheros objeto son enlazados entre ellos y además con otros ficheros objetos ( fichero.obj) , con librería propias ( fichero4.lib) y del sistema ( .lib) dando lugar a un único fichero ejecutable ( fichero.exe).

  • Para ejecutar el fichero ( fichero.exe) resultante, se escribe el nombre de dicho fichero (fichero), y se debe pulsar ENTER: fichero

    Archivos.

    Muchas aplicaciones requieren leer información de un periférico auxiliar de almace-namiento. Tal información se almacena en el periférico en la forma de un archivo de datos. Por lo tanto, los archivos de datos permiten almacenar información de modo permanente, para ser accedida o alterada cuando sea necesario.

    En C existe existen un conjunto extenso de funciones de biblioteca para crear y procesar archivos de datos. A diferencia de otros lenguajes de programación, en C no se distingue entre archivos secuenciales y de acceso directo (acceso aleatorio). Pero existen dos tipos distintos de archivos de datos, llamados archivos secuenciales de datos (o estándar) y archivos orientados a sistema (o de bajo nivel). Generalmente es más fácil trabajar con archivos de datos secuenciales que con los orientados a sistema.

    Apertura y Cierre de un archivo:

    Cuando se trabaja con archivos secuenciales de datos, el primer paso es establecer un área de buffer, donde la información se almacena temporalmente mientras se está transfiriendo entre la memoria de la computadora y el archivo de datos. Este buffer permite leer y escribir información del archivo más rápidamente de lo que sería posible de otra manera. El área de buffer se establece escribiendo:

    FILE *ptvar; donde FILE (se requieren letras mayúsculas) es un tipo especial de estructura que establece el área de buffer y ptvar la variable puntero indica el inicio de esta área. El tipo de estructura de FILE está definido en el sistema de archivos include, típicamente en stdio.h. El puntero ptvar se refiere a menudo a un archivo secuencial.

    Para abrir un archivo se utiliza la función de biblioteca fopen. Se escribe como:

    ptvar = fopen(nombre_archivo, tipo_archivo);

    donde nombre_archivo y tipo_archivo son cadenas que presentan, respectivamente, el nombre del archivo y la manera en la que el archivo será utilizado. El nombre_archivo debe ser consistente con la manera en que se nombran los archivos en el sistema operativo utilizado, y tipo_archivo está compuesto por una o varias de las cadenas que aparecen en la siguiente tabla:

    Tipo_Archivo

    Significado

    "r"

    Abrir un archivo existente solo para lectura.

    "w"

    Abrir un nuevo solo para escritura. Si existe un archivo con el nombre_archivo especificado, será destruido y creado uno nuevo en su lugar.

    "a"

    Abrir un archivo existente para añadir (añadir información nueva al final del archivo). Se creará un archivo nuevo si no existe un archivo con el nombre nombre_archivo especificado.

    "r+"

    Abre un archivo existente para lectura y escritura.

    "w+"

    Abre un archivo nuevo para lectura y escritura. Si existe un archivo con el nombre_archivo especificado, será destruido y creado uno nuevo en su lugar.

    "a+"

    Abrir un archivo existente para leer y añadir. Se creará un archivo nuevo si no existe un archivo con el nombre nombre_archivo especificado.

    Se retorna un valor NULL si no se puede abrir el archivo, por ejemplo si un archivo existente no se encuentra.

    Finalmente un archivo de datos debe cerrarse al final del programa. Esto puede realizarse con la función de biblioteca fclose, aunque la mayoría de los compiladores de C cerrarán automáticamente todos los archivos de una sesión.

    Edición de un Programa

    Para editar un programa, primeramente se debe llamar, para su ejecución, al programa editor o procesador de textos que se vaya a utilizar. Se puede utilizar el procesador de textos suministrado con el compilador o un propio procesador. El nombre del fichero para salvar el programa en el disco, debe tener como extensión .c. El paso siguiente es escribir el texto correspondiente al programa fuente. Cada sentencia del lenguaje C finaliza con un punto y coma y cada línea del programa se finaliza pulsando la tecla Enter. Los comentarios comienzan con el símbolo /* y acaban con */, y pueden extenderse a varias líneas. A continuación se presenta el siguiente ejemplo con lo dicho hasta ahora.

    /*********************Saludo*********************/

    # include stdio.h
    # include stdlib.h
    main()
    {

    char *mensaje1 = "Bienvenido a C ";
    char *mensaje2 = "esto es un ejemplo ";

    system("cls ");
    printf(" Escriba su nombre: ");
    gets(nombre);
    printf(" n%s %s n%s n ",mensaje1, nombre, mensaje2);

    }

    Las dos primeras líneas incluyen las declaraciones necesarias para las funciones que aparecen en el programa. Estas funciones son: system(), printf() y gets(). En la función main() lo que se hace es definir las cadenas de caracteres mensaje1 y mensaje2 y tomar del teclado la variable nombre. El despliegue en pantalla será algo como esto:

    “ Bienvenido a C, <nombre que se haya elegido>, esto es un ejemplo”.

    Compilar un Programa

    El siguiente paso es compilar el programa, esto es, transformar la versión del programa comprensible para el ser humano en la versión comprensible para el ordenador. Utilizando un procesador de textos o un editor, se escriben las líneas del programa, creando el fichero fuente. El compilador entonces utiliza este fichero para generar otro fichero pero escrito ahora en lenguaje máquina.

    Al compilar un programa, se pueden presentar errores de compilación, debidos a que el programa escrito no se adapte a la sintaxis y reglas del compilador; pero es posible ir corrigiendo estos errores hasta obtener una “compilación limpia”.

    Enlazar un Programa

    Enlazar es necesario ya que quizá no se desee compilar todo el programa al mismo tiempo. Por ejemplo, se puede escribir un programa largo, éste puede contener partes ya depuradas y otras pueden estar todavía en desarrollo, luego sólo se tendrá que compilar la o las partes que estén en desarrollo. En base a esto, es necesario enlazar ficheros y librerías de programa que estén previamente compiladas. Este es el trabajo del “linker”. El “linker” combina todos los ficheros objeto necesarios, librerías y programas escritos por el usuario, para producir un programa final que sea ejecutable.

    Ejecutar un Programa

    Cada vez que se realiza el proceso de compilación y enlace del programa, C genera automáticamente sobre el disco un fichero con extensión.exe. Este fichero puede ser ejecutado directamente desde el DOS, sin el soporte de C, escribiendo el nombre del fichero.exe después del prompt del DOS y pulsando Enter a continuación. Al ejecutar el programa, pueden ocurrir errores durante la ejecución. Por ejemplo, puede darse una división por cero. Estos errores solamente pueden ser detectados por C cuando se ejecuta el programa, pero son notificados con el correspondiente mensaje de error.

    Hay otro tipo de errores que no dan lugar a mensaje alguno. Por ejemplo: un programa que no termine nunca de ejecutarse, debido a que presenta un bucle o loop, donde no se llega a dar la condición de termino. Para detener la ejecución se tienen que pulsar las teclas Ctrl + C.

    Depurar un Programa

    Un programa una vez ejecutado puede dar lugar a una solución incorrecta. Este caso exige un análisis minucioso de cómo se desarrolla el programa en su ejecución; esto es, entrar en la fase de depuración del programa. La forma más sencilla y eficaz para realizar este proceso, es utilizar un programa depurador.

    Funciones.

    En el lenguaje C, una función es el equivalente a un procedimiento en Pascal o a una subrutina en Fortran. Una función proporciona una forma conveniente de encapsular algunos cálculos, que se pueden emplear después sin preocuparse de su implantación.

    Con funciones diseñadas adecuadamente, es posible ignorar cómo se realiza un trabajo; es suficiente saber qué se hace. El C hace que el uso de funciones sea fácil, conveniente y eficiente; es común ver una función corta definida y empleada una sola vez, únicamente porque eso esclarece alguna parte del código. Una definición de función tiene la forma siguiente:

    tipo_de_retorno nombre_de_función ([parámetros])
    {

    declaraciones;
    instrucciones;

    }

    Las definiciones de función pueden aparecer en cualquier orden y en uno o varios ficheros fuente, pero una función no puede separarse en archivos diferentes. Para devolver valores, las funciones ejecutan la instrucción return, que puede tener una expresión que se devuelve como el valor de la función.

    Cuando una función devuelve un valor, el tipo del mismo viene especificado antes del nombre de la función. Una función no tiene necesariamente que regresar un valor.

    Conclusiones.

    A pesar de que C fue creado cerca de 1970, su estudio perdura alrededor de todo el mundo; lo que no debe sorprender, ya que este lenguaje presenta bastantes características en relación con sus defectos. Entre sus principales beneficios se encuentran: programación estructurada, economía de las expresiones, abundancia en operadores y tipos de datos, codificación en alto y bajo nivel simultáneamente, reemplaza ventajosamente la programación en ensamblador o con lenguajes de bajo nivel en general, utilización natural de las funciones primitivas del sistema, no está orientado a ninguna área en especial, producción de código objeto altamente optimizado, facilidad de aprendizaje, etc.

    Aparte de esto, C es el lenguaje de elección en la implementación de sistemas en la industria y comercio además de muchos otros entornos. Se piensa que será el lenguaje dominante en el comienzo del nuevo siglo así como también se piensa que lo fue a finales del siglo pasado.

    21




    Descargar
    Enviado por:Víctor Sepúlveda
    Idioma: castellano
    País: España

    Te va a interesar