DECLARACIÓN DE VARIABLES EN C.







Declaración de variables en C.
Los lenguajes de alto nivel facilitan mucho la creación y mantenimiento de variables. Desde un punto de vista estrictamente de bajo nivel, sería responsabilidad del programador reservar posiciones concretas de memoria para usarlas como variables, y también debería recordar sus direcciones, para poder acceder a ésas variables, indicando la dirección en una instrucción de lenguaje máquina. Esta labor se complica al crecer el número de variables, de las que hay que recordar no sólo su dirección, sino también su extensión y su tipo. Para un ser humano, resulta mucho más fácil recordar un nombre que un número, y los lenguajes de alto nivel facilitan mucho la tarea al permitirnos acceder a una variable mediante un nombre, y no mediante una dirección. En este sentido, lo único que necesita conocer un compilador de C para crear una variable es el tipo al que pertenece y el nombre que se le quiere dar; el compilador se encarga de mantener la tabla de variables, y de asociar a cada nombre la dirección correspondiente. Por tanto, la sintaxis de declaración de una variable en C es de la forma

tipo_base nombre_variable [nombre_variable2, nombre_variable3,...];

en donde:



Véase a continuación un programa en el cual se declara una variable de tipo int, otra de tipo char y una tercera de tipo float.

#include<stdio.h>
int main(int argc, char * argv[])
	{
		int unInt = 65;
		char unChar = 'A';
		float unFloat = 3.1416;

		printf("El entero vale %d\n", unInt);
		printf("El carácter vale %c\n", unChar);
		printf("El real vale %f\n", unFloat);
		
		return 0;
	}

El compilador crea automáticamente la siguiente tabla de variables, sin que el usuario tenga que preocuparse de las direcciones en que comienzan las variables, ni tampoco de su extensión. El compilador se asegura de que las operaciones realizas con estas variables sean las adecuadas para su tipo: por ejemplo, no permitirá almacenar un char en un float, ni concatenar dos int.

Nombre Dirección Extensión Tipo Valor
unInt &unInt 4 bytes int ??
unChar &unChar 1 byte char ??
unFloat &unFloat 4 bytes float ??

Variables locales y globales en C
Como se recordará, las tablas de variables pueden ser propias de una función, o bien pueden recoger las variables declaradas fuera de toda función, constituyendo la tabla de variables globales. Esto es precisamente lo que ocurre en los programas en C:
Iniciación de variables
Cuando se declara una variable, su contenido no está definido en general; téngase en cuenta que lo que ha hecho el compilador es reservar espacio y es muy posible que se estén reutilizando posiciones de memoria empleadas anteriormente por otro programa. Esas posiciones pueden contener cualquier trama de bits, que podría corresponder a un valor sin sentido para nuestros propósitos. Si utilizamos la variable sin darle valor nosotros mismos, corremos el riesgo de que adopte un valor incorrecto. Consiguientemente, es preciso dar valores a las variables antes de utilizarlas. Una forma de hacerlo es recurrir a una declaración con iniciación, que en general será de la forma
tipo variable_1 = valor_1, variable_2, ..., variable_n = valor_m; 

/* Por ejemplo */

int dia = 7, mes = 9, semana = 42;

Evidentemente, el valor asignado tiene que pertenecer al tipo de la variable declarada.En el caso de que la variable sea estructurada, es preciso escribir los valores correspondientes entre llaves, y separados mediante comas. La asignación de valores a los distintos elementos de la variable estructurada se realiza por orden cronológico (por filas, para ser exactos), y por tanto será preciso respetar el número y tipo de los elementos que formen esa variable estructurada.

Tabla de Tipos Enteros en C.
El lenguaje C posee una notable cantidad de tipos de datos enteros, con objeto de abarcar distintas gamas de alcances (con distintos consumos de memoria, claro está). Se muestra a continuación una tabla que contiene los distintos tipos enteros existentes en C, junto con sus tamaños en bytes y sus alcances.

