Sie sind auf Seite 1von 75

Programacin C++

Adolfo J. Milln
ndice:
Generalidades o Prlogo edicin Web o ndice (esta pgina) o Convenciones y notacin 0 Introduccin o 0.1 El Ordenador Electrnico Digital 0.1.1 Computacin: Mquina de Turing o 0.2 El Sistema Operativo o 0.3 El Hardware 1 Programacin C++ o 1.1 Introduccin a la POO o 1.2 El lenguaje C++ 1.2.1 Algunos conceptos bsicos o 1.3 Estructura de un programa 1.3.1 Estructura lgica 1.3.1a Sintaxis de las sentencias C++ 1.3.1b Sintaxis de las expresiones C++ 1.3.2 Almacenamiento (aspectos lgicos) o 1.4 Proceso de creacin de un programa 1.4.0 Construir un ejecutable 1.4.0a La utilidad Make (generalidades) 1.4.0a1 GNU Make 1.4.0a2 Borland Make 1.4.0b Topologa de un fichero-imagen 1.4.1 El preprocesador 1.4.1a Constantes manifiestas 1.4.1b Macros, valores y tipos estndar 1.4.2 Compilacin 1.4.3 Generador de cdigo 1.4.3a Cdigo WIntel 1.4.4 Enlazado 1.4.4a Fichero de definicin 1.4.4b Libreras: generalidades 1.4.4b1 Libreras estticas 1.4.4b2 Libreras dinmicas 1.4.4b2a Construir una DLL 1.4.4b2b Usar una DLL 1.4.4b2c Libreras de importacin 1.4.4b2d Librera de tipos 1.4.4b2e Carga retrasada 1.4.4b3 Libreras de recursos 1.4.5 Depuracin 1.4.6 Tamao de los ejecutables o 1.5 Secuencia de ejecucin 1.5.1 Funciones de terminacin

1.5.2 Soporte de runtime 1.6 Tratamiento de excepciones 1.6.1 Lanzar excepciones 1.6.1a Excepciones en la Librera Estndar 1.6.2 Capturar excepciones 1.6.3 Excepciones imprevistas 1.6.4 Especificar excepciones 1.6.5 Excepciones en la prctica o 1.7 Programacin actual 1.7.1 Tecnicismos 1.7.2 API de MS Windows 1.7.3 El shell 1.7.4 Eventos 1.7.5 Programacin Windows o 1.8 Estructura de la informacin 1.8a Colas, Pilas, Listas enlazadas 1.8b .rboles 2 Objetos y algoritmos o 2.1 Atributos o 2.2 Tipos 2.2.1 Tipos bsicos 2.2.1a Tipos carcter 2.2.1a0 Sistemas de codificacin 2.2.1a1 El carcter ancho 2.2.1a2 UCS y Unicode 2.2.2 Tipos derivados 2.2.3 Modificadores de tipo 2.2.4 Representacin interna y rango 2.2.4a Formas de representacin binaria 2.2.4b Formas de representacin simblica 2.2.4c Tamao de los tipos bsicos 2.2.5 Conversiones estndar 2.2.5a Conversiones estndar con tipos abstractos 2.2.6 Almacenamiento (aspectos fsicos) 2.2.6a Orden de almacenamiento 3 Elementos lxicos o 3.1 Comentarios o 3.2 Tokens 3.2.1 Palabras clave 3.2.1a typedef 3.2.1a1 typedefs en Windows 3.2.1b bool, false, true 3.2.1c const 3.2.1d volatile 3.2.1e typename 3.2.2 Identificadores 3.2.3 Constantes 3.2.3a Expresiones constantes 3.2.3b Constantes enteras 3.2.3c Constantes fraccionarias 3.2.3d Constantes carcter 3.2.3e Secuencias de escape 3.2.3f Constantes literales (cadenas alfanumricas) 3.2.3g Constantes de enumeracin 3.2.6 Puntuadores o

4 Estructura del lenguaje o 4.1 Declaraciones 4.1.1 Entidades 4.1.2 Declaraciones y definiciones 4.1.3 mbito 4.1.3a Tipos y variables globales 4.1.4 Visibilidad 4.1.5 Duracin 4.1.5a Control de recursos 4.1.8 Clase de almacenamiento 4.1.8a auto 4.1.8b register 4.1.8c static 4.1.8d extern 4.1.8e mutable 4.1.9 Modificadores auxiliares (de variables, funciones y clases) 4.1.11 Espacio de Nombres ("namespace") 4.1.11a Declarar un Subespacio 4.1.11b Subespacio annimo 4.1.11c Acceso a elementos 4.1.11c1 Acceso a subespacios en clases 4.1.11c2 Acceso a la Librera Estndar 4.1.11d Ampliar un Subespacio o 4.2 Punteros 4.2.1 Puntero a objeto 4.2.1a Declaracin 4.2.1b Modelado 4.2.1c Puntero a puntero 4.2.1d Puntero genrico 4.2.1e Puntero constante/a constante 4.2.1f Puntero a clase 4.2.1g Puntero a miembro 4.2.1g1 Puntero a miembro no esttico 4.2.1g1-2 Punteros internos a miembros no estticos 4.2.1g1-3 Punteros externos a miembros no estticos 4.2.1g2 Puntero a miembro esttico 4.2.2 Aritmtica de punteros 4.2.3 Referencias 4.2.4 Puntero a funcin 4.2.4a Declaracin/definicin 4.2.4b Invocacin mediante puntero 4.2.4c Funciones como parmetros o 4.3 Matrices 4.3.1 Declaracin 4.3.2 Punteros y Matrices 4.3.3 Almacenamiento de Matrices 4.3.4 Matrices alfanumricas 4.3.5 Matrices de punteros 4.3.6 Matrices de matrices 4.3.7 Inicializacin de Matrices 4.3.8 Matrices como argumento de funciones o 4.4 Funciones 4.4.1 Declaracin

4.5

o o o o

4.6 4.7 4.8 4.9

4.4.1a Sobrecarga de funciones 4.4.1b Especificadores opcionales 4.4.2 Definicin 4.4.3 Declaracin Implcita 4.4.4 Funcin main() 4.4.5 Argumentos 4.4.6 Invocacin de funciones 4.4.6a Formas de invocacin 4.4.6b Sobre el rendimiento 4.4.6c Recursin 4.4.7 Valor devuelto Estructuras 4.5.1 Declaracin 4.5.2 Inicializacin 4.5.3 Operaciones permitidas 4.5.4 Acceso a miembros 4.5.5 Estructuras y funciones 4.5.5a Crear estructuras 4.5.5b Manipular estructuras 4.5.5c Estructuras de Librera Estndar 4.5.6 Matrices de Estructuras 4.5.7 Punteros a Estructuras 4.5.8 Estructuras autoreferenciadas (rboles y listas binarias) 4.5.8a Equilibrio de rboles binarios 4.5.8b Buscar elementos en rboles binarios Campos de bits 4.6.1 Alineacin interna Uniones Enumeraciones Operadores 4.9.0 Resumen 4.9.0a Evaluacin de expresiones (asociatividad y precedencia de operadores) 4.9.1 Operadores Aritmticos 4.9.2 Operadores de Asignacin 4.9.2a Asignacin de propiedades 4.9.3 Operadores de Comparacin y Manejo de Bits (desplazamientos, bitand, bitor, xor, compl) 4.9.3a Tecnicismos y ejemplos adicionales 4.9.5 Operador Coma , 4.9.6 Operador Condicional ? : 4.9.8 Operadores Lgicos (and, or, not) 4.9.9 Operadores de Modelado de tipos 4.9.9a const_cast 4.9.9b static_cast 4.9.9c dynamic_cast 4.9.9d reinterpret_cast 4.9.9e El modelado en la programacin Windows 4.9.10 Operador de Preproceso #define #error #if, #elif, #else, #endif #ifdef, #ifndef #import #include

#line #pragma #undef Operadores de puntero Operadores Relacionales (de comparacin) Operador sizeof Operador typeid 4.9.14a Clase type_info Operadores aadidos [ ] ( ) . -> Sobrecarga de Operadores 4.9.18a Sobrecarga del operador = (asignacin) 4.9.18b Sobrecarga de operadores binarios 4.9.18b1 Sobrecarga de operadores relacionales 4.9.18b2 Sobrecarga de operadores aritmticos 4.9.18b3 Sobrecarga de operadores de manejo de bits 4.9.18c Sobrecarga de operadores unarios 4.9.18d Sobrecarga del operador [] (elemento de matriz) 4.9.18e Sobrecarga del operador -> (selector indirecto de miembro) 4.9.18f Sobrecarga del operador ( ) (invocacin de funcin) 4.9.18g Sobrecarga de operadores lgicos 4.9.18h Sobrecarga de enumeraciones 4.9.18k Sobrecarga del operador de modelado (operadores de conversin) 4.9.18t Tecnicismos 4.9.19 Operador :: de acceso a mbito 4.9.20 Operador new 4.9.20a Funcin-operador new() 4.9.20b Definir sitio de almacenamiento 4.9.20c Operador new con matrices ( new[ ] ) 4.9.20d Manejo de errores 4.9.21 Operadores delete y delete[ ] 4.10 Sentencias 4.10.1 Sententencias de etiqueta 4.10.2 Sentencias de seleccin (control de flujo) 4.10.3 Iteraciones (bucles) 4.10.4 Sentencias de salto 4.10.5 Sentencias de expresiones con coma 4.11 Clases 4.11.1 Formas de creacin 4.11.2 Declaracin 4.11.2a Construccin inicial 4.11.2a1 friend 4.11.2a2 Sobrecarga de mtodos 4.11.2a3 Clases locales 4.11.2b Herencia y accesibilidad (la cuestin del acceso) 4.11.2b1 Punteros en jerarquas de Clases 4.11.2b2 Espacio de nombres y sobrecarga de mtodos 4.11.2c Herencia mltiple 4.11.2c1 Clases-base virtuales 4.11.2d Creacin y destruccin de objetos 4.11.2d1 Constructores 4.11.2d2 Destructores 4.11.2d3 Iniciar miembros

4.9.11 4.9.12 4.9.13 4.9.14 4.9.16 4.9.18

4.11.2d4 Copiar objetos (Constructor-copia) 4.11.2d5 Observaciones y errores frecuentes 4.11.2e Acceso a miembros 4.11.3 Nombres de Clases 4.11.3a Estructuras annimas 4.11.4 Ambito de nombres 4.11.5 Instanciado de Clases 4.11.6 Puntero this 4.11.7 Miembros estticos 4.11.8 Clases polimrficas 4.11.8a Funciones virtuales 4.11.8b Funciones dinmicas 4.11.8c Clases abstractas 4.12 Plantillas (programacin genrica) 4.12.1 Funciones genricas 4.12.1a Instanciacin explcita & versin explcita (sobrecarga de funciones genricas) 4.12.1b Otros conceptos 4.12.2 Clases genricas 4.12.2a Clases genricas II 4.12.2b Clases genricas en la Librera Estndar 4.12.2b1 Punteros inteligentes (auto_ptr) 4.13 Tecnicismos 4.13.1 Reglas de buena prctica 4.13.2 Clases dentro de clases 4.13.3 Objetos-puntero 4.13.4 Tcnica manejador-cuerpo 4.13.5 Constructores virtuales 4.13.6 Conversin automtica a tipos bsicos 4.13.7 Asignacin a funciones

5. Librera Estndar

Nota: la seccin 5, correspondiente a la Librera Estndar C++, est en fase de redaccin. Por el momento estas pginas solo son accesibles parcialmente.

5.1 STL 5.1.1 Contenedores 5.1.1a Seleccionar un contenedor 5.1.1b Cuestiones adicionales 5.1.1c Secuencias 5.1.1c1 vector 5.1.1c2 vector<bool> 5.1.1c3 deque 5.1.1c4 list 5.1.1c5 stack 5.1.1d Adaptadores 5.1.1d1 queue 5.1.1d2 priority_queue

5.1.1e Contenedores asociativos 5.1.1e1 bitset 5.1.1e2 set 5.1.1e3 multiset 5.1.1e4 map 5.1.1e5 multimap 5.1.2 Iteradores 5.1.2a Iteradores de entrada (InputIterator) 5.1.2b Iteradores de salida (OutputIterator) 5.1.2c Iteradores adelante (ForwardIterator) 5.1.2d Iteradores bidireccionales (BidirectionalIterator) 5.1.2e Iteradores de acceso aleatorio (RandomAccessIterator) 5.1.2f Iteradores para flujos de entrada/salida 5.1.2g Iteradores de insercin 5.1.2h Manipulacin de iteradores 5.1.3 Algoritmos 5.1.3a Funciones y predicados 5.1.3a1 Objetos-funcin 5.1.3a2 Funcin-adaptador 5.1.3b Algoritmos no modificativos 5.1.3b1 for_each() 5.1.3b2 find() 5.1.3b3 find_end() 5.1.3b4 find_first_of() 5.1.3b5 adyacent_find() 5.1.3b6 count() 5.1.3b7 mismatch() 5.1.3b8 equal() 5.1.3b9 search() 5.1.3c Algoritmos modificativos 5.1.3c1 copy() 5.1.3c2 swap() 5.1.3c3 transform() 5.1.3c4 replace() 5.1.3c5 fill() 5.1.3c6 generate() 5.1.3c7 remove() 5.1.3c8 unique() 5.1.3c9 reverse() 5.1.3c10 rotate() 5.1.3c11 random_shuffle() 5.1.3c12 partition() 5.1.3d Operaciones de ordenacin 5.1.3e Elemento ensimo 5.1.ef Busqueda binaria 5.1.3g Composicin ("Merge") 5.1.3h Operaciones de comprobacin 5.1.3i Operaciones de modificacin en estructuras ordenadas 5.1.3j Operaciones de montn 5.1.3k Mximos y mnimos 5.1.3l Comparaciones lexicogrficas 5.1.3m Generadores de permutaciones 5.1.4 Adaptadores ("Adaptors") 5.1.5 Asignadores de memoria ("Allocators") 5.2 Internacionalizacin

5.2.1 Facetas 5.2.2 Localismos o 5.3 Entradas/Salidas (generalidades) 5.3.1 E/S en la Librera Estndar 5.3.2 Jerarqua de clases de E/S 5.3.2a ios_base 5.3.2a1 Datos de usuario (iword/pword/xalloc) 5.3.2a2 Retrollamadas ("Callbacks") 5.3.2a3 Controles de formato (flags/setf/precision/width) 5.3.2b basic_ios 5.3.2c basic_istream 5.3.2d basic_ostream 5.3.2e basic_iostream 5.3.2f basic_streambuf el bufer de flujo 5.3.3 E/S asociadas con ficheros 5.3.3a E/S no-formateadas asociadas con ficheros 5.3.3b E/S formateadas asociadas con ficheros 5.3.3c Manipuladores 5.3.3c1 Manipuladores estndar 5.3.4 Vaciado de buffers en C/C++ o 5.5 Librera clsica 5.5.1 Fecha y hora en C/C++ (generalidades) 5.5.1a Fecha y hora en la Librera Estndar 5.5.2 Directorios y ficheros en C/C++ 5.5.2a Problemas con directorios y ficheros 6. Apndices o 6.1 Reglas de lectura 7. Bibliografa 9 Librera de ejemplos o 9.0 Recursos Web o 9.1 Fecha y hora o 9.2 Datos por teclado o 9.3 Manejo de cadenas alfanumricas 9.3.1 Un "parser" muy simple 9.3.2 Una clase String o 9.4 Manejo de bits 9.4.1 Representacin interna (IEEE) o 9.5 Conversores o 9.6 Llamadas al Sistema o 9.7 Clculos matemticos

1 Programacin C++
"No programming language can be all things to all programers. No matter how many features a language has, there's always someone who wants just one more". Dan Saks "C/C++ Users Journal" Enero 1990.

1 Observaciones y comentarios sobre los lenguajes C/C++ Se ha dicho: "C++ es un lenguaje potente, pero a veces no es precisamente un paseo por el parque... De acuerdo, de acuerdo, es orientado a objetos. Correcto. Yo personalmente conozco gente que ha trabajado toda una semana en herencia mltiple, para retirarse completamente frustrado a Carolina del Norte a limpiar cochineras". Joshua Trupin: MSDN Magazine, Septiembre 2000 www.msdn.microsoft.com/msdnmag/default.asp "C hace fcil dispararse en los propios pies; C++ lo hace ms difcil, pero cuando lo haces, te vuela la pierna completa". Stroustrup. C++ es un lenguaje difcil en el que puede existir una lnea muy tenue entre una caracterstica y un 'bug' ". Programming in C++: Rules and Recommendations, Ellemtel Telecommunication Systems Laboratories, Sweden. "Para nuestro gusto C++ ya es demasiado largo y complicado". Comit de estandarizacin del C++. "Tiene una sintaxis compleja, semntica sutil y una temible reputacin ". Steve Donovan "Scripting C++". InformIT Marzo 2002 "Todos los hombres ascienden en la escala hasta alcanzar su mximo nivel de ineficacia ". Dr. Laurence J. Peter. "La pirmide de Peter". Ed. Plaza & Janes S.A. Barcelona [5]. Creo que C++ es un magnfico ejemplo de que lo anterior es aplicable tambin a los lenguajes de programacin. En efecto, C++ es una muestra de como las cosas pueden seguir mejorndose hasta llegar a un nivel de complejidad que las hace prcticamente inutilizables por el comn de los mortales, a no ser que les dediquen un mnimo de 500 horas para empezar a entenderlas [9]. Como botn de muestra, incluyo un comentario encontrado en Amazon [1] sobre el libro "The C++ Programming Language" (TC++PL para los ntimos) del ya citado Stroustrup: "Estoy en mi tercer ao de aprendizaje de C++ (s, se tarda un tiempo en conocer el C++, en conocerlo realmente), todava lo leo una y otra vez, y cada vez encuentro ms y ms cosas tiles y consejos importantes", Caliv Nir (Israel). Afortunadamente parece que las cosas vuelven a su cauce. Hace poco tiempo que algunos se dieron cuenta que el asunto haba empezado a desmadrarse, as que empezaron a quitarle cosas y a simplificarlo..., Estaba naciendo Java! (es tambin la explicacin "oficial" de Microsoft para inventar el C# ;-) Nota: Esta es la situacin por el momento, pero no os preocupis. Vemos como Java se est perfeccionando a marchas rpidas, con lo que a no tardar ser tan complejo como C++ y

habr que volver a inventar algo ms sencillo...[4] En cuanto al C#: Sea bienvenida cualquier mejora!!. Aunque se me ocurre que no estara de ms aquello de mantener una cierta "diversidad gentica" en la informtica, mucho me temo que al paso que vamos acabemos todos vestidos de gris con el "Libro Rojo" en el bolsillo :-( [6]

2 Resea histrica Desde un punto de vista histrico, el C es anterior al C++, aunque el estndar C++ fuera publicado antes que el de C [2], de modo, que paradjicamente el hijo influy en el estndar del padre. As pues, la propuesta de ANSI C recogi algunas de las normas del C++. Ambos nacieron en los prestigiosos laboratorios Bell de la AT&T (American Telephone and Telegraph) en Murray Hill (New Jersey USA) [3]. El primero de la mano de Brian W. Kernighan y Dennis M. Ritchie (la "K" y la "R" de K&R, siglas con las que se les conoce), el segundo es de Bjarne Stroustrup. La primera edicin del libro: "El lenguaje de programacin C" de K&R es de 1978. Por su parte la primera edicin de: "El lenguaje de programacin C++" de Stroustrup es de 1986. El propio Stroustrup reconoce que comenz a trabajar en su diseo en 1979; justo un ao despus de la publicacin del clsico de K&R. Las primeras versiones en uso del lenguaje (bajo el nombre de "C con clases") en el entorno de los departamentos de investigacin de la AT&T se remontan a 1980. La primera utilizacin del lenguaje fuera del entorno de investigacin data de Agosto de 1983. La designacin actual C++ fue acuada en el verano de 1.983 por Rick Mascitti. El primer compilador comercial fue lanzado en Octubre de 1985. Como todos los lenguajes (naturales y de programacin), el C++ es un resultado de la evolucin; de forma que recoge influencia de sus ancestros, algunos de los cuales ya se han sealado ( 0.1). Adems es en s mismo un producto en evolucin. Las ltimas aportaciones datan de los 80, y se refieren principalmente al mecanismo de excepciones y a las plantillas ("Templates"). Sin duda la mejor fuente de informacin sobre la historia y evolucin de este lenguaje es su propio inventor, de modo que remitimos al lector a sus propias palabras D&EC++.

2.1 Situacin actual Despus de dcadas de aparente inactividad por parte del Comit Internacional encargado de elaborar el estndar, el 25 de Marzo de 2011, los medios especializados de todo el mundo, se hacan eco de que finalmente, el comit ISO para el C++ haba sido aprobado el ltimo borrador del nuevo estndar en su reunin de primavera en Madrid. Esta versin, en la que se haba estado trabajando intensamente, era conocida comoC++09 porque se supona que sera aprobada en el 2009. Sin embargo, el proceso sufri sucesivos retrasos, de forma que finalmente era conocido quizs un poco humorsticamente- como C++0x, en relacin a que no se saba cuando aparecera. Finalmente, si las cosas discurren como es de suponer, la aprobacin ocurrir dentro de este ao, as que el prximo estndar ser el C++2011. Nota: a la hora de escribir estas lneas, la ltima revisin publicada lo era bajo el ttulo ISO_C0x_final_draft. Un documento .PDF de nada menos que 11 MB.

Podra decirse que despus de vivir una poca dorada, la popularizacin y uso del C++ haba

sufrido una cierta ralentizacin en favor de nuevas modas y nuevos lenguajes, y que incluso su desarrollo y evolucin haban quedado estancados desde los aos 70. Sin embargo, esta aparente "inactividad" ha estado lejos de ser real y el nuevo estndar incluye un sinnmero de avances conceptuales y materiales -en forma de nuevas libreras- que lo colocan de nuevo en una posicin puntera dentro del mundo de la programacin, en especial en lo que respecta a grandes sistemas. Precisamente, la razn principal de los retrasos ocurridos en el proceso de aprobacin del nuevo estndar, tienen mucho que ver con la magnitud, nmero y calado de las nuevas extensiones y libreras. Con independencia de que los cambios se hagan oficiales, muchas de las nuevas herramientas ya estaban en la calle de forma ms o menos oficial, de la mano de los fabricantes de compiladores. De forma que se habla incluso de un "renacimiento" del C++ como herramienta y concepto. Para que el lector pueda hacerse una idea de por donde van las ltimas tendencias, a continuacin le incluyo algunos enlaces ilustrativos. Entrevista de Craig Symonds y Mohsen Agsen, dos profesionales que sin duda gozan de una buena perspectiva del mundo de la programacin actual: "C++ Renaissance" Un comentario desde dentro de Microsoft, la compaa que ha gastado billones de Dlares en popularizar el denominado "cdigo no nativo" (interpretado): "C++ Renaissance: First Symptoms of Contagion in Traditionally Non-Native Teams". Artculo de Danny Kalev en Informit (conocido experto que ha sido miembro del Comit de Estandarizacin del C++): "C++ is Back with a Vengeance".

3 Estudiar C++ "C++ should not be for beginners, as it is better to learn the principles from a clearer language than be confused by what all the syntactic knobs and dials, and superfluous constructs do in C++. As for courses, C++ has proven so difficult to learn that you need lots of courses". Ian Joyner "C++??" ( [Joyner-96]).

