C es probablemente el lenguaje de programación más conocido. Es usado como un lenguaje de referencia para cursos de programación alrededor de todo el mundo y es probablemente el lenguaje más aprendido por las personas junto con Python y Java. Es un lenguaje de programación de bajo nivel y compilado, lo que significa que genera un archivo binario que puede ser directamente ejecutado y distribuido. C no esconde la complejidad y capacidades de la máquina, ofreciendo mucho poder una vez que se entienden sus fundamentos.
Declaración de Variables en C
Los lenguajes de alto nivel, como C, facilitan mucho la creación y mantenimiento de variables. 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. La forma básica de declarar una variable es: tipo_base nombre_variable;.
El tipo_base es cualquier tipo válido de C. El nombre_variable es un identificador válido en C, esto es, una letra seguida opcionalmente por otras letras y números. Téngase en cuenta que no se admiten signos diacríticos (acentos, tildes, etc.) y que las letras mayúsculas y minúsculas no se consideran equivalentes. Por otra parte, no se admite utilizar como identificadores los nombres de palabras reservadas en C. El compilador se asegura de que las operaciones realizadas con estas variables sean las adecuadas para su tipo; por ejemplo, no permitirá almacenar un char en un float, ni concatenar dos int.
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. Evidentemente, el valor asignado tiene que pertenecer al tipo de la variable declarada.
Lea también: IVA 21% Excel
Alcance de las Variables
Aquellas variables que se declaren dentro del programa principal (entre las llaves de main(), en este caso) serán variables locales de la función main(), y por tanto resultarán invisibles para cualquier otro subprograma. Aquellas variables que se declaren fuera de main(), serán variables globales dentro del fichero en que reside el programa. Esto es, todas las funciones declaradas dentro de ese fichero podrán utilizar las variables globales como si fuesen propias; esto sirve para intercambiar información entre funciones, pero puede suponer un riesgo de corrupción de información si una función hace uso inadvertidamente de una variable global en lugar de emplear una variable local.
Hay una tercera regla, que hace referencia a la situación de la declaración con respecto al lugar en que se quiera utilizar lo declarado. Para que lo declarado (la variable, prototipo o lo que sea) resulte visible para otro objeto del mismo ámbito local o global, es necesario que la declaración sea cronológicamente previa al lugar en que se quiera utilizar.
Tipos de Datos Enteros para Valores Largos
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á). Esto es crucial para declarar valores largos.
C nos provee los siguientes tipos para definir valores enteros: char, int, short, long. La mayoría del tiempo es probable que uses int para almacenar enteros. Pero en algunos casos podrías querer usar una de las otras opciones, especialmente long, long long, o sus versiones unsigned, cuando se necesitan valores más grandes.
Algunos tipos básicos de datos se pueden ampliar utilizando los cualificadores de tipos de datos short (corto), long (largo), signed (con signo) y unsigned (sin signo). Si se omite el clasificador por defecto se considera signed.
Lea también: Guía IVA reducido
El problema es que el número exacto de bits que puede ser almacenado en cada tipo de dato depende de la implementación y arquitectura. Tenemos garantizado que short no es más grande que int, y que long no es más pequeño que int. El estándar de especificaciones ANSI C determina los valores mínimos de cada tipo, lo que nos permite saber cuál es el valor mínimo que podemos esperar tener a nuestra disposición. Los tipos long long y unsigned long long pueden no encontrarse en todos los compiladores.
Enteros Sin Signo
Para todos los tipos de datos enteros, podemos anteponer unsigned para comenzar el rango desde 0, en vez de números negativos. Esto es útil cuando solo se necesitan valores positivos y se busca extender el rango superior.
| Tipo | Bytes (Mínimo Garantizado) | Rango Mínimo Garantizado (ANSI C) | Observaciones Típicas |
|---|---|---|---|
| char | 1 | -127 a 127 | A menudo se usa para caracteres ASCII. |
| unsigned char | 1 | 0 a 255 | |
| short | 2 | -32,767 a 32,767 | |
| unsigned short | 2 | 0 a 65,535 | |
| int | 2 | -32,767 a 32,767 | Típicamente 4 bytes en sistemas modernos. |
| unsigned int | 2 | 0 a 65,535 | Típicamente 4 bytes en sistemas modernos (0 a 4,294,967,295). |
| long | 4 | -2,147,483,647 a 2,147,483,647 | Garantizado de al menos 4 bytes. |
| unsigned long | 4 | 0 a 4,294,967,295 | Garantizado de al menos 4 bytes. |
| long long | 8 (típico) | -9,223,372,036,854,775,807 a 9,223,372,036,854,775,807 | Introducido en C99, puede no estar en todos los compiladores más antiguos. |
| unsigned long long | 8 (típico) | 0 a 18,446,744,073,709,551,615 | Introducido en C99, para los valores enteros positivos más grandes. |
Problema de Desbordamiento
Dados todos estos límites, puede surgir una duda: ¿cómo podemos estar seguros de que nuestros números no excederán los límites? Y ¿qué pasa si excedemos estos límites? Ciertamente, 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.
Sin embargo, si tienes un número de tipo unsigned char en 255, y lo incrementas, obtendrás un 0 de retorno. Si no tienes un valor con signo, el comportamiento es indefinido. En otras palabras, C no te protege de salirte de los límites de un tipo. Si el programa sigue su ejecución sin mostrar ninguna indicación de fallo, los resultados pueden carecer de sentido. Por tanto, será preciso asegurarse de que las operaciones realizadas no puedan dar lugar a este tipo de circunstancias, por ejemplo, verificando lo que sucede al producirse un desbordamiento para los distintos tipos de enteros disponibles.
Números de Punto Flotante para Valores Largos
Los tipos de punto flotante pueden representar un conjunto de valores mucho más grandes de lo que pueden hacer los enteros (int), y también pueden representar fracciones, algo que los enteros no pueden hacer. Los siguientes tipos: float, double, long double son usados para representar números con punto decimal (tipos de punto flotante). Todos ellos pueden representar números positivos y negativos.
Lea también: ¿Cómo localizar tus XML del SAT?
Los requerimientos mínimos para cualquier implementación en C es que float puede representar un rango entre 10^-37 y 10^+37, y típicamente es implementado usando 32 bits. double puede representar conjuntos más grandes de números. long double puede tener incluso más números. Las cifras exactas, al igual que con los números enteros, dependen de la implementación.
En una Mac moderna, un float es representado en 32 bits, y tiene una precisión de 24 bits significativos, de los cuales 8 bits son usados para codificar el exponente. Un número double se representa en 64 bits, con una precisión de 53 bits significativos, 11 bits de los cuales son usados para codificar el exponente. El tipo long double es representado en 80 bits, tiene una precisión de 64 bits significativos, y 15 bits son usados para codificar el exponente.
Declaración de Constantes
Una constante se declara de forma similar a las variables, excepto que anteponemos la palabra reservada const y siempre tienes que especificar un valor. De la siguiente forma: const int age = 37;. Esto es C perfectamente válido, aunque es común declarar las constantes con mayúsculas, así: const int AGE = 37;. Esta es solo una convención, pero una que puede ayudarte mucho mientras escribes o lees un programa C, ya que mejora su legibilidad. Los nombres en mayúscula significan constantes, nombres en minúsculas significan variables. Un nombre de constante sigue las mismas reglas que los nombres de variables: pueden contener letras mayúsculas o minúsculas, pueden contener dígitos y el guion bajo (_), pero no pueden comenzar con un dígito.
Entrada/Salida de Valores Enteros y Largos
El lenguaje C carece de operaciones propias de Entrada/Salida (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. Estos archivos se incluyen en un programa mediante la instrucción #include <stdio.h> al comienzo del programa.
Función printf() para Salida
Se pueden escribir datos en el dispositivo de salida estándar, utilizando la función de biblioteca printf(). La función tiene el formato printf(cadena_de_formato, lista_de_variables); donde cadena_de_formato es una cadena de caracteres que indica la forma en que hay que escribir en pantalla las variables. La lista_de_variables es una colección de variables formada por uno o más elementos separados mediante comas.
Los indicadores de formato comienzan por el signo % y van seguidos por caracteres que indican el tipo de formato que debe darse a la variable correspondiente. En el caso de las variables enteras, las letras que hay que utilizar son d o ld (%d o bien %ld), en función de la longitud del tipo entero empleado. Los compiladores actuales producen resultados correctos para ambos tipos de enteros empleando el factor de conversión %d. Para tipos long long, se usa %lld.
Función scanf() para Entrada
La función scanf() se puede utilizar para introducir cualquier combinación de valores numéricos, caracteres individuales y cadenas de caracteres. Su formato es similar: scanf(cadena_de_control, lista_de_direcciones);. La cadena_de_control contiene especificadores de formato que coinciden con los tipos de datos que se esperan leer. La lista_de_direcciones es una colección de direcciones de variables que scanf() emplea como destino de la información leída.
Los argumentos pueden ser variables o arreglos y sus tipos deben coincidir con los indicados por los grupos de caracteres correspondientes en la cadena de control. Dicho de otro modo, scanf() cree firmemente que las direcciones que se le proporcionan corresponden a variables de tipo correcto y extensión suficiente para almacenar el resultado de la lectura. Si se lee más información que la que cabe en la variable señalada por una dirección, los resultados son imprevisibles y generalmente negativos. Si el programador sospecha que hay peligro de entrada de datos excesivo y/o incorrectos, procederá a emplear la función fgets() para limitar la extensión de la cadena leída, realizando después una traducción al formato oportuno mediante funciones como strtol(), strtof(), etc.
Para tipos enteros, se utilizan especificadores como %d para int, %ld para long, y %lld para long long. Para sus versiones sin signo, se utilizan %u, %lu, y %llu respectivamente.
Operaciones con Enteros Largos
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. El objetivo de este procedimiento es no perder nunca información por efectuar operaciones con tipos de menor precisión que pudieran dar lugar a desbordamientos.
Los operandos que difieren en el tipo pueden sufrir una conversión de tipo antes de que la expresión alcance su valor final. Si se desea, se puede convertir el valor resultante de una expresión a un tipo de datos diferente.
Operadores Aritméticos
El operador módulo (%) proporciona el resto de la división entera de sus dos operandos; por ejemplo, 7 % 5 es 2. Los operadores aritméticos, *, / y % se encuentran dentro de un mismo grupo de precedencia, y + y - se encuentran en otro. Los operadores también tienen la regla de asociatividad, la cual siempre es de izquierda a derecha, excepto por los operadores unarios y el operador de asignación.
La precedencia natural de las operaciones se puede alterar mediante el uso de paréntesis, permitiendo estos que se puedan efectuar operaciones aritméticas de una expresión en el orden que se desee. También se pueden anidar los paréntesis, es decir, un par dentro de otro. A veces es una buena idea utilizar paréntesis para hacer más clara una expresión, aunque no sean necesarios.
Operadores Unarios
C incluye una clase de operadores que actúan sobre un solo operando para producir un nuevo valor. Estos operadores se denominan operadores unarios o monarios. El operador unario de uso más frecuente es el menos unario, que consiste en un signo menos - delante de una constante numérica, una variable o una expresión. La operación menos unaria es distinta del operador aritmético que representa la resta (-).
Otros dos operadores unarios de uso frecuente son el operador incremento, ++, y el operador decremento, --. El operador incremento hace que su operando se incremente en uno, mientras que el operador decremento hace que su operando se decremente en uno. La diferencia entre a++ y ++a es que a++ incrementa la variable a después de usarla.
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.
En C, los comentarios se denotan encerrando el texto correspondiente entre los signos /* y */. Es una buena práctica en cualquier lenguaje de programación realizar comentarios para documentar el programa, ya que son valiosísimos para comprender el código fuente y para introducir modificaciones.
