Introducción

Logo C++

Si no he perdido la cuenta, esta es la cuarta revisión del curso desde que se empezó a escribir.

Ya en su versión anterior, el curso estaba bastante completo, al menos en lo que respecta a la teoría, quedando muy pocos detalles por incluir. Esta versión se centra, sobre todo, en añadir más ejemplos resueltos, problemas propuestos y ejercicios.

Espero que este curso anime a los nuevos y futuros programadores autodidactas a incorporarse a esta gran y potente herramienta que es el C++, ese era el objetivo original de la página "Con Clase" y todavía sigue siendolo.

No he pretendido ser original, (al menos no demasiado), para elaborar este curso se han consultado libros, tutoriales, revistas, listas de correo, news, páginas web... En fin, cualquier fuente de datos que ha caído en mis manos, con el fin de conseguir un buen nivel. Espero haber conseguido mi objetivo, y seguiré completando explicaciones sobre todo aquello que lo requiera. También deseo que haya resultado ser un texto ameno, me gustaría que nadie se aburra leyendo el curso.

Pretendo también (y me gustaría muchísimo), que el curso siga siendo interactivo. Con este fin, en esta versión del curso, se ha añadido la posibilidad de que los lectores añadan sus comentarios al final de cada capítulo. Estos aportes se usarán para completar el curso.

He intentado que los ejemplos que ilustran cada capítulo se puedan compilar con cualquier versión de compilador, sin embargo, he de decir que yo he usado el compilador MinGW, (Minimalist GNU for Windows), que es una versión para Windows del compilador GCC para Unix y Linux, y que está adaptado para crear programas en Windows. Es decir, los programas que se ajusten al estándar de C++ deberían funcionar con este compilador tanto en Windows como en Linux.

Por comodidad, recomiendo usar algún IDE (Entorno de Desarrollo Integrado), como Dev-C++ de Bloodshed o Code::Blocks para crear programas en modo consola.

De modo que aprovecho para aclarar que los programas de Windows tienen dos modos de cara al usuario:

  • El modo consola simula el funcionamiento de una ventana MS-DOS, trabaja en modo de texto, es decir, la ventana es una especie de tabla en la que cada casilla sólo puede contener un carácter. El modo consola de Windows no permite usar gráficos de alta resolución. Pero esto no es una gran pérdida, pues como veremos, ni C ni C++ incluyen manejo de gráficos de alta resolución. Esto se hace mediante bibliotecas externas no estándar.
  • El otro modo es el GUI, o Interfaz Gráfico de Usuario. Es el modo tradicional de los programas de Windows, con ventanas, menús, iconos, etc. La creación de este tipo de programas se explica en otro curso de este mismo sitio, y requiere el conocimiento de la biblioteca de funciones Win API32.

Para aquellos de vosotros que programéis en otros entornos como Linux, Unix o Mac, he de decir que no os servirá el entorno Dev-C++, ya que está diseñado especialmente para Windows. Pero esto no es un problema serio, todos los sistemas operativos disponen de compiladores de C++ que soportan la norma ANSI, sólo menciono Dev-C++ y Windows porque es el entorno en el que yo me muevo actualmente.

Además intentaré no salirme del ANSI, es decir del C++ estándar, así que no es probable que surjan problemas con los compiladores.

De nuevo aprovecho para hacer una aclaración. Resumidamente, el ANSI define un conjunto de reglas. Cualquier compilador de C o de C++ debe cumplir esas reglas, si no, no puede considerarse un compilador de C o C++. Estas reglas definen las características de un compilador en cuanto a palabras reservadas del lenguaje, comportamiento de los elementos que lo componen, funciones externas que se incluyen, etc. Un programa escrito en ANSI C o en ANSI C++, podrá compilarse con cualquier compilador que cumpla la norma ANSI. Se puede considerar como una homologación o etiqueta de calidad de un compilador.

Todos los compiladores incluyen, además del ANSI, ciertas características no ANSI, por ejemplo bibliotecas para gráficos. Pero mientras no usemos ninguna de esas características, sabremos que nuestros programas son transportables, es decir, que podrán ejecutarse en cualquier ordenador y con cualquier sistema operativo.