Para tener una perspectiva histrica parece natural estudiar primero C y despus C++, con lo que se empiezan a ver las diferencias y salvedades entre uno y otro. Este es el enfoque de algunos libros; de hecho el ya comentado manual de ayuda "C++ Builder Languaje Guide" sigue ms o menos esta pauta. Dedica muchos comentarios a las diferencias entre C y C++, y al final dedica un captulo a los asuntos que son exclusivos de este ltimo. Algunos de estos libros incluso hacen comentarios curiosos, que ya solo tienen sentido desde un punto de vista histrico. Por ejemplo, citan que los ficheros fuente de los programas C deben tener nombres con terminacin ".c" y los de C++ terminaciones ".cc" o ".cpp", lo que es superfluo para los compiladores actuales. Por ejemplo, Borland C++Builder puede compilar perfectamente fuentes .c como C o como C++ con un simple indicador (-P) en la orden de llamada. Nota: Otros, como los compiladores GNU C/C++, permiten utilizar ambos criterios. Adems de poder utilizar una directiva explcita, pueden distinguir el tipo de compilacin por la terminacin de los fuentes. Por ejemplo, las terminaciones .c son tratadas y compiladas como fuentes C, mientras que los sufijos .C; .Cpp; .C++; .cc son compilados como C++. Otros compiladores C++ Unix como Solaris de SUN o HP-UX pueden compilar fuentes con terminaciones .C, .c, .cc, .cpp o .cxx.

C++ no naci como un lenguaje orientado a objetos puro. Ms bien se trataba de aadirle "objetos" al clsico C de K&R, ya que el nuevo paradigma de programacin "con objetos", se mostraba como un paso adelante en el arte de la programacin. De hecho, aunque C++ introduce nuevas palabras clave y operadores para manejo de clases, algunas de sus extensiones tienen aplicacin fuera del contexto de programacin con objetos (fuera del mbito de las clases). Esta es tambin la causa de que C++, junto con las magnficas cualidades del C, arrastre algunas de sus deficiencias. Sigue siendo permisivo (aunque menos) "intentando hacer algo razonable con lo que se haya escrito", solo que a veces, sin que nos demos cuenta, se pasa de listo. Por supuesto, con ambos lenguajes resulta cierto el viejo aforismo informtico: "Un programa no hace lo que queremos que haga, sino lo que le hemos dicho que haga". En el caso del C++ quizs tendramos que aadir: "Mas lo que l decide por su cuenta"... :-) Cuando se trata de programas sencillos, que no requieren necesariamente de las caractersticas "++" de la POO, surge inmediatamente la cuestin si ser mejor hacerlo en C o en C++, Cual es mas rpido? Cual proporciona un cdigo ms compacto? Segn dicen los expertos, con los actuales compiladores el resultado est en tablas. Las diferencias se mantienen en un margen de 5% y por lo general los resultados se encuentran a la par. Sin embargo, en cuanto se trata de escribir programas algo ms complicados que mostrar el consabido "Hola mundo", las diferencias de productividad son notables, con una indiscutible ventaja a favor del C++. Nota: En cierta forma esta discusin, usar C++ o C, es un poco bizantina, dado que a efectos prcticos el segundo es un subconjunto del primero (de hecho, los compiladores GNU C y C++ estn integrados en un solo producto). Ms bien cabra discutir si, utilizando C++, merece o no la pena usar los recursos de la POO.

Dicho esto queda una cuestin: Aunque estudiar C++ a partir de C proporciona una perspectiva histrica, una visin del desarrollo del lenguaje, de como fueron sucediendo las cosas. En mi opinin, hacerlo de esta forma no solo es innecesario sino quizs contraproducente para el estudiante normal. No digo que est de ms, solo que es un camino innecesariamente farragoso. Me parece que se trata de una situacin parecida a la de alguien que quisiera aprender Espaol empezando por aprender Latn. Por supuesto no estara de ms si tiene tiempo y gana, si se es un lingista. Se tendr un mayor conocimiento del lenguaje y su evolucin; se comprender mejor el porqu de ciertas cosas. Pero para el extranjero que simplemente necesita entenderse en Espaol, es evidentemente innecesario. Digo esto, porque al ser la POO un paso superior de abstraccin respecto de los primeros sistemas de "traslacin de frmulas". Un paso en la evolucin de los lenguajes de programacin, con un cuerpo de doctrina ya suficientemente consolidado y coherente, las cosas pueden explicarse partiendo directamente del estudio de C++ y aprovechando todas sus posibilidades desde el principio. Incluso dira que intentando "olvidarse" del viejo C que an pervive en su interior. Es perfectamente factible introducir el concepto general de clase y objeto, para decir despus que las estructuras son una "clase" especial que tiene estas y las otras propiedades (todas sus propiedades son pblicas), sin necesidad de introducir el concepto tradicional de "estructura" C, para luego decir que en C++ son un tipo especial de clase... Esto tendr la ventaja adicional de permitir al nefito empezar a pensar "en objetos" con ms rapidez. Nota: Tena ya una gran cantidad de material recopilado cuando me di cuenta de esta realidad, as que tuve que volver a rehacer una parte de la estructura de este "libro", volviendo a empezar directamente con el C++, e intentando hacer la menor cantidad de referencias al viejo C. A la hora de escribir estas lneas todava no est terminado el trabajo (no se si lo estar algn da), pero esta es la intencin, de forma que si cuando caiga en manos del lector

quedan referencias al viejo C, que no sean meramente histricas o comparativas, sepa que se debe simplemente a un trabajo inacabado.

1.1 Introduccin a la POO


1 Qu es eso de los "Objetos"? En la programacin tradicional exista una clara diferenciacin entre los datos y su manipulacin, es decir, el conjunto de algoritmos para manejarlos. Los tipos de datos eran muy simples; todo lo ms nmeros de diverso tipo y caracteres (aislados o agrupados en cadenas o matrices), pero nunca elementos heterogneos. La situacin para el programador era que el lenguaje pareca decirle: estos son los posibles contenedores para la informacin que debes manejar (los tipos de datos soportados por el lenguaje) y aqu aparte, las operaciones que puedes realizar con ellos (las operaciones definidas para dichos datos). Por ejemplo, si son datos numricos las operaciones aritmticas, las comparaciones, las asignaciones, etc. Con estas herramientas el programador construa su cdigo, y para poner un poco de orden en la maraa de algoritmos solo tena el recurso de agruparlo en funciones. Si se tena que hacer un programa de gestin comercial, la informacin correspondiente a un concepto ms o menos real, por ejemplo un "cliente", tena que ser desmenuzada en una serie de datos aislados: nombre, domicilio, poblacin, provincia, telfono, condiciones de compra, saldo, tipo de descuento, nmero de vendedor asignado, fecha de ltima compra, etc. etc. Luego, para hacer una factura, todos estos elementos eran tomados y procesados individualmente. Un primer intento de construir tipos de datos ms complejos, que se parecieran a las situaciones del mundo real y que pudieran ser tratados (ms o menos) como una unidad, lo constituyen, entre otros, las "estructuras" del C clsico ("registros" en otros lenguajes). Una estructura es una agregacin de datos de diverso tipo construida a criterio del programador mediante la agrupacin de elementos ya incluidos en el lenguaje (bsicamente los antedichos nmeros y caracteres). Esto supuso un paso en el nivel de abstraccin de los datos, aunque las herramientas para manipularlos seguan siendo elementos totalmente diferenciados. En esta etapa ya poda crearse una estructura "cliente" que albergara toda la informacin antes dispersa, pero aparte de leer y escribir la informacin en un solo bloque, poco ms poda hacerse, los datos tenan que seguir siendo procesados individualmente con expresiones parecidas a: cliente->fecha_ult_compra = date(); tot_factura = suma * cliente->descuento cliente->saldo = cliente->saldo + tot_factura; etc. Ante esta situacin surge una nueva idea: crear nuevos tipos de datos agrupando los tipos tradicionales en entidades ms complejas que reciben el nombre genrico de clases. Las clases son conjuntos de datos definidos por el programador, parecidos a las mencionadas "estructuras", pero con la importante novedad de incluir tambin las herramientas necesarias para su manipulacin; es decir: operadores y funciones de la programacin tradicional. Dicho de otro modo: los nuevos tipos de datos pueden ir acompaados de su "lgebra"; de las operaciones que se pueden realizar con ellos [3].

La idea es construir una especie de caja negra que incluya no solo los datos, tambin los algoritmos necesarios para su manipulacin (sus operaciones), de forma que pueda ser vista desde el exterior (el programador que las usa) como un ente al que se le envan preguntas u rdenes y responde con el resultado [2]. Este comportamiento de la clase frente al mundo exterior es lo que se llama su interfaz o protocolo. Cmo es el detalle de su interior no le interesa al usuario-programador en absoluto, solo le interesa conocer la forma de "comunicarse" con ella y que posibilidades le ofrece. El hecho de empaquetar juntos datos y funcionalidad, dejando visible al exterior solo lo estrictamente necesario, se denomina encapsulamiento y es otro de los distintivos de la POO. Para ilustrar la idea en un caso concreto consideremos un ejemplo clsico: Supongamos que en un programa tradicional hay que manejar nmeros complejos. La solucin es definir dos variables fraccionarias (de coma flotante) R e Y para representar respectivamente las partes reales e imaginaria de cada complejo. Luego para obtener, por ejemplo la suma (Rr, Yr), de dos complejos (R1, Y1), (R2, Y2), tenemos que codificar: Rr = R1 + R2 Yr = Y1 + Y2 para calcular el valor del mdulo m tenemos que hacer: m = SQRT( (Rr * Rr) + ( Yr * Yr) )

as sucesivamente para cualquier manipulacin que queramos efectuar. Supongamos ahora que tenemos esta posibilidad de "construir" un nuevo tipo de variable, que denominaremos la clase de los complejos (la designamos por Cc), y que hemos incluido en dicha clase la informacin necesaria para realizar todas las operaciones permitidas en el lgebra de los nmeros complejos. No solo la suma, tambin la asignacin, la comparacin, el clculo del mdulo, etc. Incluso los procedimientos para crear elementos nuevos (complejos) y para destruirlos (eliminarlos de la memoria). Ahora el funcionamiento sera algo parecido a: Cc x = (R1, Y1); Cc y = (R2, Y2);

Estamos declarando que x e y son objetos de la nueva clase y les estamos asignando los valores correspondientes. Fjese que estos objetos son casos concretos del nuevo tipo, la clase de los complejos (en el mismo sentido que un 5 es un caso concreto del concepto genrico de "nmero entero"). En el ejemplo suponemos que R1, Y1, R2, Y2 siguen siendo nmeros fraccionarios tradicionales, los mismos que en el ejemplo anterior. Fjese tambin que la asignacin x = (R1, Y1) supone que el compilador sabe que al ser x un complejo, el smbolo "=" no representa aqu la misma operacin que en el caso de, por ejemplo, z = 6; tambin supone que el compilador sabe que significa exactamente la expresin (R1, Y1) a la derecha. Si ahora queremos calcular el resultado de la suma de los dos complejos y el valor de su mdulo, solo tenemos que hacer: Cc z = x + y; float m = z.mod();

Observe que en la primera lnea el operador "+" no es exactamente el mismo que cuando se utiliza

con enteros (por ejemplo, en 3 + 5). Esto lo deduce el compilador, porque sabe que x e y son complejos (dentro de la nueva clase hemos definido la operacin "suma" entre sus elementos y le hemos asignado el operador "+"). Por otra parte, en la segunda lnea solo tenemos que mandarle al complejo z el mensaje mod() - calcula el mdulo -; la sintaxis es la sealada: z.mod(). Ya sabemos que el resultado es un escalar y lo asignamos al nmero fraccionario m. Fjese tambin que en ambas lneas el operador de asignacin "=" no representa la misma operacin. En la primera se trata de una asignacin entre complejos; en la segunda es la tradicional asignacin entre nmeros fraccionarios. Esta capacidad "camalenica" de los operadores es tpica de la POO, y se conoce como sobrecarga [4] (veremos que tambin puede haber sobrecarga de funciones). El compilador sabe en cada momento que operador debe utilizar de toda la panoplia disponible aunque estn bajo el mismo smbolo; lo sabe por el contexto (por la naturaleza de los operandos involucrados). En la POO es frecuente que mtodos anlogos de clases distintas se referencien utilizando las mismas etiquetas. Es decir: supongamos que tenemos tres clases: los Complejos, los Vectores y los Racionales. Puede ocurrir que, por ejemplo, el mtodo para calcular el mdulo se denominemod() en las tres clases. Aunque el resultado sea un nmero racional positivo en los tres casos, naturalmente su operatoria es distinta si se aplica a racionales, vectores o matrices. Sin embargo, no existe posibilidad de confusin en el programa porque cuando apliquemos el mtodo en cada caso concreto, el compilador sabe a cual de ellos nos estamos refiriendo en base a los objetos involucrados. Esta caracterstica de la POO, da origen a lo que se llama polimorfismo. El nombre es bastante descriptivo de su significado, y la posibilidad resulta ser de gran utilidad, pues simplifica la lectura del cdigo. Cada vez que encontremos una invocacin al mtodo mod tenemos una imagen mental del resultado con independencia del objeto concreto al que se aplique. Llegados a este punto, introduciremos algn vocabulario especfico para adaptarnos a la terminologa, algo especial, que utiliza la POO: En vez de "variables" como en la programacin clsica, los datos contenidos en las clases (y en los objetos concretos) se denominanpropiedades, de forma que en el ejemplo anterior, nos referiremos a las partes real R1 e imaginaria Y1 del complejo x como las propiedades R e Yde x (los complejos siguen teniendo una parte "real" y otra "imaginaria" incluso en la POO). Por su parte las operaciones de manipulacin contenidas en las clases (bajo la forma de funciones) reciben el nombre de mtodos. Siguiendo con el ejemplo anterior, en vez de referirnos a funcin mod(), que calcula el mdulo, diremos que mod es un mtodo de la clase de los complejos. En lenguajes como C++, que puede tener elementos de ambos mundos, de la programacin tradicional y de la POO, poder utilizar indistintamente los vocablos "funcin" o "mtodo" es una ventaja, ya que nos permite distinguir en cada caso a que nos estamos refiriendo, si a funciones tradicionales o a funciones pertenecientes a clases. En uno de los prrafos anteriores hemos dicho: "... estos objetos son casos concretos del nuevo tipo, la clase de los complejos..."; utilizando la terminologa correcta diramos: "... estos objetos son instancias de la clase de los complejos...". Como puede verse, instancia significa una sustanciacin concreta de un concepto genrico (la clase). En el mismo sentido podramos decir que el nmero 5 (un objeto), una instancia de la clase de los nmeros enteros, que tiene a su vez determinados mtodos; en este caso las conocidas operaciones aritmticas: suma, resta, multiplicacin..., as como las operaciones de asignacin, la comparacin, etc.

Siguiendo este criterio, la expresin: Cc x = (R1, Y1) no se dice: "declaracin de x como objeto de la nueva clase". Es ms correcto sealar: "xes una instancia de la clase de los complejos". Al hecho de sustanciar un objeto concreto de una clase se le denomina instanciar dicho objeto. Esta operacin (crear un objeto) est encomendada a un tipo especial de mtodos denominados constructores. Siguiendo con nuestro ejemplo, podemos suponer que la expresin: Cc c1(1, 3); crea un complejo c1 cuyas partes real e imaginaria valen respectivamente 1 y 3, y que este objeto ha sido creado por una funcin (mtodo) de Cccuya misin es precisamente crear objetos segn ciertos parmetros que son pasados como argumentos de la funcin. En ocasiones, cuando se quiere distinguir entre los valores y funcionalidades genricos (de las clases) y los concretos (de los objetos), se suelen emplear las expresiones: propiedades de clase y mtodos de clase para las primeras, y propiedades de instancia y mtodos de instanciapara los segundos. As mismo, cuando se quiere distinguir entre una funcin normal (de la programacin tradicional) y una funcin perteneciente a una clase (un mtodo de la clase), se suele emplear para esta ltima la expresin funcin miembro (se sobreentiende "funcin miembro_de_una_clase") [1]. Como puede suponerse, el encapsulamiento, junto con la capacidad de sobrecarga, constituyen en si mismos una posibilidad de abstraccin para los datos mayor que la que facilitaba la programacin tradicional. Ya s puede pensarse en un complejo como algo que tiene existencia "real" dentro de nuestro cdigo ya que lo tratamos (manipulamos) como ente independiente con caractersticas propias; de forma parecida a la imagen que de l tenemos en nuestra mente. Con ser lo anterior un paso importante, sin embargo, las mejoras de la POO respecto de la programacin clsica no terminan aqu. Los Lenguajes Orientados a Objeto como el C++, adems de los tipos de datos tradicionales, incluyen algunas clases pre-construidas, pero debido al hecho de que la "construccin" de un nuevo tipo de variable (como la clase Cc de los complejos en el ejemplo anterior) requiere un cierto trabajo de programacin inicial para disear la clase, se pens que sera estupendo poder aprovechar el trabajo realizado en determinados diseos para, a partir de ellos, obtener otros nuevos de caractersticas ms o menos parecidas. Se trataba en suma de intentar reutilizar el cdigo dentro de lo posible; en el mismo sentido que lo hacemos cuando tenemos que escribir una carta circular y buscamos en el ordenador si hay alguna anterior parecida para utilizarla junto con el procesador de textos, como punto de comienzo para la nueva. Para esto se dot al lenguaje de dos nuevas capacidades: la Herencia y la Composicin. Por herencia ("Inheritance") se entiende la capacidad de poder crear nuevas clases a partir de alguna anterior, de forma que las nuevas "heredan" las caractersticas de sus ancestros (propiedades y mtodos). Se trata por tanto de la capacidad de crear nuevos tipos de datos a partir de los anteriores. Una caracterstica especial de la herencia es que si se cambia el comportamiento de la clase antecesora (tambin llamada padre,base o super), tambin cambiar el comportamiento de las clases derivadas de ella (descendientes). Como puede deducirse fcilmente, la herencia establece lo que se llama una jerarqua de clases del mismo aspecto que el rbol genealgico de una familia. Se entiende tambin que estos conceptos representan niveles de abstraccin que permiten acercar la programacin a la realidad del mundo fsico tal como lo concebimos. Por ejemplo, entendemos que un motor elctrico deriva de la clase general de los motores, de la cual derivan tambin los de gasolina, diesel, vapor, etc. y que, sin dejar de ser motores, cada subclase tiene sus caractersticas peculiares. Por supuesto que no tendra ningn sentido utilizar la herencia para crear simplemente un clnico de la clase base. En realidad, como en el ejemplo de la carta circular, la herencia se emplea como

un primer paso (partir de algo existente) para a continuacin, perfilar los comportamientos o datos que queremos pulir en la nueva versin de la clase. Como lo que principalmente interesa al usuario de una clase es su interfaz, son justamente algunos aspectos de esta interfaz los que se modifican, al objeto de adecuar la nueva subclase a las necesidades especficas del caso. Por lo general esta modificacin de la interfaz se consigue de dos formas: 1. Aadiendo propiedades y/o mtodos que no existan en la clase base 2. Sobrescribiendo propiedades del mismo nombre con distintos comportamientos (sobrecarga y/o polimorfismo).

Aunque la herencia es uno de los pilares de la POO, tiene tambin sus inconvenientes. Por ejemplo, dado que el compilador debe imponer ciertas caractersticas en tiempo de compilacin sobre las clases creadas por herencia, esto resulta en cierta rigidez posterior. Sin embargo, como hemos visto, una de sus ventajas es la reutilizacin del cdigo.

La composicin (tambin llamada herencia mltiple) es la segunda va para crear nuevas clases a partir de las existentes. Por composicin se entiende la capacidad que presenta la POO de ensamblar un nuevo tipo (clase) cuyos elementos o piezas son otras clases. Es posible declarar clases derivadas de las existentes especificando que heredan los miembros de una o ms clases antecesoras. Siguiendo con el smil de la carta circular, la composicin equivaldra a escribirla reutilizando trozos de cartas anteriores. Es clsico el ejemplo de sealar que podramos crear una clase "coche" declarando que tiene un motor y cuatro ruedas, bastidor, aire acondicionado, etc, elementos estos pertenecientes a la clase de los motores, de las ruedas, los bastidores y los sistemas de climatizacin respectivamente. Este sistema tiene tambin sus ventajas e inconvenientes, pero es muy flexible, ya que incluso pueden cambiarse los componentes en tiempo de ejecucin.

2 Resumen: Como resumen de este rpido repaso, podemos sealar que la Programacin Orientada a Objetos (POO) tiene sus propios paradigmas y ventajas, entre las que destaca la reutilizacin del cdigo. A los acadmicos les gusta decir que se sustenta en cuatro columnas a las que ya hemos hecho referencia: Encapsulamiento: Poder separar la interfaz de una clase de su implementacin, o dicho en otras palabras: no es necesario conocer los detalles de cmo estn implementadas las propiedades para poder utilizarlas. Los objetos funcionan a modo de caja negra en la que estn empaquetados los datos y las instrucciones para su manipulacin, de las que conocemos solo lo necesario para utilizarla. Herencia Crear nuevos elementos a partir de los existentes de forma que heredan las propiedades de sus ancestros. Existen dos clases de herencia: simple y mltiple. Sobrecarga: Posibilidad de crear diferentes mtodos dentro de una clase que comparten el mismo nombre, pero que aceptan argumentos diferentes y se comporten de forma distinta segn la naturaleza de estos argumentos ( 4.4.1a). Polimorfismo Es una caracterstica que resulta de gran ayuda en programacin pues facilita la claridad y consistencia del cdigo, aunque es un concepto bastante genrico (y frecuentemente malinterpretado). Se conoce con este nombre el hecho de que un mtodo tiene el mismo nombre y

resulta en el mismo efecto bsico pero est implementado de forma distinta en las distintas clases de una jerarqua.

3 Bibliografa: Para una ms detallada exposicin sobre los conceptos involucrados en la POO y su relacin con el C++, sugerimos dos referencias: "Thinking in C++" ( Bruce Eckel) y "What is Object-Oriented Programming?" ( Stroustrup), aunque desafortunadamente ambas en ingls. La primera bastante didctica enfocada a los aspectos "++" del C; la segunda algo ms terica pero con la autoridad que le confiere ser del inventor del C++.

1.2 El lenguaje C++


"Learn not just the hows, but the whys too". Greg Comeau [7].

1 Generalidades C++ es un lenguaje imperativo orientado a objetos derivado del C [1]. En realidad un superconjunto de C, que naci para aadirle cualidades y caractersticas de las que careca. El resultado es que como su ancestro, sigue muy ligado al hardware subyacente, manteniendo una considerable potencia para programacin a bajo nivel, pero se la han aadido elementos que le permiten tambin un estilo de programacin con alto nivel de abstraccin. Nota: estrictamente hablando, C no es un subconjunto de C++; de hecho es posible escribir cdigo C que es ilegal en C++. Pero a efectos prcticos, dado el esfuerzo de compatibilidad desplegado en su diseo, puede considerarse que C++ es una extensin del C clsico. La definicin "oficial" del lenguaje nos dice que C++ es un lenguaje de propsito general basado en el C, al que se han aadido nuevos tipos de datos, clases, plantillas, mecanismo de excepciones, sistema de espacios de nombres, funciones inline, sobrecarga de operadores, referencias, operadores para manejo de memoria persistente, y algunas utilidades adicionales de librera (en realidad la librera Estndar C es un subconjunto de la librera C++). Respecto a su antecesor, se ha procurando mantener una exquisita compatibilidad hacia atrs por dos razones [2]: poder reutilizar la enorme cantidad de cdigo C existente, y facilitar una transicin lo ms fluida posible a los programadores de C clsico, de forma que pudieran pasar sus programas a C++ e ir modificndolos (hacindolos ms "++") de forma gradual. De hecho, los primeros compiladores C++ lo que hacan en realidad era traducir (preprocesar) a C y compilar despus [3] (las consecuencias se dejan sentir todava en el lenguaje 1.4.2). Por lo general puede compilarse un programa C bajo C++, pero no a la inversa si el programa utiliza alguna de las caractersticas especiales deC++. Algunas situaciones requieren especial cuidado. Por ejemplo, si se declara una funcin dos veces con diferente tipo de argumentos, el compilador C invoca un error de "Nombre duplicado", mientras que en C++ quizs sea interpretado como una sobrecarga de la primera funcin (que sea o no legal depende de otras circunstancias). Como se ha sealado, C++ no es un lenguaje orientado a objetos puro (en el sentido en que puede serlo Java por ejemplo), adems no naci como un ejercicio acadmico de diseo. Se trata simplemente del sucesor de un lenguaje de programacin hecho por programadores (de alto nivel)