Nombre Tamaño Alcance
 short
 16 bits 
 desde -32.768 hasta 32.767
 unsigned short
 16 bits 
 desde 0 hasta 65535
 int
32 bits 
 desde -2.147.483.648 hasta 2.147.483.647
 unsigned int
32 bits 
desde 0 hasta  4.294.967.295
 long
32 bits 
 desde -2.147.483.648 hasta 2.147.483.647
 unsigned long
32 bits 
 desde 0 hasta 4.294.967.295 
 long long
64 bits 
 desde   -9.223.372.036.854.775.808 hasta 9.223.372.036.854.775.807
 unsigned long long
64 bits 
 desde 0 hasta 18.446.744.073.709.551.615

Los tipos long long y unsigned long long pueden no encontrarse en todos los compiladores.

Véanse a continuación algunos ejemplos de declaraciones de variables pertenecientes a distintos tipos enteros:
short alfa;
unsigned short b, campo, d;
int e;
unsigned int f = 2, g, hache = 7; /* Declaración con asignación de valor inicial */
long largo;
unsigned long mas_largo;
long long larguisimo;
unsigned long long tremebundo = 31415926535; /* Declaración con asignación de valor inicial */

Comentarios en C
El concepto de comentario, presente en todos los lenguajes de programación, tiene una importancia mayor que la que pudiera otorgársele a primera vista. Un comentario es un mensaje del programador para otros programadores posteriores, o quizá para sí mismo al cabo de algún tiempo. En este mensaje se justifican las decisiones de codificación tomadas, esto es, se exponen las razones que impulsan al programador a emplear una cierta estructura de programa, frente a otras posibles. Estas explicaciones son valiosísimas, porque suele ser difícil interpretar el código construido por un desconocido cuando, además, el algoritmo que pretende implementar no resulta especialmente claro. En los comentarios de programa se debe incluir toda información que pueda resultar necesaria para posteriores programadores que necesiten comprender nuestro código, bien sea para emplear sus resultados o para introducir modificaciones. Estos comentarios forman parte de la Documentación del programa, y describen internamente el código fuente. Tendrán especial relación con otro aspecto crucial de la documentación: el Manual del programador.
En C, los comentarios se denotan encerrando el texto correspondiente entre los signos /* y */. Estos comentarios pueden abarcar una o más líneas de texto:

/*Esto es un comentario de una línea. */

/*
	Este comentario
	abarca varias
	líneas.
*/


En ciertos lenguajes, el programador puede insertar comentarios de un formato especial, que después serán recogidos e interpretados por ciertas herramientas para así crear automáticamente una utilísima documentación del programa. Véase, por ejemplo, javadoc.

Entrada/Salida de Tipos Enteros.




Nota.- Comienza aquí nuestra descripción de funciones de uso frecuente en C. Éste lenguaje está dotado de una notable colección de funciones predefinidas, valiosísimo auxiliar del programador en toda ocasión. Surge así el problema de disponer de una referencia de estas funciones, al margen de tutoriales como el presente. El problema se ha resuelto en las plataformas basadas en Unix, mediante el uso de las conocidas páginas del manual o man pages. Pero también los usuarios de otros sistemas pueden beneficiarse de su uso, al ser C razonablemente similar entre plataformas. Sería conveniente, entonces, disponer de alguna forma de acceder a las páginas man desde cualquier plataforma, y ésto precisament es lo que permite internet. Entre otras, ésta dirección permite acceder via Web a las páginas man de Unix.



Las funciones printf() y scanf().
El lenguaje C carece de operaciones propias de E/S; esto se hizo para hacerlo independiente de las máquinas en que se utilizaría, y exige crear para cada máquina las funciones de entrada/salida necesarias. Esas funciones se declaran en un archivo de encabezado, stdio.h, y esta es la razón por la cual se incluye ese archivo en todos los programas que contengan operaciones de entrada/salida.
Las funciones que se emplean para efectuar operaciones de E/S con enteros son:
printf(cadena_de_formato, lista_de_variables);
y
scanf(cadena_de_formato, lista_de_direcciones);
en donde los significados de cadena_de_formato, lista_de_variables y lista_de_direcciones son como sigue:



