InicioCiencia EducacionC++ (Primera parte)
El lenguaje C++
"Learn not just the hows, but the whys too". Greg Comeau .
§1 Generalidades
C++ es un lenguaje imperativo orientado a objetos derivado del C . En realidad un superconjunto de C, que nació para añadirle cualidades y características de las que carecía. El resultado es que como su ancestro, sigue muy ligado al hardware subyacente, manteniendo una considerable potencia para programación a bajo nivel, pero se la han añadido elementos que le permiten también un estilo de programación con alto nivel de abstracción.
Nota: estrictamente hablando, C no es un subconjunto de C++; de hecho es posible escribir código C que es ilegal en C++. Pero a efectos prácticos, dado el esfuerzo de compatibilidad desplegado en su diseño, puede considerarse que C++ es una extensión del C clásico. La definición "oficial" del lenguaje nos dice que C++ es un lenguaje de propósito general basado en el C, al que se han añadido 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 librería (en realidad la librería Estándar C es un subconjunto de la librería C++).
Respecto a su antecesor, se ha procurando mantener una exquisita compatibilidad hacia atrás por dos razones : poder reutilizar la enorme cantidad de código C existente, y facilitar una transición lo más fluida posible a los programadores de C clásico, de forma que pudieran pasar sus programas a C++ e ir modificándolos (haciéndolos más "++" de forma gradual. De hecho, los primeros compiladores C++ lo que hacían en realidad era traducir (preprocesar) a C y compilar después (las consecuencias se dejan sentir todavía 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 características especiales de C++. Algunas situaciones requieren especial cuidado. Por ejemplo, si se declara una función dos veces con diferente tipo de argumentos, el compilador C invoca un error de "Nombre duplicado", mientras que en C++ quizás sea interpretado como una sobrecarga de la primera función (que sea o no legal depende de otras circunstancias).
Como se ha señalado, C++ no es un lenguaje orientado a objetos puro (en el sentido en que puede serlo Java por ejemplo), además no nació como un ejercicio académico de diseño. Se trata simplemente del sucesor de un lenguaje de programación hecho por programadores (de alto nivel) para programadores, lo que se traduce en un diseño pragmático al que se le han ido añadiendo todos los elementos que la práctica 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" ). Estos condicionantes tienen su cara y su cruz; en ocasiones son motivo de ciertos "reproches" por parte de sus detractores, en otras, estas características son precisamente una cualidad. De hecho, en el diseño de la Librería Estándar 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 programación extraordinariamente flexible (programación genérica).
Aunque C++ introduce nuevas palabras clave y operadores para manejo de clases, algunas de sus extensiones tienen aplicación fuera del contexto de programación con objetos (fuera del ámbito de las clases), de hecho, muchos aspectos de C++ que pueden ser usados independientemente de las clases .

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, también puede hacer que ciertos errores sean difíciles de descubrir" ( Shildt). Respecto al C++ podríamos decir otro tanto, pero hemos de reconocer que su sistema de detección de errores es mucho más robusto que el de C, por lo que algunos errores de este serán rápidamente detectados.
Desde luego, C++ es un lenguaje de programación extremadamente largo y complejo; cuando nos adentramos en él parece no acabar nunca. Justo cuando aprendemos un significado descubrimos que una mano negra ha añadido otras dos o tres acepciones para la misma palabra. También descubrimos que prácticamente no hay una regla sin su correspondiente excepción. Cuando aprendemos que algo no se puede hacer, hay siempre algún 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 creación. De hecho, muchos sistemas operativos , compiladores e intérpretes han sido escritos en C++ (el propio Windows y Java). Una de las razones de su éxito es ser un lenguaje de propósito general que se adapta a múltiples situaciones. Para comprobar el éxito e importancia de los desarrollos realizados en C++ puede darse una vuelta por la página que mantiene el Dr. Stroustrup al respecto: www.research.att.com.
Tanto sus fervientes defensores como sus acérrimos detractores han hecho correr ríos 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 justificación para el diseño 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 programación (metaprogramación).
En mi opinión, cualquier lenguaje de propósito general que como C++, permita tocar ambos mundos, la programación de bajo nivel y altos niveles de abstracción, resultará siempre e inevitablemente complejo. Ocurre lo mismo con los lenguajes naturales que son también extraordinariamente complejos (esto lo saben bien los gramáticos). Cualquier comunicación entre humanos presupone una ingente cantidad de conocimientos y suposiciones previas entre los interlocutores. A pesar de lo cual, la comunicación exacta y sin ambigüedades entre dos personas no resulta fácil.
§2 Consejos para mejorar el rendimiento
Lo mismo que en su ancestro, en el diseño del C++ primó sobre todo la velocidad de ejecución del código . Tanto uno como otro representan los ejecutables más rápidos que se pueden construir para una máquina y circunstancias determinadas. En este sentido, la única alternativa de mejora es la codificación manual, el "pulido" de determinadas rutinas (o de todo el código) 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 intrínsecamente rápido, y de que los compiladores modernos son bastante "inteligentes" en este sentido (adoptan automáticamente las decisiones que resultan en el código de ejecución más 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 numérica. En especial en los contadores de bucles. Las operaciones con enteros son del orden de 10 a 20 veces más rápidas que las de números en coma flotante.
• Use operadores incremento y decremento ++/-- ( 4.9.1)
• Use variables de registro, en especial en los bucles críticos, sobre todo si son anidados ( 4.1.8b).
• Use aritmética de punteros frente a subíndices de matrices ( 4.2.2).
• En problemas de computación numérica recuerde que el cálculo 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 mínimo las funciones virtuales ( 1.4.4; 4.11.8a), así como los punteros a funciones-miembro ( 4.2.1g)
o Tenga en cuenta lo señalado respecto al rendimiento al tratar de:
o Sustituciones inline en funciones definidas por el usuario ( 4.4.6b)
• Preste atención al modo de uso de aquellas funciones de librería que se presentan en dos versiones ( 5.1 Funciones y macros)
Los compiladores modernos permiten fijar que criterio de optimización será dominante: La velocidad de ejecución o el tamaño. Tanto Borland C++ ( 1.4.3) como MS Visual C++ utilizan la misma convención de llamada para este propósito (opciones -O2 o -O1 respectivamente). Por su parte, GNU gcc dispone de varias opciones de optimización. En particular, la opción -Os adopta las medidas tendentes a reducir el tamaño del código resultante.
Nota: Aparte de las decisiones de optimización que puedan adoptar automáticamente los compiladores y las reglas de precaución anteriores, las modernas "suites" ofrecen herramientas de análisis que permiten conocer de forma objetiva como es la utilización de recursos dentro del programa, de forma que se puedan adoptar precauciones en las zonas que resulten más costosas, y concentrar nuestros esfuerzos de optimización en la zonas donde resulten más provechosos. El compilador GNU cpp dispone de la utilidad gcov, que ofrece estadísticas para analizar el rendimiento del código. Entre otros datos:
• Cómo se ejecuta cada línea de código
• Qué línea se está ejecutando actualmente.
• Cuanto tiempo consume cada sección de código
• Posibilidad de análisis a nivel de fichero o de función
Inicio.
________________________________________
Los lenguajes imperativos son aquellos en los que se especifica cómo conseguir los objetivos que se persiguen. C; C++; Javascript y Perl, entre otros muchos, pertenecen a esta categoría. Por contra, los lenguajes declarativos son aquellos en los que se especifica que objetivo se persigue, sin preocuparse por el cómo. SQL y HTML son quizás los ejemplos más representativos de esta categoría.
El propio Stroustrup reconoce en el prólogo de su primer libro sobre C++: "Se ha dado gran importancia a mantener compatibilidad con C .... C++ debe la mayor parte a C. C es mantenido como un subconjunto" ( Stroustrup 1987).
De hecho, el mentado compilador Zortech C++, se ofrecía como: "The World's first 'TRUE' C++ compiler for MS-DOS".
Stroustrup señala que una de las razones que llevaron a K&R a diseñar el C fue no tener que programar en ensamblador (*), y que en el diseño de C++ se cuidó no perder las ventajas ganadas en este sentido. Precisamente comenzó a desarrollar C++ como un lenguaje que le ayudara en el trabajo de diseño de un Sistema Operativo distribuido.
(*) En ocasiones, C ha sido considerado como un "lenguaje ensamblador de alto nivel".
"You are not doing OO just because you are compiling with C++" Ian Joyner "C++??" ( 7).
Bjarne Stroustrup. Entrevista con Al Stevens en Dr. Dobb's Journal Sept. 1992.
Entrevista en "C++ Pointers and Dynamic Memory Management" de Michael C. Daconta. Edit. John Wiley & Sons, Inc. ISBN 0-471-04998-0






1.2.1 Léxico y conceptos fundamentales
"Este ha sido mi último curso. Me acabo de jubilar después de cerca de 40 años dedicado a la enseñanza. A pesar de la poca consideración que mi profesión ha tenido económica y socialmente, acabo con la misma ilusión con la que comencé. E igualmente convencido de la trascendencia de la tarea de educar. Sólo me duele un poco el comprobar la menor eficacia que ésta ha tenido estos últimos años.

¿Cuáles 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 visión lúdica de la educación, ni las políticas educativas con pretensiones de progresismo que priman el uniformismo sobre la búsqueda de la excelencia han favorecido en absoluto la cultura del esfuerzo, imprescindible en cualquier proceso personal de mejora, como es la educación". Federico Gómez Pardo en "La voz del Lector" de "El Confidencial Digital" (22-06-2006) ECD.
§1 Presentación
Creo que buena parte de la dificultad del principiante respecto a algunos conceptos del lenguaje C++, y de muchas otras áreas de la ciencia informática, proviene de un conocimiento incompleto o vago de algunos conceptos fundamentales. En realidad se trata de una cuestión semántica; de conocer el significado exacto de algunas palabras; de disponer de un vocabulario mínimo que sirva de soporte para entender el resto. Ocurre con frecuencia que los textos informáticos están plagados de términos que supuestamente son de uso común, pero que en realidad no lo son tanto y cuyo significado tampoco está claramente explicado en ningún sitio. Se dan por sabidos, pero las más de las veces el estudiante es incapaz de verbalizar correctamente su significado. Es importante que este "corpus" mínimo sea conocido sin ambigüedades, de forma que los conceptos construidos sobre él no tengan fisuras y resulten de una solidez conceptual a toda prueba.
Estas cuestiones semánticas son evidentemente el objeto fundamental de los gramáticos y de los puristas del lenguaje (aunque sean lenguajes artificiales como los de programación), de forma que en el caso del C++, la primera preocupación del Estándar es definir sin ambigüedad una serie de términos. Introduciré aquí algunos de los que creo más importantes, en la seguridad de que su conocimiento atañe 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 interés en la ciencia informática actual .
Escribir un programa es establecer el comportamiento de una máquina mediante una serie de algoritmos que definirán su funcionamiento ( 1.4). Según el DRAE (Diccionario de la Real Academia Española de la Lengua) un algoritmo es un conjunto ordenado y finito de operaciones que permite hallar la solución de un problema . En informática se utiliza en el sentido de un conjunto ordenado y finito de instrucciones que gobiernan el comportamiento de una máquina para conseguir un comportamiento determinado. Las instrucciones se expresan en un lenguaje artificial (inventado conscientemente por el hombre) denominado lenguaje de programación.
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 su gramática. El lenguaje C++ alcanzó oficialmente esta madurez y universalidad en 1989, de forma que a partir de entonces, el Estándar C++ establece la gramática del lenguaje que nos ocupa.
Aunque todos los lenguajes de programación tienen su "Gramática", el enfoque respecto a la misma varía 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 gramática rígida permite afinar mucho en los matices de lo que deseamos hacer; manipular los conceptos (principalmente los datos) con gran precisión, y ser advertidos de posibles errores involuntarios. Pero hay que estar muy atento a los detalles del código, y hasta que no se tiene cierta práctica, los mensajes de aviso y errores del compilador pueden desesperar a cualquiera.
Otro extremo está representado por los lenguajes de sintaxis más o menos "borrosa". Por ejemplo JavaScript o Perl, en los que el compilador, o intérprete, pretende adivinar qué se supone que queremos hacer. Este comportamiento se conoce como DWIM ("Do what I mean" .

La gramática 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: semánticas (de significado) y sintácticas (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 sintácticas son muchas, pero comienzan por las que definen el alfabeto del lenguaje. Es decir: el conjunto de grafos que pueden utilizarse para escribir su código.
C++ permite utilizar un alfabeto de 96 caracteres ASCII en la escritura de sus programas. De ellos 91 son los caracteres gráficos (representables por un grafo) que se incluyen en la tabla; los cinco restantes son caracteres no representables: Espacio; tabulación horizontal; tabulación vertical; salto de formato y nueva línea.
Caracteres gráficos 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 sintácticas) el compilador debe lanzar un mensaje de error o indicación 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 semánticas no ocurre siempre así. El estándar 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 infracción, 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 semánticas definidas en el Estándar pueden considerarse de significado diagnosticable, a excepción de aquellas que tienen una indicación explícita de que no necesitan diagnóstico.

Las reglas están definidas de modo que cuando se establece una condición 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 ejecución, el Estándar no establece ningún comportamiento especial para el programa.

Comportamiento dependiente de la implementación: Cuando el comportamiento de un programa correcto con datos correctos, depende de la implementación. Este comportamiento debe estar documentado en cada implementación.
Comportamiento indefinido: Es el comportamiento de un programa en aquellas circunstancias o situaciones para las que el Estándar no especifica un comportamiento específico. Sería el caso del comportamiento en runtime de un programa que recibe datos incorrectos. Observe que, según lo indicado al principio, muchos casos de programas erróneos no generan un comportamiento indefinido, sino un aviso de diagnóstico.
Implementación: Un compilador concreto para una plataforma determinada.
Límites de implementación: Restricciones impuestas a los programas por una implementación.
Programa correcto ("Well-formed": Programa C++ construido de acuerdo con las reglas sintácticas de este lenguaje, con reglas de semántica diagnosticable, y que sigue la regla de una sola definición ( 4.1.2). Un programa incorrecto ("Ill-formed" es el que no sigue los condicionantes anteriores.
Un identificador es un conjunto de caracteres alfanuméricos de cualquier longitud que sirve para identificar las entidades del programa. Los identificadores pueden ser combinaciones de letras y números, 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 específico 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 condición de que estén en ámbitos ( 4.1.3) diferentes, o que sean funciones con distinto número y/o tipo de argumentos.
Nota: otro tipo especial de identificadores lo constituyen las etiquetas; en vez de representar entidades sirven para significar sentencias . Se utilizan para identificar el destino en sentencias de salto ( 4.10.4).

La introducción de un nombre en el programa puede efectuarse mediante alguna de estas formas:
• Si el nombre representa a una entidad, mediante una declaración ( 4.1.2).
• Si el nombre representa a una sentencia (es una etiqueta), mediante una sentencia etiquetada ( 4.10.1) o una instrucción de salto goto .
Que un mismo nombre utilizado en dos unidades de compilación 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 operación, que asocia sin ambigüedad cada nombre con una declaración de dicho nombre, se denomina búsqueda de nombres ("Name-lookup". Si al final del proceso no ha aparecido la declaración correspondiente entonces el programa es incorrecto ("Ill-formed" ).
Cuando el identificador corresponde a una función, puede ocurrir que el "Name-lookup" asocie un mismo nombre con más de una declaración. Es el caso de funciones sobrecargadas, en las que un mismo nombre corresponde a varias definiciones de la "misma" función. En estos casos, después de esta primera búsqueda tiene lugar otro proceso denominado de resolución de sobrecarga ("Overload resolutión" 4.4.1a) en el que se intenta averiguar a que definición concreta corresponde el identificador.
El proceso de búsqueda de nombres sigue un conjunto relativamente extenso de reglas que cubren todas las posibilidades que pueden presentarse; además existen modificadores que alteran la forma de esta búsqueda, los denominados especificadores o modificadores de acceso. Ver en la hoja adjunta una somera descripción del proceso ( Name-lookup).

El concepto de entidad es muy amplio; corresponde a: un valor; clase; elemento de una matriz; variable; función; miembro de clase; instancia de clase; enumerador; plantilla, o espacio de nombres del programa (más detalles en 4.1.1).
Un objeto es una entidad a la que corresponde una zona de almacenamiento , razón por la que se dice de ellos que tienen existencia real o física. Los objetos son introducidos en el programa mediante una declaración, y creados mediante una definición ( 4.1.2). Gozan de una serie de propiedades que vienen definidas desde el momento de su creación, entre ellas está la duración de su almacenamiento, que tiene influencia en su ciclo vital ("Lifetime", y puede ser de tres tipos: estática, dinámica y automática ( 4.1.5). Otra propiedad no menos importante de los objetos es su tipo, que también viene determinado desde el momento de su creación ( 2.2).

Las entidades del programa pueden tener otros atributos o propiedades cuya comprensión es igualmente importante: dirección (Lvalue) y valor (Rvalue). Sus características 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 dirección; espacio de almacenamiento que pueda recibir un valor .
Una entidad puede tener uno o ambos de estos atributos. Por ejemplo:
char func () { ... }
char c = '5';
const int k = 2 + 3;
La función func puede ser considerada un Rvalue en el sentido que devuelve un valor: también la variable c; la constante k, o la expresión 2 + 3 pueden ser consideradas Rvalues porque tienen o representan un valor.
Tanto la variable c como la constante k son también Lvalues, en el sentido que ambas disponen de una dirección y un espacio de almacenamiento correspondiente (que en el primer caso puede ser alterado y en segundo no). Sin embargo, ni la función func, ni las expresións '5' o 2 + 3 pueden considerarse como tales porque no les corresponde una dirección 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 expresión es un conjunto de operadores, operandos y signos de puntuación que especifican una computación (suponen una secuencia de instrucciones concretas para la máquina); generalmente, como resultado de esta computación producen un valor, aunque también, como consecuencia de ella pueden producirse otros efectos denominados efectos laterales ( 1.3.1).
Inicio.
________________________________________
Cuando a principios de los 70 la programación no-estructurada cayó en descrédito, las etiquetas, que en forma de número de sentencia acompañaba a estas en algunos lenguajes, desaparecieron. Como a pesar de todo en algunas ocasiones persiste la necesidad de acudir al denostado GOTO, se introdujeron estos identificadores para señalar el destino de estos saltos imprescindibles (las sentencias catch del mecanismo de excepciones son otro vestigio de aquello).
El lenguaje C++ establece que antes de utilizar un identificador hay que declararlo, excepto en el caso de etiquetas ("Labels".
Observe que esta definición es más amplia que la tradicional de la POO, en la que un objeto es la instancia de una clase.
El término algoritmo y la propia ciencia del álgebra, son otra aportación cultural que debemos al Islám. Esta ciencia debe mucho a Muhammad Ibn Musa, que fue miembro de la “Casa de la Sabiduría”, una notable academia científica de Bagdad en época del califa Al-Ma'amun (813-833). El propio vocablo "algoritmo" deriva del sobrenombre "Al-Khwarizmi" de Ibn Musa. Los algoritmos informáticos siguen de cerca el concepto matemático de función. Aunque estas últimas pueden exigir un número infinito de pasos para llegar a la solución.
Acrónimo inglés de "Read the fucking manual". El creador del lenguaje nos informa que generalmente es una buena idea y que la "F" es muda (no se pronuncia). Glosario de términos C++ de Bjarne Stroustrup www.research.att.com
Formalmente un Lvalue es una expresión que se refiere a un objeto o a una función (una invocación a función es un Lvalue solo si el valor devuelto es una referencia). Un Lvalue es modificable si no se refiere a una función, una matriz, o un objeto constante.
Desde luego, lo ideal sería un comportamiento DWIW ("Do What I Want", pero ya hemos señalado que los lenguajes actuales son del tipo DWIS ("Do What I Say". Quizás lo contrario sería extremadamente peligroso...
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 cuestión que puede ser abordada desde varios puntos de vista, en este capítulo consideramos solo dos:
Componentes lógicos: se refiere a los diversos elementos que componen una aplicación, desde los más complejos hasta los más simples. Si comparamos un programa con un edificio, los elementos irían desde el edificio como un todo, a los ladrillos (sus elementos más pequeños) 1.3.1.
Almacenamiento: se refiere a como están alojadas sus partes en la máquina que lo ejecuta. Esta cuestión puede ser abordada desde dos perspectivas:
• Organización lógica. Comprende las características de los diversos tipos de almacenamiento que se distinguen en un programa C++ ( 1.3.2).
• Organización física. Toma en consideración los diversos dispositivos físicos utilizados por el programa para alojarse él mismo y sus datos ( 2.2.6).
1.3.1 Estructura lógica.
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 that’s a very difficult combination.” James Maguire en su artículo "The 'Anti-Java' Professor and the Jobless Programmers". Basado en una entrevista con Robert Dewar, profesor emérito de Ciencia de la Computación en New York.
§1 Sinopsis
Desde el punto de vista lógico, 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 manipulación. Su representación codificada adopta dos formas: una entendible por la máquina (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 didáctica; son un vehículo para introducir al lector en la comprensión global del concepto y no un asunto dogmático e inamovible, ya que este tipo de asuntos depende grandemente del punto de vista que se adopte. Como botón de muestra, vaya por delante la definición de lo que es un programa en palabras de Ellis y Stroustrup [6 §3.3], que por lo demás ofrece un punto de vista muy interesante: "Un programa consiste en uno o más ficheros enlazados juntos. Un fichero consiste en una secuencia de declaraciones" (ver Declaraciones y definiciones en 4.1.2). A continuación admiten que esta afirmación puede parecer extraña a aquellos acostumbrados a pensar en un programa como una serie de sentencias ejecutables acompañadas de las declaraciones de las variables correspondientes. Pero recuerdan que en C++, las sentencias están contenidas en funciones, y que en realidad, el cuerpo de una función es en sí mismo una declaración (de la propia función). En consecuencia, puesto que las declaraciones pueden contener inicializadores, también deben ser consideradas como "ejecutable" (lo que hemos denominado algoritmo).
§2 Aplicación
Comprende ejecutables y datos. Puede haber múltiples ficheros de ambos tipos (ficheros de datos y ejecutables).

§3 Programa
Parte de una aplicación (código) que puede cargarse y ejecutarse independientemente.

§4 Fichero Se llaman así (abreviadamente) los ficheros que contienen el código fuente (ficheros .C / .CPP) escrito por el programador. Un "fuente" se compila de una vez, cuando recibe la acción del preprocesador ( 1.4.1), dando lugar a lo que técnicamente se denomina unidad de compilación ( 1.4.2). Un solo fuente puede ser dividido en múltiples ficheros, cada uno de los cuales puede contener varias funciones, aunque la inversa no es cierta: una función no puede ser dividida entre varios fuentes.
La mayoría de las aplicaciones de cierto porte ocupan más de un fuente. Los diversos ficheros son creados y mantenidos por distintos programadores. Después, los ficheros son compilados y enlazados para producir una aplicación final.
Es conveniente recordar que C y C++ se desarrollaron en ambientes UNIX, por lo que desde su cuna son lenguajes sensibles a las mayúsculas/minúsculas ("case sensitive" . 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 distinción no es tan importante. Por ejemplo, en C++ la declaración
int X, x;
produce dos variables distintas.

§5 Función:
Una parte de un programa (subrutina) con un nombre, que puede ser invocada (llamada a ejecución) 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 función en cualquier programa C++; es llamada desde unas rutinas especiales "de inicio" que se incluyen automáticamente en todo programa C++. Esta función 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 vacía, de sentencias delimitadas por corchetes { } ( 3.2.6). Desde el punto de vista sintáctico, 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:
... // espacio global del fichero
main { // 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 analogía 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 oración gramatical es una palabra o conjunto de ellas, con que se expresa un sentido gramatical completo , las sentencias se componen de una o varias expresiones y tienen sentido computacional completo.
La sentencia es la unidad lógica completa más simple en un programa; en C/C++ terminan en punto y coma ;. Un caso especial es la expresión nula ( ; aislado). En el apartado 1.3.1a se describe su sintaxis y en el capítulo 4.10 se ofrece su definición formal y una clasificación 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 Expresión
Siguiendo en sentido ascendente de simplicidad en el lenguaje natural, después de las oraciones están las frases, Conjunto de palabras que basta para formar sentido, especialmente cuando no llega a constituir una oración cabal . Su equivalente en el lenguaje C++ serán las expresiones.
Las expresiones son secuencias de tokens (operadores, operandos y elementos de puntuación) que especifican una computación; tienen sentido computacional en sí mismas. Son los bloques de computación más 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 expresión es algo arbitraria. Una o varias expresiones terminadas en punto y coma constituyen una sentencia. Desde el punto de vista lógico, 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 ejecución de sus sentencias, no así de sus expresiones dentro de ellas, toda vez que el Estándar es permisivo en algunos aspectos de detalle.
Es importante señalar que, en contra de lo que ocurre en otros lenguajes, en C/C++, el final de línea no significa necesariamente el fin de la expresión, de forma que esta puede ocupar más de una línea (en realidad el compilador ignora los pares de caracteres CR/LF que añaden los editores al final de línea). Por ejemplo: la expresión
int x = 5*(4+(3/2-1))
puede ser escrita como:
int x =
5*
(
4+
(3/2-1)
);

Los conocedores de otros lenguajes advertirán que en C++ no tiene sentido la barra inclinadas con que otros lenguajes indican que la siguiente línea de código es en realidad una continuación de la anterior. Se exceptúa naturalmente el caso de constantes de cadena, donde si es preciso indicar al compilador que, en su caso, la cadena continúa en la línea siguiente. Ejemplo, aunque es correcto escribir:
int x =
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 continúa en la línea siguiente:
char* string = "Las constantes de cadena pueden tener
cualquier longitud";

Del mismo modo que los gramáticos distinguen varios tipos de frases (hecha, musical, proverbial, etc), los informáticos distinguen también varios tipos de expresiones. Los ejemplos que siguen son meramente ilustrativos y no pretenden ser una relación completa:
• De asignación: Si tienen uno, o varios, operadores de asignación ( 4.9.2). Ejemplos:
x += 3;
z = y = x/2;
• Declarativas: Si dan a conocer un identificador al compilador ( 4.1.2). Ejemplos:
extern int funcint x, char c);
class MiClase;
• Definiciones: Establecen la creación de un objeto reservándole espacio físico. Ejemplo:
int x = 2;
MiClase c1 (x, y, z);
• Condicionales: Definen alternativas de acción en el programa. Ejemplos:
int y = 6 ? 7: 8;
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 comparación de bits ( 4.9.3). Ejemplos:
x | y;
x << 2;
Las expresiones pueden producir un Lvalue ( 2.1.5), un Rvalue ( 2.1.6) o ningún valor. Ejemplo:
int x; // no produce ningún valor
x = 2; // produce un valor (resultado)
Además de producir un valor, las expresiones pueden tener efectos laterales (tanto si producen un valor como si no lo producen). Ejemplo:
x = ++a;
Además de producir un valor (un resultado 4.9.2), esta expresión tendría dos efectos laterales:
1º: Incrementar en 1 el valor de a,
2º: Asignar el valor modificado de a a la variable x.
El "resultado" de la expresión coincide precisamente con el valor de x .
Las expresiones se evalúan siguiendo ciertas reglas de conversión , 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 paréntesis en la expresión ( 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 ningún límite formal.

§9 Token
Los tokens ( 3.2) son los elementos en que el preprocesador desmenuza el código fuente. En un lenguaje de programación, los tokens son el equivalentes al conjunto de las palabras y signos de puntuación en el lenguaje natural escrito.
En el lenguaje C++ estas "palabras" pueden ser de varios tipos: palabras clave, identificadores, constantes, operadores, signos de puntuación (puntuadores) y comentarios (son eliminados). Los tokens están separados por elementos de separación que reciben el nombre genérico de separadores ("Whitespaces".
Inicio.
________________________________________
Los métodos utilizados en las conversiones aritméticas estándar se han detallado en 2.2.5
Esta característica: producir un resultado, de las expresiones de asignación, es lo que posibilita la existencia en C++, de expresiones como:
x = y = z+1;
Me encantaría conocer una versión española de esta palabra inglesa.
Este es el sentido gramatical de la palabra oración según el Diccionario Usual de la Real Academia de la Lengua Española. Según el Diccionario de Uso del Español de María Moliner, la oración es la unidad más pequeña de lenguaje organizado gramaticalmente. Si nos referimos al lenguaje natural, conceptualmente la oración consta de dos partes: un sujeto del que se dice o afirma algo, y un predicado, que es lo que se dice del sujeto, y puede ser de dos clases, nominal y verbal. Es nominal cuando lo que se dice del sujeto está contenido en un nombre o adjetivo, es verbal cuando lo que se dice del sujeto está contenido fundamentalmente en un verbo.
Este es el sentido de la palabra "frase" según el Diccionario de la Real Academia de la Lengua Española.
Stroustrup & Ellis: ACRM.
1.3.1a. Sentencias
§1. Introducción
Al igual que los lenguajes naturales , los lenguajes computacionales tienen una gramática, 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 gramática, 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 enseña a formar las oraciones y expresar conceptos.. En el caso de los lenguajes computacionales, enseña 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 sintácticas.. A continuación se expone la sintaxis permitida en C++ para las sentencias ("Statements". . Junto a una traducción, más o menos afortunada al español , hemos incluido el original inglés para que no quepa la más mínima ambigüedad 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 Gramática Independiente del Contexto de Chomsky . 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í tendríamos que interpretar:. Una sentencia está compuesta por una sentencia etiquetada, o una sentencia compuesta, o una sentencia-expresión, 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 lista-de-sentencias; todo ello entre corchetes { } (el símbolo <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 declaración o una lista-de-declaraciones seguida de una declaración. Es decir, una lista-de-declaraciones puede contener a su vez otra lista de declaraciones.
La lista indica que una sentencia-expresión está constituida por una expresión seguida de un punto y coma (, aunque la expresión es opcional.. Quiere esto decir que un punto y coma aislado es una sentencia-expresión sintácticamente correcta en C++; es lo que se denomina sentencia-expresión nula.
Nota: Como este "libro" está dirigido principalmente a estudiantes de C++ que necesiten del lenguaje para sus aplicaciones prácticas, 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 indicación de para qué sirven.. Son simplemente una indicación exquisitamente exacta de cómo se usan desde el punto de vista formal, lo que para el programador de a pie tiene escaso o nulo valor .
sentencia:
sentencia etiquetada (labeled-statement)
sentencia compuesta (compound-statement)
sentencia-expresión (expression-statement)
sentencia de selección (selection-statement)
sentencia de iteración (iteration-statement)
sentencia de salto (jump-statement)
sentencia ensamblador (asm-statement)
declaración simple (declaration)
bloque-intento ( 1.6) (try-block)
sentencia etiquetada:
identificador : sentencia
case expresión constante : sentencia
default : sentencia
sentencia compuesta: ( 3.2.6)
{ <lista-de-declaraciones> <lista-de-sentencias> } (<declaration-list> <statement-list>
lista-de-declaraciones:
declaración
lista-de-declaraciones. declaración
lista-de-sentencias:
sentencia
lista-de-sentencias. sentencia
sentencia-expresión:
<expresión> ;
sentencia ensamblador ( 4.10):
asm tokens nueva-línea (newline)
asm tokens;
asm { tokens; <tokens;>= <tokens;>}
sentencia de selección ( 4.10.2):
if ( expresión ) statement
if ( expresión ) statement else statement
switch ( expresión ) statement
sentencia de iteración ( 4.10.3):
while ( expresión lógica ) sentencia
do sentencia while ( expresión ) ;
for (sentencia de inicio-for <expresión> ; <expresión> sentencia
sentencia de inicio-for:
sentencia-expresión
declaración simple
sentencia de salto ( 4.10.4):
goto identificador ;
continue ;
break ;
return <expresión> ;
Inicio.
________________________________________
. Debo advertir que he visto algunas traducciones del original inglés, principalmente realizadas en Latinoamérica, que a los Españoles nos resultan chocantes y poco afortunadas.. Supongo que ocurrirá exactamente lo mismo a la inversa.
. Lenguaje utilizado por los humanos para comunicarse entre sí de forma "natural":. Español, Japonés, Inglés, Alemán, etc.
. Noam Chomsky, nacido en 1.928 en Philadelphia (Pennsylvania) USA, hijo de un inmigrante ruso, estudió lingüística, matemáticas y filosofía, interesándose también por la política, pero es más reconocido por sus trabajos en gramática generativa, que basada en la lógica y en la matemática moderna, fue posteriormente aplicada a la descripción de los lenguajes naturales.

1.3.1b Sintaxis de las expresiones C++
§1 Sinopsis:
En este epígrafe se incluye una descripción formal de la gramática permitida a las expresiones en C++. Como puede verse, las expresiones son definidas recursivamente, de forma que las subexpresiones pueden ser anidadas sin ningún límite formal, aunque quizás el compilador pueda reportar un error de límite de memoria si no puede compilar una expresión muy compleja.
Como en el caso de las sentencias ( 1.3.1a), en los casos que pudieran resultar dudosos, junto a la traducción al español se ha incluido el original inglés.

Expresiones primarias: (primary-expression):
literal
this
:: identificador
:: nombre de función-operador (operator-function-name)
:: nombre cualificado
(expresión)
expresión-de-identificación
literal:
constante entera (integer-constant)
constante carácter (character-constant)
constante fraccionaria (floating-constant)
cadena de caracteres (string-literal)
expresión-de-identificación:
nombre (no cualificado)
nombre-cualificado
nombre (no cualificado):
identificador
nombre de función-operador
conversion-function-name
~ nombre-de-clase
nombre-cualificado
nombre-cualificado: (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 < identificación-de-tipo > ( expresión )
dynamic_cast < identificación-de-tipo > ( expresión )
reinterpret_cast < identificación-de-tipo > ( expresión )
static_cast < identificación-de-tipo > ( expresión )
typeid ( expresión )
typeid ( nombre-de-tipo )
lista de expresiones: (expression-list)
expresión-de-asignación
lista-de-expresiones , expresión-de-asignación
expresión-unitiaria: (unary-expression)
postfix-expression
++ expresión unitaria
- - expresión-unitaria
operador-unario expresión-de-modelado
sizeof expresión-unitaria
sizeof ( nombre-de-tipo )
expresión de asignación-de-memoria (allocation-expression)
expresión-de-desasignación
operador-unario: (alguno de los siguientes)
& * + - ! ~
expresión-de-asignación-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 [ <expresión> ]
expresión de desasignación: (deallocation-expression)
<::> delete expresión-de-modelado
<::> delete [ ] expresión-de-modelado
expresión-de-modelado: (cast-expression)
expresión-unitaria
( nombre-de-tipo ) expresión-de-modelado
expresión-pm: (pm-expression)
expresión-de-modelado
pm-expresion .* expresión-de-modelado
pm-expresion ->* expresión-de-modelado
expresión-multiplicativa: (multiplicative-expression)
expresión-pm
expresión-multiplicativa * pm-expression
expresión-multiplicativa / pm-expression
expresión-multiplicativa % pm-expression
expresión-aditiva: (additive-expression)
expresión-multiplicativa
expresión-aditiva + expresión-multiplicativa
expresión-aditiva - expresión-multplicativa
expresión-de-desplazamiento: (shift-expression):
expresión-aditiva
expresión-de-desplazamiento << expresión-aditiva
expresión-de-desplazamiento >> expresión-aditiva
expresión relacional: (relational-expression)
expresión-de-desplazamiento
expresión-relacional < expresión-de-desplazamiento
expresión-relacional > expresión-de-desplazamiento
expresión-relacional <= expresión-de-desplazamiento
expresión-relacional >= expresión-de-desplazamiento
expresión-de-igualdad: (equality-expression)
expresión-relacional
expresión-de-igualdad == expresión-relacional
expresión-de-igualdad != expresión-relacional
expresión-AND: (AND-expression)
expresión-de-igualdad
expresión-AND & expresión-de-igualdad
expresión-de-OR-exclusivo: (exclusive-OR-expression)
expresión-AND
expresión-de-OR-exclusivo ^ expresión-AND
expresión-de-OR-inclusivo: (inclusive-OR-expression)
expresión-de-OR-exclusivo
expresión-de-OR-inclusivo | expresión-de-OR-exclusivo
expresión-lógica-AND: (logical-AND-expression)
expresión-de-OR-inclusivo
expresión-lógica-AND && expresión-de-OR-inclusivo
expresión-lógica-OR: (logical-OR-expression)
expresión-lógica-AND
expresión-lógica-OR || expresón-lógica-AND
expresión condicional:
expresión-lógica-OR
expresión-logica-OR ? expresión : expresión-condicional
expresión-de-asignación: (assignment-expression)
expresión condicional
expresión-unitiaria operador-de-asignación expresión-de-asignación
operador-de-asignación (alguno de los siguientes):
= *= /= %= += -=
<< => >= &= ^= |=
expresión:
expresión-de-asignación
expresión, expresión-de-asignación
expresión-constante:
expresión-condicional
Datos archivados del Taringa! original
16puntos
3,333visitas
0comentarios
Actividad nueva en Posteamelo
0puntos
4visitas
0comentarios
Dar puntos:

Dejá tu comentario

0/2000

Autor del Post

l
lordpanther🇦🇷
Usuario
Puntos0
Posts18
Ver perfil →
PosteameloArchivo Histórico de Taringa! (2004-2017). Preservando la inteligencia colectiva de la internet hispanohablante.

CONTACTO

18 de Septiembre 455, Casilla 52

Chillán, Región de Ñuble, Chile

Solo correo postal

© 2026 Posteamelo.com. No afiliado con Taringa! ni sus sucesores.

Contenido preservado con fines históricos y culturales.