Programación en C/Texto completo

De Wikilibros, la colección de libros de texto de contenido libre.
Saltar a: navegación, buscar
Esta es la versión para imprimir de Programación en C.
  • Si imprimes esta página, o eliges la opción de Vista preliminar de impresión de tu navegador, verás que desaparecen este cuadro y los elementos de navegación de arriba y de la izquierda, pues no son útiles en una versión impresa.
  • Pulsando antes en Refrescar esta página te asegurarás de obtener los últimos cambios del libro antes de imprimirlo.
  • Para más información, puedes ver Wikilibros:Versión para imprimir.

Sumario


Prólogo

El avance de la tecnología y la concurrente entrega de información, nos permite generar una edición, variable y alternativa en la enseñanza del lenguaje de programación que más impacto social ha tenido en la historia de la informática. Este libro ha sido forjado con la incansable ayuda de informáticos de habla hispana, quienes byte a byte han colaborado por hacer de la información una fuente de conocimiento global. De la misma forma, continúa hoy siendo modificado en una constante búsqueda de superación de la calidad.

Esta obra está llamada a ser la piedra angular en la enseñanza de la programación, logrando abarcar todos los aspectos del lenguaje en diversos niveles y de esta forma ser tanto una referencia técnica para quienes ya dominan el lenguaje como una introducción sencilla para quienes están empezando a conocerlo.

Viajaremos por la historia del lenguaje, veremos su propósito e indagaremos en la ciencia de la programación. El fin es otorgar al lector una doctrina clara de la programación y el lenguaje C; lo induciremos a conseguir un manejo importante del lenguaje.

¿Para quién es este libro?

Este libro está dirigido a todos los que deseen obtener conocimientos de programación, pues el objetivo explícito que nos ha motivado a crearlo es difundir la importancia del lenguaje C en el mundo de la informática. Si nos enfocamos a un grupo social específico, podremos indicar que este libro contribuirá con los estudiantes de carreras del área informática, debido a que los temas convenidos, son parte de su plan de estudios.


¿Por qué otro manual de C?

Porque el lenguaje C es la base fundamental de la programación. Para quienes están en el ambiente de la informática es crucial tener por lo menos nociones de este lenguaje. Varios sistemas operativos, cientos de bibliotecas, y miles de programas están construidos utilizando C, al conocerlo es posible entender, colaborar y desarrollar en este lenguaje.

Los sistemas, programas, juegos y herramientas que nosotros disfrutamos hoy fueron construidos por personas como nosotros, que empezaron con nuestro mismo potencial y fueron aprendiendo a hacer cosas fantásticas con las herramientas que tenían a mano.

Una razón importante para otro libro de lenguaje C es también que los libros existentes muestran muy poca documentación de calidad. En Internet existe una cantidad inmensa de información publicada pero está dispersa, y mal manejada en algunos sitios. Es la intención de este libro crear un buen compendio de información, que permita a los interesados aprender a programar en C.


Enlaces

Nociones básicas de programación (generales, independientes del lenguaje):

Wikilibro similar a éste en inglés

esquema de operadores y expresiones

Licencia y autores

Ver el historial de cada página para el resto de autores.

¿Cómo contribuir a este WikiLibro?

Contribuir con este libro es muy simple primero deberías registrarte un usuario y/o entrar (esto no es necesario pero si muy conveniente) a Wikilibros, para que podamos identificar tus ediciones. Luego, si nunca has contribuido en un proyecto de wikipedia o en otro wiki deberías leer el manual de uso de wikilibros.

Una vez hecho esto todo lo que tienes que hacer es agregar el contenido que consideres necesario para el libro. Para editar cualquier sección basta con hacer click al link que dice editar en la pestaña en la parte superior de la pagina, seria bueno revisar (antes de editar cualquier cosa) la pestaña de discusión que está ahí para ser usada. Ten en cuenta que el material que ya está fue escrito por personas que deseaban contribuir igual que tú, así que trata de respetarlo. Aunque con esto no me refiero a que si se necesita alguna corrección, reorganización, quitar partes que sean ambiguas, no dejes de hacerlo. Lo mejor sería que todos contribuyéramos de cualquier manera al libro.

Además, es recomendable consultar la página de discusión del libro y la del articulo en particular que quieras modificar, ya que de esta manera se pueden coordinar esfuerzos.

Recuerda que todo el contenido que añadas al libro es publicado bajo la licencia GFDL, por lo que no uses material que no haya sido escrito por ti o que no esté ya publicado bajo GFDL. Recientemente wikimedia decidió adoptar la Licencia Creative Commons Compartir-Igual 3.0 para todos los aportes, por lo que actualmente el libro tiene una licencia dual.

Introducción

Objetivos

El objetivo principal de este Wikilibro es que cualquier persona sin conocimientos previos de programación pueda ser capaz de programar en el lenguaje C.

Una vez logrado el dominio del lenguaje, es probable que los lectores se interesen por otros temas más complejos que superen a los temas básicos. También les será más o menos sencillo aprender cualquier otro lenguaje de programación estructurada.

Sin embargo, este no es un libro que apunte únicamente a programadores principiantes. También puede resultar de interés para quienes ya tengan experiencia en el área de programación. En esta introducción hay dos secciones en las que se explica para los dos grupos principales de lectores qué camino seguir para comenzar a programar en el lenguaje C o bien perfeccionar conocimientos.

El lenguaje C es tan usado porque es un lenguaje de programación que emplea pocas instrucciones en lenguaje máquina para traducir elementos del código. Esto reduce los tiempos de ejecución de los programas.

Nota sobre la exactitud

Muchas de las cosas expresadas en este wikilibro, especialmente en los primeros capítulos, no son completamente exactas, aunque son buenas aproximaciones. Los detalles más exactos irán apareciendo posteriormente, una vez que los materiales anteriores hayan sido correctamente asimilados por el lector. En general, dadas dos definiciones o datos contradictorios en este wikilibro, debe considerarse siempre como más exacto al segundo, habiendo aparecido el primero como una introducción más general al tema.

Estándar utilizado

El lenguaje C fue creado en los años setenta, y a lo largo de su historia ha pasado por muchas modificaciones, tanto con respecto a la sintaxis como con respecto al código incluido dentro de la biblioteca estándar. Es por ello que se fueron desarrollando estándares, para que todos sepan con qué versión del lenguaje se está trabajando.

Los distintos estándares del lenguaje C han sido: el C de Kernighan y Ritchie, un estándar no-oficial que surgió luego de la publicación de su libro en 1978; el C89 o C90, el primer estándar oficial, posterior a la publicación de los estándares ANSI en 1989 e ISO en 1990; y el C99, publicado en 1999.

En este libro se utilizará el estándar C99, si bien por cuestiones de estilo y compatibilidad muchas veces se utilizará código compatible con el estándar C89.

Para los principiantes

Para quien no haya programado antes, es recomendable seguir el orden del libro. Los temas están especialmente organizados de manera incremental o acumulativa. Tal vez, lo que se te va a hacer más útil en el camino del aprendizaje es la constancia; sé terco, no trastabilles, no te rindas, tal vez tu pregunta sea ¿cuántas veces tengo que intentar?, las veces necesarias para lograr tu objetivo, sería la respuesta.

Claro que el principal enemigo de nosotros los humanos es el tiempo y por eso en caso de que de verdad estés trancado en algo busca ayuda de alguien que sepa "más que tú". ¿Que no tienes a nadie a tu alrededor con esa característica? Tal vez no buscaste bien y tal vez quieras usar la red de redes. Utiliza los buscadores, pregunta en IRC, en foros de programación, en listas de correo.

Para los más avanzados

El lanzamiento queda libre por supuesto, solo tú sabes lo que necesitas. Las reglas del juego son las mismas de siempre: primero saber lo que se quiere o necesita y atacar por ahí.

En este caso, te será útil acceder a los contenidos a partir del índice, eligiendo sólo aquellos que te sean necesarios.

Requisitos

Se presupone que los lectores tienen conocimientos elementales de informática a nivel de usuario, y son capaces de instalar un compilador del lenguaje C en sus sistema. Los detalles sobre la instalación se verán en la sección Herramientas.

Con respecto al Hardware, sólo será necesario contar con una PC con sistema operativo, donde sea posible instalar un compilador, y en lo posible un entorno de desarrollo. Cuanto mejor sea la computadora, más rápido será el proceso de compilación y ejecución de los programas. Sin embargo, cualquier PC sirve para aprender con los ejemplos de este libro.

Para quienes no tengan conocimientos básicos de programación, puede ser una buena idea comenzar leyendo los primeros capítulos del Wikilibro Fundamentos de programación, ya que algunos temas explicados en ese libro se asumen ya conocidos.

Finalmente, un requisito imprescindible en todo programador es tener sentido común. Muchas veces se pueden adoptar mejores o peores soluciones ante los diversos problemas, y la decisión de cuál elegir pasa por la aplicación del sentido común.

Herramientas

Para programar tanto en C, como en C++, Java o cualquier otro lenguaje de programación, necesitamos contar con aplicaciones o herramientas que nos permitan poner en funcionamiento nuestro programa.

El lenguaje de programación C es compilado, así que en este caso necesitaremos un compilador, que será el encargado de transformar nuestro código fuente en código que la computadora pueda ejecutar.