para programadores, lo que se traduce en un diseo pragmtico al que se le han ido aadiendo todos los elementos que la prctica aconsejaba como necesarios, con independencia de su belleza o purismo conceptual ("Perfection, in some language theoretical sense, is not an aim of C++. Utility is" [6]). Estos condicionantes tienen su cara y su cruz; en ocasiones son motivo de ciertos "reproches" por parte de sus detractores, en otras, estas caractersticas son precisamente una cualidad. De hecho, en el diseo de la Librera Estndar C++ ( 5.1) se ha usado ampliamente esta dualidad (ser mezcla de un lenguaje tradicional con elementos de POO), lo que ha permitido un modelo muy avanzado de programacin extraordinariamente flexible ( programacin genrica). Aunque C++ introduce nuevas palabras clave y operadores para manejo de clases, algunas de sus extensiones tienen aplicacin fuera del contexto de programacin con objetos (fuera del mbito de las clases), de hecho, muchos aspectos de C++ que pueden ser usados independientemente de las clases [5].

Del C se ha dicho: "Por naturaleza, el lenguaje C es permisivo e intenta hacer algo razonable con lo que se haya escrito. Aunque normalmente esto es una virtud, tambin puede hacer que ciertos errores sean difciles de descubrir" ( Shildt). Respecto al C++ podramos decir otro tanto, pero hemos de reconocer que su sistema de deteccin de errores es mucho ms robusto que el de C, por lo que algunos errores de este sern rpidamente detectados. Desde luego, C++ es un lenguaje de programacin extremadamente largo y complejo; cuando nos adentramos en l parece no acabar nunca. Justo cuando aprendemos un significado descubrimos que una mano negra ha aadido otras dos o tres acepciones para la misma palabra. Tambin descubrimos que prcticamente no hay una regla sin su correspondiente excepcin. Cuando aprendemos que algo no se puede hacer, hay siempre algn truco escondido para hacerlo, y cuando nos dicen que es un lenguaje fuertemente tipado ("Strong type checking"), resulta completamente falso. A pesar de todo, ha experimentado un extraordinario xito desde su creacin. De hecho, muchos sistemas operativos [4], compiladores e intrpretes han sido escritos en C++ (el propio Windows y Java). Una de las razones de su xito es ser un lenguaje de propsito general que se adapta a mltiples situaciones. Para comprobar el xito e importancia de los desarrollos realizados en C++ puede darse una vuelta por la pgina que mantiene el Dr. Stroustrup al respecto: www.research.att.com. Tanto sus fervientes defensores como sus acrrimos detractores han hecho correr ros de tinta ensalzando sus cualidades o subrayando sus miserias, aunque todo el mundo parece estar de acuerdo en que es largo y complejo. Ha servido de justificacin para el diseo de otros lenguajes que intentan eliminar sus inconvenientes al tiempo que mantener sus virtudes (C# y Java por ejemplo), y una de sus ltima incorporaciones, las plantillas ( 4.12), ha sido origen de un nuevo paradigma de programacin (metaprogramacin). En mi opinin, cualquier lenguaje de propsito general que como C++, permita tocar ambos mundos, la programacin de bajo nivel y altos niveles de abstraccin, resultar siempre e inevitablemente complejo. Ocurre lo mismo con los lenguajes naturales que son tambin extraordinariamente complejos (esto lo saben bien los gramticos). Cualquier comunicacin entre humanos presupone una ingente cantidad de conocimientos y suposiciones previas entre los interlocutores. A pesar de lo cual, la comunicacin exacta y sin ambigedades entre dos personas no resulta fcil.

2 Consejos para mejorar el rendimiento

Lo mismo que en su ancestro, en el diseo del C++ prim sobre todo la velocidad de ejecucin del cdigo [4]. Tanto uno como otro representan los ejecutables ms rpidos que se pueden construir para una mquina y circunstancias determinadas. En este sentido, la nica alternativa de mejora es la codificacin manual, el "pulido" de determinadas rutinas (o de todo el cdigo) en ensamblador, aunque evidentemente esto es impracticable para aplicaciones medianamente grandes, a no ser que se disponga de todos los recursos y tiempo del mundo. Con todo, a pesar de ser un lenguaje intrnsecamente rpido, y de que los compiladores modernos son bastante "inteligentes" en este sentido (adoptan automticamente las decisiones que resultan en el cdigo de ejecucin ms eficiente), es mucho lo que puede hacer el programador para favorecer esta rapidez con solo adoptar algunas sencillas precauciones. Estos son los consejos: Use enteros (int) con preferencia sobre cualquier otro tipo de variable numrica. En especial en los contadores de bucles. Las operaciones con enteros son del orden de 10 a 20 veces ms rpidas que las de nmeros en coma flotante. Use operadores incremento y decremento ++/-- ( 4.9.1) Use variables de registro, en especial en los bucles crticos, sobre todo si son anidados ( 4.1.8b). Use aritmtica de punteros frente a subndices de matrices ( 4.2.2). En problemas de computacin numrica recuerde que el clculo de funciones trascendentes es por lo general muy lento. Use referencias para argumentos y valores devueltos en funciones, antes que objetos "por valor" ( 4.2.3) Al definir clases utilice al mnimo las funciones virtuales ( 1.4.4; 4.11.8a), as como los punteros a funciones-miembro ( 4.2.1g) o Tenga en cuenta lo sealado respecto al rendimiento al tratar de: o Sustituciones inline en funciones definidas por el usuario ( 4.4.6b) Preste atencin al modo de uso de aquellas funciones de librera que se presentan en dos versiones ( 5.1 Funciones y macros)

Los compiladores modernos permiten fijar que criterio de optimizacin ser dominante: La velocidad de ejecucin o el tamao. Tanto Borland C++ ( 1.4.3) como MS Visual C++ utilizan la misma convencin de llamada para este propsito (opciones -O2 o -O1 respectivamente). Por su parte, GNU gcc dispone de varias opciones de optimizacin. En particular, la opcin -Os adopta las medidas tendentes a reducir el tamao del cdigo resultante. Nota: Aparte de las decisiones de optimizacin que puedan adoptar automticamente los compiladores y las reglas de precaucin anteriores, las modernas "suites" ofrecen herramientas de anlisis que permiten conocer de forma objetiva como es la utilizacin de recursos dentro del programa, de forma que se puedan adoptar precauciones en las zonas que resulten ms costosas, y concentrar nuestros esfuerzos de optimizacin en la zonas donde resulten ms provechosos. El compilador GNU cpp dispone de la utilidad gcov, que ofrece estadsticas para analizar el rendimiento del cdigo. Entre otros datos: Cmo se ejecuta cada lnea de cdigo Qu lnea se est ejecutando actualmente. Cuanto tiempo consume cada seccin de cdigo Posibilidad de anlisis a nivel de fichero o de funcin

1.2.1 Lxico y conceptos fundamentales


"Este ha sido mi ltimo curso. Me acabo de jubilar despus de cerca de 40 aos dedicado a la enseanza. A pesar de la poca consideracin que mi profesin ha tenido econmica y socialmente, acabo con la misma ilusin con la que comenc. E igualmente convencido de la trascendencia de la tarea de educar. Slo me duele un poco el comprobar la menor eficacia que sta ha tenido estos ltimos aos. Cules han sido las causas? Ni la mentalidad hedonista que rehuye cualquier tipo de sacrificio, ni el permisivismo de los padres que satisfacen todos los caprichos de los hijos, ni los planes de estudios que se fundamentan en una visin ldica de la educacin, ni las polticas educativas con pretensiones de progresismo que priman el uniformismo sobre la bsqueda de la excelencia han favorecido en absoluto la cultura del esfuerzo, imprescindible en cualquier proceso personal de mejora, como es la educacin". Federico Gmez Pardo en "La voz del Lector" de "El Confidencial Digital" (22-06-2006) ECD.

1 Presentacin Creo que buena parte de la dificultad del principiante respecto a algunos conceptos del lenguaje C++, y de muchas otras reas de la ciencia informtica, proviene de un conocimiento incompleto o vago de algunos conceptos fundamentales. En realidad se trata de una cuestin semntica; de conocer el significado exacto de algunas palabras; de disponer de un vocabulario mnimo que sirva de soporte para entender el resto. Ocurre con frecuencia que los textos informticos estn plagados de trminos que supuestamente son de uso comn, pero que en realidad no lo son tanto y cuyo significado tampoco est claramente explicado en ningn sitio. Se dan por sabidos, pero las ms de las veces el estudiante es incapaz de verbalizar correctamente su significado. Es importante que este "corpus" mnimo sea conocido sin ambigedades, de forma que los conceptos construidos sobre l no tengan fisuras y resulten de una solidez conceptual a toda prueba. Estas cuestiones semnticas son evidentemente el objeto fundamental de los gramticos y de los puristas del lenguaje (aunque sean lenguajes artificiales como los de programacin), de forma que en el caso del C++, la primera preocupacin del Estndar es definir sin ambigedad una serie de trminos. Introducir aqu algunos de los que creo ms importantes, en la seguridad de que su conocimiento atae no solo al lenguaje C++, sino al acerbo cultural de cualquier interesado en esta disciplina.

2 Algunos conceptos RTFM Siglas por las que se conoce un principio universal y de gran inters en la ciencia informtica actual [5]. Escribir un programa es establecer el comportamiento de una mquina mediante una serie de algoritmos que definirn su funcionamiento ( 1.4). Segn el DRAE (Diccionario de la Real Academia Espaola de la Lengua) un algoritmo es un conjunto ordenado y finito de operaciones que permite hallar la solucin de un problema [4]. En informtica se utiliza en el sentido de un conjunto ordenado y finito de instrucciones que gobiernan el comportamiento de una mquina para conseguir un comportamiento determinado. Las instrucciones se expresan en un lenguaje artificial (inventado conscientemente por el hombre) denominado lenguaje de programacin.

Al llegar a cierto grado de madurez y universalidad algunos lenguajes son estandarizados, lo que significa que un comit internacional ( 1) se encarga de establecer sus reglas de uso. En el caso de los lenguajes (naturales o artificiales) estas reglas constituyen lo que se denomina sugramtica. El lenguaje C++ alcanz oficialmente esta madurez y universalidad en 1989, de forma que a partir de entonces, el Estndar C++ establece la gramtica del lenguaje que nos ocupa. Aunque todos los lenguajes de programacin tienen su "Gramtica", el enfoque respecto a la misma vara grandemente de unos a otros. En ciertos casos, como en C++, estas reglas son tremendamente estrictas, lo que tiene sus pros y sus contras. Por ejemplo, una gramtica rgida permite afinar mucho en los matices de lo que deseamos hacer; manipular los conceptos (principalmente los datos) con gran precisin, y ser advertidos de posibles errores involuntarios. Pero hay que estar muy atento a los detalles del cdigo, y hasta que no se tiene cierta prctica, los mensajes de aviso y errores del compilador pueden desesperar a cualquiera. Otro extremo est representado por los lenguajes de sintaxis ms o menos "borrosa". Por ejemplo JavaScript o Perl, en los que el compilador, o intrprete, pretende adivinar qu se supone que queremos hacer. Este comportamiento se conoce como DWIM ("Do what I mean") [ 7].

La gramtica se concreta en una serie de reglas y condiciones, aunque puede haber otras. Por ejemplo, de estilo. Las que imprescindiblemente debe cumplir un programa C++ para ser correcto, son de dos clases: semnticas (de significado) y sintcticas (de forma 1.3.1a). Justamente es el contenido de estas normas lo que hace que un programa C++ sea distinto de un programa Java o Fortran por ejemplo. Las reglas sintcticas son muchas, pero comienzan por las que definen el alfabeto del lenguaje. Es decir: el conjunto de grafos que pueden utilizarse para escribir su cdigo. C++ permite utilizar un alfabeto de 96 caracteres ASCII en la escritura de sus programas. De ellos 91 son los caracteres grficos (representables por un grafo) que se incluyen en la tabla; los cinco restantes son caracteres no representables: Espacio; tabulacin horizontal; tabulacin vertical; salto de formato y nueva lnea.

Caracteres grficos del alfabeto C++: a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 _ { } [ ] # ( ) < > % : ; . ? * + / ^ & | ~ ! = , \ "


La norma C++ establece que cuando en un programa no se respetan las formas (reglas sintcticas) el compilador debe lanzar un mensaje de error o indicacin de que no entiende lo que queremos decir. Por ejemplo, el caso de que olvidemos el punto y coma al final de una sentencia. Sin embargo, con las reglas semnticas no ocurre siempre as. El estndar distingue dos tipos de reglas de significado: diagnosticables y no diagnosticables. En cuanto a las primeras, denominadas reglas de significado diagnosticable ("Diagnosable semantic rules"), la Norma establece que en caso de infraccin, el compilador enviar al menos un mensaje de error. En el caso de las segundas no se requiere esta exigencia.

Nota: en principio, todo el conjunto de reglas semnticas definidas en el Estndar pueden considerarse de significado diagnosticable, a excepcin de aquellas que tienen una indicacin explcita de que no necesitan diagnstico.

Las reglas estn definidas de modo que cuando se establece una condicin diagnosticable y la forma del programa no cumple este requisito, deber generarse un mensaje de error correspondiente. Sin embargo, cuando se establece un requisito diagnosticable para un dato y este no se cumple durante la ejecucin, el Estndar no establece ningn comportamiento especial para el programa.

Comportamiento dependiente de la implementacin: Cuando el comportamiento de un programa correcto con datos correctos, depende de la implementacin. Este comportamiento debe estar documentado en cada implementacin. Comportamiento indefinido: Es el comportamiento de un programa en aquellas circunstancias o situaciones para las que el Estndar no especifica un comportamiento especfico. Sera el caso del comportamiento en runtime de un programa que recibe datos incorrectos. Observe que, segn lo indicado al principio, muchos casos de programas errneos no generan un comportamiento indefinido, sino un aviso de diagnstico. Implementacin: Un compilador concreto para una plataforma determinada. Lmites de implementacin: Restricciones impuestas a los programas por una implementacin. Programa correcto ("Well-formed"): Programa C++ construido de acuerdo con las reglas sintcticas de este lenguaje, con reglas de semntica diagnosticable, y que sigue la regla de una sola definicin ( 4.1.2). Un programa incorrecto ("Ill-formed") es el que no sigue los condicionantes anteriores. Un identificador es un conjunto de caracteres alfanumricos de cualquier longitud que sirve para identificar las entidades del programa. Los identificadores pueden ser combinaciones de letras y nmeros, y cada lenguaje tiene sus propias reglas que definen como pueden estar construidos ( 3.2.2 Identificadores C++). Nota: algunas combinaciones de caracteres, las denominadas palabras-clave ("Keywords"), tienen un significado especfico para el lenguaje y no pueden ser utilizadas en otro contexto ( 3.2.1).

Generalmente los identificadores se introducen en el programa para designar una entidad; entonces se dice de ellos que son un nombre. C++ permite utilizar el mismo identificador para designar entidades diferentes a condicin de que estn en mbitos ( 4.1.3) diferentes, o que sean funciones con distinto nmero y/o tipo de argumentos. Nota: otro tipo especial de identificadores lo constituyen las etiquetas; en vez de representar entidades sirven para significar sentencias [1]. Se utilizan para identificar el destino en sentencias de salto ( 4.10.4).

La introduccin de un nombre en el programa puede efectuarse mediante alguna de estas formas: Si el nombre representa a una entidad, mediante una declaracin ( 4.1.2). Si el nombre representa a una sentencia (es una etiqueta), mediante una sentencia etiquetada ( 4.10.1) o una instruccin de salto goto[2].

Que un mismo nombre utilizado en dos unidades de compilacin designe o no a una misma entidad, depende del tipo de enlazado que tenga el identificador en cada una de ellas ( 1.4.4). Cuando compilador encuentra un identificador en un programa, supone que es un nombre o una etiqueta y antes de realizar el "parsing" ( 1.4), intenta determinar a que entidad representa. Esta operacin, que asocia sin ambigedad cada nombre con una declaracin de dicho nombre, se denomina bsqueda de nombres ("Name-lookup"). Si al final del proceso no ha aparecido la declaracin correspondiente entonces el programa es incorrecto ("Ill-formed" ). Cuando el identificador corresponde a una funcin, puede ocurrir que el "Name-lookup" asocie un mismo nombre con ms de una declaracin. Es el caso de funciones sobrecargadas, en las que un mismo nombre corresponde a varias definiciones de la "misma" funcin. En estos casos, despus de esta primera bsqueda tiene lugar otro proceso denominado de resolucin de sobrecarga ("Overload resolutin" 4.4.1a) en el que se intenta averiguar a que definicin concreta corresponde el identificador. El proceso de bsqueda de nombres sigue un conjunto relativamente extenso de reglas que cubren todas las posibilidades que pueden presentarse; adems existen modificadores que alteran la forma de esta bsqueda, los denominados especificadores o modificadores de acceso. Ver en la hoja adjunta una somera descripcin del proceso ( Name-lookup).

El concepto de entidad es muy amplio; corresponde a: un valor; clase; elemento de una matriz; variable; funcin; miembro de clase; instancia de clase; enumerador; plantilla, o espacio de nombres del programa (ms detalles en 4.1.1). Un objeto es una entidad a la que corresponde una zona de almacenamiento [ 3], razn por la que se dice de ellos que tienen existencia real o fsica. Los objetos son introducidos en el programa mediante una declaracin, y creados mediante una definicin ( 4.1.2). Gozan de una serie de propiedades que vienen definidas desde el momento de su creacin, entre ellas est la duracin de su almacenamiento, que tiene influencia en su ciclo vital ("Lifetime"), y puede ser de tres tipos: esttica, dinmica y automtica ( 4.1.5). Otra propiedad no menos importante de los objetos es su tipo, que tambin viene determinado desde el momento de su creacin ( 2.2).

Las entidades del programa pueden tener otros atributos o propiedades cuya comprensin es igualmente importante: direccin (Lvalue) y valor (Rvalue). Sus caractersticas se detallan en el apartado correspondiente ( 2.1), pero adelantemos aqu que Rvalue se asigna al concepto de "valor"; algo que tenga valor es un Rvalue (o tiene esta propiedad). Por su parte Lvalue se asimila al concepto de direccin; espacio de almacenamiento que pueda recibir un valor [ 6]. Una entidad puede tener uno o ambos de estos atributos. Por ejemplo:

char func () { ... } char c = '5'; const int k = 2 + 3; La funcin func puede ser considerada un Rvalue en el sentido que devuelve un valor: tambin la variable c; la constante k, o la expresin 2 + 3 pueden ser consideradas Rvalues porque tienen o representan un valor. Tanto la variable c como la constante k son tambin Lvalues, en el sentido que ambas disponen de una direccin y un espacio de almacenamiento correspondiente (que en el primer caso puede ser alterado y en segundo no). Sin embargo, ni la funcin func, ni las expresins '5' o 2 + 3 pueden considerarse como tales porque no les corresponde una direccin o espacio de almacenamiento donde pueda colocarse un Rvalue. En este contexto tiene sentido decir que un Lvalue modificable es aquel que puede cambiar el valor contenido en su almacenamiento.

Una expresin es un conjunto de operadores, operandos y signos de puntuacin que especifican una computacin (suponen una secuencia de instrucciones concretas para la mquina); generalmente, como resultado de esta computacin producen un valor, aunque tambin, como consecuencia de ella pueden producirse otros efectos denominados efectos laterales ( 1.3.1).

1.3 Estructura de un programa


"The input of computation is energy and information; the output is order, structure, extropy" Kevin Kelly: "God Is the Machine" en Wired Magazine www.wired.com

1 Sinopsis La estructura de un programa es una cuestin que puede ser abordada desde varios puntos de vista, en este captulo consideramos solo dos: Componentes lgicos: se refiere a los diversos elementos que componen una aplicacin, desde los ms complejos hasta los ms simples. Si comparamos un programa con un edificio, los elementos iran desde el edificio como un todo, a los ladrillos (sus elementos ms pequeos) 1.3.1. Almacenamiento: se refiere a como estn alojadas sus partes en la mquina que lo ejecuta. Esta cuestin puede ser abordada desde dos perspectivas: Organizacin lgica. Comprende las caractersticas de los diversos tipos de almacenamiento que se distinguen en un programa C++ ( 1.3.2). Organizacin fsica. Toma en consideracin los diversos dispositivos fsicos utilizados por el programa para alojarse l mismo y sus datos ( 2.2.6).

1.3.1 Estructura lgica.


Part of the problem is that programming is hard to teach. Programming is a mixture of a highly technical skill and an aesthetic art. And thats a very difficult combination. James Maguire en su artculo "The 'Anti-Java' Professor and the Jobless Programmers". Basado en una entrevista con Robert Dewar, profesor emrito de Ciencia de la Computacin en New York.

1 Sinopsis Desde el punto de vista lgico, puede considerarse que los programas comprenden dos tipos de elementos diferentes: estructuras de datos y algoritmos. O dicho en otras palabras: datos, e instrucciones para su manipulacin. Su representacin codificada adopta dos formas: una entendible por la mquina (ejecutable y ficheros de datos) y otra entendible por el humano (fuente). Para el conjunto de ambas puede considerarse una escala conceptual que, si vamos de lo general a lo particular, podemos representarla como sigue: Nota: clasificaciones como la que aqu proponemos solo tiene una finalidad didctica; son un vehculo para introducir al lector en la comprensin global del concepto y no un asunto dogmtico e inamovible, ya que este tipo de asuntos depende grandemente del punto de vista que se adopte. Como botn de muestra, vaya por delante la definicin de lo que es un programa en palabras de Ellis y Stroustrup [63.3], que por lo dems ofrece un punto de vista muy interesante: "Un programa consiste en uno o ms ficheros enlazados juntos. Un fichero consiste en una secuencia de declaraciones " (ver Declaraciones y definiciones en 4.1.2). A continuacin admiten que esta afirmacin puede parecer extraa a aquellos acostumbrados a pensar en un programa como una serie de sentencias ejecutables acompaadas de las declaraciones de las variables correspondientes. Pero recuerdan que en C++, las sentencias estn contenidas en funciones, y que en realidad, el cuerpo de una funcin es en s mismo una declaracin (de la propia funcin). En consecuencia, puesto que las declaraciones pueden contener inicializadores, tambin deben ser consideradas como "ejecutable" (lo que hemos denominado algoritmo).

2 Aplicacin Comprende ejecutables y datos. Puede haber mltiples ficheros de ambos tipos (ficheros de datos y ejecutables).

3 Programa Parte de una aplicacin (cdigo) que puede cargarse y ejecutarse independientemente.

4 Fichero fuente:

Se llaman as (abreviadamente) los ficheros que contienen el cdigo fuente (ficheros .C / .CPP) escrito por el programador. Un "fuente" se compila de una vez, cuando recibe la accin del preprocesador ( 1.4.1), dando lugar a lo que tcnicamente se denomina unidad de compilacin ( 1.4.2). Un solo fuente puede ser dividido en mltiples ficheros, cada uno de los cuales puede contener varias funciones, aunque la inversa no es cierta: una funcin no puede ser dividida entre varios fuentes. La mayora de las aplicaciones de cierto porte ocupan ms de un fuente. Los diversos ficheros son creados y mantenidos por distintos programadores. Despus, los ficheros son compilados y enlazados para producir una aplicacin final. Es conveniente recordar que C y C++ se desarrollaron en ambientes UNIX, por lo que desde su cuna son lenguajes sensibles a las maysculas/minsculas ("case sensitive") [3]. Esto no representa inconveniente para los programadores acostumbrados a entornos Unix/Linux, pero suele ser fuente de errores para los que han desarrollado en Sistemas MS-DOS y Windows donde esta distincin no es tan importante. Por ejemplo, en C++ la declaracin int X, x; declara dos variables distintas.

5 Funcin: Una parte de un programa (subrutina) con un nombre, que puede ser invocada (llamada a ejecucin) desde otras partes tantas veces como se desee. Opcionalmente puede recibir valores (argumentos); se ejecuta y puede devolver un valor ( 4.4). main ( 4.4.4) es la primera funcin en cualquier programa C++; es llamada desde unas rutinas especiales "de inicio" que se incluyen automticamente en todo programa C++. Esta funcin es el punto de inicio del programa desde el punto de vista del programador (donde este toma el control).

6 Bloque Lista, que puede estar vaca, de sentencias delimitadas por corchetes { } ( 3.2.6). Desde el punto de vista sintctico, un bloque puede ser considerado como una sola sentencia (sentencia compuesta 4.10). Dentro de las posibilidades de memoria, los bloques pueden ser anidados a cualquier nivel (los bloques pueden contener otros bloques). El aspecto de los bloques "anidados" es como sigue: ... main { .... { ... // // // // // espacio global del fichero comienzo del bloque main espacio del bloque main bloque anidado espacio del bloque anidado

} .... }

// fin de bloque // fin del bloque main

7 Sentencia Si establecemos una analoga entre un lenguaje natural y un lenguaje computacional como C++, podemos afirmar que las sentencias("Statements") juegan en C++ el mismo papel que las oraciones en el lenguaje natural, y del mismo modo que una oracin gramatical es una palabra o conjunto de ellas, con que se expresa un sentido gramatical completo [4], las sentencias se componen de una o varias expresiones y tienen sentido computacional completo. La sentencia es la unidad lgica completa ms simple en un programa; en C/C++ terminan en punto y coma ;. Un caso especial es la expresin nula ( ; aislado). En el apartado 1.3.1a se describe su sintaxis y en el captulo 4.10 se ofrece su definicin formal y una clasificacin de los distintos tipos que existen en C++. Un caso especial lo componen las directivas de preprocesado ( 4.9.10), que suponen transformaciones en el fuente por parte del preprocesador ( 1.4.1).

8 Expresin Siguiendo en sentido ascendente de simplicidad en el lenguaje natural, despus de las oraciones estn las frases, Conjunto de palabras que basta para formar sentido, especialmente cuando no llega a constituir una oracin cabal [5]. Su equivalente en el lenguaje C++ sern las expresiones. Las expresiones son secuencias de tokens (operadores, operandos y elementos de puntuacin) que especifican una computacin; tienen sentido computacional en s mismas. Son los bloques de computacin ms simples con los que se construye un programa [6 5] aunque no pueden ejecutarse separadamente sino cuando forman una sentencia. Como ver el lector, la diferencia entre sentencia y expresin es algo arbitraria. Una o varias expresiones terminadas en punto y coma constituyen una sentencia. Desde el punto de vista lgico, cada sentencia se ejecuta de una vez (aunque sus expresiones sean evaluadas individualmente siguiendo ciertas reglas ). En el programa suele estar muy claro cual es el orden de ejecucin de sus sentencias, no as de sus expresiones dentro de ellas, toda vez que el Estndar es permisivo en algunos aspectos de detalle. Es importante sealar que, en contra de lo que ocurre en otros lenguajes, en C/C++, el final de lnea no significa necesariamente el fin de la expresin, de forma que esta puede ocupar ms de una lnea (en realidad el compilador ignora los pares de caracteres CR/LF que aaden los editores al final de lnea). Por ejemplo: la expresin int x = 5*(4+(3/2-1))

puede ser escrita como: int x = 5* ( 4+ (3/2-1) );

Los conocedores de otros lenguajes advertirn que en C++ no tiene sentido la barra inclinadas \ con que otros lenguajes indican que la siguiente lnea de cdigo es en realidad una continuacin de la anterior. Se excepta naturalmente el caso de constantes de cadena, donde si es preciso indicar al compilador que, en su caso, la cadena contina en la lnea siguiente. Ejemplo, aunque es correcto escribir: int 5; En cambio es incorrecto poner char* string = "Las constantes de cadena pueden tener cualquier longitud"; En este caso es necesario indicar al compilador que la cadena contina en la lnea siguiente: char* string = "Las constantes de cadena pueden tener \ cualquier longitud"; x =

Del mismo modo que los gramticos distinguen varios tipos de frases (hecha, musical, proverbial, etc), los informticos distinguen tambin varios tipos de expresiones. Los ejemplos que siguen son meramente ilustrativos y no pretenden ser una relacin completa: De asignacin: Si tienen uno, o varios, operadores de asignacin ( x += 3; z = y = x/2; Declarativas: Si dan a conocer un identificador al compilador ( extern int funcX(int x, char c); class MiClase; Definiciones: Establecen la creacin de un objeto reservndole espacio fsico. Ejemplo: int x = 2; MiClase c1 (x, y, z); Condicionales: Definen alternativas de accin en el programa. Ejemplos: int y = 6 ? 7: 8; 4.1.2). Ejemplos: 4.9.2). Ejemplos:

if (salida = 'S') break; De salto: Obligan al programa a seguir en un sitio distinto de la secuencia natural (la siguiente sentencia). Ejemplos: break; continue; Bitwise. De manejo y comparacin de bits ( x | y; x << 2; Las expresiones pueden producir un Lvalue ( valor. Ejemplo: int x; x = 2; 2.1.5), un Rvalue ( 2.1.6) o ningn 4.9.3). Ejemplos:

// no produce ningn valor // produce un valor (resultado)

Adems de producir un valor, las expresiones pueden tener efectos laterales (tanto si producen un valor como si no lo producen). Ejemplo: x = ++a; Adems de producir un valor (un resultado 1: Incrementar en 1 el valor de a, 2: Asignar el valor modificado de a a la variable x. El "resultado" de la expresin coincide precisamente con el valor de x [2]. Las expresiones se evalan siguiendo ciertas reglas de conversin [1], agrupamiento, asociatividad y precedencia ( 4.9). Estas reglas dependen de varios factores: el operador; la naturaleza de los operandos utilizados en cada caso, y de la presencia de parntesis en la expresin ( 4.9.0a). En el apartado 1.3.1b se describe completamente su sintaxis formal as como su precedencia y asociatividad. Su sintaxis muestra que las expresiones son definidas recursivamente, y que las subexpresiones pueden ser anidadas sin ningn lmite formal. 4.9.2), esta expresin tendra dos efectos laterales:

9 Token Los tokens ( 3.2) son los elementos en que el preprocesador desmenuza el cdigo fuente. En un lenguaje de programacin, los tokens son el equivalentes al conjunto de las palabras y signos de puntuacin en el lenguaje natural escrito.

En el lenguaje C++ estas "palabras" pueden ser de varios tipos: palabras clave, identificadores, constantes, operadores, signos de puntuacin (puntuadores) y comentarios (son eliminados). Los tokens estn separados por elementos de separacin que reciben el nombre genrico de separadores ("Whitespaces").

1.3.1a. Sentencias
1. Introduccin Al igual que los lenguajes naturales [2], los lenguajes computacionales tienen una gramtica, es decir, un conjunto de reglas que describen los elementos que componen el lenguaje y la forma correcta de utilizarlos ( 1.2.1).. Una parte de la gramtica, la sintaxis, se ocupa de las reglas para combinar adecuadamente los elementos del lenguaje de forma que tengan un sentido.. En el caso de los lenguajes naturales, la sintaxis ensea a formar las oraciones y expresar conceptos.. En el caso de los lenguajes computacionales, ensea a construir sentencias que describan operaciones correctas de un computador.

2. Sintaxis de las sentencias C++ Si nos referimos concretamente a la sintaxis, sus reglas pueden ser expresadas en forma de una lista, que adecuadamente interpretada, describe estas reglas sintcticas.. A continuacin se expone la sintaxis permitida en C++ para las sentencias ("Statements"). . Junto a una traduccin, ms o menos afortunada al espaol [1], hemos incluido el original ingls para que no quepa la ms mnima ambigedad al respecto, ya que el rigor y el "purismo" son imprescindibles al tratar estos aspectos gramaticales del lenguaje. Una lista de este tipo describe la sintaxis de lo que se denomina una Gramtica Independiente del Contexto de Chomsky [3]. En este caso define la sintaxis de una sentencia (primera entrada de la lista).. Nos describe que una sentencia est compuesta por los elementos que siguen en la lista (sangrados).. Aqu tendramos que interpretar:. Una sentencia est compuesta por una sentencia etiquetada, o una sentencia compuesta, o una sentencia-expresin, Etc.. A su vez, las entradas sucesivas de la lista (en azul), describen la sintaxis de cada elemento.. Por ejemplo, una sentencia compuesta est formada a su vez por una lista-de-declaraciones que puede ir seguida de una listade-sentencias; todo ello entre corchetes { } (el smbolo <xxx> indica que el componente xxx es opcional).. A su vez, la lista-de-declaraciones est formada por ... Etc. Etc. Resulta evidente que muchas de estas definiciones son recursivas. . Por ejemplo, vemos que una lista-de-declaraciones est formada por una declaracin o una lista-de-declaraciones seguida de una declaracin. Es decir, una lista-de-declaraciones puede contener a su vez otra lista de declaraciones. La lista indica que una sentencia-expresin est constituida por una expresin seguida de un punto y coma (;), aunque la expresin es opcional.. Quiere esto decir que un punto y coma aislado es una sentencia-expresin sintcticamente correcta en C++; es lo que se denomina sentenciaexpresin nula. Nota: Como este "libro" est dirigido principalmente a estudiantes de C++ que necesiten del lenguaje para sus aplicaciones prcticas, debemos advertir aqu que estas cuestiones "gramaticales" son estrictamente formales, lo que dicho sencilla y llanamente, significa que de

una de estas listas no es posible sacar ninguna idea concreta de qu es una sentencia, ni la menor indicacin de para qu sirven.. Son simplemente una indicacin exquisitamente exacta de cmo se usan desde el punto de vista formal, lo que para el programador de a pie tiene escaso o nulo valor [4]. sentencia: sentencia etiquetada sentencia compuesta sentencia-expresin sentencia de seleccin sentencia de iteracin sentencia de salto sentencia ensamblador declaracin simple bloque-intento ( 1.6) sentencia etiquetada: identificador : sentencia case expresin constante : sentencia default : sentencia sentencia compuesta: ( (labeled-statement) (compound-statement) (expression-statement) (selection-statement) (iteration-statement) (jump-statement) (asm-statement) (declaration) (try-block)

3.2.6)
(<declaration-list> <statement-list>)

{ <lista-de-declaraciones> <lista-de-sentencias> } lista-de-declaraciones: declaracin lista-de-declaraciones. declaracin lista-de-sentencias: sentencia lista-de-sentencias. sentencia sentencia-expresin: <expresin> ; sentencia ensamblador (

4.10):

asm tokens nueva-lnea (newline) asm tokens; asm { tokens; <tokens;>= <tokens;>} sentencia de seleccin (

4.10.2):

if ( expresin ) statement if ( expresin ) statement else statement switch ( expresin ) statement sentencia de iteracin (

4.10.3):

while ( expresin lgica ) sentencia do sentencia while ( expresin ) ; for (sentencia de inicio-for <expresin> ; <expresin>) sentencia sentencia de inicio-for: sentencia-expresin declaracin simple sentencia de salto ( goto identificador ; continue ; break ; return <expresin> ;

4.10.4):

1.3.1b Sintaxis de las expresiones C++


1 Sinopsis: En este epgrafe se incluye una descripcin formal de la gramtica permitida a las expresiones en C++. Como puede verse, las expresiones son definidas recursivamente, de forma que las subexpresiones pueden ser anidadas sin ningn lmite formal, aunque quizs el compilador pueda reportar un error de lmite de memoria si no puede compilar una expresin muy compleja. Como en el caso de las sentencias ( 1.3.1a), en los casos que pudieran resultar dudosos, junto a la traduccin al espaol se ha incluido el original ingls.

Expresiones primarias:

(primary-expression):

literal this :: identificador :: nombre de funcin-operador :: nombre cualificado (expresin) expresin-de-identificacin literal: constante entera

(operator-function-name)

(integer-constant)

constante carcter constante fraccionaria cadena de caracteres expresin-de-identificacin: nombre (no cualificado) nombre-cualificado nombre (no cualificado): identificador nombre de funcin-operador conversion-function-name ~ nombre-de-clase nombre-cualificado nombre-cualificado:

(character-constant) (floating-constant) (string-literal)

(qualified-name)

nombre-cualificado-de-clase :: nombre expresiones: (postfix-expression)

primary-expression postfix-expression [ expression ] postfix-expression ( <expression-list> ) simple-type-name ( <expression-list> ) postfix-expression . name postfix-expression -> name postfix-expression ++ postfix-expression -const_cast < identificacin-de-tipo > ( expresin ) dynamic_cast < identificacin-de-tipo > ( expresin ) reinterpret_cast < identificacin-de-tipo > ( expresin ) static_cast < identificacin-de-tipo > ( expresin ) typeid ( expresin ) typeid ( nombre-de-tipo ) lista de expresiones: (expression-list)

expresin-de-asignacin lista-de-expresiones , expresin-de-asignacin expresin-unitiaria: (unary-expression)

postfix-expression ++ expresin unitaria - - expresin-unitaria operador-unario expresin-de-modelado sizeof expresin-unitaria

sizeof ( nombre-de-tipo ) expresin de asignacin-de-memoria expresin-de-desasignacin

(allocation-expression)

operador-unario: (alguno de los siguientes) & * + - ! ~ expresin-de-asignacin-de-memoria: (allocation-expression)

<::> new <placement> new-type-name <inicializador> <::> new <placement> (nombre-de-tipo) <inicializador> placement: (lista-de-expresiones ) new-type-name: especificadores-de-tipo <declarador-new> declarador-new: (new-declarator)

ptr-operator <new-declarator> declarador-new [ <expresin> ] expresin de desasignacin: (deallocation-expression) <::> delete expresin-de-modelado <::> delete [ ] expresin-de-modelado expresin-de-modelado: (cast-expression)

expresin-unitaria ( nombre-de-tipo ) expresin-de-modelado expresin-pm: (pm-expression) expresin-de-modelado pm-expresion .* expresin-de-modelado pm-expresion ->* expresin-de-modelado expresin-multiplicativa: (multiplicative-expression)

expresin-pm expresin-multiplicativa * pm-expression expresin-multiplicativa / pm-expression expresin-multiplicativa % pm-expression expresin-aditiva: (additive-expression)

expresin-multiplicativa expresin-aditiva + expresin-multiplicativa expresin-aditiva - expresin-multplicativa expresin-de-desplazamiento: (shift-expression):

expresin-aditiva expresin-de-desplazamiento << expresin-aditiva expresin-de-desplazamiento >> expresin-aditiva expresin relacional: (relational-expression) expresin-de-desplazamiento expresin-relacional < expresin-de-desplazamiento expresin-relacional > expresin-de-desplazamiento expresin-relacional <= expresin-de-desplazamiento expresin-relacional >= expresin-de-desplazamiento expresin-de-igualdad: (equality-expression) expresin-relacional expresin-de-igualdad == expresin-relacional expresin-de-igualdad != expresin-relacional expresin-AND: (AND-expression)

expresin-de-igualdad expresin-AND & expresin-de-igualdad expresin-de-OR-exclusivo: (exclusive-OR-expression) expresin-AND expresin-de-OR-exclusivo ^ expresin-AND expresin-de-OR-inclusivo: (inclusive-OR-expression) expresin-de-OR-exclusivo expresin-de-OR-inclusivo | expresin-de-OR-exclusivo expresin-lgica-AND: (logical-AND-expression) expresin-de-OR-inclusivo expresin-lgica-AND && expresin-de-OR-inclusivo expresin-lgica-OR: (logical-OR-expression)

expresin-lgica-AND expresin-lgica-OR || expresn-lgica-AND expresin condicional:

expresin-lgica-OR expresin-logica-OR ? expresin : expresin-condicional expresin-de-asignacin: (assignment-expression) expresin condicional expresin-unitiaria operador-de-asignacin expresin-de-asignacin operador-de-asignacin (alguno de los siguientes): = << *= => /= >= %= &= += ^= -= |=

expresin: expresin-de-asignacin expresin, expresin-de-asignacin expresin-constante: expresin-condicional

1.3.2 Almacenamiento
Nota: en este captulo tratamos algunos aspectos lgicos del almacenamiento de un programa en una computadora (cdigo y datos). En el captulo 2.2.6 se amplan algunos conceptos relativos al almacenamiento de los datos que incluyen detalles sobre el soporte fsico utilizado.

1 Prembulo Los aspectos concretos de almacenamiento de un programa dependen de la plataforma, no existe un modelo nico. Por ser con mucho el ms extendido, nos referiremos aqu al modelo de almacenamiento tpico de un programa ejecutable estndar (no una librera dinmica [ 0] ) en el entorno Windows 32. Modelo que, salvo algunos detalles, puede aplicarse a otras plataformas modernas.

2 El proceso de carga En el entorno Windows-32, cuando el Sistema carga un ejecutable (fichero-imagen) desde la memoria externa (disco), se inicia un proceso bastante complejo hasta que sus distintos mdulos, que aqu se llaman segmentos, quedan alojados en las posiciones de memoria asignadas por el Sistema. Nota: Los distintos segmentos no necesariamente resultan contiguos entre s, pero las entidades de cada segmento s estn dispuestas de forma contigua dentro de ellos.

Para entender el proceso, recordemos que la mquina no entiende de conceptos tales como a = b + 1, para el procesador no existen las variables a o b. En consecuencia, la actuacin combinada del compilador y el linker debe reducir la expresin anterior a algo como "copia en el registro R el contenido de las n posiciones de memoria a partir de la posicin x (direccin de la variable b); smale una unidad, y copia el resultado en n posiciones de memoria a partir de la posicin z(direccin que corresponder a partir de ahora a la variable a)". Toda esta informacin se encuentra codificada en binario en el fichero imagen, pero el problema es que el enlazador no puede conocer de antemano en qu posiciones de memoria ser cargado el programa para su ejecucin (en realidad puede ocupar posiciones distintas en cada ejecucin). En consecuencia, las posiciones x y zconsignadas en el fichero imagen, no son posiciones absolutas sino relativas a una posicin inicial que ser asignada por el Sistema en el momento de la carga. La consecuencia final es que, adems del acomodo en memoria de los distintos mdulos, la utilidad de carga debe recalcular las direcciones contenidas en la imagen, sumndoles un desplazamiento inicial ("offset") segn el punto de inicio del segmento correspondiente. Recuerde que en el ejecutable no existen identificadores (nombres), solo direcciones de memoria. Es frecuente utilizar la siguiente terminologa: Direccin base BA ("Base Address") de un fichero imagen cargado en memoria, es la direccin de inicio. Direccin virtual relativa RVA ("Relative Virtual Address") de un objeto en un fichero imagen, es el valor de su direccin despus de cargado en memoria menos la direccin base del fichero imagen. Direccin virtual VA ("Virtual Address") de un objeto en un fichero imagen cargado en memoria, es el valor de su direccin. La razn de que se denomine "virtual" es porque el Sistema Operativo crea un espacio de direcciones distinto para cada proceso que corre en el sistema. Este espacio es independiente de la memoria fsica, ya que puede utilizar memoria virtual (en disco).

La disposicin final de los mdulos en memoria sigue un esquema como el de la figura, pero en un SO moderno no debe pensarse en la imagen cargada en memoria como un todo indivisible. De hecho, el segmento de cdigo es bastante independiente del de datos (en la figura lo hemos separado deliberadamente). Puede a su vez estar constituido por un solo trozo o por varios que pueden ser cargados en memoria de forma no simultanea, solo cuando se necesitan (carga bajo demanda). Adems, puede ocurrir que los distintos segmentos obtengan distintos permisos de uso por parte del Sistema Operativo. Por ejemplo, la aplicacin propietaria puede tener permiso de solo lectura ("read-only") sobre el segmento de cdigo, y de lectura-escritura para el resto. Sin embargo, en las libreras dinmicas (compartidas), el segmento de cdigo puede tener autorizacin de lectura para cualquier proceso que lo solicite..

Montn local (local heap) Pila (Stack)


Datos estticos no inicializados Datos estticos inicializados

3 Segmento de cdigo

En la parte ms baja del espacio ocupado por la aplicacin se sita el cdigo del programa, denominado tambinsegmento de cdigo ("Code segment") contiene el cdigo (algoritmo) ejecutable y todos los valores que han podido ser resueltos en tiempo de compilacin, como es el caso de algunas constantes que no reciben un Lvalue. Recordemos que en esta zona est el cdigo de todas las funciones (incluyendo las funciones-clase 4.11.5), y que los punteros-a-funcin sealan precisamente direcciones de este segmento.

Cdigo del programa (code segment)

En realidad no necesariamente existe un segmento de cdigo para cada instancia del programa en ejecucin. Por razn de eficiencia y economa de espacio, existen casos como el de las mencionadas libreras dinmicas (DLLs), en los que varias instancias de un mismo programa comparten el mismo segmento de cdigo (por supuesto cada instancia tiene su propio segmento de datos). Cuando ocurre esto se dice que el cdigo es reentrante y puede ser utilizado simultneamente por varias instancias sin peligro de corrupcin debido precisamente a la independencia de los datos de cada una de ellas.

4 Segmento de datos Donde termina el segmento de cdigo, comienza una zona destinada a datos, es el denominado segmento de datos ("Data segment") o memoria local, aunque tampoco debe pensarse en el segmento de datos como un todo de tamao fijo e indivisible. Est organizado en cuatro subzonas que no necesariamente son contiguas (puede que lo sean), con caractersticas particulares y distintas en funcin del tipo de dato y cmo son manejados por el programa. Durante el proceso de carga, el Sistema les asigna el espacio necesario dentro de la memoria total disponible. A continuacin, cuando se inicia el proceso, estas zonas son inicializadas. Las zonas de datos estticos permanecen constantes en tamao a lo largo de la vida del programa, precisamente porque aqu se guardan variables y constantes cuyo tamao es conocido en tiempo de compilacin. Pero la pila y el montn, pueden variar de tamao durante la ejecucin porque guardan objetos que son conocidos en runtime. En el primer caso (la pila) porque ah se guardan los valores de las funciones, y en el caso de la invocacin de funciones con gran nivel de anidamiento ( 4.4.6b) esta estructura puede crecer indefinidamente. En el caso delmontn, puede ocurrir que en tiempo de ejecucin el proceso demande ms memoria para datos persistentes, espacio que ms adelante puede ser liberado de nuevo (3.4 ). En ambos casos, el tamao de estas zonas puede aumentar y disminuir, y consecuentemente, la memoria total demandada al Sistema por el proceso en ejecucin. Nota: piense que un Sistema Operativo moderno asigna espacio segn demandan sus aplicaciones [4]. Cuando no hay memoria interna disponible, la simula mediante un mecanismo de memoria virtual ( H5.1), que utiliza el disco para estos menesteres. Tericamente el Sistema puede proporcionar memoria a una aplicacin mientras exista espacio en disco, pero tambin puede prevenirse que una aplicacin no pueda asignar memoria por encima de un cierto valor (HEAPSIZE 1.4.4a). La subzona que le corresponde a un dato concreto depende de las siguientes caractersticas: Que el dato sea conocido en tiempo de compilacin o en runtime. Que el dato sea persistente (esttico) o se destruya cuando el programa sale del bloque en que fue definido (automtico)

Nota: algunas caractersticas de los elementos descritos en este captulo, por ejemplo su tamao, pueden ser establecidos mediante las rdenes oportunas al enlazador ("Linker" 1.4). Ver al respecto: Fichero de definicin ( 1.4.4a).

4.1 Datos estticos Son objetos cuya vida se extiende durante todo el programa. Tienen existencia semntica y fsica (disponen de espacio de almacenamiento) antes que el programa inicie su ejecucin y terminan al

finalizar este. Corresponden a entidades declaradas fuera de cualquier funcin (mbito de fichero) o de mbito de funcin que han sido declaradas estticas. Pueden ser de cualquier tipo y se almacenan en una zona especial del segmento de datos. Seran el caso de los objetos sealados en el ejemplo: static int x = 12; // Esttica iniciada static int y; // dem no iniciada int X = 10; // Ok. tambin esttica iniciada int Y; // dem no iniciada ... void foo (int x) { static int n1 = 22; // Esttica iniciada static int n2; // Esttica no iniciada ... } Nota: aparte de los "estticos", existe otro tipo de datos persistente, que son almacenados en otro sitio y declarados de modo distinto. Son los objetos del montn que se describen ms adelante . Aparte de la zona de almacenamiento y su forma de declaracin, se diferencian de los estticos en que, aunque son de duracin indefinida, esta es iniciada y cancelada por el programador a voluntad. Resulta as que desde el punto de vista de la persistencia, en los programas C++ se distinguen tres tipos de entidades: Objetos automticos: Se almacenan en la pila y tienen duracin de bloque de sentencia. Se dice de ellos que tienen almacenamiento dinmico. Objetos estticos: Tienen la misma duracin que el programa. Se dice de ellos que tienen almacenamiento esttico. Objetos del montn. Tienen duracin entre dos instantes a voluntad del programador: el momento de su creacin y el de su destruccin. Se dice de ellos que tiene almacenamiento asignado ("Allocated storage").

4.1a Datos estticos inicializados Esta primera zona contiene datos estticos inicializados. Es decir, que reciben un valor explcito en el cdigo del programa. Suelen ser cadenas literales y entidades numricas, aunque pueden ser de cualquier tipo. Es tambin el caso de objetos estticos (instancias de clases) cuya inicializacin corre a cargo del constructor correspondiente. Por ejemplo, los iostreams de la Librera Estndar C++ de E/S ( 5.3)

4.1b Datos estticos no inicializados Anlogos a los anteriores pero sin inicializacin. Hemos indicado que en cualquier caso, dispongan de inicializacin explcita o no, los objetos estticos tienen existencia fsica antes que se inicie la ejecucin del programa. A este respecto, el Estndar establece que todas las variables estticas que no hubiesen sido inicializadas se inicializan a 0 cuando el segmento de datos es creado en memoria. Nota: Cuando en los textos informticos se dice coloquialmente "guardar datos en el segmento", se refieren a alguna de estas dos zonas de datos estticos.

4.2 Pila ("Stack")

Es un rea muy importante manejada directamente por la UCP para alojar datos durante la ejecucin del programa. Su nombre deriva de su propio mecanismo de funcionamiento. Es un almacn de datos contiguos del tipo LIFO ("Last In First Out" 1.8). Frecuentemente es comparada con una pila de platos; el ltimo en ser colocado es el primero en ser retirado. Se usa para muchas cosas, por ejemplo, aqu se almacenan las variables locales automticas y los datos involucrados en el mecanismo de invocacin de funciones, de forma que si se utilizan muchas de estas invocaciones forma anidada o recursiva, la pila crece. En algunos sistemas, la pila y el montn son contiguos y el crecimiento desmesurado de la pila puede llegar a sobrescribir el rea inferior del montn. Ms informacin al respecto en: (

4.4.6b Carga y descarga de funciones).

Los movimientos en el stack son generalmente rpidos, a veces basta una simple instruccin del procesador para almacenar o borrar algo en la pila. Los objetos colocados en ella se asocian a una duracin automtica . El trmino se refiere a que es el compilador el que determina cuando se destruyen. El lenguaje C++ se caracteriza por hacer un uso extensivo de la pila (muchos objetos son "automticos" por defecto) y el mecanismo de invocacin de funciones se basa en su utilizacin. Decimos que C++ es un lenguaje orientado a la pila. La localizacin y desalojo de variables de la pila se realiza de forma automtica (son decisiones tomadas por el compilador), no obstante, la directiva register ( 4.1.8b) permite indicar que algunas variables que normalmente iran en esta zona, sean alojadas en los registros del procesador. Precisamente este "automatismo" hace que la llamada explcita al destructor de objetos que hayan sido construidos en esta zona sea extremadamente peligroso, ya que si se realiza antes de que el objeto salga de mbito, el destructor ser llamado de nuevo cuando sea liberado el marco de la pila correspondiente a dicho mbito. A cambio la pila presenta la comodidad que supone la destruccin automtica de los objetos alojados en ella cuando salen de mbito (con la liberacin de la memoria correspondiente), lo que supone que no hay peligro de prdidas inadvertidas porque el programador olvide destruir el objeto. Es el siguiente caso: class MiClase { ......} ... { // un mbito cualquiera... .... MiClase objeto1; .... } // Ok. objeto1 es destruido al llegar a este punto

4.3 Montn local ("Local heap") Es un rea fija de memoria, asignada en runtime por las rutinas de inicio antes de que comience la ejecucin de main ( 4.4.4). Se usa para la asignacin dinmica de memoria. Por ejemplo, cuando pedimos al programa que asigne memoria mediante malloc, o cuando creamos un nuevo objeto con el operador new ( 4.9.20). Por razn de su origen y comportamiento, a los objetos almacenados es esta zona se les denomina "objetos del montn" ("Heap objects"), "de memoria dinmica" ("Dynamic memory") o que estn en "almacenamiento libre" ("Free store"). Las asignaciones de memoria del montn son generalmente ms lentas que las de pila. Adems los objetos situados en este rea tienden a ser persistentes [3]; se mantienen hasta que el

programador decide su destruccin, con la liberacin consiguiente de la memoria previamente asignada; por ejemplo con una llamada al destructor de una clase, la funcin free o la palabra delete. Nota: esta lentitud en el manejo de los objetos del montn es determinante en el rendimiento del programa, de forma que la agilidad total de un compilador dependen grandemente de su eficacia en el manejo de la memoria dinmica.

El uso de la pila o del montn supone una eleccin de prioridades entre velocidad de creacin, de liberacin de almacenamiento y automatismo, frente a un mayor control. En algunas circunstancias estos asuntos son de tener en cuenta. Puede ser conveniente sacrificar la flexibilidad y automatismo (que ofrece la pila) porque prefiramos controlar exactamente el tamao y tiempo de vida de un objeto (cualidades que ofrece el montn). En C clsico existen varias funciones de librera que permiten asignar y rehusar espacio en el montn; son las funciones malloc( ), calloc( ),realloc( ), y free( ) que estn tambin disponibles en C++, aunque este tiene un sistema propio de ms fcil uso con las palabras clave new ydelete.

4.3.1 Inconvenientes del montn El mecanismo proporcionado por C++ para manejo de esta zona de memoria es muy simple: podemos afirmar que bsicamente se limita a asignar memoria con new y a rehusarla con delete. Sin embargo esta simplicidad paga un precio: Existen dos inconvenientes que pueden causar grandes problemas y quebraderos de cabeza a los programadores C++. Nos referimos a los peligros de prdidas y de fragmentacin de memoria. El primero se presenta cuando el programador olvida rehusar algn trozo de memoria previamente asignado cuando ya no es necesario. Como el compilador no tiene por s mismo noticia de la duracin de los objetos alojados en el montn, es el programador el que debe ocuparse de su destruccin cuando no sean ya necesarios. De lo contrario, si se olvida de hacerlo se producirn prdidas de memoria; zonas de memoria no utilizadas pero que el programa las considera no utilizables. Es el caso del siguiente ejemplo: class MiClase { ......} ... { // un mbito cualquiera... .... MiClase* obj1Ptr = new MiClase; .... } /* Peligro!! prdida irremediable de memoria a menos que se incluya la sentencia delete obj1Ptr; antes del corchete de cierre */

No piense el lector que puede curarse fcilmente de este peligro teniendo simplemente "buena memoria", es decir, acordndose de destruir todos los objetos previamente creados. Existen circunstancias insidiosas contra las que se requiere cierta perspicacia y entrenamiento. Para caer en la trampa pueden bastar estas "inocentes" sentencias (ver una explicacin en 3.2.3f): char* a="Capitulo-1"; char* b="Capitulo-2"; ...

a=b; En otros casos el motivo de la prdida es un poco ms sofisticado (ver ejemplo:

4.11.2d2).

Como consecuencia, las prdidas "misteriosas" de memoria son muy frecuentes en los programas C++. En especial el uso de punteros en procesos de lgica muy intrincada puede ser una autntica pesadilla en cuanto a su depuracin. Aunque raros, existen algunos programas comerciales que ayudan al programador en la investigacin de este tipo de problemas, son denominados "Leakage detectors" en la literatura inglesa [1]. Adems, la Librera Estndar C++ ha adoptado tambin alguna medida al respecto (Punteros inteligentes 4.12.2b1). Nota: en algunos lenguajes como JAVA o C#, donde al contrario que en C/C++ la velocidad no es un asunto primordial, esto se evita con el recolector de basura (GC "Garbage collector"), que se ocupa de liberar esta memoria cuando comprueba que no ser ms necesaria. Sin embargo esta "comodidad" tiene un costo en tiempo de ejecucin. Aunque C++ permite que uno pueda construir su propio recolector de basura [ 2], no es una caracterstica incluida en el lenguaje de forma estndar, porque como hemos sealado en mltiples ocasiones, la rapidez de ejecucin es una de sus premisas de diseo.

El segundo inconveniente es la fragmentacin cuando se solicitan y rehsan sucesivamente muchos trozos de memoria. En estos casos puede ocurrir que en algn momento no exista un trozo de memoria contigua suficientemente grande como para alojar un objeto de un tamao determinado (algo parecido a lo que ocurre con la fragmentacin de disco). La solucin al problema son los denominados compactadores del montn ("Heap compactor") que en algunos lenguajes se preocupan de mantener juntos y contiguos los trozos ocupados, manteniendo contigua la zona libre. Naturalmente esto tiene tambin su costo en tiempo de proceso, pues requiere que mientras se realizan los movimientos de bloques de memoria (que se realiza en un hilo -thread- de segundo plano 1.7), se congele la actividad del programa, en especial el uso de punteros a tales zonas, que deben ser reasignados despus de los movimientos. Para conseguirlo, la memoria se utiliza a travs de los denominados manejadores de memoria ("Memory handles"), y del mencionado mecanismo de bloqueo, que alterna la actuacin del compactador con la posible utilizacin de punteros. Para mitigar los problemas antes mencionados, existen libreras comerciales para C++ bajo el nombre genrico de gestores de memoria ("Memory managers"). Son herramientas de runtime, que ayudan a prevenir y detectar algunos errores corrientes en el uso del montn. Por ejemplo, escribir ms all del final de la memoria correspondiente a un objeto, olvidar destruirlo, o intentar borrarlo dos veces [5].

4.3.2 En realidad hay varios "montones" (heaps) segn se considere la cuestin a nivel local (de aplicacin) o a nivel global (del Sistema Operativo). El mecanismo de funcionamiento (para BC++) y la nomenclatura utilizada en cada caso son como sigue: Local heap: (montn local), al que hemos hecho referencia solo para nuestra aplicacin o librera. ; zona de memoria disponible

El mximo de bloques de memoria que pueden situarse en el montn local es de 64K menos el tamao de la pila (stack) y de las zonas de variables globales (estticas). Por esta razn el montn

local est mejor adaptado para bloques de memoria pequeos (de 256 bytes o menos). El tamao por defecto para el montn local es 8 K, pero puede cambiarse (en el fichero .DEF). Observe que este lmite se refiere a 64K bloques, no 64K Bytes. De hecho los bloques pueden ser todo lo grandes que se necesite siempre que exista memoria disponible. Cuando la memoria fsica se agota, el sistema signa memoria virtual , pero cuando el montn es muy grande, el sistema se ralentiza notablemente, ya que el mtodo de asignacin de memoria dinmica del compilador es bsicamente un mecanismo de bsqueda de un bloque libre (del tamao adecuado para la demanda del momento), en una cadena en la que se alternan zonas libres y ocupadas. Global heap: (montn global), zona de memoria disponible para todas las aplicaciones. Aunque el montn global puede contener bloques de cualquier tamao, est pensado para bloques grandes (de 256 bytes o ms). Bajo el sistema Windows, en modos normal y 386 extendido, cada bloque del montn global necesita de un extra de al menos 20 bytes. En este sistema existe adems una limitacin de 8192 bloques para el montn global, de los cuales solo algunos estn disponibles para una aplicacin determinada. Nota: el compilador BC++ puede subdividir bloques del montn global en trozos ms pequeos a fin de reducir la posibilidad de alcanzar el lmite del sistema ( HeapLimit y HeapBlock). heap suballocator: Cuando en un programa, el manejador del montn (heap manager) asigna un bloque grande de memoria, simplemente asigna un bloque del montn global (global heap) utilizando la rutina GlobalAlloc de Windows. En cambio, cuando se asigna un bloque pequeo, el "heap manager" de BC++ asigna un bloque grande del montn global y lo subdivide en bloques ms pequeos segn la necesidad. El mecanismo de reasignacin de estos bloques pequeos reutiliza todo el espacio disponible antes de que el manejador del montn tenga que asignar un nuevo bloque del montn global (que a su vez es nuevamente subdividido).

1.4 Proceso de creacin de un programa


Regla de oro de la programacin: !! Nunca est terminado del todo

1 Sinopsis Programming is the art of expressing solutions to problems so that a computer can execute those solutions. Bjarne Stroustrup: "Programming: Principles and Practice Using C++". Escribir un programa es establecer el comportamiento de una mquina mediante una serie de algoritmos que definirn su funcionamiento . En el estado actual de la ciencia, este algoritmo se plasma por escrito utilizando un lenguaje artificial comprensible por el humanoprogramador. Generalmente estas instrucciones, que aqu se denominan cdigo fuente, vienen acompaadas de algunos datos en forma de texto o imgenes, contenidas en uno o varios ficheros

denominados ficheros de recursos ("resources"). Sin embargo, las instrucciones y recursos solo pueden ser utilizadas por la mquina despus de un proceso de traduccin que es realizado por la propia mquina (puede ser distinta de la que ejecuta el programa). El proceso exige que el cdigo fuente sea transformado en una nueva secuencia de instrucciones segn un nuevo sistema de codificacin (el lenguaje mquina), y que los recursos adopten una disposicin particular. Este conjunto de instrucciones y datos, que constituyen el denominado ejecutable, corresponden a acciones concretas y datos, que pueden ser entendidas, ejecutadas y utilizados por la mquina. En general este comportamiento pretende modelar o mimetizar el comportamiento de una entidad del mundo real, o de una abstraccin que hemos imaginado; y es de tipo genrico. Se pretende que la mquina se comporte como una funcin que acepta un conjunto de condiciones de entrada y devuelve como salida un comportamiento concreto y predecible para cada combinacin de las condiciones de entrada.

2 Presentacin del problema Hay bastante literatura sobre programacin en general; a los acadmicos les gusta hablar de "Teora de la Programacin", y mucha gente se ha dedicado a especular sobre el tema. Incluso hay modas al respecto [4]. Es posible confeccionar una lista de las caractersticas que "debe" y "no debe" tener un buen programa (incluyendo la del Jefe, que solo tiene dos puntos: "Que est para ayer; que salga barato"). El propio Stroustrup ( TC++PL) compara las condiciones para escribir un buen programa con las de escribir buena prosa. Segn l, existen dos respuestas: "Saber que se quiere decir" y "Prctica. Imitar buenos escritores". Ms adelante nos recuerda que aprender a manejar bien un lenguaje puede constar tanto tiempo y esfuerzo como aprender a expresarse en un lenguaje natural o tocar un instrumento. Por supuesto sera un atrevimiento por mi parte contradecir tan docta opinin, pero puestos a filosofar me gustara puntualizar que el verdadero problema est en el segundo punto de la segunda respuesta; la primera, aunque ciertamente importante, me parece la verdad de Perogrullo . Siempre me ha parecido que programar (programar bien) tiene mucho de arte. Me parece que debe ocurrir como con la msica; seguramente muchos pueden decir que debe tener una buena ejecucin de violn, pero imitar a Paganini debe ser harina de otro costal. Seguramente los profesores de armona saben que debe tener y no tener una buena sinfona, pero otra cosa debe ser imitar a Mozart. Bajando a la tierra; tampoco se trata aqu de hacer "Paganinis de la programacin C++" (ya me gustara para m); el mensaje que quisiera transmitir es doble: El contenido en un viejo Refrn Espaol: "La Universidad no presta lo que la naturaleza no da". Como suena un poco duro, aadir un consuelo para los que somos menos dotados; un proverbio que le hace tiempo, en lnea con la respuesta de Stroustrup: "Por el dinero del trabajo los Dioses lo venden todo". A continuacin se comentan brevemente los pasos imprescindibles en la creacin de un programa C++. Vaya por delante, que las anotaciones de los puntos 3, 4 y 5 son opinin del que suscribe basados en la propia experiencia, por tanto totalmente subjetivos y opinables.

3 Comprender el problema. "Custom development is that murky world where a customer tells you what to build, and you say, "are you sure?" and they say yes, and you make an absolutely beautiful spec, and say, "is this what you want?" and they say yes,

and you make them sign the spec in indelible ink, nay, blood, and they do, and then you build that thing they signed off on, promptly, precisely and exactly, and they see it and they are horrified and shocked, and you spend the rest of the week reading up on whether your E&O insurance is going to cover the legal fees for the lawsuit you've gotten yourself into or merely the settlement cost. Or, if you're really lucky, the customer will smile wanly and put your code in a drawer and never use it again and never call you back ". Joel on Software "Set Your Priorities" www.joelonsoftware.com "As more became known about what people wanted to do with computer, it became clear that there would always be increasingly more complex problems to solve. A part of that realization is the realization that our ability to accurately describe the problem determines the ability for the problem to be solved. Most people are incapable of clearly and precisely articulating -- to the level necessary -- the problems that they're trying to solve. This is a problem that is getting larger and not smaller". Robert Bogue (Jupitermedia Corp) en "Breaking Down Software Development Roles". "Often, a problem is only fully understood through the process of programming a solution for it". Bjarne Stroustrup: "Programming: Principles and Practice Using C++".

Esta es la tpica obviedad que a veces se pasa por alto. Hemos dicho que escribir un programa es establecer el comportamiento de una mquina; parece lo ms natural del mundo enterarse primero de cual es ese comportamiento. Tener una imagen mental lo ms clara posible de las caractersticas de lo que pretendemos modelar. Esta cuestin es lo que los tericos denominan el "espacio" del problema, "'What' domain" en la literatura inglesa. A esta fase se la suele denominar anlisis, y mi consejo particular es que despus de una primera toma de contacto, el segundo paso sea definir de la forma ms detallada posible el principio y el final del problema. Es decir: cual es la informacin de partida (incluyendo su formato y en que soporte se recibe) y cual es la informacin final y en que soporte se proporcionar; no es lo mismo mostrar una imagen que componer una factura o disparar un proceso si un sensor analgico-digital nos suministra una determinada seal (por citar algn ejemplo). Normalmente en ambas cuestiones tiene mucho que decir el cliente [2], es lo que se llama especificacin; el resto (lo que hay entre los datos de entrada y la salida), debe rellenarlo el programador. Generalmente si se tienen bien definidos ambos extremos, se tiene resuelta la mitad del problema; cuando se tengan diseados los ficheros se tendrn dos terceras partes -ver a continuacin-. Este sistema tiene adems la ventaja de poner inmediatamente de manifiesto las indefiniciones de partida; a veces los clientes no saben exactamente qu desean y hay que ayudarles a centrar el problema. Dentro de esta fase tiene especialsima importancia el tema de los lmites; esto se refiere al orden de magnitudes que se manejarn. De que rango sern las magnitudes numricas? Podrn adoptar valores negativos? Hay informacin alfanumrica? Como son de largas estas cadenas?. Especialmente si el programa implica diseo de archivos (como es casi seguro), Cual podr llegar a ser su tamao dentro de la vida del programa?. Si se manejan ficheros u objetos binarios, Como son de grandes? Que concepto tiene el cliente de los que sera "rpido" o "lento"? (milisegundos, minutos, horas?). En esta fase sea especialmente precavido y no se crea a pi juntillas todo lo que le digan (intente hacer de abogado del diablo).

Como postre, disee las lneas maestras de una estrategia de recuperacin de errores de ejecucin, incluyendo que har con los no recuperables (errores fatales). Piense por ejemplo que si algn da lo llaman para ver "que ha pasado", quizs le interese disponer de un volcado de texto ASCII en el disco con una descripcin del estatus del programa como parte de las funciones de salida ( 1.5). Hoy da, cuando se empieza a hablar de frigorficos que avisarn de que faltan provisiones o de lavadoras que avisarn al tcnico si se estropean, no estara de ms que sus programas estuviesen a la altura de las circunstancias.

4 Disear los ficheros y mdulos Si el programa debe utilizar ficheros que no vengan impuestos (ya existentes), y suponiendo que todo lo anterior est suficientemente claro, este es el momento de hacerlo. Ponga por escrito la especificacin de tales ficheros, incluyendo el nombre que dar a las variables y, en su caso, el que tendrn en el disco o almacenamiento externo. Esto puede concretarse quizs a la definicin de algunas estructuras ( 4.5). En esta fase es posible que tenga que repreguntar alguna cosa que se pas por alto. Teniendo ya una imagen ms o menos clara de lo que har su programa, si ste es mediano o grande, es posible que todava tenga que realizar una labor previa antes de ponerse a escribir el cdigo: disear a grandes rasgos cuales sern los mdulos del programa; mdulos que se correspondern aproximadamente con la distribucin del cdigo en ficheros fuente independientes. Quizs tenga que decidir tambin si algunas partes aparecern como libreras [1]. Recuerde lo indicado al respecto al tratar de los Subespacios de Nombres ( 4.1.11). Esta fase es especialmente importante en el caso de programas muy grandes, cuyo desarrollo se reparte entre varios programadores que se encargan de uno o varios de estos mdulos. En estos casos, el anlisis, la especificacin, la subdivisin en partes (con sus especificaciones particulares), y la asignacin de estas como tareas a los programadores, lo habr realizado el jefe de programacin y desarrollo.

5 Escribir el cdigo Suponiendo cumplimentados los pasos anteriores, el programador est en condiciones de construir una imagen mental clara de como ser esa conexin entre la informacin de entrada y la salida, es lo que se denomina "espacio" de la solucin ("'How' domain"); su forma concreta es justamente el fuente del programa que se pretende. La codificacin consiste justamente trasportar a papel (en el lenguaje de programacin elegido) la imagen mental de esa conexin. Para escribir el cdigo fuente de un programa C++ solo se puede utilizar un subconjunto de 96 caracteres del juego total de caracteres US-ASCII ( 2.2.1a). Son los siguientes [8]:

Juego de caracteres imprimibles:

a A 0 _

b B 1 {

c C 2 }

d D 3 [

e E 4 ]

f F 5 #

g G 6 (

h H 7 )

i I 8 <

j k l m n o p q r s t u v w x y z J K L M N O P Q R S T U V W X Y Z 9 > % : ; . ? * + / ^ & | ~ ! = , \ "

Caracteres no-imprimibles denominados separadores Espacio horizontal; Tabulacin horizontal (TAB); Tabulacin vertical (VT); Salto de forma (FF); Nueva lnea (NL).
Nota: Para escribir el cdigo solo hace falta un editor de texto plano, aunque las modernas "suites" de programacin incluyen editores especficos que estn conectados con el depurador, el compilador el enlazador (ver ms adelante) e incluso el sistema de ayudas, de forma que, por ejemplo, pueden mostrarnos automticamente la sentencia en la que se ha producido un error de compilacin, o la pgina correspondiente del manual si pulsamos en una palabra reservada y pedimos ayuda (F1 generalmente). Tambin muestran en diversos colores las palabras clave, los comentarios, Etc. Los ms avanzados disponen incluso de opciones que podramos calificar de "inteligentes", en el sentido que pueden prever cual ser nuestro prximo movimiento en funcin de la sentencia que estamos escribiendo (por ejemplo, ofrecernos una lista de las propiedades y mtodos de una clase si nos estamos refiriendo a ella). Durante la fase de escritura no desdee dos puntos: Incluir la mayor cantidad de comentarios y aclaraciones posibles. Cuando se est muy "metido" en el programa todo parece evidente, pero piense que tiene que retocarlo dentro de unos aos, quizs entonces le parezca "Chino" y agradecer haber dejado la mayor cantidad de documentacin y aclaraciones al respecto. Incluso si es seguro que no volver a verlo, piense en el sufrido programador que le seguir si tiene que habrselas con su cdigo. En este sentido C++ no es precisamente COBOL, aunque afortunadamente permite incluir en el fuente comentarios todo lo extensos que se desee ( 3.1). No caiga tampoco en el error de pensar que esa informacin ya est en la documentacin escrita que le han obligado a entregar junto con los fuentes; posiblemente dentro de unos aos Usted mismo no encuentre esos documentos. Incluir la mayor cantidad posible de rutinas y condiciones de comprobacin de errores. Piense que el operador es un "manazas" o que los datos pueden venir con alguna corrupcin, error de transmisin, etc. Verifique constantemente que las condiciones son las esperadas ( 1.4.5).

Una vez que se tiene el cdigo fuente (en uno o varios mdulos), el proceso de traducirlo a instrucciones comprensibles por el procesador (lenguaje mquina) puede hacerse siguiendo dos modelos: los intrpretes y los compiladores [3]. En el caso de lenguajes compilados como C++, el fichero de texto plano (ASCII 2.2.1a) que contiene el fuente del programador (con la terminacin .C .CPP), es sometido a un proceso de varias fases que terminan en la obtencin del ejecutable.

De forma genrica, todo este proceso se denomina "compilacin", aunque es una generalizacin un tanto incorrecta, ya que la compilacin propiamente dicha es solo una de las etapas intermedias. Sera ms correcto decir "construccin" del ejecutable, aunque por la extensin y generalizacin de su uso seguiremos utilizando el trmino genrico "compilacin" para referirnos a l. Los procesos de construccin del ejecutable se esquematizan en la figura que comentamos a continuacin:

6 Preproceso En la primera fase de la compilacin; un programa especial, denominado make , es encargado de iniciar el proceso llamando a los diversos mdulos que se encargan de la construccin del ejecutable (en 1.4.0 se amplan detalles sobre esta parte del proceso). El primero de estos mdulos es el preprocesador. [13] El preprocesador ( 1.4.1) estudia el texto buscando directivas de preprocesado ( 4.9.10), por ejemplo sentencias que pueden ser suprimidas, incluye los ficheros correspondientes a las directivas #include, sustituye los #define, elimina los comentarios y expande las macros encontradas en el fuente y en los propios ficheros incluidos. El resultado obtenido es lo que se denomina unidad de compilacin ( 1.4.2).

7 Anlisis sintctico Puesto que el fichero fuente est escrito en un "lenguaje" (C++ en este caso) que tiene sus propias reglas de sintaxis (como los lenguajes naturales), el compilador debe comprobar que estas reglas se han respetado. Este anlisis ("Parsing") es realizado por el analizador sintctico [10].

En esta fase se realizan comprobaciones como que los parntesis estn cerrados, que no aparecen expresiones incompletas, etc. Para realizar esta labor, el "parser" debe identificar los tokens ( 3.2), de forma que el fuente es tokenizado, esto es, reducido a tokens y separadores. El fuente es escaneado, el analizador sintctico (parser) extrae los tokens, seleccionando el que coincida con la secuencia de caracteres ms larga posible dentro de la secuencia analizada [9]. Por ejemplo, la palabra clave external es reconocida como un solo token (identificador de clase de almacenamiento) en vez de seleccionar extern (una palabra reservada) y al (que sera un identificador). Los separadores (whitespaces) es el nombre genrico dado a los espacios (32), tabulaciones verticales VT (11), horizontales TAB (9) nueva linea NL (10) y retorno de carro CR (13). Los separadores sirven para indicar donde empiezan y terminan las palabras, pero despus de esto cualquier separador redundante es descartado. Por ejemplo, las dos secuencias: int i; float f; int i; float f; son lxicamente equivalentes y el resultado del anlisis son las seis palabras siguientes: int i ; float f ; El carcter ASCII espacio puede formar parte de cadenas literales (alfanumricas), en cuyo caso es protegido del proceso de anlisis, permaneciendo como parte de la cadena. Por ejemplo: char name[] = "Playa Victoria"; es reducido a siete tokens, incluyendo una cadena literal "Playa Victoria" char name [ ] = "Playa Victoria" ;

8 Anlisis semntico En lenguajes como el Ensamblador la comprobacin se limita al anlisis anteriormente sealado; con esto se garantiza que el fuente original es correcto (sintcticamente), es decir, es un escrito correcto desde el punto de vista del lenguaje, otra cosa es que tenga un sentido computacional correcto, o diga tonteras, incongruencias o sinsentidos [6].

Por supuesto la meta del compilador es conseguir descubrir con anticipacin (al runtime) el mximo de errores posibles. En los lenguajes de alto nivel, esto se consigue con una cierta comprobacin del "sentido" o "significado" del escrito, es el denominado anlisis semntico (anlisis del significado). La mejor baza de que dispone C++ para esta segunda comprobacin es la comprobacin esttica de tipos ( 2.2). Es decir, que las variables y las operaciones entre ellas se usan correctamente; esto supone verificar que las llamadas a funciones, los valores devueltos por estas y los operandos de las expresiones corresponden con el tipo que se les supone en cada caso. Por ejemplo: int x; char func(); .... x = func(); En este caso, la primera lnea declara que la variable x es tipo int (entero); la segunda declara que la funcin fun devuelve un carcter (char); si una lneas ms adelante se pretende igualar la variable x con el valor devuelto por la funcin, el analizador semntico estara en condiciones de asegurar que existe una incongruencia en las pretensiones del programador, generando el correspondiente mensaje de advertencia o error .

9 Generador de cdigo Todos estos tokens identificados por el analizador sintctico, son organizados en una estructura como las hojas de un rbol. A continuacin, el generador de cdigo recorre este rbol traduciendo sus hojas directamente en cdigo de la mquina para la que se compila [11]. Si se solicita, el compilador tambin puede en esta fase generar un fichero en lenguaje macro ensamblador para su posible inspeccin por el programador (este cdigo es tambin dependiente de la mquina para la que se compila y fcilmente entendible por el humano; puede contener incluso comentarios para facilitar su lectura). Nota: Los compiladores modernos suelen incluir opciones que permiten generar cdigo optimizado para el tipo de procesador que se utilizar. Por ejemplo, el compilador Borland C++ dispone de opciones que permiten generar cdigo optimizado para procesadores Intel de modelos especficos ( 1.4.3a). Como cabra esperar, el compilador GNU c++ es el que ofrece ms posibilidades en este sentido, que incluyen el tipo de procesador dentro de una larga lista de fabricantes, y dentro de estos diferentes modelos. En concreto, para la familias Intel i386 y x86-64, permite elegir entre 20 posibilidades diferentes!!.

A veces, despus del generador de cdigo puede ejecutarse un optimizador (peephole optmizer). Este generador de cdigo sera propiamente elcompilador, es decir, el encargado de traducir algo entendible por el humano en cdigo mquina. En cualquier caso, el resultado es un fichero "objeto", generalmente con la terminacin .obj o .o. Tambin puede ordenarse al compilador que incluya en el "objeto", determinada informacin adicional que ser utilizada ms tarde por el depurador , por ejemplo los nmeros de lnea de las sentencias. Cuando se hace as, se habla de una compilacin

"provisional" o de "depuracin"; distinta de la que se realiza para la versin definitiva (de campo) del programa en la que no se incluyen este tipo de informacin que ya no es necesaria.

10 Enlazado El ltimo paso en construir un ejecutable es el enlazado. Recibe este nombre el proceso de aglutinar todos los recursos en un solo fichero ejecutable ( 1.4.4). Estos recursos son desde luego los ficheros-objeto obtenidos en la compilacin de los diversos mdulos (ficheros .c) que componen el programa. Adems, si se han utilizado funciones o clases de librera [1] (como es casi seguro), el enlazador ("Linker") es el programa encargado de incluir los mdulos adecuados en el fichero ejecutable final. As pues, la funcin primordial del enlazador es resolver todas las referencias que puedan existir en el programa, es decir: que cada invocacin a un valor o a una funcin corresponda una direccin donde se encuentra el recurso correspondiente, y que estn todos contenidos en un solo fichero que pueda ser cargado y ejecutado por el Sistema Operativo. Eventualmente algunos recursos pueden estar en otros ficheros distintos del ejecutable, libreras de enlazado dinmico (en Windows se denominan DLLs). En cuyo caso el enlazador tambin incluir las direcciones y convenciones de llamada adecuadas para que puedan ser trados a ejecucin desde el programa principal. Por ltimo, el enlazador se encarga de insertar en el ejecutable un trozo de cdigo especial: el mdulo inicial, que es el encargado de iniciar la ejecucin ( 1.5). Hay que tener en cuenta que generalmente el enlazador puede producir diversos tipos de resultados:

10.1 Versin de depuracin Se trata de una versin en la que dentro del propio ejecutable, se incluye informacin adicional no estrictamente necesaria para la ejecucin sino para la depuracin (por ejemplo los nmeros de lnea del cdigo fuente que corresponde a cada sentencia). Estos ejecutables permiten la ejecucin en un modo especial, en la que por ejemplo, pueden ejecutarse las sentencias paso a paso, o que el programa se detenga al llegar a diversos puntos establecidos de antemano; ver el contenido de las variables, el estado de la pila y otros aspectos internos muy tiles cuando se intentan depurar errores de tiempo de ejecucin. Esto se consigue porque el programa corre bajo control de otro programa que acta de controlador de la ejecucin, es el depurador ("Debugger"). Nota: El depurador puede ser en realidad un mdulo adicional al de inicio, inserto en el ejecutable, que se inicia antes que la propia funcin main ( 4.4.4), de forma que puede controlar la ejecucin. Por esta razn entre otras, las versiones de depuracin son mayores (en tamao del fichero) que las definitivas o "de campo" [12].

10.2 Versin de publicacin

Es la versin definitiva que saldr al pblico (se entregar al usuario). Se distingue de las versiones internas en que no incluye informacin para depuracin. Es buena prctica incluir en cada versin publicada informacin sobre el nmero de versin del programa y de compilacin (esto suele venir indicado en la siguiente forma: Versin xx.yy.zz build nnn). Generalmente los programas sufren muchas modificaciones a lo largo de su vida (correccin de errores, perfeccionamientos, versiones para diversas plataformas, etc), y es preciso identificarlos. Es costumbre hablar de "versiones", que se identifican por grupos de nmeros separados por puntos. Por ejemplo: Versin xx.yy.zz. Cada fabricante de software, grupo de trabajo o programador, utiliza una convencin, establecindose que tipo de cambios dan lugar a diferencias de versin en el grupo de cifras xx; en el yy o en el zz. Generalmente se acepta que los cambios de mayor nivel (xx) representan versiones totalmente nuevas del programa; que requieren incluso rescribir los manuales de uso. Los cambios menores corresponden a modificaciones en el grupo yy (por ejemplo utilizar otras versiones de las libreras o distinto compilador); finalmente los cambios de detalle representan modificaciones en el grupo zz. Viene a representar cambios mnimos, que no merecen una alteracin del ltimo grupo de cifras [7], pero cambios al fin y al cabo (cuando se recompila es porque algo ha cambiado, aunque sea un comentario en el fuente). Es tambin costumbre incluir un ltimo identificador: El nmero de compilacin o construccin ("build" en la literatura inglesa); es un nmero progresivamente creciente para cada compilacin distinta. A ttulo de ejemplo, en la pgina adjunta se muestra la clasificacin utilizada para las sucesiones versiones de los productos de un conocido fabricante ( Designacin de versiones).

10.3 Librera En las pginas siguientes veremos que como resultado de la "compilacin", no siempre se desea conseguir un ejecutable; al menos no en el sentido tradicional del trmino, sino una librera (de las que existen varios tipos), o un fichero objeto. En lo que respecta al lenguaje C++, existen dos tipos fundamentales: Estticas y Dinmicas. Las primeras son colecciones de ficheros precompilados, cuyo cdigo puede ser aadido a un ejecutable en el proceso de enlazado (los ficheros de la Librera Estndar 5 que acompaan a los compiladores C++ son de este tipo). Las segundas son autnticos ejecutables externos que son invocados desde otro ejecutable y devuelven el control a este cuando terminan su ejecucin. Ms detalles al respecto en: ( 1.4.4a).

11 Errores

Volver al principio (rescribir el cdigo)

La verdadera prueba de fuego del programador se presenta cuando lanza la orden de compilar y enlazar su programa. Todos los mdulos involucrados en los pasos anteriores, compilador, analizador sintctico y enlazador pueden detectar errores en nuestro cdigo y mostrar los mensajes correspondientes.

11.1 Tipos de errores En cuanto al momento en que se producen, son bsicamente de tres tipos: De tiempo de compilacin. Se engloban aqu los errores detectados por preprocesador, el analizador sintctico y el propio compilador. Los hay meramente sintcticos, por ejemplo

un parntesis no cerrado; tambin de tipo lgico, por ejemplo la referencia a una variable no declarada previamente, etc. etc. De tiempo de enlazado. Son detectados por el enlazador. Por ejemplo una llamada a funcin cuya definicin no aparece por ninguna parte (el enlazador no es capaz de encontrarla en los directorios que tiene asignados como "normales" para buscar); tambin la inversa: dos funciones del mismo nombre situadas en dos mdulos (fuentes) distintos (la referencia aparece duplicada). De tiempo de ejecucin (runtime). Existe finalmente una ltima clase de errores: los que se producen cuando se ejecuta el programa; son precisamente los ms difciles de diagnosticar y verificar, sobre todo en aplicaciones grandes (los relativos a "prdidas misteriosas" de memoria y punteros descontrolados son especialmente temibles).

11.2 Gravedad de los errores Los errores producidos durante la compilacin son de dos tipos, segn su gravedad: Errores fatales ("Errors"): Son errores graves, el proceso no puede continuar y es detenido despus de mostrar la informacin pertinente. Advertencias ("Warnings"): No son errores graves pero si circunstancias sospechosas o inusuales de las que el compilador entiende que merecen una advertencia por si es algo que se nos ha escapado inadvertidamente (por ejemplo: Una variable declarada que no se utiliza para nada ms). En estos casos, el proceso continua y si no hay errores graves se construye un ejecutable.

En todos los casos el aviso incluye indicacin del fichero ("fuente" .C/.CPP), el nmero de lnea, y el nombre de la funcin donde se produce el error, as como una explicacin ms o menos clara de su motivo. En principio pueden ser cuestiones banales, como haber olvidado poner un punto y coma ; al final de una sentencia (muy corriente en los que estamos acostumbrados a programar en otros lenguajes). En otros casos los mensajes son especialmente crpticos, sobre todo para el profano, pero poco a poco los entendemos mejor y podemos aprender mucho de ellos si prestamos la debida atencin y entendemos su "porqu". Recordar que todos los compiladores disponen de opciones para modificar el nmero y tipo de los errores y advertencias ("Warnings") que aparecen. Respecto a los primeros, puede instruirse al compilador para que suspenda la actividad al aparecer el primer error, o que contine hasta que aparezca un nmero determinado de ellos. Respecto a los avisos, puede ordenarse que no muestre ninguno, o que sea ms o menos benevolente en determinados aspectos. Por ejemplo, puede indicarse que la comprobacin siga estrictamente el estndar C++ y que avise de cualquier desviacin al respecto (los compiladores suelen permitir ciertas "peculiaridades" que no son estndar). Nota: La descripcin e informacin acerca del error o advertencia, dependen de la plataforma, pero hemos de sealar que existen notables diferencias en cuanto al grado de desarrollo de los diversos compiladores, en especial respecto a los "Warnings". Por ejemplo, en este sentido el Compilador Borland C++ 5.5 es mucho menos riguroso que el producto homlogo de Microsoft, cuyo sistema de verificacin es con mucho superior al del primero, de forma que no es infrecuente que advertencias ms o menos serias e incluso algunos errores advertidos por Visual C++ 6.0 sean totalmente ignorados por Builder. A su vez los errores y advertencias sealados por el compilador GNU Cpp suelen ser ms explicativos que los sealados por Borland o Visual (que en este sentido son ms crpticos).

11.3 Generalidades sobre los errores de compilacin Respecto a los errores de compilacin, es importante hacer una advertencia al nefito: Con frecuencia el compilador nos informa de error en una lnea ms abajo de donde est verdaderamente. Por ejemplo, olvidar un punto y coma de final de sentencia puede dar lugar a que el compilador nos informe de un error incomprensible dos o tres lnea ms abajo. Cuando se realizan modificaciones en fuentes grandes y no se tiene mucha prctica, es preferible realizar cambios pequeos y compilar sistemticamente despus de cada uno. As sabremos que el error corresponde a lo ltimo que hemos tocado. Hay veces en que quitar una simple coma en una sentencia produce una listado de 15 o 20 errores en lneas siguientes. !Sbitamente nada tiene sentido para el compilador !!. [5] En las asignaciones del tipo: Lvalue = Rvalue; en las que intentamos asignar un valor Rvalue ( 2.1 que puede ser el resultado de una expresin) a un Lvalue ( 2.1), son muy frecuentes los errores en que el compilador produce un mensaje del siguiente aspecto: Error .... Cannot convert 'xxxxx' to 'yyyyy' in function .... Lo importante a resear aqu, es que las expresiones xxxxx e yyyyy informan sobre el tipo de objeto que hay en cada lado de la expresin de asignacin. Nota: En el captulo dedicado a los tipos de datos ( el compilador clasifica los objetos segn su tipo. 2.2) se describe detalladamente como

En las asignaciones, el Lvalue debe recibir un valor de su mismo tipo. Si el tipo del Rvalue no concuerda con l, el compilador puede intentar adecuarlo, pero si esto no es posible, se produce un error como el sealado. En l se nos indica que el tipo xxxxx, que corresponde al Rvalue (el resultado de la expresin a la derecha del operador = ), no puede ser convertido al tipo yyyyy del Lvalue. Hay que advertir que las expresiones xxxxx e yyyyy estn codificadas. Cada compilador utiliza un algoritmo interno para designar cada uno de los innumerables tipos que puede existir en C++. En concreto, la designacin utilizada en estos mensajes es la misma que utiliza en el operadortypeid ( 4.9.14). En situaciones difciles, es mucha la informacin que puede obtenerse de estas expresiones si se observan detenidamente.

Aunque la comprobacin esttica de tipos, y del cumplimiento de las reglas sintcticas realizada por el compilador, resultan muy eficientes en lo que respecta a la deteccin de errores, en realidad, el trabajo dista de ser completo y suficiente. Existen multitud de circunstancias potencialmente errneas que son pasadas por alto. En especial las relacionadas con prdidas de memoria; existencia de punteros descolgados; bucles infinitos; objetos declarados pero no utilizados, y un largo etctera. Algunos de estos errores pueden permanecer agazapados en el cdigo y solo aparecer en circunstancias muy especiales, incluso despus de que la aplicacin haya sido rodada largo tiempo sin contratiempos. Muchas de estas circunstancias pueden evitarse, o al menos mitigarse, siguiendo ciertas pautas y recomendaciones "de buena prctica", muchas de las cuales estn contenidas en la obra TC++PL de Stroustrup; la mayora en forma de advertencias sobre

cosas que "no" debe hacerse. Sin embargo, el problema persiste, mxime en un lenguaje como C++ plagados de peligros potenciales que acechan en el arcn, y con el que es posible "volarse la pierna completa". Para reforzar la calidad del cdigo y prevenir errores posteriores (de run-time), se han creado multitud de herramientas. Entre las ms conocidas se encuentran las siguientes: Lint, denominadas as en atencin a que tienen su origen en una utilidad de este nombre (lint) desarrollada inicialmente en el entorno Unix. Estas utilidades se ejecutan sobre el fuente sin compilar (no confundirlas con los depuradores "debugger" -de run-time-, aunque tambin sirven para "depurar" el cdigo); comprueban la sintaxis y errores en los tipos de datos de forma ms concienzuda y profunda que los compiladores C/C++, y avisan de gran cantidad de peligros potenciales; incorrecciones; desviaciones sobre las reglas universalmente aceptadas como de "buena prctica", etc. Actualmente han llegado a un elevado nivel de sofisticacin, de forma que un buen Lint puede evitarnos muchas horas de depuracin. En realidad es una herramienta que no debera faltar en el taller del programador profesional. cb. Esta utilidad, originaria del SO AIX, reformatea el cdigo fuente contenido en un fichero y lo vuelca sobre el dispositivo estndar de salida ( stdout 5.3) utilizando un formateo basado en sangrados y espaciados, que ayudan a interpretar la estructura del cdigo. cflow. Esta utilidad, originaria del SO AIX, analiza el contenido de un fichero objeto C/C++ y proporciona en la salida estndar (stdout) un grfico de sus referencias externas. cxref. Esta utilidad, anloga a las anteriores, analiza los fuentes C/C++ y genera una tabla con todos los smbolos encontrados en cada fichero, incluyendo los nombres de los parmetros formales de las funciones (contenidos en la definicin de la funcin). La tabla es mostrada en el dispositivo estndar de salida ( stdout), e incluye el sitio en que cada referencia se ha resuelto (suponiendo que la definicin est en el cdigo analizado).

Si est interesado en las caractersticas y posibilidades de estos productos, la mayora comerciales y algunos muy costosos, aparte de la correspondiente bsqueda en Google, existe un interesante artculo de Scott Meyers (uno de los "Gurus" del C++) y Martin Klaus titulado "A First Look at C++ Program Analyzers", en el que se repasan las cualidades y caractersticas de distintos paquetes, incluyendo una evaluacin de su comportamiento frente a lo que el autor considera deseable. Aparecido en el nmero de Febrero de 1997 del Dr. Dobb's Journal, existe una versin de prepublicacin accesible on-line, que es incluso ms completa que el artculo publicado (probablemente debido a las exigencias de maquetacin de la revista): www.aristeia.com. Uno de los productos punteros y ms antiguos, es el de Gimpel Software www.gimpel.com; esta empresa dispone de dos versiones denominadas PC-Lint y FlexeLint. La primera para Windows, la segunda, ms general, para cualquier entorno que soporte un compilador C, incluyendo Unix y Linux. Si tiene inters en comprobar ms de cerca el tipo de informacin que proporcionan estas utilidades, en el sitio de este fabricante existe una seccin denominada Bug del mes ("Bug of the month") en la que se exponen ejemplos de cdigo, junto con el resultado del anlisis (despus de pulsar un botn). Adems de smamente instructivos, los casos propuestos pueden servirle para auto-evaluar sus conocimientos de C++ al respecto. A mi entender tambin pueden constituir una magnfica fuente de inspiracin para los enseantes que busquen material para ejemplo o evaluacin (sed piadosos en los exmenes porque algunos son realmente para niveles avanzados. No se puede pretender que despus de un semestre de estudio, el alumno est en condiciones de adivinar correctamente el "bug C/C++ del mes" :-)

11.4 Errores de ejecucin

Para los errores de tiempo de ejecucin se requieren estrategias especiales. En principio, durante la fase de comprobacin inicial, se tienen las ofrecidas por el depurador . Prcticamente todos los entornos de desarrollo disponen de un depurador ms o menos potente y sofisticado. Puede afirmarse que el depurador es otra herramienta que no debe faltar en el arsenal de cualquier programador profesional, en especial porque hay errores que son prcticamente imposibles de diagnosticar y corregir sin su ayuda. Como se ha indicado, el depurador incluye en el ejecutable un cdigo especial que realiza las funciones de depuracin deseadas, pero aparte de los que podramos denominar estndar (cuyos mdulos son incluidos en durante la fase de enlazado del ejecutable), existen herramientas especficas que analizan el ejecutable y son capaces de detectar determinados errores e inconsistencias. Estas herramientas realizan su trabajo durante la ejecucin, para lo que modifican el cdigo a analizar incluyendo determinados mdulos que les permiten controlar el desarrollo de la ejecucin (se dice que "instrumentan" el cdigo). La forma de realizar esta "instrumentacin" depende de la herramienta: puede realizarse durante la compilacin ("compile-time"), aadiendo cdigo que no aparece en el fuente; durante el enlazado ("link-time"); durante la carga ("loadtime"), cuando el ejecutable es acomodado en memoria, o antes de la carga, sobre cualquier ejecutable listo para ser usado. Generalmente estas herramientas controlan la ejecucin, toman nota de las incidencias, y finalmente proporcionan un informe de las mismas cuando la ejecucin finaliza. Nota: no existe una denominacin unificada para este tipo de productos. Quizs el ms conocido es es BoundsChecker, de Numega www.numega.com (actualmente aparece como Compuware). Tambin puede intentar una bsqueda en Google bajo el epgrafe "Memory debugger".

Despus de todas las medidas preventivas ya reseadas, cuando finalmente, despus de las pruebas de "laboratorio" damos por bueno el programa, este queda merced a si mismo; a la calidad de su propio mecanismo de defensa. Como errar es humano, los diseadores del C++ pensaron que a pesar de la programacin ms cuidadosa, siempre pueden presentarse circunstancias excepcionales o imprevistas. Para poder hacerles frente, dotaron al lenguaje de opciones especiales con las que tratar este tipo de situaciones, de forma que pudiese seguir todo bajo control; estos recursos especficos se exponen con detalle en el captulo dedicado al Tratamiento de Excepciones ( 1.6).

12 Recursos Ya hemos sealado que para construir un programa C++ basta un editor de texto plano y un compilador C++ para la mquina y Sistema en que deba ejecutarse, y que en el trmino "Compilador" incluimos todas las herramientas auxiliares, enlazador, libreras, etc. Por supuesto que en este sentido, las plataformas comerciales, en especial las versiones denominadas "Enterprise", ofrecen unas prestaciones inigualables, incluyendo potentes depuradores, diseo grfico de elementos con capacidad de arrastrar y soltar ("dragg and drop") elementos, y conjuntos preconstruidos de clases que simplifican extraordinariamente la construccin de determinados aspectos de las aplicaciones. Por ejemplo, el manejo de bases de datos o comunicaciones. En esta categora podemos incluir productos como C++Builder de Borland o Visual C++ de Microsoft para el entorno Windows. En la pgina dedicada a los Compiladores encontrar algunas referencias ( Compiladores).

Si desea saber ms sobre aspectos relacionados con la compilacin, preproceso, anlisis sintctico y semntico, traduccin del cdigo, etc, mi consejo es que consulte "Compiladores y Procesadores de Lenguajes" [Jimnez-04]

1.4.0 Construir un ejecutable (compilacin)

"For all intent and purpose, any description of what the codes are doing should be construed as being a note of what we thought the codes did on our machine on a particular Tuesday of last year. If you're really lucky, they might do the same for you someday. Then again, do you really feel *that* lucky? ". R. Freund. "Readme" de una subrutina matemtica (tomado de las FAQ de gnuplot).

1 Sinopsis En el presente Curso C++ nos referimos infinidad de veces al "Compilador". Sin embargo, hablando en propiedad no existe realmente tal cosa; en realidad los procesos anteriormente descritos ( 1.4) se ejecutan por una serie de aplicaciones distintas: El preprocesador, el analizador sintctico, el generador de cdigo, el enlazador y algunas otras auxiliares, aunque de forma genrica nos referimos a ellas como "el compilador". Adems, los "compiladores" ofrecen una serie de herramientas adicionales que no son propiamente para construir ejecutables, sino para labores auxiliares como puede ser la construccin de libreras. Estas herramientas son conocidas colectivamente como binutils. Nota: en el caso del "Compilador" Borland C++, se dispone de los mdulos que siguen, cada uno de los cuales realiza una labor concreta: ILINK32 EXE, BCC32 EXE, BRC32 EXE, BRCC32 EXE, COFF2OMF EXE, CPP32 EXE, FCONVERT EXE, GREP EXE, IMPDEF EXE, IMPLIB EXE, MAKE EXE, TDUMP EXE, TLIB EXE, TOUCH EXE, TRIGRAPH EXE, TD32 EXE (ver detalles 1.4.0w1). En el caso del compilador GNU para C++, adems del compilador propiamente dicho g++, estn el enlazador ld; el intrprete de comandos make, y una serie de utilidades auxiliares (binutils) tales como ar, nm, objcopy, objdump, ranlib, readelf, size, windres, etc.

Para construir el ejecutable es necesario cubrir sucesivamente diversas etapas utilizando los mdulos adecuados, y los resultados de unos como entradas de los siguientes. Sin embargo, para facilitar el proceso, los "compiladores" cuentan con una utilidad a la que se conoce genricamente como compilador pero que en realidad es un programa supervisor ("Front-end") que se encarga de invocar los mdulos sucesivos en el orden correcto y con los parmetros adecuados al fin que se persigue. En el caso del "Compilador" Borland C++ es BCC32.exe y CL.exe en MS Visual C++. En el caso del "Compilador" GNU C++, el programa supervisor es gcc, que acta como front-end del preprocesador (cpp), que acta a su vez como front-end generador de cdigo y este del enlazador (ld). Lo corriente es que el programa supervisor acepte una larga fila de parmetros que controlan su funcionamiento, dado que l mismo debe controlar una serie de procesos (en el caso de Bcc32, la lista asciende a ms de 135 opciones distintas). Viene a ser para el programador una especie de navaja suiza, la herramienta utilizada habitualmente. Solo en casos excepcionales se utilizan

directamente el preprocesador, el generador de cdigo, el enlazador o cualquiera de las utilidades necesarias para la construccin de ejecutables y libreras. Nota: en general es posible comprobar la secuencia de actuacin del "compilador" invocndolo con la opcin -v ("verbose"). Por ejemplo, de esta forma es posible comprobar que durante su operacin, el compilador GNU cc invoca al enlazador, de nombre ld.

2 El proceso de construccin de un ejecutable (o librera) depende de la complejidad del proyecto. Consideramos tres casos: Compilaciones unitarias: Cuando se trata de un solo fuente que produce un ejecutable. Veremos que en estos casos puede mandarse una orden directa al programa supervisor . Compilaciones medianas: Cuando el proyecto involucra unos pocos fuentes que tambin producen un ejecutable. En estos casos no es corriente que el programa supervisor sea invocado directamente mediante un solo comando. Se recurre a ficheros auxiliares (de configuracin), que contienen los parmetros; los ficheros de configuracin son ledos e interpretados por el supervisor . Proyectos grandes: En ocasiones los proyectos software implican centenares de fuentes que producen decenas de ejecutables de diversos tipos, incluidas libreras estticas y dinmicas. En estos casos no se puede hablar de simple compilacin; ms bien de "construir" la aplicacin. Mantener tales proyectos es cuestin complicada, generalmente se realiza mediante herramientas auxiliares. En el apartado correspondiente ( 1.4.0a) reseamos una utilidad especfica para controlar estas "construcciones".

Nota: precisamente uno de los puntos fuertes de las "Suites" RAD ( 1.8) de programacin del tipo C++Builder o MS Visual C++, es la cmoda gestin de proyectos grandes. Sin embargo, abrir un "proyecto" para una aplicacin de un par de fuentes o un sencillo ejemplo, supone matar pulgas a caonazos.

3 Compilaciones unitarias En estos casos puede mandarse una orden directa al supervisor indicndole cual es el fuente (fichero .c .cpp) que hay que compilar, y l se encarga de realizar todo el proceso, invocando en ltimo extremo al enlazador que construye el ejecutable. Por ejemplo: en el caso de Borland C++ 5.5, Bcc32.exe est alojado en el directorio ...BorlandCPP\bin\. Suponiendo que queremos compilar un fuente denominado hola.c con las opciones por defecto ( 1.4.3), podemos lanzar una orden directa desde el CLI [1]: bcc32 hola.c Si realizamos muchas de estas compilaciones y/o utilizamos opciones, puede ser cmodo construir un fichero de proceso por lotes (.BAT en los entornos MS-DOS y Windows) que facilite la labor. A continuacin se muestra el contenido de uno de estos ficheros (compi.BAT), que utilizo habitualmente para este tipo de compilaciones: ECHO OFF rem compi.BAT Compilar UN solo .C como C++ Normas Relajadas

bcc32 -IE:\BorlandCPP\Include -LE:\BorlandCPP\Lib -Vd -P -j5 %1%.C rem -P Perform C++ compile regardless of source extension rem -Vd for loop variable scoping rem -j5 Errors: stop after n messages (Default = 25) if errorlevel 1 goto ERROR IF NOT ERRORLEVEL 1 ECHO !! OK. Compilacion C++ (-Vd) GOTO END : ERROR ECHO !! ERROR en la compilacion.... Pause : END echo BORRADO fichero .TDS!! erase %1%.TDS Puede comprobarse que el fichero no necesita que se le indique el nombre completo del objeto a compilar (l incluye la terminacin .c). En consecuencia, solo hay que escribir: compi hola En este caso, los comandos -IE:\BorlandCPP\Include y -LE:\BorlandCPP\Lib, instruyen al compilador de los directorios donde debe buscar los ficheros de cabecera (comando -I) y las libreras (comando -L), que estn en el directorio BorlandCPP de la unidad lgica E: Si se realizan frecuentemente compilaciones de tipo distinto (con diversas opciones en la lnea de comando), puede ser buena idea preparar los ficheros .BAT correspondientes, por ejemplo: compil.bat; compi1.bat; etc.

4 Compilaciones medianas Salvo en programitas de ejemplo y verificacin, es muy raro que un programa C++ exista en un solo fichero fuente. En estos casos el proceso se complica un poco ms, porque hay que instruir al compilador de todos los fuentes que debe procesar. Por supuesto, en uno de ellos debe existir una funcin main ( 4.4.4). Ilustraremos la operativa con un sencillo programa de dos mdulos pA.c y pB.c, y un fichero de cabecera especfico <Cabe-1.h> que suponemos en el mismo directorio que los fuentes (no es conveniente mezclar nuestras propias cabeceras con las del compilador). Fichero Cabe-1.h: // Cabe-1.h #include <iostream.h> #include <conio.h> #define Salida(msg) cout << #msg << endl; # define PAUSA for( ; ; ) if(getch()!=0) break Fichero pA.c: #include <Cabe-1.h> // pA.c Prueba MODULOS-1

extern void func(float); extern var1; namespace { // Anonimo float pi = 3.14; // identificador conocido solo en este fichero } int main() { // ======= float pi = 0.1; cout << "pi = " << pi << endl; func(pi); // invocada funcin externa con pi local cout << "Variable es " << var1 << endl; Salida(Pulse una tacla para terminar); PAUSA; Salida("el programa ha terminado :-)"); return 0; } Fichero pB.c: #include <Cabe-1.h> // pB.c Prueba MODULOS-2

int var1 = 33; namespace { // subespacio anonimo float pi = 10.0001; // conocido solo en este fichero void func(void) { std::cout << "Invocada func-1: pi = " << pi << endl; } } void func(float f) { std::cout << "Invocada func-2: f = " << f << endl; } La salida, despus de construido el ejecutable es: pi = 0.1 Invocada Segunda func(): f = 0.1 Variable es 33 Pulse una tacla para terminar "el programa ha terminado :-)" El comando necesario para construir el ejecutable es: bcc32 -ID:;E:\BorlandCPP\Include -LE:\BorlandCPP\Lib -Vd -P -Q pa.C pb.c Observe que en este caso se ha alterado ligeramente el comando -I, para que el preprocesador encuentre la cabecera <Cabe-1.h>, que est en el directorio actual (el directorio de trabajo est en la unidad lgica D:). Para automatizar el proceso, en estos casos utilizo un fichero compiv.bat del siguiente aspecto: ECHO OFF rem Compiv.BAT Compilar 2 fuentes (pa.c & pb.c) como C++

bcc32 -ID:;E:\BorlandCPP\Include -LE:\BorlandCPP\Lib -Vd -P -Q pa.C pb.c rem -Q extended compiler error information if errorlevel 1 goto ERROR IF NOT ERRORLEVEL 1 ECHO !! OK. Compilacion C++ (-Vd) GOTO END : ERROR ECHO !! ERROR en la compilacion.... Pause : END echo Fichero .TDS BORRADO!! erase %1%.TDS

5 Ficheros de configuracin Ocurre que en ocasiones la lnea de comando para invocar el supervisor es demasiado larga an en proyectos pequeos. El motivo puede ser doble: por un lado puede contener muchas opciones de compilacin y enlazado, hemos indicado que el programa Bcc32 puede contener ms de 135 opciones distinta. De otro lado (y esto es lo ms frecuente), puede contener una larga serie de nombres de ficheros fuente y/o libreras que deben ser enlazadas juntas. El resultado es que incluso en un fichero de proceso por lotes .BAT como el anterior resultara una lnea larga y farragosa. Para simplificar el proceso se utilizan ficheros auxiliares que contienen los parmetros y/o opciones de compilacin. Estos ficheros son ledos e interpretados por el supervisor. Existen dos opciones al respecto [2]: Los ficheros de configuracin ("Configuration files") y los ficheros de rplica ("Computer response files"). Adems de simplificar la utilizacin repetitiva de largos comandos de compilacin, ocurre que existe una limitacin en cuanto a la longitud mxima que puede tener la lnea de comando en los Sistemas Operativos, y estos ficheros representan una forma de soslayar dicha limitacin.

5.1 Ficheros de configuracin Los ficheros de configuracin son ficheros de texto ASCII de terminacin .CFG que contienen opciones para el programa supervisor; cada opcin debe ir separada por un espacio o nueva lnea. Cuando se invoca Bcc32.exe, busca un fichero de configuracin por defecto con el nombre Bcc32.CFG en el directorio actual, y si no lo encuentra, en el directorio donde reside el compilador. El programa de instalacin del compilador Borland crea un fichero Bcc32.CFG con el siguiente contenido (suponemos que se ha instalado en el directorio E:\BorlandCPP): -I"e:\BorlandCPP\include" -L"e:\BorlandCPP\lib" Adems del fichero por defecto, es posible utilizar varios otros en la misma lnea de comando. Para invocar el programa supervisor con otro fichero de configuracin, adems del estndar, se utiliza la siguiente sintaxis:

+[path]nombre-de-fichero Por ejemplo, la lnea de comando que sigue invoca al supervisor con un fichero denominado Pro1.CFG: BCC32 +C:\PROYECTO\Pro-1.CFG fuente.cpp En cualquier caso, las opciones de la lnea de comando pueden coexistir con un fichero de configuracin, pero tienen precedencia sobre las indicaciones contenidas en aquel. Por ejemplo: BCC32 +C:\PROYECTO\Pro-1.CFG -P fuente.c

5.2 Ficheros de rplica Los ficheros de rplica ("Response files") pueden contener opciones para el supervisor y/o nombres de ficheros (los de configuracin solo pueden contener opciones para el supervisor). Un fichero de respuesta es tambin un fichero de texto ASCII donde cada entrada debe estar separada por un espacio o nueva lnea. Los ficheros de respuesta pueden tener cualquier terminacin (los ficheros de rplica incluidos con el compilador BC++ tienen la extensin .RSP).

5.2.1 Contenido El contenido es exactamente el mismo que se incluira en la lnea de comando si la invocacin se hiciera manualmente y la lnea pudiera ser lo suficientemente larga, pero teniendo en cuenta que en el fichero de rplica se puede partir una lnea y seguir en la siguiente si se termina la primera con +. Por ejemplo, un fichero RespFile.RSP: /c c0ws+ myprog,myexe + mymap + mylib cws pasado al enlazador con el comando: Ilink32 a la lnea de comando: @RespFile.RSP (ver invocacin ), equivale

ILINK32 /c c0ws myprog,myexe,mymap,mylib cws Como puede verse, a diferencia de la lnea de comando, el fichero de rplica s puede tener varias lneas. Observe que si una lnea debe seguir en la siguiente pero la opcin termina en el carcter +, por ejemplo la opcin /v+, la lnea del fichero de respuesta debe terminar en: ...../v+ +. Observe tambin que las opciones que deben ir separadas por comas en la lnea de comando, deben seguir separadas por comas en el fichero de rplica (lnea myprog,myexe + del ejemplo).

5.2.2 Invocacin

La sintaxis para invocar un ficheros de rplica con el compilador es: BCC32 @[path]fichero-replica.txt Para invocar varios al mismo tiempo se emplea la siguiente sintaxis: BCC32 @[path]fichero-1.txt @[path]fichero-2.txt Tambin en este caso las opciones de la lnea de comando tienen precedencia sobre las indicadas en los ficheros de rplica. Recuerde que no solo el compilador (BCC32) acepta este tipo de ficheros como parte del comando de entrada; el enlazador (ILINK32) y otras utilidades tambin puede aceptar este tipo de fichero de rdenes (por ejemplo la utilidad TLIB 1.4.0w1).

1.4.0a Utilidad make (construir proyectos grandes)


"The art of constructin makefiles is arcane; fortunately, Microsoft development environment constructs these automatically for you. Other development environments provide similar tools and facilities, either as explicit control files or as part of a "proyect description file"". Brent E. Rector y Joseph M. Newcomer. "Win32 Programming". Addison-Wesley. Abril 2005.

1 Presentacin del problema: En la prctica los programas C++ no son tan simples que ocupen un solo fichero fuente, lo normal es que ocupen varios, y los proyectos grandes pueden contener decenas de fuentes, a los que hay que sumar las libreras estndar o especficas; los ficheros de recursos ("resource files"); los de cabecera, etc. Estos proyectos pueden dar lugar a mltiples ficheros ejecutables y a libreras de distinto tipo. En estas circunstancias, si un proyecto se compone de una serie de n fuentes, digamos por ejemplo: p1.c; p2.c; p3.c; ... pn.c; para construir la aplicacin es necesario compilar individualmente (cada uno quizs con sus propias opciones de compilacin), con lo que se obtienen n objetos: p1.obj; p2.obj; p3.obj; ... pn.obj, que posteriormente son enviados al enlazador para construir los ejecutables y/o libreras. Cada vez que se realiza una modificacin en un fuente, o se cambia la versin de una librera, un fichero de recursos, un fichero de cabecera, etc. es necesario recompilar los nuevos objetos y volver a enlazar. En proyectos grandes es un gran consumo de tiempo rehacer toda la aplicacin cada vez, incluyendo los mdulos que no han sufrido modificacin (como hemos hecho en el ejemplo anterior 1.4.0), por lo que se intenta que los cambios, recompilaciones, enlazados, etc. solo afecten a los mdulos estrictamente necesarios. Adems, pueden existir decenas de comandos involucrados en la operacin, cada uno con multitud de argumentos. Todo lo cual es difcil de mantener y memorizar.

2 Make Con objeto de facilitar el trabajo de controlar qu objetos, estn desfasados respecto a otros mdulos, y qu partes de la aplicacin deben ser reconstruidas, bien porque son de fecha anterior a la ltima modificacin del fuente, o porque sencillamente no existen (fuentes nuevos que no han

sido compilados ninguna vez), los entornos de programacin C++ disponen de una utilidad especial denominada Make (en Borland C++ esmake.exe; en MS Visual C++ es nmake.exe). Make es una utilidad que simplifica los ciclos de compilacin y enlazado; ayuda a construir rpidamente grandes proyectos porque permite compilar solo aquellos fuentes que han sufrido modificacin desde la ltima compilacin y reconstruir los ejecutables que dependen de los objetos resultantes. Adems, permite establecerse una serie de reglas que especifican como debe procederse con las circunstancias especiales de cada caso. Pero make es algo ms que una utilidad ligada a la compilacin y enlazado, se trata de una herramienta genrica para ejecutar comandos en base a ciertas dependencias, y aunque generalmente est ligado a las operaciones de compilar y enlazar ficheros, puede especificarse prcticamente cualquier comando aceptado por el Sistema Operativo. Nota: en cierta forma, Make es una utilidad un tanto extraa, o cuando menos "peculiar". Por sus caractersticas no se parece a nada que hayamos visto antes; de forma que cuesta un poco "meterse" en su filosofa de trabajo. A pesar de ser cierto lo dicho -que es una herramienta genrica-, en realidad su diseo est orientado a resolver un problema muy concreto (descrito brevemente en el punto anterior), lo que es sin duda el origen de su singularidad. Como suele ocurrir con este tipo de aplicaciones de diseo muy especfico, hace bien su trabajo (para propsitos generales existen lenguajes Script mucho ms adecuados). Adems no es una herramienta exclusiva de C++, otros compiladores utilizan la misma o muy parecida, ya que resuelve un problema que es general a todos los lenguajes que dependen de un proceso de compilacin y enlazado. El lector que se enfrenta por primera vez al asunto, puede pensar que las definiciones y explicaciones que se exponen a continuacin son un "rollo" difcil de entender. De momento no tiene imgenes mentales previas sobre las que construir las ideas que le ayuden a comprender de que se est hablando (tampoco se pueden poner ejemplos hasta que se tienen un mnimo de definiciones y explicaciones), pero tened paciencia, en cuanto se llega a los ejemplos concretos se empieza a entender "de que va el asunto" y cual es su intrngulis.

3 Filosofa de trabajo La filosofa de trabajo de Make se basa en una sencilla frase: "Ejecutar comandos en base a ciertas dependencias para conseguir determinados objetivos". Adelantemos aqu que las palabras: comandos, dependencias y objetivos ("targets") deben tomarse en un sentido amplio. Comandosson acciones que pueden responder cualquier orden aceptada por el SO y/o especficas que puede realizar Make; dependencias son una serie de relaciones que puede establecer el programador; suelen referirse a relaciones entre ficheros. Generalmente del tipo " la ltima modificacin del fichero XXX debe ser posterior a la del fichero YYY; en caso contrario, se ejecutar el comando zzz". Cuando las dependencias se refieren a la existencia de ciertos ficheros, que son necesarios para crear otro, tambin es frecuente designarlas como prerrequisitos. Los objetivos o resultados son generalmente la construccin de ejecutables; ficheros objeto; ficheros de recursos, o libreras de cualquier tipo, aunque un targetpuede ser tambin el nombre que se le da a una determinada accin. Por ejemplo, borrar ciertos ficheros; moverlos de sitio; etc. En este ltimo caso se denominan objetivos adicionales o falsos ("phony targets"). Observe que, en determinados casos, los prerrequisitos (existencia de ciertos ficheros como condicin previa para la construccin de otros) son tambin objetivos para Make, por lo que deben incluirse los comandos necesarios para su construccin [1], pero estos objetivos no siempre son necesarios. Si tales ficheros existen y estn actualizados, no son considerados como "targets". En

este sentido, se denominan objetivos principales ("Goals") los que constituyen el fin ltimo de Make. Los dems objetivos son accesorios y no siempre necesarios. Es evidente que en la invocacin de Make se pretende siempre al menos un goal, aunque puede ser un phony goal.

4 Makefiles Make es en realidad un intrprete de comandos con su propio lenguaje de "scripts" [3], de forma que las instrucciones de operacin se encuentran en un fichero de texto ASCII denominado makefile (puede tener distintas terminaciones, por ejemplo .mak). Un makefile contiene reglas que en cada caso indican al intrprete qu resultados ("goals") se pretenden; qu acciones son necesarias, y qu condiciones deben presentarse para que tales acciones sean ejecutadas. Make analiza este escript para determinar el orden en que deben ser construidos los distintos componentes. Como resultado se pueden obtener uno o varios ficheros (generalmente ejecutables) a los que denominamos fichero resultado. Por ejemplo, si un ejecutable se obtiene a partir de tres ficheros-objeto, estos deben ser construidos antes de ser entregados al enlazador para que forme con ellos un solo fichero. Esto puede deducirlo Make automticamente, sin necesidad de que coloquemos las instrucciones que generan los ficherosobjeto antes de las que generan el ejecutable. Los makefiles pueden incluso contener instrucciones para instalar el programa resultante en la mquina cliente. Distribucin e instalacin de aplicaciones. Generalmente las aplicaciones son distribuidas en forma de ficheros comprimidos que contienen los ejecutables y sus libreras. Por ejemplo, DLLs, as como los ficheros de datos; de ayuda; de configuracin, etc. Generalmente un programa de instalacin es el encargado de desempaquetar los mdulos e instalarlos en los lugares adecuados de la mquina del cliente, y en caso necesario. Por ejemplo, en muchas aplicaciones Windows, incluir las entradas correspondientes en el Men de Inicio, y realizar las modificaciones pertinentes en el Fichero de Registro. En ocasiones se incluye tambin un programa de desinstalacin que, en su caso, adems de deshacer los cambios efectuados en el fichero de registro, se encarga de borrar los ficheros y/o directorios instalados. En el mundo Linux es muy frecuente que las aplicaciones sean distribuidas mediante los fuente, delegndose en el usuario la tarea de construir la aplicacin e instalarla [6]. Los ficheros necesarios suelen venir tambin en formato comprimido, de los que existen varios tipos, aunque los ms comunes son los conocidos tarball; ficheros obtenidos con una utilidad (tar) que los empaqueta en un nico fichero .tar. A continuacin otra utilidad comprime el fichero resultante, con lo que se obtiene un fichero .tar.gz o algo similar. Para facilitar la construccin, junto con los fuentes y cualquier fichero auxiliar necesario, suele incluirse un makefile. Aunque tambin es frecuente que el makefile sea construido por el propio usuario en base un script de nombre configure, que puede ser ejecutado (mejor diramos interpretado) por el shell del Sistema [7]. configure est diseado de forma que genere un makefile adecuado a las caractersticas del sistema anfitrin, para lo que comprueba el tipo de procesador; si es de 32 o 64 bits. Etc. El sistema permite que la aplicacin resultante pueda ser confeccionada "a medida" del anfitrin. Adems, en ocasiones se permite que el usuario personalice algunas opciones de la aplicacin segn sus preferencias o necesidades. Por ejemplo, la localizacin preferida para los ficheros de la aplicacin, seleccin del idioma, etc.

Los makefile son ficheros ASCII (texto plano) que contienen las instrucciones, que al ser

interpretadas por make, permiten construir el proyecto. Esto supone que para cada fichero a construir, el makefile incluya informacin del siguiente tenor: Objetivos: definen nombre y extensin del fichero que se desea construir. Tanto el principal ("goal") como los dependientes. Camino ("path"): lista de directorios que informan al compilador donde encontrar los ficheros necesarios (libreras, includes, etc). Dependencias: fichero/s cuyo "timestamp" (fecha y hora de creacin) debe comprobarse para ver si son ms modernos que el del fichero a construir, y en tal caso, proceder a la construccin o reconstruccin. Comandos: rdenes que se trasladan directamente al "Shell" del SO o representan la invocacin de determinadas utilidades (compilador, enlazador, etc). Son los responsables de alcanzar los objetivos propuestos.

Naturalmente, la redaccin de las lneas de rdenes del makefile requiere el conocimiento del lenguaje "script" de la versin de Make utilizada, as como del compilador, enlazador, compilador de recursos y cualquier otra herramienta necesaria para construir el proyecto. Recordemos que, tanto las peculiaridades de Make como del resto de herramientas, dependen de la plataforma (Windows, Unix, Linux, Solaris, etc.) y que las "suites" de programacin C++ actuales permiten construir el makefile de un proyecto de forma automtica [5]. Sin embargo, aun utilizando entornos de programacin avanzados, no est de ms un mnimo conocimiento de las peculiaridades de Make, ya que, a semejanza de lo que ocurre con la edicin de pginas Web, donde de vez en cuando es necesario manejar directamente el cdigo HTML, tambin aqu es a veces necesario hacer retoques manuales en el makefile o utilizar "includes" de nuestra propia cosecha. En general las lneas del fichero makefile son de cuatro tipos: Comentarios: Cualquier cosa que se ponga detrs del carcter almohadilla #. Por ejemplo: bcc32 hola.cpp # compilar el fichero hola.cpp # la lnea anterior produce un ejecutable Reglas Las hay de dos tipos: o Implcitas: se refieren a dependencias y comandos que ataen a la compilacin o enlazado de ficheros especficos; vienen preconstruidas en la lgica interna de Make y no necesitan ser explicitadas por el programador . o Explcitas: instrucciones para la consecucin de los objetivos redactadas por el programador . Macros : Se trata de expansiones/sustituciones parecidas a las del propio lenguaje C++ . Representan una comodidad sintctica, y al igual que sus homnimas del C++, presentan la ventaja de poder agrupar determinadas definiciones en un solo punto, lo que hace ms fcil el mantenimiento de los makefiles. Directivas: Recuerdan las de Pascal o las del propio lenguaje C++ .

5 Reglas:

Como se ha sealado, en principio Make tiene sus propias opciones por defecto, pero el fichero de instrucciones makefile puede contener reglas especficas para cada caso. Observe que aqu, "regla" puede tomarse en un sentido muy amplio; como una accin que ocurre solo bajo determinadas condiciones. Como se ha indicado, las reglas pueden ser de dos tipos: explcitas e implcitas , y se componen de dos o ms lneas con la siguiente sintaxis: resultado : prerrequisitos ... comando ... ... # lnea de dependencias # lnea de comando

Las lneas de comando deben tener un sangrado (al menos un espacio) respecto a las lneas de dependencias correspondientes [2]. Por ejemplo, una regla podra tener el siguiente diseo: fuente.exe: fuente.o bcc32 -P -RT fuente.cpp # Lnea de dependencias # lnea de comando (ojo al sangrado)

Aqu fuente.exe es el objetivo o resultado ("target") deseado y fuente.o es el prerrequisito. La regla debe interpretarse como sigue:sifuente.exe no existe, o su timestamp es anterior al de fuente.o, o fuent.o no existe, ejecutar el comando indicado en la segunda lnea (obtener el fuente.exe a partir de la compilacin y enlazado de fuente.cpp). Tambin es destacable que una regla puede carecer de prerrequisitos, lo que ocurre cuando no se requieren ningunas condiciones especiales para obtener el resultado (ejecutar el/los comandos asociados). Sera el siguiente caso: cleanAll: rm -f pr1.o pr2.o pr3.o main.exe

Mientras que la lnea de dependencias utiliza diversas sintaxis, segn se trate de reglas explcitas o implcitas, la lnea de comando siguen siempre la misma. Make soporta mltiples lneas de dependencia para un solo resultado, y un resultado puede tener mltiples lneas de comando. Sin embargo, solo una lnea de dependencia debe contener una lnea de comando correspondiente. Por ejemplo: Destino1: dependent1 dep2 dep3 dep4 dep5 Destino1: dep6 dep7 dep8 bcc32 -c $** Cualquier lnea del makefile puede continuar en la lnea siguiente utilizando la barra inclinada ( \ ) al final de la primera lnea. Ejemplo: MIFUENTE.EXE: FILE1.OBJ \ FILE2.OBJ bcc32 file1.obj file2.obj # Lnea de dependencia # continuacin de la anterior # Lnea de comando

5.1 Reglas explcitas

Las reglas explcitas son las instrucciones contenidas en el makefile para conseguir cierto resultado. Como se ha indicado, tienen al menos dos lneas, de dependencia y de comando. Su sintaxis es la siguiente: resultado [resultado...]:[:][{path}] [prerrequisito[s]...] dependencia [prefijo] comando # Lnea de comando # L. de

Volvemos a insistir que la accin (indicada por el comando) no tiene que consistir necesariamente en construir un ejecutable, y que el resultado puede ser un simple nombre que identifica una serie de relaciones. Por ejemplo el fichero: # Makefile para copiar ficheros .htm en el directorio de objetos copiahtm : {D:\ZWeb\Zhome\Cpp\}E1.htm E2.htm &copy $** objetos sera un makefile correcto para Make 5.2 de Borland, que no tendra ninguna relacin con la construccin de un ejecutable. Aqu el resultado copiahtm sera lo que hemos denominado un phony goal ; una etiqueta para designar las relaciones sealadas en el resto de la lnea. El comando expande el contenido de la etiqueta (gracias al indicador &) como si fuese una macro, y aplica al resultado de la expansin, el comando copy del "Shell" del SO. Como es de prever, el resultado es que los ficheros E1.htm yE2.htm del directorio D:\ZWeb\Zhome\Cpp, son copiados al directorio objetos.

Lnea de dependencia: se componen de uno o ms nombres de fichero resultado seguidos de ( : ) o dos dobles puntos ( :: ). Un doble punto (: ) significa que una regla se refiere al resultado/s; dos dobles puntos ( :: ) significan que dos o ms reglas son para el/los resultado/s. resultado: especifica el nombre del objetivo ("target") a conseguir. Generalmente es un nombre de fichero. Debe ser un comienzo de lnea en el fichero makefile, y no puede ser precedido con espacios o tabulaciones. Para indicar ms de un objetivo, deben separarse sus nombres con espacios o tabulaciones. No puede utilizarse un nombre de resultado ms de una vez en una regla explcita. path: una lista de direcciones que indican a make donde buscar los ficheros dependientes. Estas direcciones deben estar separadas por punto y coma (;), encerrando todo con corchetes { } (no dejar espacio entre el corchete de cierre y el primer fichero !!). Ejemplo : copiahtm : {D:\ZWeb\Zhome\Cpp\}E1.htm E1_1.htm

prerrequisito/s: nombre de los objetivos que deben satisfacerse antes de ejecutar el comando sealado por la regla. Generalmente son nombres de ficheros cuyas fecha y hora deben ser comprobadas por make para comprobar si son ms recientes que el fichero destino. Cada nombre debe ser precedido por un espacio. Si un fichero dependiente aparece en algn sitio como resultado, make actualiza o crea ese destino antes de utilizarlo como prerrequisito en el resultado original. Es lo que se conoce como dependencia

encadenada. Lnea de comando: Puede ser cualquier comando aceptado por el SO. Las lneas de comando deben ser sangradas (indentadas) al menos con un espacio o tabulacin ( de lo contrario son interpretadas como lneas dependencias !!). Pueden indicarse mltiples rdenes separadas por espacios. La sintaxis geneal es: [prefijo] comando Ejemplos: &copy $** objetos # representa el sangrado inicial; & es el prefijo cd.. bcc32 -c mysource.c COPY *.OBJ C:\PROJECTO bcc32 -c $(SOURCE) @bcc32 diff.obj -bcc32 hola.cpp

El prefijo es opcional, y depende de la implementacin. comando puede ser prcticamente cualquiera aceptado por el SO, adems, cada versin de Make acepta algunos comandos especficos.

5.2 Reglas implcitas Son reglas preconstruidas en la lgica interna de Make, que permiten construir los tipos de ficheros ms usuales sin que el programador tanga que definir explcitamente los comandos necesarios. Estas reglas suelen utilizar algunas macros tambin predefinidas (ver a continuacin ). Por ejemplo, el Make de GNU dispone de una regla implcita que le permite construir ficheros .o a partir de ficheros .cpp (fuentes C++): %.o: %.cpp $(COMPILE.cpp) $(OUTPUT_OPTION) $< a su vez, COMPILE.cpp y OUTPUT_OPTION son sendas macros tambin predefinidas: COMPILE.cpp = $(COMPILE.cc) COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c CXX = g++ OUTPUT_OPTION = -o $@ En consecuencia, la lnea de comando de la regla resulta equivalente a g++ $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $< Aqu g++ representa la invocacin del compilador GNU C++ ( g++.exe); a suv vez $@ y $< son variables automticas de Make, cuyos valores se calculan cada vez que se

ejecuta la regla en base al contenido de la lnea de dependencias. En concreto, $@ se traduce en el nombre del resultado (target) de la regla, mientras que $< es el nombre del primer prerrequisito. El resultado es que en esta regla implcita, pueden ser tomados como equivalentes al nombre del fichero objeto (target) y del fichero fuente (prerrequisito). En consecuencia, la lnea de comando anterior es equivalente a: g++ $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o %.o %.cpp

La utilidad de todo esto puede entenderse si consideramos un ejemplo. Supongamos que compilamos un proyecto que depende de dos fuentes: main.cpp y modulo.cpp. La construccin del proyecto puede ser encomendada a un makefile adecuado a nuestro entorno: INCLUDES = -I"C:/GNU/include" \ -I"C:/GNU/include/c++" main.exe: main.o modulo.o g++ main.o modulo.o -o "main.exe" -L"C:/GNU/lib" main.o: main.cpp g++ -c main.cpp -o main.o $(INCLUDES) modulo.o: modulo.cpp g++ -c modulo.cpp -o zstring.o $(INCLUDES) El makefile anterior, aunque correcto, puede ser simplificado con mnimas modificaciones hasta quedar con el siguiente diseo: CXXFLAGS = -I"C:/GNU/include" \ -I"C:/GNU/include/c++" main.exe: main.o modulo.o g++ main.o modulo.o -o "main.exe" -L"C:/GNU/lib" main.o: main.cpp modulo.o: modulo.cpp La razn es que, ante reglas como las de las dos ltimas lneas, para las que no existe ninguna lnea de comando que informe sobre la forma de obtener los ficheros .o a partir de los fuente .cpp, Make hecha mano de sus reglas implcitas para ver si existe una forma de hacerlo. La regla comentada al principio del epgrafe es la adecuada, as que es aplicada con las sustituciones pertinentes en cada instancia, y el proyecto es construido sin dificultad. Observe que ha sido necesario renombrar la macro original INCLUDES, que ahora pasa a denominarse CXXFLAGS. La razn es la ya comentada de que Make utiliza ciertos nombres en sus reglas implcitas; nombres que deben ser respetados si queremos aprovechar las posibilidades de este tipo de reglas. Como ltima observacin, sealar que el proyecto podra ser construido con un makefile an ms simple: CXXFLAGS = -I"C:/GNU/include" \ -I"C:/GNU/include/c++"

main.exe: main.o modulo.o g++ main.o modulo.o -o "main.exe" -L"C:/GNU/lib" La razn es que, aunque en este caso no se indica que main.o o modulo.o deban ser obtenidos de la compilacin de sus homnimos .cpp, Make dispone de reglas implcitas para intentar la compilacin en caso de que tales ficheros existan. La forma de hacerlo es anloga a la comentada.

6 Macros El lenguaje "script" de make permite el uso de macros parecidas (aunque diferentes) a las del lenguaje C++ ( 4.9.10b ). Una macro tiene dos partes: La etiqueta identificativa y el texto en que se expandir. El proceso de usar la macro tiene dos pasos: definir la macro y sealar el punto de expansin (donde de realiza la sustitucin de la macro -la etiqueta- por el texto). La definicin se realiza colocando una etiqueta (nombre de la macro); el smbolo de asignacin ( = ) y el texto. Como en el caso de C++, suelen utilizarse maysculas en las etiquetas. La etiqueta debe aparecer en el primer carcter de la lnea (sin espacio o sangrado previo de ningn tipo). Por ejemplo: COMPILA = bcc32 -MProyecto.map # define la macro COMPILA

El sitio de la sustitucin se seala encerrando en un parntesis la etiqueta de la macro a expandir precedido del smbolo $. Por ejemplo, la lnea de comando: $(COMPILA) -eProyecto.exe pa.obj pb.obj ser expandida a: bcc32 -MProyecto.map -eProyecto.exe pa.obj pb.obj # Expande la macro COMPILA

Recordar que, como todo en el lenguaje C++, la etiqueta de la macro es sensible a mayscula/minscula; COMPILA y Compila seran macros distintas. El texto en que se expandir la macro puede contener hasta 4096 caracteres (Borland C++ make), cada macro se define en una lnea diferente, aunque una macro puede contener ms de una lnea separadas por la barra inclinada "\". Si existen dos macros distintas con la misma etiqueta, la segunda sobreescribe a la primera definicin. 6.1 Macros predefinidas Es digno de mencin que Make tiene dispone de gran nmero de macros predefinidas (dependen de la implementacin) que pueden ser aprovechadas para simplificar la redaccin del texto y para las reglas implcitas. Si una lnea del makefile redefine alguna de estas macros predefinidas, es vlida la regla del prrafo anterior (la versin del makefile sobreescribe a la definicin inicial). Por ejemplo, la versin GNU de Make dispone de la siguiente macro predefinida: RM = rm -f

en consecuencia, en dicha versin de Make es posible utilizar la siguiente regla sin necesidad de definir previamente la macro: clean: ${RM} objeto.o cabecera.h ejecutable.exe

7 Directivas Los scripts de make permiten incluso directivas del tipo include, undef, ifdef, if, elif etc. que recuerdan las del propio lenguaje C++ ( 4.9.10

8 Invocacin La sintaxis para invocar make depende de la plataforma, pero generalmente es del tipo: make [opciones] [resultado [resultado]]

Las opciones son indicaciones que controlan aspectos del funcionamiento de Make. Generalmente puede obtenerse una lista de ellas y su significado invocando make con el argumento -? o -h. La palabra "make", las opciones y los resultados deben estar separados por espacios. resultado es el nombre del fichero/s que se pretende construir. Este nombre se ha indicado tambin en makefile (el fichero que lee Make para extraer las instrucciones).

En principio Make tiene sus propias opciones de operacin por defecto, pero la invocacin puede imponer sus propias reglas. Por ejemplo, en ausencia del argumento resultado en la invocacin, Make establece que el goal es el primer resultado que aparece reseado en el makefile, pero invocndolo con un argumento puede establecerse que el goal sea otro. Por ejemplo, teniendo el makefile mk1.txt: # Malefile mk1.txt all: main.exe

clean: erase main.o erase main.h erase main.exe main.exe: main.o main.h g++.exe main.o -o "main.exe" -L"C:/DEV-CPP/lib" La invocacin make -f mk1.txt

obliga a Make a utilizar el fichero mk1.txt en lugar del makefile establecido por defecto (por ejemplo make.mak para Borland 5). En este caso, elgoal es all, el primer resultado que se encuentra, lo que conduce a que se ejecute la regla de las lneas 10-11, con el resultado de que se enlaza el fichero main.o para obtener el ejecutable main.exe. En cambio, la invocacin make utilizara el makefile por defecto. En caso de que utilizramos Borland 5 y que no existiera el fichero make.mak, se obtendra un error. Finalmente, la invocacin make -f mk1.txt clean seala a Make que se pretende el resultado sealado por la regla clean; lo que conduce a que se ejecuten los comandos de las lneas 6 a 8, sin que tenga lugar ninguna otra accin.

9 Secuencia de ejecucin Despus de cargar el makefile, make trata de construir solamente el primer resultado explcito incluido en el makefile. Para esto comprueba la fecha y hora de los ficheros dependientes del primer resultado ("goal"). Si son ms recientes que las del fichero resultado, make ejecuta el comando asociado para actualizarlo. Si alguno de los ficheros dependientes del primer resultado es utilizado a su vez como resultado en algn otro punto del makefile, make comprueba dichas dependencias y construye ese resultado antes que construir el primero. Este comportamiento se denomina dependencia encadenada. Ejemplo: ejecutable.exe : objeto.o bcc32 objeto.o ... objeto.o : cabecera.h trigraph cabecera.h # L.1: lnea de dependencias # L.2: lnea de comando # L.3: lnea de dependencias # L.4: lnea de comando

En este makefile, el goal es ejecutable.exe; se supone que este fichero se construye con el comando sealado en L.2, y est relacionado conobjeto.o. Si la fecha y hora de objet.o son posteriores a las de ejecutable.exe (o sencillamente ejecutable.exe no existe), se ejecuta el comando. No obstante, como el prerrequisito objeto.o aparece a su vez como resultado de la regla L.3, deben satisfacerse primero las condiciones de este fichero, para lo que se invoca el comando sealado en L.4 (si objeto.o no existe o su "timestamp" es anterior que el decabecera.h). Por ltimo se ejecutara L.2. Si se produce algn fallo durante el proceso, make borra los ficheros que estuviese construyendo. Si desea que make conserve el fichero destino a pesar de un posible fallo, debe utilizarse la directiva .precious. Los comandos Ctrl+Break o Ctrl+C permiten abortar el trabajo de make una vez iniciado.

Nota: la utilidad Make es ms potente y compleja de lo que se ha sealado someramente en este captulo [4], en especial las opciones relativas a macros y las directivas de proceso. No pretendemos aqu reproducir el manual del Make de Borland, de GNU, ni de ningn otro compilador especfico, solo indicar las lneas generales de lo que puede esperarse de esta utilidad y su filosofa de funcionamiento. No obstante, esta introduccin le permitir comenzar practicar con sus propios makefiles, entender mejor los captulos que siguen dedicados a versiones especficas de Make, e ir explorando sus posibilidades con ayuda del manual de su compilador.

Das könnte Ihnen auch gefallen