Guión 1: Fundamentos básicos del lenguaje C
 
Introducción.

El Lenguaje C fue creado por Denis Ritchie y Ken Thompson en 1972 como herramienta para los programadores y su principal objetivo consiste en ser un lenguaje útil. Posee las siguientes características:
  • Moderno. Los programas desarrollados utilizan las técnicas de programación estructurada y poseen un diseño modular.
  • Eficiente. Su diseño aprovecha las posibilidades y los recursos de los ordenadores actuales.
  • Portátil. Los programas escritos en C para un ordenador concreto son, por lo general, fácilmente transportables a otro ordenador de distinta arquitectura con muy pocas modificaciones, siempre que en el desarrollo de éstos se utilice el conjunto estándar de bibliotecas del lenguaje.
  • Potente y flexible. Se emplea para resolver problemas físicos y de ingeniería. A diferencia de los demás lenguajes de alto nivel, posee control sobre aspectos asociados a los lenguajes ensambladores.
  • Compilado. Es necesario disponer de un editor de texto y un compilador en modo de línea de órdenes, o de un entorno integrado de desarrollo (IDE). El compilador permite, a partir del programa en código fuente, obtener la traducción a código objeto en formato ejecutable. En el sistema operativo Windows, los ficheros ejecutables poseen la extensión .EXE.

Variables, constantes y operadores.
Un programa necesita trabajar con datos.
Algunos están preseleccionados antes de la ejecución del programa y mantienen sus valores inalterados durante la misma. Se denominan constantes.
Otros pueden recibir una o más asignaciones de valor durante la ejecución del programa. Se denominan variables.

Ejemplo 1.1. Realizar un programa que imprima por pantalla cuántos huevos hay en cuatro docenas.

#include <stdio.h>

#define DOCE 12

int main() {

  int huevos;
  int docenas = 4;

  huevos = DOCE * docenas;

  printf("\nHay %d huevos en %d docenas.\n", huevos, docenas);
 
  system("pause");
  return 0;
}

Ejercicio: Modificar el programa anterior para que imprima por pantalla cuántas docenas son setenta y dos huevos.


El tipo int.

Es el tipo de dato entero básico y normalmente utiliza una palabra del procesador para su almacenamiento.

En los ordenadores personales suele ocupar cuatro bytes (treinta y dos bits) y permite un rango de representación de valores comprendido entre -(231) y 231-1.

Declaración de variables: Se emplea la palabra clave int seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

Las variables se pueden inicializar (asignarles un valor inicial) en la propia sentencia de declaración.

Cualquier número entero, escrito sin punto decimal y sin exponente, es reconocido por el compilador como una constante entera.

Especificadores de formato: %d (base diez), %o (octal) y %x (hexadecimal).

Ejemplo 1.2. Escribir un programa que asigne a una variable de tipo entero el número cien y muestre en pantalla dicho valor los siguientes formatos: base diez, base ocho (octal) y base dieciseis (hexadecimal).
#include <stdio.h>

int main() {

  unsigned x = 100;

  printf("\nBase diez: %d, Formato octal: %o, Formato hexadecimal: %x.\n", x, x, x);

  system("pause");

  return 0;
}
Modificadores del tipo entero básico: unsigned, long y short. La especificación del lenguaje garantiza el cumplimiento de la siguiente desigualdad: short [int] <= int <= long [int]. Como regla general, los compiladores asignan al tipo short un tamaño de dieciséis bits y al tipo long un tamaño de treinta y dos bits. El tipo int podrá tener el tamaño del tipo long o el del tipo short.

El modificador unsigned desplaza el rango de representación del tipo entero que lo precede, de modo que el primer valor dentro del rango es el cero y todos los demás son números naturales consecutivos. De este modo, un entero sin signo utiliza el subrango reservado a los enteros negativos para representar ahora enteros positivos.

Ejemplo 1.3. Desarrollar un programa que muestre en pantalla el rango de representación de un entero corto con signo y de un entero corto sin signo.
#include <stdio.h>