Además, para facilitar la tarea de los programadores existen los denominados Entorno de desarrollo integrados (IDE). En muchos casos, estos entornos incluyen un compilador, un depurador, y otras herramientas.

Las herramientas a instalar dependerán del sistema operativo utilizado. A continuación se listan algunas posibilidades para el sistema operativo Windows o GNU/Linux, no es imprescindible utilizar estas herramientas en particular, cualquier compilador puede servir.


Windows

Uno de los entornos de desarrollo más conocidos entre los programadores de C sobre Windows, tanto novatos como expertos, es el Bloodshed Dev-C++, que es un entorno libre multiplataforma. Tal entorno de desarrollo fue abandonado y retomado mejorándolo pasando a llamarse WxDev-C++. Otro entorno libre y gratuito es el Code::Blocks. Ambos entornos pueden utilizarse tanto para C como para C++.

También hay otras alternativas privativas como los compiladores de Borland o de Microsoft (Microsoft Visual C++).

GNU/Linux

En los sistemas GNU/Linux, será necesario tener instaladas las herramientas gcc y make y la versión 6 de la glibc con su documentación, que son las que permitirán compilar los programas.

Para escribir y modificar el código, es posible utilizar cualquier editor de texto plano (en lo posible que cuente con resaltado de sintaxis), como son emacs, vim, kate, gedit o geany.

Sin embargo, para quienes son novatos en la programación, es recomendable utilizar un entorno de desarrollo como son el Anjuta DevStudio (para el entorno GNOME) o KDevelop (para el entorno KDE), ya que incluyen facilidades adicionales para la ejecución y solución de problemas.

Los programas mencionados se incluyen dentro de la instalación estándar de la mayoría de las distribuciones actuales de GNU/Linux, de modo que para instalarlos sólo será necesario seguir el procedimiento usual de instalación de aplicaciones para la distribución deseada.



Historia de C

El lenguaje de programación C fue creado por Dennis Ritchie entre 1969 y 1973 cuando trabajaba en Bell Laboratories de AT&T junto con Ken Thompson en el diseño del sistema operativo UNIX. C fue creado para poder escribir dicho sistema operativo en un lenguaje de alto nivel, independiente del hardware donde se ejecutara.

Contar con un lenguaje de alto nivel permitió el avance de los sistemas operativos, ya que el mismo código podía ser utilizado en las distintas plataformas, propiciando la reutilización de código y reduciendo los tiempos de desarrollo. Así es que los sistemas operativos basados en UNIX, el sistema BSD, el sistema GNU/Linux y muchos otros fueron desarrollados en C.

Además, con el paso del tiempo se han desarrollado cientos de bibliotecas que permiten a los programadores de C utilizar el código desarrollado por otros para la realización de tareas comunes. Esto, a su vez, ha propiciado el desarrollo de aplicaciones en lenguaje C.

Actualmente es imposible contar la cantidad de aplicaciones y herramientas desarrolladas en C.

Evolución

A mediados de los años 60s, Martin Richards diseñó el lenguaje BCPL con la finalidad de usarlo para escribir software de sistemas operativos y compiladores.

En 1969, Ken Thompson escribió el Lenguaje B, en Bell Laboratories, con el objetivo de recodificar UNIX (escrito hasta ese momento en lenguaje ensamblador) usando un lenguaje de alto nivel más portable y flexible.

Durante los siguientes años, Dennis Ritchie modificó el lenguaje B, llegando a crear el lenguaje C y reescribiendo el sistema UNIX en dicho lenguaje; añadió características nuevas, como son el diseño de tipos y las estructuras de datos.

En 1978, Dennis Ritchie y Brian Kernighan publicaron la primera edición del libro El lenguaje de programación C. Este libro fue durante años la especificación informal del lenguaje. El lenguaje descrito en la primera edición de este libro, fue conocido como "el C de Kernighan y Ritchie" o simplemente "K&R C". En este libro se introdujeron nuevas características al lenguaje: los tipo de datos struct, long int y unsigned int; los operadores =+ y =- fueron sustituidos por += y -=.

A mediados de los años 80, Bjarne Stroustrup (también de los laboratorios Bell), crea el lenguaje C++, un lenguaje basado en C, con numerosas características adicionales, siendo la principal que está orientado a objetos. Si bien se han creado muchos lenguajes basados en C, C++ es el que ha permanecido más asociado a C.

En los años siguientes a la publicación del C de Kernighan y Ritchie, se añadieron al lenguaje muchas características no oficiales, que estaban presentes en algunos compiladores y no en otros. Fue por ello que en 1989 ANSI (American National Standards Institute) publicó el primer estándar oficial de C, que es conocido como ANSI C.

En este estándar se tomaron muchas de las funcionalidades no oficiales y se agregaron funcionalidades nuevas como los prototipos de función, y un preprocesador mejorado. También se cambió la sintaxis de la declaración de parámetros de funciones, para que incluyeran el tipo junto con el nombre.

Al año siguiente, en 1990 se publicó la estandarización ISO del lenguaje. Este estándar es básicamente el estándar ANSI, con unas pocas modificaciones de formato. A este estándar se lo conoce, entonces, como C89, o C90, y se trata del mismo lenguaje.

Basándose en el estándar ANSI que estaba en preparación, en 1988 Kernighan y Ritchie publicaron la segunda edición de su libro, que es aún hoy utilizada como una de las referencias principales del lenguaje.

Durante los siguientes años, el lenguaje C permaneció sin demasiados cambios. Sin embargo, como había sucedido antes, los distintos compiladores fueron incorporando características adicionales, que otros compiladores no tenían, siendo C++ la principal influencia.

Fue por ello que a finales de los noventa se decidió revisar el estándar de C, lo que llevó a la publicación del estándar C99. Este estándar incluye varias nuevas características como son: las funciones inline; la posibilidad de declarar variables en cualquier parte del código; los comentarios de una sola línea utilizando //; los tipos de datos long long int, bool y complex, entre otras.

Aún hoy el proceso de evolución del lenguaje sigue avanzando, y desde 2007 se está trabajando en el armado de un nuevo estándar.

Más información

Fundamentos de programación

En este capítulo veremos un resumido listado de conceptos básicos, esta información puede encontrarse en forma más elaborada en el WikiLibro Fundamentos de programación.

Definiciones

  • Se denomina algoritmo a una secuencia de instrucciones que permiten obtener un resultado en particular. No necesariamente son programas de computadora, una receta de cocina, o las instrucciones para cambiar un neumático son ejemplos de algoritmos de la vida real.
  • Las computadoras, son maquinas sin inteligencia propia, cuya única finalidad es interpretar el código que se les provee.
  • El lenguaje de máquina es el único lenguaje que la computadora "entiende" y es capaz de ejecutar.
  • Los lenguajes de programación son el medio de comunicación entre el programador y una computadora. El programador escribe en algún lenguaje de programación y utiliza las herramientas provistas por ese lenguaje para transformarlo en lenguaje de máquina.
  • Finalmente, denominamos programa a una secuencia de órdenes a ser ejecutadas por una computadora. Un programa debe estar escrito en algún lenguaje de programación, y puede incluir uno o más algoritmos.

Tipos de lenguajes

Existe una gran cantidad de lenguajes de programación, que están pensados para distintas finalidades, siguen distintos paradigmas, y de una u otra forma se diferencian de los demás.

Esquemas de programación

El esquema de programación llamado Programación Imperativa, consiste en escribir una secuencia de instrucciones una detrás de la otra, que se ejecutarán en orden. Algunas de esas instrucciones pueden hacer que la máquina pase a una instrucción que no sea la siguiente, tal vez porque se cumpla una condición que hayamos establecido.

En los últimos años ha tomado fuerza otro paradigma de computación, llamado Programación Orientada a Objetos , en el cual se intentan modelar los sistemas creados como extensiones de la realidad mediante la definición de "objetos" que modelan entidades de la vida real y que interactúan entre sí mediante "mensajes" llamadas métodos.

El lenguaje C es un lenguaje imperativo, no orientado a objetos.

Alto o bajo nivel

Por otro lado, los lenguajes de programación se clasifican en niveles. Un lenguaje es de más bajo nivel cuanto más cercano esté al código de máquina, y un lenguaje que es de más alto nivel cuanto más lejano esté de la máquina y más cercano al lenguaje humano.

C es un lenguaje de alto nivel aunque tiene muchas características de lenguaje de bajo nivel (como el uso que permite hacer de la memoria). Estas características hacen que C sea un lenguaje muy potente, ya que permite optimizar al máximo los recursos de la máquina. Por ende, esto también hace que la dificultad y que los errores que se puedan cometer programando aumenten. Así que a C se le considera de nivel medio.

Lenguajes de más alto nivel que C son aquellos en los que el programador no necesita encargarse de manipular la memoria, como Java, C#, Python, Ruby, entre otros.

Compilados o interpretados

Otra forma de clasificar a los lenguajes de programación que es según la forma en que se ejecutan sus órdenes. Existen los lenguajes que son interpretados, cuyas órdenes pasan a través de un intérprete que se encarga de ejecutarlas (a partir del código fuente) en el mismo momento en que están siendo leídas. Algunos de los lenguajes interpretados son Python, Perl o Tcl, entre muchos otros.

La contraparte de los lenguajes interpretados son los lenguajes compilados (como el mismo C) que se diferencian en que las órdenes son transformadas a lenguaje de máquina que se almacena en un archivo ejecutable. Ese archivo puede ejecutarse luego, sin recurrir al compilador.