Ejemplo
/* Formatos enteros en C */
#include<stdio.h>
int alfa = 1, beta = 2, delta = 3;
void main(void)
{
printf("Alfa vale %d, beta vale %d y delta vale %d. Gamma vale %d\n",
                alfa, beta, delta); /* Aquí sobra el último %d */
printf("Alfa vale %d, beta vale %d\n",
                alfa, beta, delta); /* Aquí sobra la última variable */
}
/* Resultados de la ejecución
Alfa vale 1, beta vale 2 y delta vale 3. Gamma vale 40242420
Alfa vale 1, beta vale 2
*/
Las operaciones de lectura de enteros son similares a las de escritura. Esta vez la cadena de especificación describe el formato que adoptarán los datos suministrados; el compilador analiza estos datos y efectúa las transformaciones pertinentes, almacenando los valores obtenidos en las variables situadas en los correspondientes lugares cronológicos.

Ejemplo
#include<stdio.h>
/* Este programa muestra la forma de leer y escribir enteros */
int a, b, c;
void main(void)
{
/* Aquí se piden los datos, y se leen individualmente */
 printf("Escriba el valor de a: ");
scanf("%d", &a);
printf("Escriba el valor de b: ");
scanf("%d", &b);
printf("Escriba el valor de d: ");
scanf("%d", &c);
/* Aquí se escriben los números leídos */
printf("El primero es %d, el segundo es %d y el tercero es %d\n\n", a, b, c);
 /* Ahora se leen tres números en una sola línea */
printf("Escriba tres enteros separados por espacios:\n\n");
scanf("%d %d %d", &a, &b, &c);
printf("Ahora el primero es %d, el segundo vale %d y el tercero es %d\n\n", a, b, c);
 }

Véanse también los ejercicios del final del capítulo.

Operaciones con enteros en C.
Como cabía esperar de un lenguaje destinado a la programación de sistemas, C permite manipular enteros de forma rápida y flexible. Los operadores aritméticos de tratamiento de enteros admiten cualquier tipo de entero, y permiten evaluar expresiones mixtas (con distintos tipos de enteros) siguiendo el principio de promoción de tipos: todos los valores se traducen temporalmente al tipo de mayor precisión; luego se efectúan las operaciones y se traduce automáticamente el resultado al tipo de destino. Elobjetivo de este procedimiento es no perder nunca información por efectuar operaciones con tipos de menor precisión que pudieran dar lugar desbordamientos.

La tabla de operadores aritméticos para enteros en C es la que puede verse a continuación:

Operación Operador Ejemplo
Suma + c = a + b;
Resta - c = a - b;
Multiplicación * c = a * b;
División / c = a / b;
Módulo % c = a % b;

El operador módulo (%) proporciona el resto de la división entera de sus dos operandos; por ejemplo, 7 % 5 es 2. En C, no hay operador módulo para números reales.

Ejemplo
#include<stdio.h>
/* Este programa muestra un uso elemental de las cuatro reglas */
short a, b; /* Variables globales */
int main(void)
{
	printf("Este programa muestra el uso de las cuatro reglas\n\n");
	printf("Escriba dos números enteros separados por un espacio:");
	scanf("%d %d", &a, &b);
	printf("\n\n%d + %d = %d\n", a, b, a + b);
	printf("%d - %d = %d\n", a, b, a - b);
	printf("%d * %d = %d\n", a, b, a * b);
	printf("%d / %d = %d\n", a, b, a / b);
	printf("\nTerminación normal del programa.\n\n");
	return 0;
}