int main() {

  short x = 0x7fff;
  unsigned short y = 0xffff;


  printf("\nRango de un entero corto con signo: [%hd, %hd].", x + 1, x);
  printf("\nRango de un entero corto sin signo: [%hu, %hu].\n\n", y + 1, y);

  system("pause");
  return 0;
}
Ejercicio: Escribir un programa que muestre en pantalla el rango de representación de un entero largo con signo y de un entero largo sin signo. El especificador de formato empleado para imprimir un entero largo con signo es %ld y el utilizado para imprimir un entero largo sin signo es %lu.

Para declarar una constante de tipo entero largo y sin signo se usan respectivamente los sufijos L o l y U o u.

#define DOS_MIL_LARGO_SIN_SIGNO 2000UL


El tipo char.

Se utiliza para almacenar caracteres, tal como letras y signos de puntuación, aunque desde un punto de vista técnico se trata de un entero de un byte (ocho bits).

Para manejar caracteres muchos ordenadores usan el código ASCII, mediante el cual se asocian números enteros a caracteres.

Declaración de variables: Se emplea la palabra clave char seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

En algunos desarrollos del Lenguaje C más recientes se permite el uso de las palabras claves unsigned y signed asociadas al tipo char.

Cuando se encierra un único carácter entre comillas simples, el compilador lo identifica como una constante de tipo carácter y almacena en el ordenador en formato binario el entero correspondiente según el código de entrada/salida utilizado.

Para imprimir un dato de tipo char como carácter debe usarse el especificador de formato %c.

Ejemplo 1.4. Desarrollar un programa que lea un carácter desde el teclado y lo muestre en pantalla junto con el código de entrada/salida correspondiente a dicho carácter.
#include <stdio.h>