Los lenguajes compilados tienen la ventaja de la velocidad y la eficiencia, pero los interpretados tienen la ventaja de que, generalmente, son muy portables y de más alto nivel.

Estructura de la memoria

Parte de esta potencia de C viene de que permite acceder con mucha libertad a la memoria de la máquina. Para entender un poco cómo es posible, debemos entender cómo se guardan los datos en la memoria.

Imaginemos que la memoria tiene un montón de casillas, una enorme fila de casillas, cada una de las cuales contiene un dígito binario (bit):

0101001010100001010101001010000100111010110010010101001011010110001101010110101010110111...

Es exactamente así, pero es más cómodo recordar que esos bits se encuentran agrupados de ocho en ocho, formando octetos (bytes):

01010010 10100001 01010100 10100001 00111010 11001001 01010010 11010110 00110101 01101010 10110111 ...

Cada octeto puede contener combinaciones distintas de ceros y unos, es decir, cualquier número entre 0 y 255:

82 161 84 161 58 201 82 214 181 106 183 ...

También podemos representar estos números en base hexadecimal:

0x52 0xA1 0x54 0xA1 0x3A 0xC9 0x52 0xD6 0x35 0x6A 0xB7 ...

O considerarlos caracteres, mediante alguna codificación:

R ¡ T ¡ : É R Ö 5 j · ...

Este es el tipo de dato más elemental que nos podemos encontrar en C: el caracter. Un caracter ocupa exactamente un byte (8 bits) de memoria, y puede contener un número entre 0 y 255, o entre -128 y 127, dependiendo si queremos considerarlo como sin signo o con él.

Primer programa en C

En el libro "El Lenguaje de Programación C", Kernighan y Ritchie introdujeron al lenguaje C utilizando un sencillo programa que mostraba un saludo por la pantalla. Desde entonces se hizo tradición empezar con cualquier lenguaje de programación con el ejemplo del Hola mundo.

En particular en C se involucran muchas partes y sintaxis del lenguaje, por lo cual es especialmente útil verlo como el primer ejemplo de programación en C.

Ejemplo: Hola mundo

/* Inclusión de archivos */
#include <stdio.h>

/* Función principal */
int main (int argc,char **argv)
{
   /* Impresión por pantalla y salida del programa*/
   printf("Hola mundo\n");
   return 0;
}

Para poder editar y ejecutar este programa será necesario utilizar algún editor y luego un compilador, como se explicó en la sección Herramientas necesarias.

Si se tiene el compilador gcc en un entorno UNIX o GNU/Linux, la forma sencilla de compilar y ejecutar será:

$ gcc holamundo.c
$ ./a.out
Hola Mundo
$

Es decir que el compilador genera un archivo, en este caso llamado a.out, y la salida generada por ese archivo es "Hola mundo". A continuación una explicación detallada sobre el proceso de compilación del programa, y luego un análisis línea por línea del contenido de este ejemplo.


Pre-requisitos para la compilación de programas

Como ya se mencionó, será necesario tener instalado el compilador y un editor o entorno de desarrollo que permitan escribir el código a compilar. Para más información ver la sección Herramientas necesarias.

El código a compilar debe guardarse con un nombre que represente al programa en cuestión y la extensión .c. En el caso del ejemplo del Hola mundo, el archivo puede llamarse hola.c.

En las explicaciones a continuación, se asume que se cuenta con un compilador instalado y se ha editado un archivo hola.c que se quiere compilar. Si tu sistema operativo no aparece en esta lista busca en internet, ya que seguro que existe algún compilador para ese sistema.

Compilación de programas según la plataforma

Windows

Para compilar un programa C en entornos Windows, debemos seguir una serie de pasos que varían según el compilador de C que queramos utilizar. Antes que nada, sería bueno que se revises la documentación del compilador elegido para conocer los comandos exactos.

Compilación del código fuente

Si se utiliza un entorno de desarrollo, será posible compilar directamente desde el entorno, mediante un botón o una combinación de teclas.

Si se ejecuta el compilador desde la línea de comandos, la línea será distinta según el compilador utilizado. A continuación algunos ejemplos de ciertos comandos según el compilador:

  • En Turbo C de Borland es: tcc hola.c
  • En C++ de Borland: bcc hola.c
  • En Visual C de Microsoft: cl hola.c
  • En GNU gcc: gcc hola.c o cc hola.c
  • El C de Zortech: ztc hola.c

Una vez compilado el código fuente se genera un archivo llamado archivo objeto o programa objeto que es luego enlazado mediante el enlazador, para generar el archivo ejecutable.

Los compiladores actuales suelen hacer dos funciones de una vez, compilando y enlazando todo en una sola función, aunque es posible pedirles que no lo hagan mediante parámetros adicionales.

Según el compilador y la configuración utilizada, se obtendrán dos o tres archivos:

El archivo fuente

  hola.c

El archivo objeto

  hola.obj

El archivo ejecutable

  hola.exe

Este último es el que nos interesa, puesto a que es el código ejecutable, el programa en sí. Al ejecutarlo se producirá la salida deseada en una ventana de consola.

Salida por pantalla

Si ejecutamos en entorno Windows el programa directamente desde el navegador de archivos, o también desde algunos entornos de desarrollo, lo que sucederá será que apenas abierta la ventana de la consola, se mostrará la cadena esperada y luego de terminada la función, la consola se cerrará sin tener el tiempo suficiente de ver nuestro mensaje en pantalla.

Para poder ver la salida por pantalla será necesario ejecutar el programa desde la línea de comandos, o modificar la configuración del entorno de desarrollo para que muestre la salida por pantalla al ejecutar el programa.

Una posible solución es agregar una función adicional a nuestro "hola.c":

/* Inclusión de archivos */
#include <stdio.h>
#include <stdlib.h>

/* Función principal */
int main (int argc,char **argv)
{
   /* Impresión por pantalla y salida del programa*/
   printf("Hola mundo\n");
   system ("pause");
   return 0;
}

Las dos líneas agregadas permiten que utilicemos la biblioteca stdlib, que incluye la función system y que mediante esta función se ejecute el comando pause del sistema, que evita que el programa siga hasta que se presione una tecla.

Así es posible visualizar que la salida de hola.c se completó perfectamente.


GNU

Si bien existen otros compiladores, lo más usual y más sencillo para compilar un programa en GNU/Linux es el compilador gcc, ya que es el que se incluye en todas las distribuciones.

De cualquier forma, es posible realizar la compilación desde línea de comandos o desde el entorno gráfico.

Para realizarla desde línea de comandos, será necesario contar con una terminal (xterm, konsole, gnome-terminal, etc). No es necesario contar con permisos de root para crear o compilar programas. En esa terminal será necesario escribir

gcc hola.c

Si no existen errores en el código, este comando nos creará un archivo ejecutable, que por omisión se llama "a.out", y que podemos ejecutar desde la línea de comandos de la siguiente forma:

./a.out
Hola mundo 

Es una buena idea especificar el nombre que el archivo ejecutable tendrá, pasando como parámetro al compilador la opción -o, de la siguiente forma:

gcc hola.c -o hola

Con lo cual, el nombre del archivo creado será hola. Este archivo no tiene extensión ya que es la forma usual de llamar a los archivos ejecutables en los entornos UNIX y GNU/Linux, sin embargo funcionaría de la misma forma si se llamara hola.exe.

Para ejecutarlo, haremos los mismo que en el caso anterior:

./hola
Hola mundo 

Existen otros parámetros que podemos especificar al compilador en la línea de comandos, dependiendo del tipo de programa, y en función de la complejidad del mismo. Por ejemplo, podemos agregar las siguientes opciones:

gcc hola.c -o hola -Wall -pedantic

La opción -Wall nos mostrará todos los avisos que produzca el compilador, no solamente los errores. Los avisos nos indican dónde y/o porqué podría surgir algún error en nuestro programa.

La opción -pedantic nos aporta más información sobre los errores y los avisos mostrados por GCC.

Diseccionando el "Hola Mundo"

A continuación veremos cuál es la estructura básica de un programa en C, para poder entender qué hace cada una de las líneas de nuestro sencillo programa.

Es probable que lo primero que salte a la vista sea la línea:

   printf("Hola mundo\n");

Esta es la línea que hace aparecer la cadena Hola Mundo en nuestra pantalla. Notamos que en C la sentencia para imprimir algo por pantalla es printf() y, además, hay que colocar paréntesis alrededor de lo que queremos imprimir para utilizarla.

Esto se debe a que en C, printf es una función, que imprime su argumento (la cadena Hola Mundo\n) en la pantalla. Se denomina invocar una función a la acción de utilizarla para que realice una acción.

Podemos observar también que la cadena a imprimir termina con una extraña combinación: \n. La combinación \n no representa a dos caracteres independientes, sino que representa un único carácter no imprimible: el salto de línea. Sin el salto de línea, el resultado al ejecutar el programa sería:

$ ./a.out
Hola Mundo$

Es decir que no hay salto de línea entre la cadena impresa, y la siguiente entrada de la línea de órdenes, que no es lo que esperábamos.

Lo último a notar en la línea es que termina con un punto y coma. En C, todas las sentencias terminan con un punto y coma. Al principio puede parecer obvio dónde termina una sentencia, pero ya veremos más adelante que no lo es tanto.