Este programa muestra el uso de operaciones con números enteros, y permite verificar fácilmente lo que sucede en caso de desbordamiento. Dicho de otra manera: ¿qué sucede cuando se suman dos números mayores que 16384? Téngase en cuenta que hemos utilizado deliberadamente enteros del tipo short, cuyo rango de validez oscila entre -32768 y 32767. El compilador de C no indica el desbordamiento, limitándose a mostrar el resultado (incorrecto).

Ejercicios propuestos

  1. Ejercicio 0101r01.-Modificar el ejercicio anterior para que haga uso de los distintos tipos de números enteros que constan en la Tabla. Verificar lo que sucede al producirse un desbordamiento para los distintos tipos de enteros disponibles. Nota .- Este programa hace uso de funciones definidas por el usuario; sus prototipos o encabezados pueden verse a continuación, bajo el comentario. El programa principal (la función main()) se reduce entonces a ir llamando (invocando, en la jerga informática) a todas estas funciones. Obsérvese que en las funciones se han definido variables locales (a y b); estas variables locales pertenecen al espacio de nombres de cada función, y por tanto se trata de variables distintas e independientes aunque se les de igual nombre. Como comprobación, haga cambios en las funciones, modificando las sentencias ejecutadas, los valores calculados, etc. Al compilar y ejecutar, podrá comprobar que los cambios efectuados en una función no afectan para nada a las demás

    El programa podría ser el siguiente:
    #include<stdio.h>
    /*
    	Prototipos
    */
    void probar_operaciones_short(void);
    void probar_operaciones_unsigned_short(void);
    void probar_operaciones_int(void);
    void probar_operaciones_unsigned_int(void);
    void probar_operaciones_long(void);
    void probar_operaciones_unsigned_long(void);
    void probar_operaciones_longlong(void);
    void probar_operaciones_unsignedlonglong(void);
    /*
    	Programa principal
    */
    int main(int argc, char * argv[])
    {
    	printf("Este programa muestra el uso de las cuatro reglas\n\n");
    	probar_operaciones_short();
    	probar_operaciones_unsigned_short();
    	probar_operaciones_int();
    	probar_operaciones_unsigned_int();
    	probar_operaciones_long();
    	probar_operaciones_unsigned_long();
    	probar_operaciones_longlong();
    	probar_operaciones_unsignedlonglong();
    	
    	printf("\nTerminación normal del programa.\n\n");
    	return 0;
    }
    /*
    	Definición de las funciones
    */
    void probar_operaciones_short(void) {
    	short a, b;
    	printf("\nEscriba dos short separados por un espacio: ");
    	
    	scanf("%hd %hd", &a, &b);
    	
    	printf("\n\n%d + %d = %d\n", a, b, a + b);
    	printf("%d - %d = %d\n", a, b, a - b);
    	printf("%d * %d = %d\n", a, b, a * b);
    	printf("%d / %d = %d\n", a, b, a / b);
    }
    void probar_operaciones_unsigned_short(void) {
    	unsigned short a, b;
    	printf("\nEscriba dos unsigned short separados por un espacio: ");
    	
    	scanf("%hd %hd", &a, &b);
    	
    	printf("\n\n%u + %u = %u\n", a, b, a + b);
    	printf("%u - %u = %u\n", a, b, a - b);
    	printf("%u * %u = %u\n", a, b, a * b);
    	printf("%u / %u = %u\n", a, b, a / b);
    }
    void probar_operaciones_int(void) {
    	int a, b;
    	printf("\nEscriba dos int separados por un espacio: ");
    	
    	scanf("%d %d", &a, &b);
    	
    	printf("\n\n%d + %d = %d\n", a, b, a + b);
    	printf("%d - %d = %d\n", a, b, a - b);
    	printf("%d * %d = %d\n", a, b, a * b);
    	printf("%d / %d = %d\n", a, b, a / b);
    }
    void probar_operaciones_unsigned_int(void) {
    	unsigned int a, b;
    	printf("\nEscriba dos unsigned int separados por un espacio: ");
    	
    	scanf("%d %d", &a, &b);
    	
    	printf("\n\n%u + %u = %u\n", a, b, a + b);
    	printf("%u - %u = %u\n", a, b, a - b);
    	printf("%u * %u = %u\n", a, b, a * b);
    	printf("%u / %u = %u\n", a, b, a / b);
    }
    void probar_operaciones_long(void) {
    	long a, b;
    	printf("\nEscriba dos long separados por un espacio: ");
    	
    	scanf("%ld %ld", &a, &b);
    	
    	printf("\n\n%ld + %ld = %ld\n", a, b, a + b);
    	printf("%ld - %ld = %ld\n", a, b, a - b);
    	printf("%ld * %ld = %ld\n", a, b, a * b);
    	printf("%ld / %ld = %ld\n", a, b, a / b);
    }
    void probar_operaciones_unsigned_long(void) {
    	unsigned long a, b;
    	printf("\nEscriba dos unsigned long separados por un espacio: ");
    	
    	scanf("%ld %ld", &a, &b);
    	
    	printf("\n\n%lu + %lu = %lu\n", a, b, a + b);
    	printf("%lu - %lu = %lu\n", a, b, a - b);
    	printf("%lu * %lu = %lu\n", a, b, a * b);
    	printf("%lu / %lu = %lu\n", a, b, a / b);
    }
    void probar_operaciones_longlong(void) {
    	long long a, b;
    	printf("\nEscriba dos long long separados por un espacio: ");
    	
    	scanf("%qd %qd", &a, &b);
    	
    	printf("\n\n%qd + %qd = %qd\n", a, b, a + b);
    	printf("%qd - %qd = %qd\n", a, b, a - b);
    	printf("%qd * %qd = %qd\n", a, b, a * b);
    	printf("%qd / %qd = %qd\n", a, b, a / b);
    }
    void probar_operaciones_unsignedlonglong(void) {
    	unsigned long long a, b;
    	printf("\nEscriba dos unsigned long long separados por un espacio: ");
    	
    	scanf("%qd %qd", &a, &b);
    	
    	printf("\n\n%qd + %qd = %qd\n", a, b, a + b);
    	printf("%qd - %qd = %qd\n", a, b, a - b);
    	printf("%qd * %qd = %qd\n", a, b, a * b);
    	printf("%qd / %qd = %qd\n", a, b, a / b);
    }
    /*
    Resultados de la ejecución
    Este programa muestra el uso de las cuatro reglas
    Escriba dos short separados por un espacio: 32768 32767
    -32768 + 32767 = -1
    -32768 - 32767 = -65535
    -32768 * 32767 = -1073709056
    -32768 / 32767 = -1
    Escriba dos unsigned short separados por un espacio: 65536 65535
    0 + 65535 = 65535
    0 - 65535 = 4294901761
    0 * 65535 = 0
    0 / 65535 = 0
    Escriba dos int separados por un espacio: 2147483648 2147483647
    -2147483648 + 2147483647 = -1
    -2147483648 - 2147483647 = 1
    -2147483648 * 2147483647 = -2147483648
    -2147483648 / 2147483647 = -1
    Escriba dos unsigned int separados por un espacio: 4294967296 4298672295
    0 + 3704999 = 3704999
    0 - 3704999 = 4291262297
    0 * 3704999 = 0
    0 / 3704999 = 0
    Escriba dos long separados por un espacio: 2147483648 2147483647
    -2147483648 + 2147483647 = -1
    -2147483648 - 2147483647 = 1
    -2147483648 * 2147483647 = -2147483648
    -2147483648 / 2147483647 = -1
    Escriba dos unsigned long separados por un espacio: 4294967296 4298672295
    0 + 3704999 = 3704999
    0 - 3704999 = 4291262297
    0 * 3704999 = 0
    0 / 3704999 = 0
    Escriba dos long long separados por un espacio: 9223372036854775808 9223372036854775807
    9223372036854775807 + 9223372036854775807 = -2
    9223372036854775807 - 9223372036854775807 = 0
    9223372036854775807 * 9223372036854775807 = 1
    9223372036854775807 / 9223372036854775807 = 1
    Escriba dos unsigned long long separados por un espacio: 18446744073709551616 18446744073709551615
    9223372036854775807 + 9223372036854775807 = -2
    9223372036854775807 - 9223372036854775807 = 0
    9223372036854775807 * 9223372036854775807 = 1
    9223372036854775807 / 9223372036854775807 = 1
    Terminación normal del programa.
    */
    Notas
    Este programa muestra la aplicación práctica de los distintos formatos de lectura y escritura necesarios para manejar los tipos enteros disponibles en C. Ahora bien, nuestro objetivo era comprobar los problemas que surgen cuando se produce un desbordamiento, y esto se ha mostrado haciendo uso de valores que sobrepasan precisamente en una unidad el mayor valor admisible para los distintos tipos enteros. En todos los casos, el programa sigue su ejecución sin mostrar ninguna indicación de fallo, pero los resultados carecen de sentido. Por tanto, será preciso asegurarse de que las operaciones realizadas no puedan dar lugar a este tipo de circunstancias.
    Ciertamente procede un comentario tranquilizador: como puede observarse, C ofrece tipos de datos enteros de alcance tan grande que sería preciso abordar problemas de Física o de Matemáticas para alcanzar sus límites. En un uso normal, la corrección aritmética está prácticamente asegurada.

  2. Ejercicio 0101r02.-[Anticipación] Considérese la sentencia siguiente:
    if (a % b == 0) printf("%d no es primo porque es divisible por %d", a, b);
    Esta sentencia imprimirá en pantalla un mensaje si el resto de dividir a por b es cero, esto es, si a es divisible por b. Se pide escribir un programa que permita averiguar si es primo un número menor que 20.
    El programa podría ser como sigue:
    #include<stdio.h>
    int main(int argc,char * argv[])
    {
    	int numero;
    	int es_primo;
    	int i;
    	
    	do
    		{
    			printf("\nPor favor, escriba un entero entre 1 y 20: ");
    			scanf("%d", &numero);
    		} while (numero < 1 || numero > 20);
    	
    	es_primo = 1; /* suponemos inicialmente que el número es primo */
    	
    	for(i=2;i<numero;i++)
    		if (numero % i == 0)
    			{
    				es_primo = 0;
    				break;
    			}
    	
    	if (es_primo)
    		printf("\nEl número %d es primo.\n\n", numero);
    	else
    		printf("\nEl número %d no es primo, es divisible por %d.\n\n", numero, i);
    	return 0;
    }
    /*
    Resultado de varias ejecuciones del programa:
    Por favor, escriba un entero entre 1 y 20: 8
    El número 8 no es primo, es divisible por 2.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 2
    El número 2 es primo.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 1
    El número 1 es primo.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 3
    El número 3 es primo.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 4
    El número 4 no es primo, es divisible por 2.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 5
    El número 5 es primo.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 6
    El número 6 no es primo, es divisible por 2.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 7
    El número 7 es primo.
    [maxus~:] coti% ./a.out
    Por favor, escriba un entero entre 1 y 20: 45
    Por favor, escriba un entero entre 1 y 20: 20
    El número 20 no es primo, es divisible por 2.
    */

    Notas
    La ejecución muestra que el programa admite únicamente números entre 1 y 20, según lo especificado. Aún cuando no se muestra una ejecución exhaustiva, para todos los posibles valores admitidos, se puede observar que en efecto el programa es capaz de determinar si un número entre 1 y 20 es primo o no. ¿Cómo se podría reducir el número de operaciones? La reducción del número de operaciones es una optimización del programa que sólo tendrá sentido al investigar números muy grandes; téngase en cuenta que una reducción del 50% en el número de operaciones supone una reducción máxima del 50% en los tiempos de ejecución.