El lenguaje de programación C




Introducción

Características del lenguaje C

El lenguaje de programación C es un lenguaje de alto nivel que trabaja con tipos de datos básicos como ser: carateres, enteros, decimales, colecciones de caracteres y colecciones de cualquier otro tipo de datos; también trabaja con operaciones a nivel de bits y direccionamiento de memoria.

El lenguaje de programación C posee gran portabilidad, de manera que se puede instalar en cualquier sistema operativo. Es utilizado para la progremación de sistemas, construcción de interpretes, compiladores, editores de texto, microcontroladores, entre otras cosas.

El lenguaje C consta de:

  • El lenguaje como tal: tipos de datos, expresiones, estructuras de control, funciones.
  • Extensiones en forma de macros y un conjunto de librerías predefinidas.

Estructura de un programa de C

Un programa de C puede contener uno o más módulos (archivos de código fuente).

  • Cada módulo puede contener:
    • directivas del precompilador para incluir otros ficheros (#include) y directivas para definir constantes y macros (#define).
    • declaraciones de variables y prototipos de funciones.
    • una o más funciones
    • comentarios
  • Cada función puede contener:
    • declaraciones.
    • uno o más bloques
    • comentarios
  • Cada bloque puede contener:
    • declaraciones.
    • una o más sentencias
    • comentarios
  • Cada sentencia debe estar terminada por ;.
  • Cada bloque de sentencias se encierra entre llaves {...}.
  • La función main es la primera que se ejecuta y no puede faltar en un programa de C.
  • Los comentarios pueden aparecer en cualquier lugar del código. Existen dos tipos de comentario:
    • Comentario de bloque, empieza con /* y termina con */. Este tipo de comentario puede abarcar varias líneas.
    • Comentario de línea, empieza con // y termina al final de la línea.

Elaboración de un programa

El desarrollo de programas incluyen diferentes fases o tiempos, que son los espacios donde el programador y el compilador o intérprete necesitan de cierto tiempo para construir un programa.

  • Fase de desarrollo: es el tiempo en el cual el programador está escribiendo código y estructurando de manera lógica su programa. Básicamente, en esta fase el programador traduce sus ideas o plantea el problema a través de un lenguaje de programación.
  • Fase de compilación: es el tiempo en el que el compilador se encarga de revisar la sintaxis, la semántica, y la estructura general del programa. En esta fase tienen lugar los errores de compilación debido a errores sintácticos, semánticos o falta de alguna librería.
  • Fase de ejecución: es el tiempo en el cual el programa se está ejecutando y haciendo lo que se le ordenó que hiciera. En esta fase suele suceder que el programa se detiene por que:
    • los datos que se le proporcionan son incompatibles con los que el programa espera.
    • se producen errores lógicos, que es cuando el programa hace las cosas de diferente manera a como el programador espera que se hagan.

Primeros ejemplos

El siguiente programa es un programa básico de C.

#include <stdio.h>

int main() {
  printf("Hola, este es un programa basico de C!\n");
  return 0;
}
  • #include <stdio.h>: esta librería significa standard input-output header. stdio.h es el archivo de cabecera que contiene las definiciones de las macros, las constantes, las declaraciones de funciones de la biblioteca estándar de C. Generalmente se usa para hacer operaciones de entrada y salida (E/S).
  • main(): esta es la función principal del programa, ninguna función dentro del programa puede invocar a main. Esta función se considera el punto de entrada a todas las funciones, bloques de código y sentencias del programa. Es la primera función en ejecutarse y la última en devolver resultados.
  • printf(...): esta función pertenece a la librería stdio.h y sirve para imprimir datos en pantalla. Si la librería stdio.h no se incluye, esta función hará que el programa retorne error en tiempo de compilación.
  • return: esta palabra reservada sirve para especificar el punto de salida de la función, generalmente return tiene una expresión asociada a la derecha que es el valor que la función está retornando.

Vamos a demostrar un segundo programa de C a partir de un diagrama de flujo. Este programa hará lo siguiente:

  • dada una temperatura en grados Faharenheit calcular la temperatura respectiva en grados Celsius.
  • los resultados se deben imprimir en pantalla en forma de una tabla básica.
  • se debe calcular la temperatura en grados celsius en una escala de 20 grados Faharenheit.

Este es el diagrama de flujo del programa:

El respectivo programa de C es el siguiente:

#include <stdio.h>

int main() 
{
    int fahrenheit, celsius;
    int inferior, superior, escala;

    inferior = 0;
    superior = 300;
    escala = 20;
    fahrenheit = inferior;

    printf("|Fahrenheit\t|Celsius (~)\t|\n");

    while ( fahrenheit <= superior ) {
        celsius = 5 * (fahrenheit - 32 ) / 9;
        printf("|\t%d\t|\t%d\t|\n", fahrenheit, celsius);
        fahrenheit = fahrenheit + escala;
    }
    return 0;
}

Vamos a analizar el programa anterior por partes:

Parte 1

int fahrenheit, celsius;
int inferior, superior, escala;

En la función main() se declaran las variables que se necesitarán durante el programa. Para simplificar el programa se declaran todas las variables con el tipo de dato int.

Parte 2

inferior = 0;
superior = 300;
escala = 20;
fahrenheit = inferior;

Aquí se inicializan todas las variables necesarias. El proceso de inicializar consiste en asignarle un valor a una variable mediante el operador de asignación =.
A una variable se le puede asignar otra variable (a esto se le conoce como propiedad transitiva
)
, siempre y cuando sean del mismo tipo de dato; por ejemplo: fahrenheit e ìnferior son del mismo tipo de dato.

Parte 3

printf("|Fahrenheit\t|Celsius (~)\t|\n");

while ( fahrenheit <= superior ) {
  celsius = 5 * (fahrenheit - 32 ) / 9;
  printf("|\t%d\t|\t%d\t|\n", fahrenheit, celsius);
  fahrenheit = fahrenheit + escala;
}

En esta parte es donde se define la lógica del programa y la manera en que se le presentará al usuario la salida en pantalla.

printf("|Fahrenheit\t|Celsius (~)\t|\n"); imprime en pantalla el encabezado de la tabla utilizando el caracter de escape \t para dejar espacio entre una columna y otra.

while ( fahrenheit <= superior ) {
  celsius = 5 * (fahrenheit - 32 ) / 9;
  printf("|\t%d\t|\t%d\t|\n", fahrenheit, celsius);
  fahrenheit = fahrenheit + escala;
}
  • El bloque de código anterior es un ciclo que se va a repetir mientras fahrenheit <= superior.
  • La sentencia celsius = 5 * (fahrenheit - 32 ) / 9; es la fórmula matemática para calcular la temperatura en grados celsius conociendo la temperatura en grados fahrenheit.
  • La sentenia printf("|\t%d\t|\t%d\t|\n", fahrenheit, celsius); se encarga de mostrar los resultados en pantalla.
  • La sentencia fahrenheit = fahrenheit + escala; se encarga de aumentar en 20 la temperatura en grados fahrenheit, de manera que esta comienza en 0, luego pasa a 20, luego a 40 y así de manera sucesiva hasta llegar a 300.

Compilación de un programa de C

Si está utilizando Visual Studio Code haga clic en Terminal en la barra de menús y luego haga clic en Nueva Terminal. Espere que cargue la Terminal y verifique que la ruta de la Terminal sea la misma en donde usted tiene almacenado su archivo de código fuente.

El compilador de C se invoca de la siguiente manera:

gcc <nombre del programa>.c -o <nombre del programa>

Luego hace Enter y si no se produjo ningún mensaje significa que su código se compiló satisfactoriamente y procede al siguiente paso que es la ejecución del programa.

La línea que escribió en la Terminal desencadena una serie de acciones según se muestra en la siguiente figura:

A continuación hay una breve explicación del proceso de creación de un programa ejecutable que se muestra en la figura anterior:

  • Programa fuente: son el/los archivos de código fuente (generalmente archivos .c y .h) que el desarrollador ha escrito, en los cuales ha definido la estructura lógica del programa.
  • Compilador: es el encargado de analizar el código fuente escrito por el desarrollador, si hay errores lo devuelve al proceso anterior, en caso contrario crea un programa objeto.
  • Programa objeto: es el programa escrito por el compilador y que es entendido solamente por el procesador ya que este programa solo contiene ceros y unos.
  • Enlazador: este suele ser llamado linker y es el encargado de enlazar el código objeto con las librerías del sistema operativo en el cual se va a crear el archivo ejecutable.
  • Librerías: son archivos que el sistema operativo incluye como requisitos para crear un archivo ejecutable. Estas librerías son incluídas al enlazador para crear el programa ejecutable.
  • Programa ejecutable: es el programa final con el cual podrá interactuar el usuario, este programa puede ser ejecutado en cualquier computadora que tenga el sistema operativo en el cual fué creado el programa. Si el programa genera errores en tiempo de ejecución se deberá modificar el código fuente.

Para ejecutar su programa escriba en la Terminal:

./<nombre del programa>

Tipos, operadores y expresiones

Nombres de variables

  • Los nombres de variables pueden tener letras, números y el símbolo _.
  • Los nombres de las variables no pueden comenzar con un número.
  • Deben empezar con una letra (pueden empezar por _ pero no es recomendable por que puede haber una colisión de nombres con las funciones y variables de biblioteca).
  • Pueden llevar mayúsculas y minúsculas.
  • Las palabras reservadas de C no pueden utilizarse como nombres de variables.

Tipos y tamaños de datos

Los tipos de datos más comunes de C son:

  • char: Contiene un caracter (tiene tamaño de un byte).
  • int: Contiene un número entero.
  • float: Contiene un número real.
  • double: Contiene un número real de doble presición (puede almacenar una base y un exponente).

Declaraciones de variables

  • Todas las variables deben ser declaradas antes de su uso.
  • Las declaraciones deben estar escritas al principio de cada función o bloque de sentencias.
  • La declaración de una variable esta compuesta por la declaración de un tipo de dato y uno o más nombres de variables. Ejemplo:
int numero;
int i, j;
char letra;
char letra1, letra2;
float x, pi;
  • Las variables pueden inicializarse justo en la declaración (por cuestiones de legibilidad del código suele hacerse la inicialización por separado).
float pi = 3.1416;

Operadores aritméticos

  • + suma
  • - resta
  • * multiplicación
  • / división. Si los operandos son enteros la división será entera.
  • % resto. Solo es funcional para el tipo de dato int.

Operadores de relación y lógicos

  • Operadores de relación
    • < menor que
    • <= menor o igual que
    • > mayor que
    • >= mayor o igual que
    • == igual a
    • != distinto de
  • Operadores lógicos
    • && AND
    • || OR
    • ! NOT

Los operadores lógicos se evalúan de izquierda a derecha, y se detiene la evaluación al determinar que la expresión es verdadera.

Control de flujo

Sentencias, bloques, estructuras condicionales y repetitivas

  • Una expresión seguida de un ; es una sentencia.
    • Toda sentencia debe ír terminada con ;.
    • Pueden ír varias sentencias en una misma línea separadas por , y agregar ; al final de la última sentencia
  • Las llaves {} se emplean para agrupar sentencias, a esto se le denomina bloque.
  • Dentro de un bloque puede haber sentencias y declaraciones.

if else

La estructura condicional if else tiene la siguiente forma:

if (expresion) {
  sentencia1;
}
else {
  sentencia2;
}
  • Se evalúa expresion. Si es distinta de 0 (verdadera) se ejecuta sentencia1, si es 0 se ejecuta sentencia2
  • En algunos casos no será necesario hacer uso de else.
  • Tanto sentencia1 como sentencia2 deben ír entre llaves {}.
  • expresion NO debe llevar ; al final.

else-if

La estructura condicional else-if tiene la siguiente forma:

if (expresion1) {
  sentencia1;
} else if (expresion2) {
  sentencia2;
} else if (expresion3) {
  sentencia3;
}
else {
  sentencia n;
}
  • Una estructura else-if permite tomar decisiones múltiples y se evalúan en orden de arriba hacia abajo.
  • Al encontrar la primera expresión verdadera las expresiones de abajo no se evaluarán.

switch

La estructura condicional switch tiene la siguiente forma:

switch (expresion) {
  case constante1:
    sentencias;
    break;
  case constante2:
    sentencias;
    break;
  case constante3:
    sentencias;
    break;
  default:
    sentencias;
}
  • Permite tomar una decisión en base al valor que contenga expresion.
  • expresion puede ser de tipo entero o de tipo caracter.
  • El valor de expresion se va comparando con las constantes por orden y una vez que coincide con una de ellas se ejecutan todas las sentencias (incluyendo las de los cases siguientes) hasta que se encuentra la sentencia break.
  • break es una palabra reservada del lenguaje C.

while

La estructura repetitiva while tiene la siguiente forma:

while(expresion) {
  sentencias;
}
  • Se evalúa expresion, y si es distinta de cero (distinto de falso) se ejecutan las sentencias que se encuentran dentro de {}.
  • Cada sentencia debe terminar con ;.

do-while

La estructura repetitiva do-while tiene la siguiente forma:

do {
  sentencias;
} while (expresion);
  • Esta estructura hace lo mismo que una estructura while, con la diferencia que esta permite que se ejecuten las sentencias al menos una vez antes de evaluar expresion. (ver ejemplo: comparativa entre while y do-while).

Comparativa entre while y do-while

Vamos a imprimir un menú principal similar al que aparece en teléfonos de la línea Tigo al marcar #555#. Se hará la comparativa entre como podría ser el código fuente del menú si se implementara la estructura repetitiva while y como podría ser si se implementara la estructura repetitiva do-while.

Diagrama de flujo del programa utilizando estructura while

Implementación utilizando while

#include <stdio.h>

int main()
{
  int opcion = 1;
  
  while (opcion > 0 && opcion < 7) {
    printf("TIGO MONEY\n");
    printf("1- Mi cuenta\n");
    printf("2- Transferencias\n");
    printf("3- Pagos\n");
    printf("4- Recarga\n");
    printf("5- Remesas internacionales\n");
    printf("6- Agentes cercanos\n\n");
    
    scanf("%d", &opcion);
  }
  
  printf("Cancelar\n");
  
  return 0;
}

Diagrama de flujo del programa utilizando estructura do-while

Implementación utilizando do-while

#include <stdio.h>

int main()
{
  int opcion;
  
  do {
    printf("TIGO MONEY\n");
    printf("1- Mi cuenta\n");
    printf("2- Transferencias\n");
    printf("3- Pagos\n");
    printf("4- Recarga\n");
    printf("5- Remesas internacionales\n");
    printf("6- Agentes cercanos\n\n");
    
    scanf("%d", &opcion);
  } while (opcion > 0 && opcion < 7);
  
  printf("Cancelar\n");
  
  return 0;
}

Haga las comparaciones entre ambas implementaciones usted mismo(a).

for

La estructura repetitiva for tiene la siguiente forma:

for (expresion1; expresion2; expresion3) {
  sentencias;
}
  • Una estructura for tiene 3 expresiones:
    • expresion1: declara una variable interna que servirá solamente dentro de la estructura for. Ejemplo: int i = 0
    • expresion2: esta es la condición, si es distinta de 0 (distinta de falso) el ciclo se sigue repitiendo. Ejemplo i < 10
    • expresion3: esta se encarga de aumentar la variable interna del ciclo en un numero arbitrario. Ejemplo: i = i + 1
  • Cada expresión dentro de los () se separa por ; excepto la tercera expresión.
  • Si expresion2 es distinta de falso se ejecutarán las sentencias.

Comparativa entre while y for

Vamos a escribir un programa que pida del teclado una tabla de multiplicar y a través de un ciclo se imprima la multiplicación del 1 al 12. Se hará la comparativa entre como podría ser el código fuente del menú si se implementara la estructura repetitiva while y como podría ser si se implementara la estructura repetitiva for.

Diagrama de flujo del programa utilizando estructura while

Implementación utilizando while

#include <stdio.h>

int main()
{
  int numero;

  printf("Ingrese numero a multiplicar: ");
  scanf("%d", &numero);

  int i = 1;
  while (i <= 12) {
    printf("%d * %d = %d\n", numero, i, (numero * i));
    i = i + 1;
  }
  
  return 0;
}

Diagrama de flujo del programa utilizando estructura for

Implementación utilizando for

#include <stdio.h>

int main()
{
  int numero;

  printf("Ingrese numero a multiplicar: ");
  scanf("%d", &numero);

  for (int i = 1; i <= 12; i = i + 1) {
    printf("%d * %d = %d\n", numero, i, (numero * i));
  }
  
  return 0;
}

Haga las comparaciones entre ambas implementaciones usted mismo(a).