Observemos ahora la siguiente sentencia del programa:

   return 0;

Luego de esta sentencia, termina el programa. En el caso de la instrucción return dentro de la función main, el resultado es que se finaliza el programa, comunicándole al sistema operativo que el valor de retorno (un código numérico que el sistema utiliza para saber si el programa ha funcionado bien o ha dado fallos) es 0, es decir, correcto.

Las dos últimas sentencias se encuentran encerradas entre llaves. De esta manera, forman un bloque, es decir, un grupo de sentencias que se ejecutarán siempre de forma correlativa.

¿Y qué es esa línea que precede (en realidad, que da nombre) al bloque?

int main (int argc, char **argv)

Pues es la definición de una función, en este caso llamada main. En C (y en general en todos los lenguajes de programación estructurada) todo se hace a base de funciones, como main y printf.

La función main es especial, porque es el la que se invoca cuando se ejecuta el programa. Todos los programas en C comienzan su ejecución al principio de la función main, y cuando ésta acaba, el programa también.

Veamos con más detalle la definición de la función:

int main (int argc, char **argv)
{
   ...
}
  • El nombre de la función que viene a continuación, entre llaves, es main.
  • Recibe dos argumentos: int argc y char **argv (que representan a la cantidad de argumentos ingresados al ejecutar el programa y a los valores de estos argumentos respectivamente).[1]
  • La función devuelve como resultado un número entero, int (que es el 0 de la instrucción return).[2]

Finalmente, y un tanto aparte (está separada del resto por una línea en blanco), tenemos la línea:

#include <stdio.h>

Que parece bastante distinta al resto del programa, y que, además, parece no tener sentido, puesto que ya hemos definido la función main que hace todo el trabajo.

Efectivamente, esa línea no es parte del programa, aunque sea imprescindible. La línea es una instrucción del preprocesador de C, como nos lo indica el símbolo #, y lo que hace es incluir en ese punto el contenido de otro fichero, antes (de ahí el nombre de preprocesador) de que comience la compilación. El fichero stdio.h es el que contiene la definición de la función printf(), que antes utilizamos pero que no escribimos, ya que forma parte de la biblioteca estándar de C.

Comentarios

Una vez escrito un código, tratar de entenderlo un año más tarde solo con leerlo puede ser frustrante: no hay manera de saber (si el programa es medianamente complicado) qué es cada variable, o qué hace cada bloque de código. Por esto, en cualquier lenguaje de programación son importantes los comentarios.

Un comentario en C es todo lo que se encuentre entre los símbolos /* y */. Hay que tener en cuenta que los comentarios no se pueden anidar: si dentro de un comentario hay un /*, seguirá siendo el primer */ el que finalice el comentario, no se esperará al segundo.

Hay otro tipo de comentarios en C, procedentes del lenguaje C++, e incorporadas al estándar de C a partir de C99: //. Todo lo que esté después de estos signos, hasta el final de la línea, se considerará un comentario y el compilador no lo tomará en cuenta.

En el ejemplo presentado pueden verse tres líneas con comentarios, que documentan someramente las distintas funcionalidades del código. En los próximos capítulos podrán verse mejores usos de los comentarios dentro del código.

También podría decirse que es una herramienta básica basada en compilador


  1. En un capítulo posterior podrá ver un ejemplo del uso de los parámetros que recibe main.
  2. Es importante señalar que el estándar dice que main deberá definirse como función que retorna un entero, o de lo contrario el resultado queda indefinido.



Tipos de datos

Historia

En el lenguaje C estandarizado como C89, existían cuatro tipos de datos básicos que son: los números enteros, los números reales, los caracteres, y los punteros. A partir del estándar C99 se agregan: los valores lógicos (verdadero o falso) y los números complejos.

Estos tipos de datos son parte del lenguaje, y por ello se los considera primitivos. Más adelante veremos que con el uso de estructuras y uniones es posible crear tipos compuestos de datos a partir de estos tipos primitivos.

En este capítulo veremos los enteros, los reales y los caracteres. Más adelante se verán otros tipos de datos más complejos, como son los vectores, las cadenas de caracteres, y los punteros en general.

Enteros

Los enteros son el tipo de dato más primitivo en C. Se usan para representar números enteros. Pero siempre se pueden encontrar otras aplicaciones para los números enteros. En general se pueden usar para representar cualquier variable discreta.

Los tipos de datos enteros son: short, int, long y long long, cada uno representando un número entero de un tamaño o capacidad determinado. Según el compilador y la plataforma de hardware, cada uno de estos tipos de dato puede ocupar desde 1 byte hasta 8 bytes en memoria (para más detalles busca en la referencia).

Además, el lenguaje C hace la distinción de si el entero es con signo (signed) o sin signo (unsigned). En caso de que no se declare si es con signo o sin signo, se toma con signo.

Algunos ejemplos de declaraciones de enteros:

  int a;
  unsigned int a;
  signed long a;
  signed long long a = 10000000;

Todos los números son representados en memoria mediante una cadena de bits. En el caso de los números con signo, el bit más significativo es el que se usa para representar el signo. La representación de los números negativos se realiza mediante el complemento a dos, que es una técnica que permite operar con los números negativos de forma lógica.

A modo de ejemplo, la representación en memoria del número -8 en una variable de 2 bytes, entera, con signo, sería la siguiente:

 1111111111111000

Flotantes

Se denomina flotantes a los tipos de datos que representan a los números reales, ya que utilizan un sistema de representación basado en la técnica de coma flotante, que permite operar con números reales de diversas magnitudes, mediante un número decimal llamado mantisa y un exponente que indica el orden de magnitud.

El tipo de dato flotante en lenguaje C sólo tiene dos tamaños: el float y el double, que son 4 bytes y 8 bytes respectivamente. Se los puede utilizar tanto para representar números decimales, como para representar números enteros con un orden de magnitud muy grande.

La forma de declarar una variable flotante es escribiendo en una línea uno de los tipos de datos flotantes y a continuación el nombre de la variable y tal vez algún valor que se les quiera dar.

Algunos ejemplos:

  float a;
  double a = 1e23;
  double a = 3.1416;
  float a = 4e-9;
  double a = -78;

Hay que tener en cuenta que aunque los valores flotantes son más convenientes para algunas aplicaciones, hay casos en los que se prefieren los enteros. Esto se debe a que los números flotantes no necesariamente tienen soporte de hardware, en particular en las plataformas integradas. Una alternativa que se utiliza en estas situaciones es interpretar los enteros como decimales de forma que 150 se interprete como 1.5 y 2345 como 23.45.

Para el caso de los flotantes de 4 bytes, se utiliza 1 bit para el signo, 7 bits para el exponente y 24 bits para el valor del número. El procedimiento para almacenar un número en una variable flotante es el siguiente:

  1. Se convierte a binario la parte entera.
  2. Se coloca el signo en el bit más significativo de la misma manera que en los enteros (1 para el - y 0 para el +).
  3. Se mueve la coma (en la representación binaria de la parte entera) hasta que esté a la derecha del primer uno y éste se descarta (el uno más significativo). El valor del exponente será el número de posiciones que se movió la coma. El exponente usa la representación de un entero con complemento a dos.
  4. Se convierte en binario la parte decimal del número. Esto usando el peso de los bits. el bit decimal más significativo vale 1/2, el siguiente vale 1/4, el otro 1/8, el otro 1/16 y así hasta completar lo que falta para los 23bits del valor.
  5. Se concatena todo y ese es el valor flotante representado en memoria.

Caracteres

Los caracteres se representan utilizando el tipo char, que tiene sólo 1 byte de tamaño. Este tipo se utiliza para representar los 256 caracteres de la tabla de caracteres del sistema. El tipo char es también un tipo entero, ya que puede tomar valores de 0 a 255. Por lo tanto también puede ser signed o unsigned.

En cuanto a la forma de declarar variables de tipo char es la misma forma que con los otros tipos.

  char a;
  char a = 's';
  unsigned char a = 48;

Como puedes ver, se le puede asignar un número a una variable char, ya que se trata de un tipo entero. En muchas situaciones se utiliza el tipo char para almacenar números pequeños, ya que ocupa en memoria sólamente un byte.

Es importante notar que con la llegada de la codificación UTF-8, los caracteres de los diversos idiomas pueden ocupar 1, 2, 3 o 4 bytes, de modo que el tipo char ya no alcanza para la representación de todos los caracteres posibles. Por ello, el estándar C99 introduce el tipo wchar que puede ocupar más de 1 byte, según sea necesario para la codificación utilizada por el sistema.


Interacción con el usuario

En este capítulo veremos un poco más sobre como interactuar con el usuario de nuestros programas desde la consola, utilizando printf() como vimos en el primer ejemplo "Hola mundo", así como scanf() para la lectura del teclado.

Imprimir por pantalla

Como hemos visto hasta ahora en los ejemplos, hay una función que utilizamos para sacar por pantalla textos arbitrarios o el resultado de alguna operación: la función printf().

Si miramos (en la documentación) su definición, no nos aclarará demasiado:

int printf (const char *TEMPLATE, ...)

...claro que por algo tiene una sección completa de la documentación para ella sola.

Veámosla poco a poco. Se trata de una función de la biblioteca estándar, lo que quiere decir que para utilizarla tenemos que incluir previamente su definición. La encontraremos en <stdio.h>.