Este curso es sobre C++, con respecto a las diferencias entre C y C++, habría mucho que hablar, pero no es este el lugar adecuado. Si sientes curiosidad, consulta la sección de preguntas frecuentes. Pero para comprender muchas de estas diferencias necesitarás cierto nivel de conocimientos de C++.

Los programas de ejemplo que aparecen en el texto están escritos con la fuente courier y en color azul con el fin de mantener las tabulaciones y distinguirlos del resto del texto. Cuando sean largos se incluirá también un fichero con el programa, que se podrá descargar directamente.

Cuando se exponga la sintaxis de cada sentencia se adoptarán ciertas reglas, que por lo que sé son de uso general en todas las publicaciones y ficheros de ayuda. Los valores entre corchetes "[ ]" son opcionales, con una excepción: cuando aparezcan en negrita "[ ]", en ese caso indicarán que se deben escribir los corchetes. El separador "|" delimita las distintas opciones que pueden elegirse. Los valores entre "<>" se refieren a nombres. Los textos sin delimitadores son de aparición obligatoria.

Proceso para la obtención de un programa ejecutable

Probablemente este es un buen momento para explicar cómo se obtiene un fichero ejecutable a partir de un programa C++.

Para empezar necesitamos un poco de vocabulario técnico. Veremos algunos conceptos que se manejan frecuentemente en cualquier curso de programación y sobre todo en manuales de C y C++.

Fichero fuente y programa o código fuente

Los programas C y C++ se escriben con la ayuda de un editor de textos del mismo modo que cualquier texto corriente. Los ficheros que contiene programas en C o C++ en forma de texto se conocen como ficheros fuente, y el texto del programa que contiene se conoce como programa fuente. Nosotros siempre escribiremos programas fuente y los guardaremos en ficheros fuente.

Interpretes y compiladores

Antes, mencionar que tanto C como C++ son lenguajes compilados, y no interpretados. Esta diferencia es muy importante, ya que afecta mucho a muchos aspectos relacionados con la ejecución del programa.

En un lenguaje interpretado, el programa está escrito en forma de texto, es el propio programa fuente. Este programa fuente es procesado por un programa externo, el intérprete, que traduce el programa, instrucción a instrucción, al tiempo que lo ejecuta.

En los lenguajes interpretados no existen programas ejecutables directamente por el ordenador. El intérprete traduce, en tiempo real, cada línea del programa fuente, cada vez que se quiere ejecutar el programa.

El los lenguajes compilados el proceso de traducción sólo se hace una vez. El programa compilador toma como entrada el código fuente del programa, y da como salida un fichero que puede ser ejecutado por el ordenador directamente.

Una vez compilado, el programa ejecutable es autónomo, y ya no es necesario disponer del programa original ni del compilador para ejecutarlo.

Cada opción tiene sus ventajas e inconvenientes, y algunas características que son consideradas una ventaja, pueden ser un inconveniente en ciertas circunstancias, y viceversa.

  • Los lenguajes interpretados son fácilmente modificables, ya que necesitamos tener el el código fuente disponible en el ordenador. En los compilados, estos ficheros no son necesarios, una vez compilados.
  • Los lenguajes interpretados necesitan un programa externo, llamado intérprete o a veces máquina virtual, o framework. Este programa actua como intermediario entre el fuente y el sistema operativo. En los compilados ese papel lo desempeña el compilador, pero al contrario que con el intérprete, una vez ha hecho su trabajo, no es necesario que esté presente para ejecutar el programa.
  • Estas dos características, lógicamente, hacen que los programas compilados requieran menos espacio de memoria que los interpretados (si contamos el espacio usado por el intérprete), y en general, los compilados son más rápidos, ya que sólo se compilan una vez, y el tiempo dedicado a esa tarea no se suma al de ejecución.

Entre los lenguajes interpretados están: BASIC (Código de instrucciones de propósito general para principiantes), Java, PHP. Muchos lenguajes de script, etc.

Entre los lenguajes compilados están: C, C++, Pascal.

Ficheros objeto, código objeto y compiladores

Como hemos dicho antes, en los lenguajes compilados, los programas fuente no pueden ejecutarse. Son ficheros de texto, pensados para que los comprendan los seres humanos, pero incomprensibles para los ordenadores.