int main() {

  unsigned char ch;

  printf("\nIntroduce un carácter: ");
  scanf("%c", &ch);
  printf("El código de %c es %u.\n\n", ch, ch);

  system("
pause");
  return 0;
}
Cuando se usa un código ASCII, se debe tener en cuenta la diferencia entre un número y un carácter numérico representativo de una cifra.

Ejemplo 1.5. Escribir un programa que permita constatar la diferencia entre un entero de una sola cifra y un carácter numérico que representa la misma cifra que constituye el entero.
#include <stdio.h>

int main() {

  char entero = 4;
  char cifra = '4';


  printf("\nValor entero: %d, valor cifra: %d.\n", entero, cifra);

  system("pause");
  return 0;
}
Existen ciertos caracteres no imprimibles, los cuales representan acciones a ejecutar por el ordenador, tales como el retroceso, el salto de línea, el pitido del altavoz, etc. Para representarlos se pueden emplear las secuencias de escape.

                                                          Carácter                                                        Acción
                                                          \n                                                                 nueva línea
                                                          \t                                                                 tabulador
                                                          \b                                                                 retroceso
                                                          \r                                                                 retorno de carro
                                                          \f                                                                 salto de página
                                                          \\                                                                 barra atrás
                                                          \'                                                                 comilla simple
                                                          \"                                                                 comillas
                                                          \a                                                                 pitido

Ejercicio: Desarrollar un programa ilustrativo que haga uso de las secuencias de escape presentadas. Éstas pueden incluirse como caracteres que formen parte de uno o varios mensaje a imprimir por pantalla mediante la función printf().


Los tipos float y double.

Los programas que implican un gran número de cálculos matemáticos y necesitan mayor precisión a medudo utilizan números de punto flotante.

Para almacenar un dato de tipo float se usan treinta y dos bits: ocho bits para expresar el valor del exponente y su signo, y veinticuatro bits para representar la parte no exponencial. Este modo de almacenamiento permite una precisión de siete cifras decimales y un exponente comprendido entre 10-37 y 10+38.

Muchos compiladores aceptan también el tipo double, que emplea el doble de bits que el tipo anterior.

Declaración de variables: Se emplea la palabra clave float o double, según corresponda, seguida de los identificadores de las variables separados por comas. La sentencia acaba en punto y coma.

Para representar una constante en punto flotante se escribe una serie de dígitos incluyendo un punto decimal y a continuación la letra e o E seguida de un exponente con signo, que indica la potencia de diez a utilizar.

Ejemplo 1.6. Escribir un programa en el que se declaren tres variables de tipo real, dos de simple precisión y una de doble precisión, y que a cada una de ellas se le asigne un número real escrito en notación exponencial. Finalmente, el programa deberá imprimir el valor de las variables en notación exponencial (especificador de formato %e).
#include <stdio.h>

int main() {

  float num1 = -1.56E+12, num2 = 2.87e-3;
  double num3 = 35e2;

  printf("\nnum1: %e, num2: %e, num3: %e.\n", num1, num2, num3);
  system("pause");
  return 0;
}
El compilador supone que todas las constantes de punto flotante que aparecen en el programa son de tipo double, a fin de asegurar la máxima precisión en los cálculos.

Los especificadores de formato a usar son %f para notación decimal y %e para notación exponencial.

Ejemplo 1.7. Desarrollar un programa que muestre en pantalla, en notación decimal y en notación exponencial, el valor de una variable real de simple precisión a la cual se le ha asignado previamente el número 32000.0.
#include <stdio.h>

int main() {

  float valor = 32000.0;

  printf("\n%f equivale a %e.\n", valor, valor);

  system("pause");
  return 0;
}
Ejercicio: Modificar el programa del ejemplo 1.6 para que, además de imprimir el valor de las variables en notación exponencial, imprima también el valor de éstas en notación decimal.


Otros tipos.

El tipo void (vacío) se emplea para escribir funciones que carecen de valor de retorno (ver guión correspondiente).
El tipo long double permite declarar variables reales destinadas a la realización de cálculos de alta precisión.

Ejemplo 1.8. Escribir un programa que imprima por pantalla el tamaño en bytes del tipo long double. Para ello deberá usarse el operador sizeof(), el cual se explicará más adelante.
#include <stdio.h>

int main() {

  long double x = 1.0;

  printf("\nEl tipo \"long double\" tiene %d bytes.\n", sizeof(x));

  system("pause");
  return 0;
}

Operadores y precedencias.

Operador de asignación (=). Almacena el valor de la expresión situada a la derecha del operador en la variable que se halla a la izquierda del mismo. El tipo de la expresión cuyo valor se va a asignar debe ser compatible con el de la variable objeto de la asignación.

Operadores aritméticos (+, -, *, / y %). El operador de división (/) funciona de manera diferente según el tipo de los operandos: Si ambos son enteros se realizará una división entera. En este caso, el operador módulo (%) permite obtener el resto de la división entera. Por el contrario, si uno de ellos o ambos son reales, tendrá lugar una división real.

Operadores de incremento (++) y decremento (--). El primero aumenta en una unidad el valor de la variable sobre la que actúa, mientras que el segundo disminuye en una unidad dicho valor. La utilización de ambos operadores da lugar a un código objeto más compacto y eficiente que el uso de la asignación ordinaria para incrementar o decrementar una variable en una unidad.

Ejemplo 1.9. Desarrollar un programa que, mediante los incrementos sucesivos de una variable, muestre en pantalla los cinco primeros números naturales.
#include <stdio.h>

int main() {

  unsigned n = 0;

  printf("\nCinco primeros naturales: ");

  n++; /* 1 */
  printf("%u,
", n);
  n++; /* 2 */
  printf("%u, "
, n);
  n++; /* 3 */
  printf("%u, ", n
);
  n++; /* 4 */
  printf("%u, "
, n);
  n++; /* 5 */
  printf("%u.\n"
, n);

  system("pause");
  return 0;
}
Ejercicio: Modificar el programa del ejemplo 1.9 para que imprima por pantalla en orden descendente los cinco primeros números naturales.

Operadores de actualización de variables (+=, -=, *=, /= y %=). Realizan la operación aritmética correspondiente entre el valor de la variable de la izquierda y el de la expresión de la derecha. El resultado obtenido se asigna a la variable. El empleo de estos operadores permite al compilador generar un código objeto más compacto y eficiente que el uso de la asignación ordinaria. Asimismo, es posible simplificar determinadas expresiones y hacerlas más legibles.

Ejemplo 1.10. El siguiente programa permite comprobar que determinadas expresiones complejas pueden simplificarse si se utilizan los operadores de actualización de variables y que, además, las expresiones modificadas resultan ser equivalentes a las originales en cuanto a los resultados obtenidos.
#include <stdio.h>

int main() {

  int t = 4, x = 5, y = -6;

  t = t * (10 * x + 5 * y + 5);
  printf("\nExpresión original: t -> %d.", t);

  t = 4; /* restaurar valor inicial */
  t *= 10 * x + 5 * y + 5;

  printf("\nExpresión modificada: t -> %d.\n\n", t);

  system("pause");
  return 0;
}
Operadores relacionales (<, <=, >, >=, == y !=). Se emplean para comparar los valores de dos expresiones. Éstas comparaciones dan como resultado un valor lógico o booleano (cierto o falso), y suelen actuar como condiciones simples en las sentencias de selección e iteración para controlar la ejecución de las mismas (ver guión correspondiente).

En C toda expresión siempre tiene un valor.

Ejemplo 1.11. Escribir un programa que muestre en pantalla los valores de una expresión relacional cierta y de otra falsa.
#include <stdio.h>

int main() {

  int cierto, falso;

  cierto = (10 > 2); /* expresión cierta */
  falso = (10 == 2); /* expresión falsa */

  printf("\nCierto: %d, falso: %d.\n", cierto, falso);


  system("pause");
  return 0;
}
Operadores lógicos (&& (AND), || (OR) y ! (NOT)). Permiten combinar dos o más expresiones de relación. De este modo, es posible escribir una condición compuesta a partir de varias condiciones simples. Los operadores lógicos actúan sobre los resultados individuales de las expresiones relacionales (condiciones simples) para dar lugar a un resultado lógico global.

Ejemplo 1.12. Desarrollar un programa que ilustre el funcionamiento de los operadores lógicos (&&, || y !). Para ello, solicitará al usuario que escriba dos valores lógicos (uno o cero) y posteriormente imprimirá por pantalla los resultados de aplicar estos operadores a los valores anteriores.
#include <stdio.h>

int main() {

  int a, b;

  printf("\nIntroducir dos valores de verdad (uno o cero): ");
  scanf("%d %d", &a, &b);

  printf("\n%d && %d -> %d.", a, b, a && b);
  printf("\n%d || %d -> %d.", a, b, a || b);
  printf("\n!%d -> %d.", a, !a);
  printf("\n!%d -> %d.\n\n", b, !b);


  system("pause");
  return 0;
}
El operador sizeof() devuelve el tamaño en bytes de la expresión o del tipo de dato que se le pase como argumento.

Ejemplo 1.13. Escribir un programa que muestre en pantalla los tamaños de los tipos de los datos elementales. No es necesario considerar el modificador unsigned, puesto que no afecta al tamaño de los tipos modificados.
#include <stdio.h>

int main() {

  printf("\nchar -> %d.", sizeof(char));
  printf("\nshort -> %d.", sizeof(short));
 
printf("\nint -> %d.", sizeof(int));
  printf("\nlong -> %d.", sizeof(long));
  printf("\nfloat -> %d.", sizeof(float));
  printf("\ndouble -> %d.", sizeof(double));
  printf("\nlong double -> %d.\n\n", sizeof(long double))
;

  system("pause");
  return 0;
}
Ejercicio: Modificar el programa del ejemplo 1.13 para que imprima por pantalla los tamaños de distintas expresiones cuyos tipos coincidirán con los que aparecen en la versión original del código.


Ejercicios de afianzamiento.

Desarrollar un programa que muestre en pantalla la longitud de la circunferencia y el área del circulo cuyo radio se suministra mediante el teclado como número real de doble precisión.

Escribir un programa que imprima por pantalla el discriminante (b2 - 4 a c) de la ecuación de segundo grado a x2 + b x + c = 0. Para ello, el programa solicitará al usuario la introducción de los tres coeficientes reales a través del teclado.

Desarrollar un programa que lea del teclado una temperatura en grados Farenheit y muestre en pantalla la temperatura equivalente en grados Celsius. La conversión de grados Farenheit (F) en Celsius (C) viene dada por la siguiente expresión: C = 5/9 * (F - 32).
Nota: Para que la división de 5 entre 9 proporcione como resultado un valor distinto de cero, el dividendo, el divisor o ambos deben ser de tipo real (float o double). Esto puede conseguirse mediante un moldeado (conversión de tipo) o escribiendo directamente las constantes como números reales.

Codificar un programa que imprima por pantalla los cinco primeros términos de la sucesión An = 1/2n. El primer término de la misma se corresponde con n igual a uno.

Modificar el programa anterior para que muestre la suma de los cinco primeros términos de la sucesión An = 1/2n. El primer término de la misma se corresponde con n igual a uno.