Lo primero que vemos en la definición es que es una función de tipo int, lo que quiere decir que devuelve un entero. Ese entero es el número de caracteres impresos en la pantalla, o un número negativo en caso de que se produzca algún error.

Lo siguiente a notar es su primer argumento: const char *TEMPLATE. Se trata de una cadena de caracteres (char *) que no será modificada por la función (const), con lo que puede ser una constante de cadena o una variable que contenga una cadena, pero siempre debe acabar con el carácter nulo \0.

Y luego vienen esos extraños puntos suspensivos. Esa elipsis nos indica que como argumentos adicionales de printf() podemos poner una serie ilimitada de otros argumentos, que se supone que la función sabrá qué hacer con ellos. Y eso es justamente lo que hace tan fabulosa y útil a printf().

Como hemos visto, el uso más simple de printf() es imprimir una cadena de texto simple y corriente. Como ya vimos:

printf("Hola Mundo\n"); /*imprime la cadena*/

Y también hemos visto printf() también puede, con un argumento extra y una sintaxis especial, imprimir un número entero que hayamos almacenado en una variable:

char resultado;

resultado=5+2;
printf("Resultado de la suma: %i\n",resultado);

Aquí el punto de inserción es la secuencia %i. printf() siempre trata las secuencias que comiencen por % como secuencias de control que le dicen que debe imprimir algo que le proporcionamos en los otros argumentos. Así, podemos imprimir varios enteros distintos en los sitios que queramos de la cadena, insertando varias de estas secuencias %i:

int numero;

numero=3;
printf("El doble de %i es %i y su cuadrado es %i\n",numero,numero*2,numero*numero);

Lectura de datos del teclado

La entrada de datos se puede hacer de muchas maneras y entre ellas están desde el uso de dispositivos especiales hasta nuestro simple teclado. La entrada de datos se refiere a cualquier forma de influencia del usuario sobre los datos que posee el sistema.

Con el fin de mostrar una forma de entrada simple para el aprendizaje vamos a hablar de la función scanf() que se encuentra definida en <stdio.h> y que se usa para capturar diferentes tipos de datos.

La función scanf()

scanf() es una de las funciones más usadas por los principiantes para hacer entrada de datos en el lenguaje C. Tiene una sintaxis muy parecida a printf: recibe una cadena con el formato de los datos y luego se ponen las variables en orden que correspondan a ese tipo de datos. Es decir, así como en printf se pueden mostrar por pantalla los datos de varias variables en una misma sentencia, en scanf se pueden capturar varios datos en una sola sentencia.

  #include <stdio.h>
  int main() {
      int a;
      printf ("diga un valor para a:");
      scanf("%i",&a);
      printf ("el valor es: %i\n",a);
      return 0;
  }

Por ahora no nos interesan las demás sentencias, sólo la que contiene scanf. En el código se ve lo siguiente:

      scanf("%i",&a);

Se observa que la funcion printf dejó en pantalla una petición para que el usuario introdujera un valor. Entonces, scanf recibe como argumento una cadena del formato en que se van a capturar los datos y la lista de variables que van a recibir valores y que deben coincidir con los del formato.

En este caso la cadena de formato, "%i", especifica que el usuario ingresará un número entero. Luego se designa a la variable a para contener a ese número. El símbolo (&) que precede a a es para especificar que lo que se está enviando como argumento no es el valor que posee la variable a sino la dirección de memoria en que se encuentra. En este momento eso no tiene mucha relevancia, sólo hay que recordar que se debe usar el símbolo & dentro del scanf. En el momento en que hablemos de punteros veremos más detalles de esto.

Otro ejemplo del uso de scanf:

  #include <stdio.h>
  int main() {
      int a,b;
      printf ("introduzca dos valores con el formato \"a,b\" :");
      scanf("%i,%i",&a,&b);
      printf ("el primer valor : %i\n",a);
      printf ("el segundo valor : %i\n",b);
      return 0;
  }

Aquí hemos introducido una nueva variable en el código. La cadena de formato, "%i,%i" especifica que el usuario ingresará un número, seguido de una coma, y luego otro número. El primer %i será capturado por la variable a y el segundo por b.


Expresiones

Vamos a tratar ahora de que el ordenador haga un poco de matemáticas para nosotros. Por ejemplo, que realice unas pocas sumas, restas multiplicaciones y divisiones.

#include <stdio.h>

int main(void)
{
    int resultado;

    resultado=5+2;
    printf("Resultado de la suma: %i\n",resultado);
    resultado=5-2;
    printf("Resultado de la resta: %i\n",resultado);
    resultado=5*2;
    printf("Resultado de la multiplicación: %i\n",resultado);
    resultado=5/2;
    printf("Resultado de la división: %i\n",resultado);
    return(0);
}

Después de grabarlo (por ejemplo, con el nombre ejemplo.c), lo compilamos y ejecutamos, con (respectivamente):

$ gcc ejemplo.c
$ ./a.out
Resultado de la suma: 7
Resultado de la resta: 3
Resultado de la multiplicación: 10
Resultado de la división: 2
$

Fijémonos en la línea del principio de la función main:

 int resultado;

Esta línea lo que hace es reservar un trozo de memoria, del tamaño de un int (normalmente 4 bytes), y asignarle el nombre resultado, para poder después referirnos a él. A partir de este momento, podemos considerar que en nuestro programa existe una variable, que no tiene valor definido, pero a la que le podremos dar valor posteriormente.

Las líneas con printf() ya las conocemos, pero hay algo en ellas que no habíamos visto antes. Esos %i y la parte de resultado son nuevas para nosotros.

La función printf() no sólo sabe imprimir cadenas simples, como "Hola Mundo\n", sino también imprimir variables. Para ello, en el lugar de la cadena donde queremos que aparezca el valor de la variable, introducimos lo que se llama una cadena de conversión de printf(). Estas cadenas siempre empiezan por %, siendo %i la cadena para imprimir un entero, como es en nuestro caso int resultado. Finalmente, printf() debe saber qué valor escribir, por eso le damos otro argumento (u otros), usando , como separador, que contienen las variables cuyos valores queremos mostrar.

En el resto del programa hemos visto cómo decirle al ordenador que ejecute una suma, una resta, una multiplicación y una división entera, con los operadores +, -, * y /. Es de notar que el resultado de una operación como estas entre números enteros será siempre otro entero, como se puede observar en la división, en la que no obtenemos un bonito decimal, sino un resultado entero. Además, hemos visto que el resultado de esas operaciones, que llamamos expresiones, puede ser asignado a una variable:

    resultado = 7;

Esa asignación se hace mediante el operador de asignación: =. Con él, ya conocemos cinco operadores.

Pero, como = también es un operador, ¿cómo sabe el ordenador qué operador debe ejecutar primero? Y si es un operador, ¿por qué no da un resultado? ¿No crea una expresión?

Operadores Precedencia
* / Izq. a Der.
+ - Izq. a Der.
= Der. a Izq.

Empezando por las últimas preguntas, el operador de asignación sí crea una expresión, como los operadores de suma, resta, multiplicación y división, y esa expresión tiene un resultado, que es el valor que obtiene el lado izquierdo al realizar la operación. En cuanto a saber qué se debe ejecutar primero, el ordenador tiene una lista de precedencia, según la cual siempre ejecuta primero las multiplicaciones y divisiones, de izquierda a derecha, a continuación las sumas y restas, de izquierda a derecha, y a continuación las asignaciones, de derecha a izquierda. Para más detalles acerca de la precedencia de los operadores ver el anexo de los operadores.

En cuanto a los caracteres de punto y coma, notamos aquí que una expresión también puede ser una sentencia por sí misma, sin necesidad de que haya ninguna función. De hecho, una sentencia puede no tener siquiera una expresión. La línea: ; es una sentencia perfectamente válida, la sentencia vacía, que sera útil en puntos donde el lenguaje requiera una sentencia pero no sea necesaria para nuestro programa.


Instrucciones de control

Como ya se ha mencionado, C es un ejemplo de programación estructurada. En este tipo de programación, es necesario contar con ciertas estructuras que permitan controlar el flujo del programa, es decir, tomar decisiones y repetir acciones.

La estructura condicional if ... else

En la gran mayoría de los programas será necesario tomar decisiones sobre qué acciones realizar. Esas decisiones pueden depender de los datos que introduzca el usuario, de si se ha producido algún error o de cualquier otra cosa.

La estructura condicional if ... else es la que nos permite tomar ese tipo de decisiones. Traducida literalmente del inglés, se la podría llamar la estructura "si...si no", es decir, "si se cumple la condición, haz esto, y sino, haz esto otro".

Un ejemplo sencillo sería el siguiente (no se trata de un programa completo, sino tan sólo una porción de código):

	if (edad < 18) 
		printf("No puedes acceder.\n");
	else
		printf("Bienvenido.\n");

Este código de ejemplo dice que si el valor de la variable edad es menor que 18 se imprimirá "No puedes acceder.\n", mientras que en caso contrario se imprimirá "Bienvenido.\n".

Como se ve en el ejemplo, la estructura de un condicional es bastante simple:

	if (condición) {
		sentencias_si_verdadero;
	} else {
		sentencias_si_falso;
	}