Para conseguir un programa ejecutable hay que seguir algunos pasos. El primero es compilar o traducir el programa fuente a su código objeto equivalente. Este es el trabajo que hacen los compiladores de C y C++. Consiste en obtener un fichero equivalente a nuestro programa fuente comprensible para el ordenador, este fichero se conoce como fichero objeto, y su contenido como código objeto.

Los compiladores son programas traductores, que leen un fichero de texto que contiene el programa fuente y generan un fichero que contiene el código objeto.

El código objeto no suele tener ningún significado para los seres humanos, al menos para la mayoría de los humanos que conozco, y menos directamente. Además es diferente para cada ordenador y para cada sistema operativo. Por lo tanto existen diferentes compiladores para diferentes sistemas operativos y para cada tipo de ordenador.

Librerías o bibliotecas

Junto con los compiladores de C y C++, se incluyen ciertos ficheros llamados bibliotecas. Las bibliotecas contienen el código objeto de muchos programas que permiten hacer cosas comunes, como leer el teclado, escribir en la pantalla, manejar números, realizar funciones matemáticas, etc. Las bibliotecas están clasificadas por el tipo de trabajos que hacen, hay bibliotecas de entrada y salida, matemáticas, de manejo de memoria, de manejo de textos, etc.

Nota: Existe una discusión permanente sobre el nombre genérico de estos ficheros. Una gran parte de personas consideran que el nombre adecuado es ficheros de biblioteca, y he de decir que esencialmente estoy de acuerdo con ellos. Sin embargo, la mayoría llamamos a estos ficheros librerías, y también me incluyo entre estos. El equívoco proviene del nombre en inglés, que es ficheros library. Este término se traduce como biblioteca, y no como librería, que es la palabra en español más parecida fonéticamente. Sin embargo esta discusión es poco importante, desde nuestro punto de vista, ya que nos entendemos igualmente con las dos palabras.

Hay un conjunto de bibliotecas (o librerías) muy especiales, que se incluyen con todos los compiladores de C y de C++. Son las librerías (o bibliotecas) ANSI o estándar. Pero también las hay no estándar, y dentro de estas las hay públicas y comerciales. En este curso sólo usaremos bibliotecas (o librerías) ANSI.

Ficheros ejecutables y enlazadores

Cuando obtenemos el fichero objeto, aún no hemos terminado el proceso. El fichero objeto, a pesar de ser comprensible para el ordenador, no puede ser ejecutado. Hay varias razones para eso:

  1. Nuestros programas usaran, en general, funciones que estarán incluidas en bibliotecas externas, ya sean ANSI o no. Es necesario combinar nuestro fichero objeto con esas bibliotecas para obtener un ejecutable.
  2. Muy a menudo, nuestros programas estarán compuestos por varios ficheros fuente, y de cada uno de ellos se obtendrá un fichero objeto. Es necesario unir todos los ficheros objeto, más las bibliotecas en un único fichero ejecutable.
  3. Hay que dar ciertas instrucciones al ordenador para que cargue en memoria el programa y los datos, y para que organice la memoria de modo que se disponga de una pila de tamaño adecuado, etc. La pila es una zona de memoria que se usa para que el programa intercambie datos con otros programas o con otras partes del propio programa. Veremos esto con más detalle durante el curso.
  4. No siempre obtendremos un fichero ejecutable para el código que escribimos, a veces querremos crear ficheros de biblioteca, y en ese caso el proceso será diferente.

Existe un programa que hace todas estas cosas, se trata del "linker", o enlazador. El enlazador toma todos los ficheros objeto que componen nuestro programa, los combina con los ficheros de biblioteca que sean necesarios y crea un fichero ejecutable.

Una vez terminada la fase de enlazado, ya podremos ejecutar nuestro programa.

Errores

Por supuesto, somos humanos, y por lo tanto nos equivocamos. Los errores de programación pueden clasificarse en varios tipos, dependiendo de la fase en que se presenten.

Errores de sintaxis: son errores en el programa fuente. Pueden deberse a palabras reservadas mal escritas, expresiones erróneas o incompletas, variables que no existen, etc. Los errores de sintaxis se detectan en la fase de compilación. El compilador, además de generar el código objeto, nos dará una lista de errores de sintaxis. De hecho nos dará sólo una cosa o la otra, ya que si hay errores no es posible generar un código objeto.