La condición, encerrada entre paréntesis, es una expresión que puede dar como resultado 0 (interpretado como falso) o cualquier valor distinto de 0 (interpretado como verdadero). Cuando la condición sea verdadera, se ejecutarán las sentencias dentro del primer bloque de código, cuando la condición sea falsa, se ejecutarán las sentencias del segundo bloque de código. Las expresiones y valores de tipo verdadero/falso son también llamados valores lógicos o booleanos.

La indentación o sangría (los espacios al comienzo de las líneas) no es necesaria, pero ayuda a la claridad del código. La utilización de las llaves {...} es obligatoria cuando se quiere utilizar más de una instrucción por bloque, y optativa cuando sólo se quiere escribir una instrucción. Por claridad, sin embargo, es recomendable utilizarlas aún cuando sólo vaya a haber una instrucción.

El bloque del else es opcional. Si no se lo encuentra, sólo se realizará la acción correspondiente al bloque if.

A continuación, un ejemplo con una función, que devuelve el mayor de dos números:

int mayor(int a, int b)
{
	if (b > a) {
		return b;
	}// No posee especificación de la parte "else", ya que no es necesaria.  
       	return a; // Finaliza la función retornando el valor de "a".
}

Operadores de comparación

El símbolo > visto en el último ejemplo es un operador, que en este caso compara dos números enteros y devuelve verdadero si el primero es mayor, falso en caso contrario.

A continuación un listado de los posibles operadores de comparación en C y su significado.

Operadores de Comparación
Operador Significado
< estrictamente menor que
> estrictamente mayor que
<= menor o igual que
>= mayor o igual que
== igual a
!= distinto de


Teniendo en cuenta que en C se toma como falso el valor 0, y como verdadero cualquier otro valor, una práctica común es expresar condiciones sin utilizar ningún operador:

float division(int dividendo, int divisor)
{
	if (divisor) {
		return dividendo / divisor;
	} else {
		printf ("No se puede dividir por cero\n");
		return 0;
	}
}

En este caso, la expresión (divisor) es equivalente a (divisor != 0).

Operadores lógicos

Los operadores && ("y"), || ("o") y ! ("no") son operadores lógicos. Permiten operar con expresiones lógicas para generar expresiones más complejas.

Por ejemplo: determinar si un año es bisiesto o no. Los años son bisiestos si son divisibles por 4, pero no si son divisibles por 100, a menos que también sean divisibles por 400.

	if ( (!(a % 4) && (a % 100)) || !(a % 400) )  {
		printf("es un año bisiesto.\n");
	} else {
		printf("no es un año bisiesto.\n");
	}

En realidad, teniendo en cuenta la prioridad de los operadores utilizados, podemos simplificar la expresión anterior del siguiente modo:

	if ( !(a % 4) && (a % 100) || !(a % 400) )  {
		printf("es un año bisiesto.\n");
	} else {
		printf("no es un año bisiesto.\n");
	}

Además, como a cada rama del if le sigue una única instrucción, podemos expresar la expresión anterior del siguiente modo:

	if ( !(a % 4) && (a % 100) || !(a % 400) )  
		printf("es un año bisiesto.\n");
	else 
		printf("no es un año bisiesto.\n");


En este caso, se utiliza el operador módulo (%), que obtiene el resto de la división entera de un número por otro. Cuando un número es divisible por otro, el resto de su división entera será cero. Siendo que cero es equivalente a falso, y cualquier valor distinto de cero es equivalente a verdadero, podemos usar el operador % para verificar si el número es múltiplo de 4, de 100 o de 400.


Evaluación de cortocircuito

La evaluación en corto circuito es una característica del lenguaje C que se utiliza para optimizar la ejecución de programas. Consiste en que el programa puede verificar si una expresión es verdadera o falsa antes de haber evaluado toda condición.

Por ejemplo, si se tiene una condición como la siguiente:

	if ((a > 2) || (b < 4)) {
		...
	}

Al ejecutarse el programa, se evaluará primero si a > 2. En el caso en que sea verdadero, no continuará con la siguiente condición, ya que el resultado será de cualquier modo verdadero.

De la misma forma, si la condición fuera:

	if ((a > 2) && (b < 4)) {
		...
	}

En este caso, si no se cumple que a > 2, no se evaluará la siguiente condición, ya que el resultado será falso de todos modos.

Esta característica no tiene demasiada importancia al comenzar a programar, pero facilitará ciertas operaciones y optimizaciones en programas avanzados.


La estructura condicional abierta y cerrada switch ... case

La estructura condicional switch ... case se utiliza cuando queremos evitarnos las llamadas escaleras de decisiones. La estructura if nos puede proporcionar, únicamente, dos resultados, uno para verdadero y otro para falso. Una estructura switch ... case, por su parte, nos permite elegir entre muchas opciones. Ejemplo:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	int dia;

	printf("que número de día de la semana es?");
	scanf("%i",&dia);

	switch(dia) {
		case 1 :
			printf("Lun, Lunes");
			break;
		case 2 :
			printf("Mar, Martes");
			break;
		case 3 :
			printf("Mier, Miercoles");
			break;
		case 4 :
			printf("Jue, Jueves");
			break;
		case 5 :
			printf("Vie, Viernes");
			break;
		case 6 :
			printf("Sab, Sabado");
			break;
		case 7 :
			printf("Dom, Domingo");
			break;
		default :
			printf("No existe");
}
return 0;
}

La estructura anterior, de realizarse con sentencias if, necesitaría cuatro de ellas, resultando un enorme bloque muy difícil de leer. En la mayoría de los casos, además, la sentencia switch proporciona una ganancia en velocidad del código, pues permite al compilador trabajar en base a que se trata de una decisión múltiple para una única variable, cosa que con sentencias if el compilador no tiene por qué detectar.

Como vemos, para cada valor de la variable se ejecuta un bloque de sentencias distinto, en el que no necesitamos llaves. Hay un caso especial, default, que se ejecuta si ningún otro corresponde, y que no es necesario poner. Es, en todo, equivalente al bloque else de una sentencia if.

Las sentencias break son muy importantes, ya que el comportamiento normal de un bloque switch es ejecutarlo todo desde la etiqueta case que corresponda hasta el final. Por ello, si no queremos que se nos ejecute más de un bloque, pondremos sentencias break al final de cada bloque excepto el último.

Es decir, las etiquetas case son puntos de entrada de la ejecución, y no implican que al acabarse el bloque case la ejecución salte al final del bloque switch. Las etiquetas case siguientes a la que hemos utilizado para entrar son, sencillamente, ignoradas.

A la ausencia de sentencias break se le llama, en ocasiones, "dejar caer la cascada switch".

El bucle while

El bucle while sirve para ejecutar código reiteradas veces.

while (/*condicion*/) {
	/* Código */
}

La condición debe de ser una expresión lógica, similar a la de la sentencia if. Primero se evalúa la condición. Si el resultado es verdadero, se ejecuta el bloque de código. Luego se vuelve a evaluar la condición, y en caso de dar verdadero se vuelve a ejecutar el bloque. El bucle se corta cuando la condición da falso.

Ejemplo: imprimir los números de 0 a 99:

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

Inicialmente se declara que la variable i tiene un valor de 0. Al iniciar el bucle, se cumple la condición i < 100, por lo que se procede a la instrucción de imprimir dicho número (cero, en el caso inicial). Posteriormente i cambiará su valor de uno en uno por la instrucción i = i + 1 y seguidamente dicho valor nuevo, será evaluado en la condicion while hasta que i llegue al valor 100, donde debido a la condicional, éste será un valor falso, dando fin al código.

El bucle for

El bucle for es un bucle muy flexible y a la vez muy potente ya que tiene varias formas interesantes de implementarlo, su forma más tradicional es la siguiente:

for (/* inicialización */; /* condición */; /* incremento */) {
	/* código a ejecutar */
}

Inicialización: en esta parte se inicia la variable que controla el bucle y es la primera sentencia que ejecuta el bucle. Sólo se ejecuta una vez ya que solo se necesita al principio del bucle.

Expresión condicional: al igual que en el bucle while, esta expresión determina si el bucle continuará ejecutándose o no.

Incremento: es una sentencia que ejecuta al final de cada iteración del bucle. Por lo general, se utiliza para incrementar la variable con que se inicio el ciclo. Luego de ejecutar el incremento, el bucle revisa nuevamente la condición, si es verdadera tiene lugar una ejecución más del cuerpo del ciclo, si es falsa se termina el ciclo y así.

Aquí se muestra el mismo ejemplo visto para el bucle while, pero implementado con un bucle for:

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

Nota: En C, la sentencia i = i + 1 puede escribirse en forma más reducida como i++. Esta forma se utiliza más comúnmente en el bucle for:

int i;
for (i=0; i < 100; i++) {
	printf("%d\n", i);
}

El bucle do...while

El bucle do...while es un bucle que, por lo menos, se ejecuta una vez. Do significa literalmente "hacer", y while significa "mientras"

Su forma es esta:

do {
	/* CODIGO */
} while (/* Condición de ejecución del bucle */)

Os muestro un ejemplo sencillo de uso:

int aleatorio;
do {
	aleatorio = rand();
} while (aleatorio != 25);

La verdad es que este ejemplo puede resultar un poco absurdo, pero es bastante intuitivo. El código del bucle asigna un valor aleatorio a la variable definida anteriormente, y mientras esa variable no tenga el valor 25, el bucle sigue ejecutándose.