Avisos: además de errores, el compilador puede dar también avisos (warnings). Los avisos son errores, pero no lo suficientemente graves como para impedir la generación del código objeto. No obstante, es importante corregir estos errores, ya que ante un aviso el compilador tiene tomar decisiones, y estas no tienen por qué coincidir con lo que nosotros pretendemos hacer, ya se basan en las directivas que los creadores del compilador decidieron durante la creación del compilador.

Errores de enlazado: el programa enlazador también puede encontrar errores. Normalmente se refieren a funciones que no están definidas en ninguno de los ficheros objetos ni en las bibliotecas. Puede que hayamos olvidado incluir alguna biblioteca, o algún fichero objeto, o puede que hayamos olvidado definir alguna función o variable, o lo hayamos hecho mal.

Errores de ejecución: incluso después de obtener un fichero ejecutable, es posible que se produzcan errores. En el caso de los errores de ejecución normalmente no obtendremos mensajes de error, sino que simplemente el programa terminará bruscamente. Estos errores son más difíciles de detectar y corregir. Existen programas auxiliares para buscar estos errores, son los llamados depuradores (debuggers). Estos programas permiten detener la ejecución de nuestros programas, inspeccionar variables y ejecutar nuestro programa paso a paso (instrucción a instrucción). Esto resulta útil para detectar excepciones, errores sutiles, y fallos que se presentan dependiendo de circunstancias distintas.

Errores de diseño: finalmente los errores más difíciles de corregir y prevenir. Si nos hemos equivocado al diseñar nuestro algoritmo, no habrá ningún programa que nos pueda ayudar a corregir los nuestros. Contra estos errores sólo cabe practicar y pensar.

Propósito de C y C++

¿Qué clase de programas y aplicaciones se pueden crear usando C y C++?

La respuesta es muy sencilla: TODOS.

Tanto C como C++ son lenguajes de programación de propósito general. Todo puede programarse con ellos, desde sistemas operativos y compiladores hasta aplicaciones de bases de datos y procesadores de texto, pasando por juegos, aplicaciones a medida, etc.

Oirás y leerás mucho sobre este tema. Sobre todo diciendo que estos lenguajes son complicados y que requieren páginas y páginas de código para hacer cosas que con otros lenguajes se hacen con pocas líneas. Esto es una verdad a medias. Es cierto que un listado completo de un programa en C o C++ para gestión de bases de datos (por poner un ejemplo) puede requerir varios miles de líneas de código, y que su equivalente en Visual Basic sólo requiere unos pocos cientos. Pero detrás de cada línea de estos compiladores de alto nivel hay cientos de líneas de código en C, la mayor parte de estos compiladores están respaldados por enormes bibliotecas escritas en C. Nada te impide a ti, como programador, usar bibliotecas, e incluso crear las tuyas propias.

Una de las propiedades de C y C++ es la reutilización del código en forma de bibliotecas de usuario. Después de un tiempo trabajando, todos los programadores desarrollan sus propias bibliotecas para aquellas cosas que hacen frecuentemente. Y además, raramente piensan en ello, se limitan a usarlas.

Además, los programas escritos en C o C++ tienen otras ventajas sobre el resto. Con la excepción del ensamblador, generan los programas más compactos y rápidos. El código es transportable, es decir, un programa ANSI en C o C++ podrá ejecutarse en cualquier máquina y bajo cualquier sistema operativo. Y si es necesario, proporcionan un acceso a bajo nivel de hardware sólo igualado por el ensamblador.

Otra ventaja importante es que C tiene más de 30 años de vida, y C++ casi 20 y no parece que su uso se debilite demasiado. No se trata de un lenguaje de moda, y probablemente a ambos les quede aún mucha vida por delante. Sólo hay que pensar que sistemas operativos como Linux, Unix o incluso Windows se escriben casi por completo en C.

Por último, existen varios compiladores de C y C++ gratuitos, o bajo la norma GNU, así como cientos de bibliotecas de todo propósito y miles de programadores en todo el mundo, muchos de ellos dispuestos a compartir su experiencia y conocimientos.