La sentencia goto

La sentencia goto sirve para indicar al programa que continue ejecutándose desde la línea de código indicada. Su sintaxis es más o menos así:

/* Código */
ETIQUETA:
/* Código */
goto ETIQUETA;
/* Código */

Así, cuando se ejecute la sentencia goto, el programa "saltará" y continuará su ejecución a partir de la etiqueta marcada.

Como se puede observar se puede usar para crear un bucle, o para ir a una parte del código u otra si se combina con una sentencia if...else. Pero por lo general puede obtenerse el mismo efecto utilizando los bucles anteriormente vistos.

Por eso, la sentencia goto es poco aceptada por la comunidad de programadores, pues puede provocar que se hagan programas un poco "sucios" y confusos. Sólo en ocasiones muy excepcionales será recomendado el uso del goto al crear iteraciones muy complejas. Sin embargo, con el pasar de los años este comando ya ha quedado prácticamente descartado del lenguaje de los programadores.


Uso de Funciones

Funciones

Como vimos anteriormente C tiene como bloque básico la función main() , también hemos visto la sentencia printf() que es otra función, y de igual forma hay muchas más funciones predefinidas, pero nosotros mismos también podemos definir nuestras propias funciones. De hecho, es fundamental hacerlo.

Podemos definir una función cualquiera de la misma manera en que definimos la función main(). Basta con poner su tipo, su nombre, sus argumentos entre paréntesis y luego, entre llaves, su código:

/* Inclusión de archivos */
#include <stdio.h>

void holamundo(void) /* Función donde se ejecuta la lógica del programa */
{
 	printf("Hola Mundo\n"); /* imprime la cadena */
	return; /* sale de la función */
}
 
int main(void) /* Función principal del programa */
{
	holamundo(); /* llamada a la función holamundo */
	return 0; /* sale del programa con código 0 (correcto) */
}

Este código es en todo equivalente al "Hola Mundo" original, sólo que nos muestra cómo escribir y cómo utilizar una función. Y además nos muestra un principio de buena programación: meter las sentencias que "hacen el trabajo" en otras funciones específicas para sacarlas de main(), dejando en ésta tan sólo un guión general de lo que hace el programa, no las órdenes específicas. De esta manera se facilita la comprensión del programa, y por tanto el futuro trabajo de modificarlo.

La sentencia return

La sentencia return puede utilizarse dentro de una función para terminar su ejecución.

En el ejemplo anterior, la función holamundo fue declarada con valor de retorno de tipo void (es decir, valor de retorno nulo). En ese caso, la sentencia return no lleva ningún parámetro adicional, ya que la función no debe devolver ningún valor a la función que la llama.

En cambio, la función main tiene un valor de retorno de tipo int, por lo que return debe ir seguido de un valor entero (0 en el ejemplo). El valor 0 se utiliza para indicar que el programa ha llegado a un punto en el que todo se ha desarrollado correctamente y se utiliza cualquier otro valor para indicar que ha habido algún tipo de error.

La instrucción return no es una función, se trata de una sentencia que lo que hace es retornar como valor de la función el valor que se le proporciona como argumento.

Argumentos

Las funciones también pueden recibir argumentos o parámetros, para modificar su comportamiento. Por ejemplo, la definición de una función para sumar dos números sería de la siguiente manera:

Declaración y definición

En el ejemplo anterior podemos notar que la función sumar figura en el código antes que main. ¿Qué pasaría si las escribiéramos en distinto orden?

#include <stdio.h>

int main(void)
{
	int suma = sumar(5, 3); /* ERROR, sumar no ha sido declarada aún */
	printf("La suma es: %d ", suma);
	return 0;
}

int sumar(int numero1, int numero2)
{
	return numero1 + numero2;
}

En este caso el programa es erróneo y no compila, ya que en la línea donde se llama a la función sumar, el compilador aún no conoce ninguna función con ese nombre, y cuáles son sus argumentos y valor de retorno.

Una posible solución es declarar el prototipo de la función al principio, para informar al compilador que existe, y luego definir el cuerpo de la misma en cualquier lugar del programa:

#include <stdio.h>

/* Declaración */
int sumar(int numero1, int numero2);

int main(void)
{
	int suma = sumar(5, 3);
	printf("La suma es: %d ", suma);
	return 0;
}

/* Definición */
int sumar(int numero1, int numero2)
{
	return numero1 + numero2;
}

Paso de Parámetros

Las funciones pueden recibir datos como lo hemos observado, pero existen dos formas de enviar los datos hacia una función por valor y por referencia, las cuales modifican en diferente forma el comportamiento del programa.


Por Valor

El paso por valor envía una copia de los parámetros a la función por lo tanto los cambios que se hagan en ella no son tomados en cuenta dentro de la función main(). Ejemplo:

/*
 * por_valor.c
 * 
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 * 
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

void sumar_valor(int numero); /* prototipo de la función */

int main(void)
{
	int numero = 57; /* definimos numero con valor de 57*/

	sumar_valor(numero); /* enviamos numero a la función */

	printf("Valor de numero dentro de main() es: %d\n", numero);
	/* podemos notar que el valor de numero se modifica
	 * sólo dentro de la función sumar_valor pero en la principal
	 * número sigue valiendo 57 
	 */

	return 0;
}

void sumar_valor(int numero)
{
	numero++; /* le sumamos 1 al numero */

	/* el valor de número recibido se aumenta en 1
	 * y se modifica dentro de la función sumar_valor()
	 */
	printf("Valor de numero dentro sumar_valor() es: %d\n", numero);

	return;
}

Por Referencia

El paso por referencia se hace utilizando apuntadores. Se envía la dirección de memoria de la variable, por lo tanto los cambios que haga la función si afectan el valor de la variable. Ejemplo:

/*
 * por_referencia.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

void sumar_referencia(int *numero); /* prototipo de la función */


int main(void)
{
	int numero = 57; /* definimos numero con valor de 57*/

	sumar_referencia(&numero); /* enviamos numero a la función */

	printf("\nValor de numero dentro de main() es: %d ", numero);
	/* podemos notar que el valor de numero se modifica
	 * y que ahora dentro de main() también se ha modificado
	 * aunque la función no haya retornado ningún valor.
	 */

	return 0;
}

void sumar_referencia(int *numero)
{
	*numero += 1; /* le sumamos 1 al numero */

	/* el valor de numero recibido se aumenta en 1
	 * y se modifica dentro de la función
	 */
	printf("\nValor de numero dentro sumar_referencia() es: %d", *numero);

	return;
}

Variables Locales y Globales

Además de pasar valores a una función, también se pueden declarar tipos de datos dentro de las funciones, estos tipos de datos declarados dentro de una función solo son accesibles dentro de esta misma función y se les conocen como variables locales, así pues podemos definir los mismos nombres de variables en diferentes funciones, ya que estas variables solo son accesibles dentro de esas funciones. Ejemplo:

/*
 * locales.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

void funcion1()
{
	int dato = 53; /* definimos dato en 53*/
	char num1 = 'a'; /* num1 vale a */

	/* imprimimos */
	printf("Funcion1, dato=%d, num1=%c\n", dato, num1);

	return;
}

void funcion2()
{
	int dato = 25; /* definimos dato en 25*/
	char num2 = 'z'; /* num2 vale z*/

	/* imprimimos */
	printf("Funcion2, dato=%d, num2=%c\n", dato, num2);

	return;
}

int main(void)
{
	funcion1(); /* llamamos a funcion1() */

	funcion2(); /* llamamos a funcion2() */

	return 0;
}

En este caso la variable dato, esta definida dentro de cada una de las funciones y son totalmente distinta una de otra y no se puede utilizar fuera de esta, así pues num2 no puede ser utilizada por la funcion1() y num1 tampoco puede ser utilizada por funcion2().

Existen pues variables que se definen fuera de la función principal main() y fuera de cualquier otra función creada por nosotros, estas variables se les conoce con el nombre de Variables Globales ya que se pueden utilizar dentro de main() y dentro de cualquier función creada por nosotros. Ejemplo:

/*
 * global.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

int variable_global = 99; /* inicializamos la variable global */

void funcion();

int main(void)
{
	/* imprimimos el valor*/
	printf("main(), acceso a variable_global %d\n", variable_global);

	/* llamamos a la función */
	funcion();

	return 0;
}

void funcion()
{
	/* imprimimos el valor*/
	printf("funcion(), acceso a variable_global %d\n", variable_global);

	return;
}

Funciones Recursivas

La recursividad (recursión) es la propiedad por la cual una función se llama a sí misma directa o indirectamente. La recursión indirecta implica utilizar más de una función.

Se puede considerar la recursividad como una alternativa a la iteración. La recursión permite especificar soluciones naturales, sencillas, que serían, en caso contrario, difíciles de resolver. Toda función recursiva debe contemplar un caso base o condición de salida, para terminar, o la recursividad no podrá terminar nunca.

Una función recursiva podría definirse así:

funcion_recursiva( /* parámetros recibidos por la función */ )
{
	/* Código */
	funcion_recursiva( ); /* llamada a la función misma */
	/* Código */
}

Uno de los ejemplos más representativos en la recursividad es el factorial de un numero ( n! ):

la definición de recursividad del factorial es:

En esta definición, n = 0, es nuestro caso base, que le da fin a la recursividad.

Entonces nuestro programa que calcula el factorial es:

/*
 *factorial.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

long factorial(int n)
{
	if (n == 0) /* caso base */
		return 1; /* como 0! = 1, se retorna 1*/
	else
		return n * factorial (n - 1); /* llamada a esta misma función */
}

int main(void)
{
	/* en este caso se llama a la función y se imprime directamente*/
	printf("%ld ", factorial(5));

	return 0;
}

También existen otros tipos de funciones recursivas como lo es el producto de dos números. El producto de a b, donde a y b son números enteros positivos seria:

Solución iterativa:

Solución recursiva:

Así pues es:

Podemos ver que la multiplicación de dos números a, b se puede transformar en otro problema más pequeño multiplicar a por (b-1), el caso base se produce cuando b = 0 y el producto es 0. Ejemplo:

/*
 * producto.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

int producto(int a, int b)
{
	if (b == 0) /* caso base */
		return 0; /* como b = 0, se retorna 0*/
	else
		return a + producto (a, b - 1); /* llamada a esta misma función */
}

int main(void)
{
	/* en este caso se llama a la función y se imprime directamente*/
	printf("%i ", producto( 7, 3));

	return 0;
}

Recursividad indirecta o recursión mutua

Esta se produce cuando una función llama a otra, que esta a su vez terminará llamando de nuevo a la primera función. El siguiente programa visualiza el alfabeto utilizando recursión indirecta o mutua:

/*
 * elalfabeto.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

void funcionA(char c); /* se declara el prototipo de la función para que el llamado */
void funcionB(char c); /* a la misma en la función no sea implícita */

int main(void)
{

	funcionA('z'); /* llamado a funcionA */

	return 0;
}

void funcionA(char c)
{
	if (c > 'a') /* caso base mientras c no sea menor que A */
		funcionB(c); /* llamado a la funcionB */

	printf("%c ", c); /* imprimimos el valor de c */
*la variable es un parametro no utilizado para este proceso
}

void funcionB(char c)
{
	funcionA(--c); /* llamado a la funcionA decrementando el valor de 'z' */
}

Recursión versus Iteración

Tanto la iteración como la recursión se basan en estructura de control: la iteración utiliza una estructura repetitiva y la recursión una estructura de selección. La iteración utiliza explícitamente una estructura repetitiva mientras que la recursión consigue la repetición mediante llamadas repetitivas a funciones.

La iteración termina si la condición del bucle no se cumple, mientras que la recursión termina cuando se reconoce un caso base.

La recursión puede presentar desventajas ante la iteración ya que se invoca repetidas veces al mecanismo de llamada de funciones y se necesita un tiempo mayor para realizar cada llamada.

La razón por la cual se puede elegir u optar por usar recursividad es que existen muchos problemas complejos que poseen naturaleza recursiva y, en consecuencia, son mas fáciles de implementar.

Ejemplo Iterativo

/*
 * iterativo.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

long factorial(int numero);

int main(int argc, char** argv)
{
	int contador = 0;

	/* calcula el factorial de 0 a 10 */
	for ( contador = 0; contador <= 10; contador++ )
		printf("%d! = %ld\n", contador, factorial( contador ));

	return 0;
}

/* funcion factorial iterativa */
long factorial( int numero )
{
	long resultado = 1;
	int i = 0;

	/* declaracion de la función factorial iterativa */
	for ( i = numero; i >= 1; i-- )
		resultado *= i;

	return resultado;
}

Ejemplo Recursivo

/*
 * recursivo.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */

#include <stdio.h>

long factorial(int numero);

int main(int argc, char** argv)
{
	int contador = 0;

	/* calcula el factorial de 0 a 10 */
	for ( contador = 0; contador <= 10; contador++ )
		printf("%d! = %ld\n", contador, factorial( contador ));

	return 0;
}

/* función factorial recursiva */
long factorial( int numero )
{                                               
	if ( numero <= 0 ) /* caso base */
		return 1; /* casos bases: 0! = 1 y 1! = 1 */
	else /* llamada recursiva */
		return numero * factorial( numero - 1 ); /* llamada a la función factorial */
}


Vectores

Los vectores son una forma de almacenar datos que permiten contener una serie de valores del mismo tipo, cada uno de los valores contenidos tiene una posición asociada que se usará para accederlos. Está posición o índice será siempre un número entero positivo.

En C la cantidad de elementos que podrá contener un vector es fijo, y en principio se define cuando se declara el vector. Los vectores se pueden declarar de la siguiente forma:

	tipo_elemento nombre[largo];

Esto declara la variable nombre como un vector de tipo_elementos que podrá contener largo cantidad de elementos, y cada uno de estos elemento podrá contener un valor de tipo tipo_elemento.

Por ejemplo:

	double valores[128];

En este ejemplo declaramos un vector de 128 elementos del tipo double, los índices de los elementos irían entre 0 (para el primer elemento y 127 para el último).

De la misma forma que con las otras declaraciones de variables que hemos visto se le puede asignar un valor iniciar a los elementos.

O también se pueden declarar:

	tipo_elemento nombre[largo]={valor_0, valor_1, valor_2};

En caso estamos asignadole valores a los primeros 3 elementos del vector nombre. Notar que largo debe ser mayor o igual a la cantidad de valores que le estamos asignando al vector, en el caso de ser la misma cantidad no aporta información, por lo que el lenguaje nos permite escribir:

	tipo_elemento nombre[]={valor_0, valor_1, valor_2};

Que declarará nombre como el vector de largo 3.

Para acceder a un elemento accederemos a través de su posición. Es decir:

	tipo_elemento elemento;
	...
	elemento = nombre[2];

Asumiendo que tenemos el vector anterior definido estaríamos guardando valor_2 en elemento.

Veamos algunos ejemplos:

/*
 * Ejemplo : El producto escalar de dos vectores
 */
#include <stdio.h>

double producto_escalar(double v1[], double v2[], int d);

int main()
{	
	const int largo = 3;
	double vector_1[] = {5,1,0};
	double vector_2[] = {-1,5,3};

	double resultado = producto_escalar(vector_1, vector_2, largo);

	// imprime el resultado
	printf("(%f, %f, %f) . (%f, %f, %f) = %f\n",
		vector_1[0], vector_1[1], vector_1[2],
		vector_2[0], vector_2[1], vector_2[2],
		resultado);
	return 0;
}

/* producto escalar entre dos vectores */
double producto_escalar(double v1[], double v2[], int d)
{
	double resultado = 0;
	int i;
	for (i=0; i < d; i++) {
		resultado += v1[i] * v2[i];
	}
	return resultado;
}

En el ejemplo anterior usamos los vectores de C para representar vectores matemáticos y calcular el producto escalar entre ellos. Una peculiaridad que se puede notar es que al recibir un arreglo en una función no se especifica el largo, volveremos a esto en un capítulo posterior.

Otra función clásica es la búsqueda de un máximo o mínimo, que podemos escribirla de la siguiente manera:

int buscar_maximo(double valores[], int num_valores)
{
	int maximo_pos = 0;
	for (int i = 1; i < num_valores; i++) {
		if (valores[i] > valores[maximo_pos]) {
			maximo_pos = i;
		}
	}
	return maximo_pos;
}

Otro ejemplo sencillo, calcular el promedio de los valores.

double promedio(double valores[], int largo)
{
	double suma=0;
	for (int i=0;i<largo;i++) {
		suma+=valores[i];
	}
	return suma/largo;
}

Cuando una función recibe un vector por parámetro y cambia su contenido y el cambio es permanente (se ve aún fuera de la función). Esto puede parecer extraño después del énfasis que pusimos en resaltar que todos los parámetros de una función se reciben por valor, pero se aclarará en el siguiente capitulo.

Mientras tanto usemos esto para definir una función que le aplique otra función que recibe por parámetro a cada elemento del vector, guardando el resultado en el mismo vector y una llamada de ejemplo a esta.

void cuadrados(double vector[], int largo)
{
	for (int i=0;i<largo;i++) {
		vector[i]=cuadrado(vector[i]);
	}
}
...
double cuadrado(double valor) {
	return valor*valor;
}
...
	cuadrados(elementos,num_elem);
...

De la misma forma que venimos usando vectores de tipos básicos, podemos tener vectores de vectores, estos se declaran de la siguiente forma:

int matriz[3][7];
int tabla[3][4]={ { 1, 2, 3, 4},
		  { 5, 6, 7, 8}, /* los espacios y saltos de líneas no son tomados en cuenta */
		  { 9,10,11,12} };
double v[2][2][2];
...
printf("tabla[0][1]: %i\n", tabla[0][3]); // Imprime 4
printf("tabla[2][0]: %i\n", tabla[2][0]); // Imprime 9 
...

En este ejemplo tabla es un vector de longitud 3, cuyos elementos son vectores de longitud 4 de elementos de tipo int.

En resumen, suponiendo que v[n] es un vector de cualquier tipo de dato con n cantidad de posiciones, al vector v se le aplican las siguientes reglas:

  1. La primera posición siempre será v[0]
  2. La última posición es v[n-1]
  3. En versiones previas a C99 n es una constante definida antes de la declaración de v[n]


GNU Free Documentation License


Version 1.2, November 2002

Copyright (C) 2000,2001,2002  Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
D. Preserve all the copyright notices of the Document.
E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
H. Include an unaltered copy of this License.
I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements."

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.