You are on page 1of 47

3 Identificacin del endianness del sistema Si estamos escribiendo una aplicacin o librera muy general, que en algn punto

deba tener en cuenta el endiannes de la mquina anfitriona, es posible averiguarlo fcilmente mediante una directiva que podemos utilizar posteriormente en las sentencias correspondientes segn el resultado. Seran las siguientes: const unsigned int myOne = 1; #define IS_BIGENDIAN (*(char*)(&myOne) == 0) #define IS_LITTLEENDIAN (*(char*)(&myOne) == 1) Observe que la direccin de myOne (que es un puntero-a-int) es convertida a puntero-achar mediante el cast correspondiente; a continuacin se compara el contenido de esta direccin con cero y con 1. En realidad se est comparando el primer bite de myOne. El truco est precisamente en comparar el contenido del primer byte (la comparacin myOne == 1 sera siempre cierta). El resultado es 0 (falso) o 1 (cierto) segn el modelo del sistema utilizado, que corresponder o no, con el del hardware subyacente segn el caso. Por ejemplo, en una mquina Intel&Windows, el programa #include <cstdlib> #include <iostream> const unsigned int myOne = 1; #define IS_BIGENDIAN (*(char*)(&myOne) == 0) #define IS_LITTLEENDIAN (*(char*)(&myOne) == 1) int main(int argc, char *argv[]) { std::cout << "Esta maquina es Big-endian: " << (IS_BIGENDIAN? "Cierto" : "Falso") << std::endl; std::cout << "Esta maquina es Little-endian: " << (IS_LITTLEENDIAN? "Cierto" : "Falso") << std::endl; system("PAUSE"); return EXIT_SUCCESS; } Produce la siguiente salida: Esta maquina es Big-endian: Falso Esta maquina es Little-endian: Cierto Presione cualquier tecla para continuar . . .

3. Elementos lxicos
"In the software professions reading is like breathing. Stopping can be dangerous to your health". David Weber C/C++ Users Journal. Marzo 1996 "Twho C++ tutorials".

1 Sinopsis Los elementos que componen el lenguaje C++, los podemos dividir en dos grandes grupos: Comentarios, que como veremos son eliminados por el preprocesador ( 3.1).

Tokens, una serie de palabras que constituyen el lenguaje que realmente entiende el compilador. Dentro de este grupo, el lenguaje C++ distingue cinco tipos distintos de palabras: Palabras clave (keywords). Son palabras reservadas por el lenguaje para propsitos especiales, y no deben ser utilizadas como identificadores ( 3.2.1). Identificadores, tambin llamados etiquetas. Son nombres arbitrarios de cualquier longitud que sirven para identificar los objetos ( 3.2.2). Constantes: Datos que estn definidos en el programa y que no pueden ser modificados a lo largo del mismo ( 3.2.3). Operadores. Un tipo de tokens que pueden aparecer en las expresiones, e indican al compilador la realizacin de determinadas operaciones matemticas ( 4.9). Puntuadores. Signos de puntuacin, anlogos a los utilizados en el lenguaje corriente ( 3.2.6).

3.1 Comentarios
"Remember, comments are for humans, so write them that way!". Aaron Weiss. "JavaScript Tutorial for Programmers".

1 Sinopsis Comentarios son anotaciones; observaciones, recordatorios, etc. en el programa. Son para uso exclusivo del programador, y eliminados del cdigo fuente en la fase de preprocesado; antes del anlisis sintctico ( 1.4). Aparte de las consideraciones estrictamente formales que se indican en esta seccin, no debemos perder de vista que los comentarios, aunque voluntarios (no es obligatorio escribirlos), representan una ayuda inestimable durante la construccin del programa. Siendo imprescindibles para el programador original, o los que le sucedan en las tareas de mantenimiento, cuando es necesario habrselas con el cdigo un tiempo despus de que fue escrito. Adems de clarificar ideas, los comentarios son tambin un valioso instrumento de depuracin, pues permiten eliminar provisionalmente secciones enteras de cdigo. En C++ coexisten dos formas de comentarios: El de C clsico y el de C++. Ambos son soportados por C++Builder, que tiene adems una extensin particular sobre el estndar ANSI, los comentarios anidados (este ltimo tipo solo debe usarse si la compatibilidad no es importante).

Tambin deben tenerse en cuenta las observaciones que siguen sobre el uso de separadores (whitespaces) y delimitadores en los comentarios para evitar otros problemas de compatibilidad.

2 Comentarios C Un comentario C es cualquier secuencia de caracteres contenida entre los delimitadores /* ... */. La totalidad de la secuencia, incluyendo los delimitadores /* y */ son sustituidos por un simple espacio despus de la expansin de macros (algunas implementaciones de C pueden eliminar los comentarios sin reemplazarlos por espacios). Ejemplo: int x = 2; /* esto es un comentario que ser eliminado o sustituido por un simple espacio despus en la frase de preprocesado. Como puede verse, el comentario puede ocupar varias lneas de texto en el cdigo fuente. Se recomienda utilizarlos con profusin, a veces todas las explicaciones son pocas, ya que el C++ es un lenguaje bastante crptico en algunas ocasiones, sobre todo algunas sentencias muy "elegantes" y comprimidas, pero ininteligibles en una primera lectura */ Ejemplo; la expresin: int /* declaracin */ i /* contador */; es analizada sintcticamente como estos tres tokens: int i y ;

3 Pegado de cadenas Algunos compiladores C clsicos utilizan el smbolo /**/ para el pegado (concatenado) de tokens, pero el nuevo Estndar ANSI recomienda que esto se haga con ## (ver ejemplos). El pegado de tokens es una tcnica que utiliza las habilidades de preprocesador para construir nombres nuevos utilizando macros ( 4.9.10b). Por ejemplo: #define VAR(i,j) #define VAR(i,j) #define VAR(i,j) (i/**/j) (i##j) (i ## j) // Incorrecto // Correcto // Correcto

Despus de estas macros, las expresiones que siguen son transformadas por el preprocesador como se indica int VAR(opcion, uno)( ); int VAR(opcion, dos)( ); int VAR(opcion, tres)( ); int opcionuno(); int opciondos(); int opciontres();

4 Comentarios C++

C++ admite comentarios de una sola lnea utilizando dos barras inclinadas ( // ) como seal de comienzo. El comentario empieza en este punto (incluyendo las seales de comienzo) y contina hasta el prximo carcter de nueva linea. class X { ... }; // esto es un comentario

5 Comentarios anidados El estndar ANSI C no permite la existencia de comentarios anidados [1]. Por ejemplo, no podemos utilizar el comentario: /* int /* declaracin */ i /* contador */; */ porque el mbito del primer /* termina en el primer */, por lo que se obtiene: i; */, lo que producira un error de sintaxis.

6 Delimitadores y espacios En casos raros, algunos espacios antes de /*; //, y despus de */, aunque no sean sintcticamente imprescindibles, deben ponerse para evitar posibles problemas de portabilidad. Por ejemplo, en este cdigo: int i = j//* divide por k*/k; +m; es analizado sintcticamente como int i = j +m; no como: int i = j/k; +m; que sera lo esperado segn las reglas de C. La forma que sigue (ms legible) evitara este problema. int i = j/ /* divide por k */ k; +m;

3.2 Tokens
1 Sinopsis

El tratar de la estructura lgica de un programa ( 1.3.1) se seal que tokens son los elementos en que el preprocesado desmenuza el cdigo fuente. En un lenguaje de programacin, los tokens son el equivalente a las palabras y signos de puntuacin en el lenguaje natural escrito. Los tokens estn separados por elementos de separacin que reciben el nombre genrico de separadores ( 1.4). Comprenden los siguientes tipos de elementos (podramos considerar que el lenguaje computacional C++ tiene las siguientes clases de "palabras"): Palabras clave ("keywords"). C++ dispone de un conjunto relativamente extenso de palabras clave, sealadas en ( 3.2.1). Identificadores. Su nmero puede ser virtualmente infinito; dentro de ciertas normas el programador es libre de elegir los que mejor se ajusten a sus necesidades.( 3.2.2). Constantes. Existen varias clases, cuyos detalles se exponen en las pginas siguientes ( 3.2.3) operadores ( 4.9) signos de puntuacin, tambin llamados puntuadores ( 3.2.6).

En ocasiones, los operadores y signos de puntuacin comparten la misma representacin escrita. En estos casos, el sentido debe deducirse del contexto y por la forma en que estn agrupados los diversos signos. El conjunto de smbolos; grupos de smbolos, y palabras utilizados en C++ como operadores y/o puntuadores, es el siguiente (los operadores se han sealado en azul):

{ <: " + ! ^= <= and xor

} :> ' = &= >= and_eq xor_eq

[ <% ? * < |= && bitand ()

] %> :: / > << || bitor []

# %: . % += >> ++ compl new

## %:%: .* ^ -= >>= -not delete

( ;

) :

\ ...

& *= <<= , not_eq new[ ]

| /= == ->* or delete[ ]

~ %= != -> or_eq

Una vez entregado el fuente al compilador, es precisamente el analizador sintctico ("parser") el encargado de es identificar los tokens. Por ejemplo, la sentencia int i; float f; es descompuesta por el "parser" en los siguientes tokens: int i ;

palabra clave identificador puntuador

float palabra clave f ;

identificador puntuador

3.2.1 Palabras clave


1 Sinopsis Las palabras clave ("Keywords") son identificadores utilizados por el lenguaje para fines especiales, y no pueden ser utilizadas como identificadores (por esta razn se suelen denominar tambin palabras reservadas). Por ejemplo, no pueden ser utilizadas como nombres de variables, clases o funciones. El primitivo C de Kerningham y Ritchie tena solo 27; el C estndar las ampli a 32, y C++ aadi algunas ms. Hay que advertir que aunque el Estndar C++ define perfectamente cuales son las palabras reservadas, incluidas a continuacin, los compiladores pueden incluir algunas otras por su cuenta, que son por tanto dependientes de la plataforma. Nota: recordar que el compilador C++Builder dispone de una opcin -A de compilacin ( 1.4.3) que permite forzar las reglas a ANSI C++ estricto, con el fin de conseguir un cdigo lo ms portable posible. Con esta opcin, las palabras clave no estrictamente ANSI son ignoradas como tales. El resto de compiladores (MS Visual y GCC) tienen opciones similares.

2 Palabras clave del C++ Estndar

Keyword

referencia

Keyword

referencia

Keyword

referencia

asm auto bool catch compl continue do else export float goto int namespace not_eq or_eq public return sizeof struct this

4.10 4.1.8a 3.2.1b 1.6 4.9.8; 4.10.4 4.10.3 4.10.2 4.12.1b 2.2.1 4.10.4 2.2.1 4.1.11 4.9.8 4.9.8 4.11.2a 4.4.7 4.9.13 4.5 4.11.6 4.9.3

and bitand break char const default double enum extern for if long new operator private register short static switch throw

4.9.8 4.9.8 4.10.4 2.2.1 3.2.1c 4.10.2 2.2.1 3.2.3g, 4.1.8d, 4.10.3 4.10.2 2.2.3 4.9.20 4.9.18 4.11.2a 4.1.8b 2.2.3 4.1.8c 4.10.2 1.6 4.7 1.4.4

and_eq bitor case class const_cast delete dynamic_cast explicit false friend inline mutable not or protected reinterpret_cast signed static_cast template true

4.9.8 4.9.8 4.10.2 4.11.2 4.9.9a 4.9.21 4.9.9c 4.11.2d1 3.2.1b 4.11.2a1 4.4.6b 4.1.8e 4.9.8 4.9.8 4.11.2a 4.9.9d 2.2.3 4.9.9b 4.12 3.2.1b

try typename using volatile

1.6 3.2.1e 4.1.11 3.2.1d;

typedef union virtual 4.1.9 wchar_t xor

3.2.1a 4.6 4.11.2c1 2.2.1a1, 4.9.8

typeid unsigned void 3.2.3d while xor_eq

4.9.14 2.2.3 2.2.1 4.10.3 4.9.8

En este apartado estudiaremos las palabras clave C++, que por una u otra razn no sean introducidas en ningn otro epgrafe. Para distinguirlas del resto, a lo largo de esta obra, cuando aparecen fuera del cdigo fuente, se imprimen en negrita y color negro. Por ejemplo, int es una palabra clave... Nota: las palabras clave __try y try son una excepcin. Dentro del mecanismo de manejo de excepciones de C++ ( 1.6), la palabratry es necesaria como contraparte de catch. Por otra parte, try no puede ser sustituida por __try. La palabra clave __try solo se utiliza emparejada con __except o __finally.

3.2.1a typedef
1 Sinopsis La palabra reservada typedef se utiliza para asignar un alias (otro nombre) a un tipo. No crea ningn nuevo tipo, solo define un nuevo identificador (type-id 2.2) para un tipo que ya tiene su propio identificador (el identificador puede ser un nombre o una expresin compleja que contiene al nombre). Es importante recalcar que el nuevo nombre es un aadido, y no sustituye al identificador original. Ambos identificadores pueden ser intercambiados libremente en cualquier expresin. Formalmente typedef es un especificador de identificador (nombre). Su introduccin en el lenguaje se debe a que, como reconoce su propio creador [1], la sintaxis de declaraciones C++ es innecesariamente dura de leer y escribir. Esto se debe a la herencia del C y a que la notacin no es lineal, sino que mimetiza la sintaxis de las expresiones que est basada en precedencias. En este sentido, la utilizacin de typedef permite paliar en parte el problema, mejora la legibilidad y ayuda a la documentacin y mantenimiento del programa, ya que permite sustituir identificadores de tipo complejos por expresiones ms sencillas.

2 Sintaxis: typedef <tipo> <alias>;

Asigna un nombre <alias> con el tipo de dato de <tipo>. Nota: para distinguir un identificador <alias> introducido con un typedef de un nombre de tipo normal <tipo>, al primero se le denomina nombre-typedef ("typedef-name") o aliastypedef ("typedef-alias"). Los typedef pueden ser usados con tipos simples o abstractos, pero no con nombres de funciones.

3 Ejemplos: typedef unsigned char BYTE; en lo sucesivo, cualquier referencia a BYTE (es una tradicin de C/C++ utilizar los alias-typedef en maysculas) equivale a colocar en su lugarunsigned char, incluso para crear nuevos tipos unsigned char: BYTE z, y // equivale a: unsigned char z, y ... typedef const float KF; typedef const float* KF_PTR; KF pi = 3.14; KF_PTR ppi = &pi; typedef long clock_t; // no sera muy C++, mejor CLOCK_T clock_t slice = clock();

3.1 En realidad el nuevo identificador introducido por typedef se comporta dentro de su mbito como una nueva palabra-clave con la que pueden declararse nuevos tipos: typedef int* Pint; ... Pint p1, p2; // Ok. p1 y p2 son punteros-a-int Tambin pueden encadenarse, de forma que pueden definirse nuevos alias en funcin de otros definidos previamente. Por ejemplo: typedef char CHAR; typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, * PSTR ;

3.2 Como en otras declaraciones, es posible definir una serie de alias-typedef mediante expresiones unidas por comas. Por ejemplo: typedef const char ... LPCCH ptr1 = "Hola PCSTR ptr2 = "Hola const char* ptr3 = * LPCCH, * PCCH, * LPCSTR, * PCSTR ; Amrica"; Amrica"; "Hola Amrica";

Los punteros ptr1; ptr2 y ptr3 son equivalentes.

Otros ejemplos (tomados de definiciones reales de MS VC++) typedef long INT_PTR, *PINT_PTR; typedef unsigned short UHALF_PTR, *PUHALF_PTR; typedef short HALF_PTR, *PHALF_PTR;

3.3 Otros ejemplos de typedef utilizados frecuentemente en la programacin Windows ( Ejemplos):

4 Asignaciones complejas Es til utilizar typedef para asignaciones complejas; en particular en la declaracin y definicin de punteros a funciones. Por ejemplo: typedef long (*(*(*FPTR)())[5])(); FPTR an; // L.1 // L.2

La sentencia L.1 define un identificador: FPTR como un puntero a funcin que no recibe argumentos y devuelve un puntero a array de 5 punteros a funcin, que no reciben ningn parmetro y devuelven long. Despus, L.2 declara que an es un elemento del tipo indicado. typedef void (new * new_handler)(); new_handler set_new_handler(new_handler); // L.3 // L.4

L:3 define new_handler como el identificador de un puntero a funcin que no recibe argumentos y devuelve void [2]. Despus L.4 declaraset_new_handler como el nombre de una funcin que devuelve el tipo new_handler recin definido y acepta un nico argumento de este mismo tipo. typedef void (X::*PMF)(int); PMF pf = &X::func; // L.5 // L.6

L.5 define el identificador PMF como puntero a funcin-miembro de la clase X que recibe un int y no devuelve nada. L.6 declara pf como tipo PMF(puntero a funcin...) que apunta al mtodo func de X.

4.1 Cuando se trata de expresiones muy complejas, puede ser til proceder por fases, aprovechando la propiedad ya enunciada (3.1 ) de que pueden definirse nuevos alias en funcin de otros definidos previamente . Por ejemplo, queremos definir un puntero p a matriz de 5 punteros a funcin que toman un entero como argumento y devuelven un puntero a carcter. En este caso, puede ser conveniente empezar por las funciones: char* foo(int); // foo es una funcin aceptando int y devolviendo char typedef char* F(int); // F es un alias de funcin aceptando int y devolviendo... a continuacin definimos la matriz:

F* m[5]; typedef F* M[5];

// m es una matriz de 5 punteros a F // M es el alias de una matriz de 5 punteros a F

Finalmente escribimos la declaracin del tipo solicitado: M* p; Cualquier manipulacin posterior del tipo mencionado resulta ahora mucho ms sencilla. Por ejemplo, la declaracin: int* foo(M*); corresponde a una funcin aceptando un puntero a matriz... que devuelve un puntero a int.

4.2 En ocasiones, la simplificacin mencionada el en prrafo anterior, permite solventar algn que otro problema con la compilacin de expresiones complejas. Por ejemplo, un miembro de cierta clase tiene la siguiente declaracin: class MYClass { ... MyNAMESPACE::Garray<MyNAMESPACE::MyString> arrQuer; ... } Como sugiere su nombre, Garray es una clase genrica, destinada a almacenar matrices de objetos de cualquier tipo, est definida en el espacio de nombres MyNAMESPACE. En este caso, el objeto arrQuer es una matriz de objetos tipo MyString, que es una clase para manejar cadenas de caracteres -similar a la conocida string de la librera estndar de plantillas STL- tambin definida en MyNAMESPACE. En resumen, arrQuer es una matriz de cadenas de caracteres. Ocurre que deseo incluir una invocacin explcita para la destruccin del objeto arrQuer en el destructor de MYClass, Algo como: class MYClass { ... MyNAMESPACE::Garray<MyNAMESPACE::MyString> arrQuer; ... ~MYClass() { arrQuer.~MyNAMESPACE::Garray<MyNAMESPACE::MyString>(); Compiler error } }

//

Sin embargo, el compilador [3] muestra un mensaje de error porque no es capaz de interpretar correctamente la sentencia sealada. En este caso, la utilizacin de un typedef resuelve el problema y el compilador construye sin dificultad la aplicacin. class MYClass { ... typedef MyNAMESPACE::Garray<MyNAMESPACE::MyString> ZSTR; ZSTR arrQuer;

... ~MYClass() { arrQuer.~ZSTR(); } }

// Compilacin Ok.

&5 Tambin es posible utilizar typedef al mismo tiempo que se declara una estructura u otro tipo de clase. Ejemplo: typedef { double re, im; } COMPLEX; ... COMPLEX c, *ptrc, Arrc[10];

La anterior corresponde a la definicin de una estructura annima que puede ser utilizada a travs de su typedef. Por supuesto, aunque no es usual necesitar el nombre y el alias simultneamente, tambin se puede poner nombre a la estructura al mismo tiempo que se declara el typedef. Por ejemplo: typedef struct C1 { double re, im; } COMPLEX; ... C1 c, *ptrc; COMPLEX Arrc[10]; Otro ejemplo tomado de una definicin real: ( 4.5.5c)

&6 Otro uso de typedef puede consistir en localizar determinadas referencias concretas en un solo punto (donde se declara el typedef), de forma que los posibles cambios posteriores solo requieren cambiar una lnea de cdigo ( en mi opinin, quizs sea esta la razn ms importante para la utilizacin de este especificador). Por ejemplo, supongamos que necesitamos una variable de 32 bits y estamos utilizando C++Builder, en el queint tiene justamente 32 bits ( 2.2.4); a pesar de ello utilizamos la expresin: typedef int INT32; en lo sucesivo, para todas las referencias utilizamos INT32 en vez de int. Por ejemplo: INT32 x, y, z; Si tuvisemos que portar el cdigo a una mquina o a un compilador en el que int fuese menor y, por ejemplo, los 32 bits exigieran un long int, solo tendramos que cambiar la lnea de cdigo del typedef: typedef long INT32; ... // Todas las dems referencias se mantienen

INT32 x, y, z;

// Ok.

7 No est permitido usar typedef con clases de declaracin adelantada ( sera incorrecto: typedef struct COMPLEX; // Ilegal!!

4.11.4). Por ejemplo

Tampoco est permitido utilizarlo en la definicin de funciones o utilizar dos veces el mismo identificador en el mismo espacio de nombres: typedef long INT32; ... typedef float INT32;

// Error!!

Cuando el alias-typedef se refiere a una clase, se constituye en un nombre de clase. Este alias no puede ser utilizado despus de los prefijosclass, struct o union. Tampoco puede ser utilizado con los nombres de constructores o destructores dentro de la clase. Ejemplo: typedef struct S { ... S(); // constructor ~S(); // destructor } STR; ... S o1 = STR(); // Ok. STR o2 = STR(); // Ok. struct STR* sp; // Error!! Observe que: typedef struct { ... S(); // Error!! ~S(); // Error!! } STR; ... S() y ~S() seran tratadas como funciones normales, no como constructor y destructor de la estructura.

8 Cuando se quiere utilizar un alias para el identificador de un espacio de nombres, el mecanismo es distinto; no es necesario utilizar typedef. Ver: Alias de subespacios ( 4.1.11a).

Typedefs en Windows:
1 Sinopsis

Los typedef son de utilizacin muy comn, ya que en muchas ocasiones suponen una economa y simplificacin en la notacin. Sin embargo, como hemos sealado ( 3.2.1a), una de las razones de su uso, y quizs la ms importante, es que permite concentrar en un solo punto del cdigo determinadas referencias, lo que resulta decisivo para la portabilidad de aplicaciones entre distintas plataformas. Este es precisamente el caso de las aplicaciones escritas para la API de Windows, donde se utilizan con profusin estos nombres alternativos. Su uso permite a los desarrolladores despreocuparse de determinadas cuestiones de detalle, y utilizar los mismos identificadores en sus aplicaciones, con independencia de que estas puedan ser posteriormente compiladas para versiones Windows de 16, 32 o 64 bits. Por ejemplo, la aplicacin puede utilizar un tipo UINT en todas las funciones que devuelven dicho tipo, con independencia de como sea interpretado finalmente por el compilador. Este detalle ser encomendado a los ficheros de cabecera, que pueden contener distintas definiciones de este typedef para cada compilador concreto. A continuacin se exponen algunos de los identificadores ms utilizados en la programacin C/C++ para Windows, junto con una breve descripcin de sus caractersticas. WINAPI En realidad no se refiere a un tipo de dato, sino a una convencin de llamada a funcin ( 4.4.6a). En las primitivas versiones de Windows, este identificador se refera a la convencin _pascal. En las versiones de 32 bits se corresponde con la convencin estndar de llamada. En particular, la funcin WinMain, que en estas aplicaciones sustituye a la funcin main del C/C++ estndar, utiliza esta convencin. Es otra convencin de llamada. En particular para aquellas fuciones de retrollamada ("call-back") de la aplicacin, que deben ser invocadas por el Sistema Operativo. Es el caso de los denominados "window procedures" y "dialog procedures". Inicialmente se referan a la convencin FAR PASCAL, pero actualmente son llamadas estndar (_stdcall). En concreto, el "window procedure" responde al siguiente prototipo: LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); LPSTR Punteros a matrices de caracteres. Generalmente cadenas alfanumricas al estilo C clsico, terminadas en el carcter nulo (NTBSs 3.2.3f). Definidas como char FAR* [3] Anlogos a los anteriores pero referidos a matrices de caracteres constantes (de solo lectura). Definido como const char FAR* Un tipo de entero sin signo cuyo tamao depende del entorno (Windows 16, 32 o 64 bits). Es sinnimo de unsigned int, y generalmente es utilizado en sustitucin del tipo WORD, excepto en las contadas ocasiones en que se desea un valor sin signo de 16 bits aunque se trate de plataformas de 32 o 64 bits. Este es el tipo devuelto por las funciones "call-back" (window procedure y dialog procedure). Las funciones call-back aceptan cuatro parmetros, dos de los cuales son de

CALLBACK

LPCSTR

UINT

LRESULT

WPARAM

propsito general. Este tipo corresponde a uno de ellos LPARAM LPVOID

Tipo del segundo parmetro de uso general de las funciones "call-back". Puntero genrico, equivalente a void*. Suele utilizarse con preferencia a LPSTR.

En la mayora de los casos, el intercambio de datos entre las aplicaciones Window y la API Sistema, se concreta en estructuras (en el sentido C del trmino) que se definen mediante los oportunos "defines" y que se manejan a travs de punteros, que aqu son conocidos como manejadores ("hanles"). Muchas funciones de la API, tanto de servicios generales como de servicios grficos (GDI), reciben y devuelven estos "handles". Que a su vez se presentan mediante distintos identificadores que finalmente son traducidos en la fase de preproceso a punteros-aestructuras de distinto tipo. A continuacin se relacionan algunos de los ms frecuentes. HANDLE manejador genrico. Debe ser sustituido por uno ms especfico siempre que sea posible. handle a instancia de una aplicacin (por ejemplo, el programa en ejecucin). handle a contexto grfico DC ("device context"). Un DC se materializa en una estructura que define un conjunto de objetos relacionados con la GDI de Windows (objetos grficos); sus atributos y modos que afectan a sus salidas. Entre estos objetos estn una pluma "pen" para trazado de lneas; un pincel "brush", para relleno de reas; un mapa de bits "bitmap"; una paleta de colores; una referencia al rea "canvas" en la que se realizarn las operaciones de dibujo, y una regin dentro de ella. handle a mdulo. handle a bitmap. handle a local. handle a global. handle a tarea (proceso). handle a fichero. handle a recurso. handle a objeto de la interfaz de diseo grfico GDI ("Graphic Design Interface"). Excepto metaficheros handle a metafichero. Nota: las aplicaciones para el SO Windows utilizan dos herramientas para

HINSTANCE HDC

HMODULE HBITMAP HLOCAL HGLOBAL HTASK HFILE HRSRC HGDIOBJ

HMETAFILE

almacenar imgenes: los metaficheros ("metafiles") y los mapas de bits ("bitmaps"). Los metaficheros son matrices de estructuras de longitud variable que, al contrario que los mapas de bits, almacenan la informacin grfica en un formato independiente de cualquier dispositivo concreto (device-independent format). HDWP

handle a una estructura DeferWindowPos(). Esta funcin cambia la estructura que define la posicin y tamao de una ventana. handle a tabla de aceleradores. Estas son matrices de estructuras ACCEL que definen combinaciones de teclas aceleradoras ("accelerator's keystrokes") de un hilo de ejecucin ("thread"). Responden a la siguiente definicin: typedef struct tagACCEL { BYTE fVirt; WORD key; WORD cmd; } ACCEL;

HACCEL

HDRVR HWND

handle a controlador de dispositivo ("driver"). handle a caja una ventana. Por ejemplo una caja de dilogo ("dialog box"); un botn; etc.

A continuacin se muestran algunos de estos typedef tomados de los ficheros de cabecera correspondientes [1]. Como puede comprobar, algunos estn definidos en funcin de otros en un proceso recursivo, aunque el preprocesador las traslada finalmente a su definicin definitiva. Por ejemplo, WPARAM es UINT que finalmente es considerado por el compilador equivalente unsigned int [2]. typedef typedef typedef typedef typedef char CHAR, CCHAR, *PCCHAR;; unsigned char BYTE, UCHAR, *PUCHAR; unsigned short WORD, USHORT, ATOM, *PUSHORT; short SHORT, *PSHORT; unsigned int UINT, WPARAM;

typedef int INT, HFILE; typedef HICON HCURSOR; typedef typedef typedef typedef typedef typedef typedef typedef double DOUBLE; unsigned long DWORD, ULONG, *PULONG; long BOOL, LONG, LPARAM, LRESULT, *PLONG; float FLOAT, *PFLOAT; unsigned char UCHAR,*PUCHAR; wchar_t WCHAR, TCHAR, OLECHAR; char *PSZ; unsigned int * PUINT;

typedef TCHAR TBYTE,*PTCH,*PTBYTE; typedef TCHAR *LPTCH,*PTSTR,*LPTSTR,*LP,*PTCHAR; typedef const TCHAR *LPCTSTR;

typedef void *HANDLE; typedef PVOID HANDLE; typedef typedef typedef typedef typedef typedef typedef typedef typedef typedef HANDLE HDWP, *PHANDLE,*LPHANDLE; DWORD COLORREF; // Valores de color RGB Windows hyper LONGLONG; DWORD * LPCOLORREF; CHAR *PCHAR, *LPCH, *PCH, *NPSTR, *LPSTR, *PSTR; const CHAR *LPCCH, *PCCH, *LPCSTR, *PCSTR ; WCHAR *PWCHAR, *LPWCH, *PWCH, *NWPSTR, *LPWSTR, *PWSTR; const WCHAR *LPCWCH, *PCWCH, *LPCWSTR, *PCWSTR, *LPCCH, *PCCH; VOID void void *PVOID,*LPVOID;

3.2.1b bool, false, true


1 Sinopsis La palabra-clave bool declara un tipo especial de variable, denominada booleana ( solo puede tener dos valores: cierto y falso [1]. 0.1) que

Nota: por razn de los valores que pueden adoptar (cierto/falso), a estas variables tambin se las denomina variables lgicas.

2 Sintaxis bool <identificador>;

3 Descripcin Evidentemente el tipo bool est especialmente adaptado a para realizar comprobaciones lgicas; de hecho, todo el lgebra de Boole se basa justamente en el uso de este tipo de variables de solo dos valores mutuamente excluyentes. Por su parte, las palabras clave false y true son literales que tienen valores predefinidos (podemos considerar que son objetos de tipo booleano de valor constante predefinido). false tiene el valor falso (numricamente es cero), true tiene el valor cierto (numricamente es uno). Estos literales booleanos son Rvalues; no se les puede hacer una asignacin (no pueden estar a la izquierda de una asignacin 2.1). bool val = false; val = true; true = 0; // declara val variable Booleana y la inicia // ahora se cambia su valor (nueva asignacin) // Error!!

4 Conversin (modelado) de tipos: Tenga en cuenta que los bool y los int son tipos distintos. Sin embargo, cuando es necesario, el compilador realiza una conversin o modelado automtico ( ), de forma que pueden utilizarse

libremente valores bool (true y false) junto con valores int sin utilizar un modelado explcito. Por ejemplo: int x = 0, y = 5; if (x == false) printf("x falso.\n"); if (y == truee) printf("y cierto.\n"); if ((bool) x == false) printf("x falso.\n");

// Ok. // Ok. // Modelado innecesario

Estas conversiones entre tipos lgicos y numricos, son simplemente una concesin del C++ para poder aprovechar la gran cantidad de cdigo C existente. Tradicionalmente en C se haca corresponder falso con el valor cero y cierto con el valor uno (o distinto de cero). El proceso consiste en que, en las expresiones aritmticas y lgicas, las variables booleanas son convertidas a enteros (int). Las operaciones correspondientes (aritmticas y lgicas) se realizan con enteros, y finalmente el resultado numrico es vuelto a bool segn las reglas de conversin siguientes: Para convertir tipos bool a tipos int: false cero true uno Para convertir tipos int a un Rvalue tipo bool: cero false cualquier otro valor true.

Para el resto de variables distintas de int (aunque sean numricas), antes de utilizarlas en expresiones lgicas ( 4.9.9), es necesario un modelado ("casting"). Ejemplo: float x = 0 long y = 5; int *iptr = 0; if (x == false) printf("x falso"); // Error. if (y == truee) printf("y cierto"); // Error. if (iptr == false) printf("Puntero nulo"); // Error. if ((bool) x == false) printf("x falso"); // Ok. if ((bool) y == true) printf("y cierto"); // Ok. if ((bool) iptr == false) printf("Puntero nulo"); // Ok. Las reglas son anlogas a las anteriores: Para convertir un Rvalue tipo bool a un Rvalue numrico: false cero true uno. Para convertir tipos aritmticos (enumeraciones, punteros o punteros a miembros de clases) a un Rvalue de tipo bool, la regla es que cualquier valor cero; puntero nulo, o puntero a miembro de clase nulo, se convierte a false. Cualquier otro valor se convierte a true.

5 Los tipos bool y los literales false y true se pueden utilizar para realizar comprobaciones lgicas. En el ejemplo que sigue se muestra como hacer test booleanos con bool, true, y false: #include <iostream>

using std::cout; using std::endl; bool func() { return NULL; return false; } int main() { bool val = false; int i = 1; int *iptr = 0; float j = 1.01; // Func devuelve un tipo bool /* NULL es convertido a Booleano (false) esta sentencia es idntica a la anterior */ // // // // // ============== val es declarada tipo bool e iniciada i es tipo int (no booleano) puntero nulo, equivale a: int *iptr = NULL j es tipo float (no booleano)

// Test para enteros if (i == true) cout << "Cierto: el valor es 1" << endl; if (i == false) cout << "Falso: el valor es 0" << endl; // Test para punteros if ((bool) iptr == false) cout << "Puntero no vlido" << endl; // Para comprobar el valor j se "modela" a tipo bool. if ((bool) j == true) cout << "el Booleano j es cierto" << endl; // Test de funcin devolviendo Booleano val = func(); if (val == false) cout << "func() devuelve falso."; if (val == true) cout << "func() devuelve cierto."; return false; // false es convertido a 0 (int) } Salida del programa: Cierto: el valor es 1 Puntero no vlido el Booleano j es cierto func() devuelve falso

6 El lenguaje C++ tiene operadores y sentencias en las que se exige la presencia de un tipo bool, son las siguientes: Operadores lgicos ( 4.9.8) AND (&&) OR (||) NOT (!). Producen un resultado booleano, y sus operandos son tambin valores lgicos o asimilables a ellos (los valores numricos son asimilados a true o false segn su valor). Operadores relacionales ( 4.9.12) <, >, <=, >=. Aceptan diversos tipos de operando, pero el resultado es de tipo booleano. Expresin condicional en las sentencias if ( 4.10.2) y en las iteraciones for, while y do..while ( 4.10.3). En todos estos casos, la sentencia condicional produce un bool como resultado. El primer operando del operador ? : ( 4.9.6), es convertido a bool.

3.2.1b bool, false, true


1 Sinopsis La palabra-clave bool declara un tipo especial de variable, denominada booleana ( solo puede tener dos valores: cierto y falso [1]. 0.1) que

Nota: por razn de los valores que pueden adoptar (cierto/falso), a estas variables tambin se las denomina variables lgicas.

2 Sintaxis bool <identificador>;

3 Descripcin Evidentemente el tipo bool est especialmente adaptado a para realizar comprobaciones lgicas; de hecho, todo el lgebra de Boole se basa justamente en el uso de este tipo de variables de solo dos valores mutuamente excluyentes. Por su parte, las palabras clave false y true son literales que tienen valores predefinidos (podemos considerar que son objetos de tipo booleano de valor constante predefinido). false tiene el valor falso (numricamente es cero), true tiene el valor cierto (numricamente es uno). Estos literales booleanos son Rvalues; no se les puede hacer una asignacin (no pueden estar a la izquierda de una asignacin 2.1). bool val = false; val = true; true = 0; // declara val variable Booleana y la inicia // ahora se cambia su valor (nueva asignacin) // Error!!

4 Conversin (modelado) de tipos: Tenga en cuenta que los bool y los int son tipos distintos. Sin embargo, cuando es necesario, el compilador realiza una conversin o modelado automtico ( ), de forma que pueden utilizarse libremente valores bool (true y false) junto con valores int sin utilizar un modelado explcito. Por ejemplo: int x = 0, y = 5; if (x == false) printf("x falso.\n"); if (y == truee) printf("y cierto.\n"); if ((bool) x == false) printf("x falso.\n");

// Ok. // Ok. // Modelado innecesario

Estas conversiones entre tipos lgicos y numricos, son simplemente una concesin del C++ para poder aprovechar la gran cantidad de cdigo C existente. Tradicionalmente en C se haca corresponder falso con el valor cero y cierto con el valor uno (o distinto de cero).

El proceso consiste en que, en las expresiones aritmticas y lgicas, las variables booleanas son convertidas a enteros (int). Las operaciones correspondientes (aritmticas y lgicas) se realizan con enteros, y finalmente el resultado numrico es vuelto a bool segn las reglas de conversin siguientes: Para convertir tipos bool a tipos int: false cero true uno Para convertir tipos int a un Rvalue tipo bool: cero false cualquier otro valor true.

Para el resto de variables distintas de int (aunque sean numricas), antes de utilizarlas en expresiones lgicas ( 4.9.9), es necesario un modelado ("casting"). Ejemplo: float x = 0 long y = 5; int *iptr = 0; if (x == false) printf("x falso"); // Error. if (y == truee) printf("y cierto"); // Error. if (iptr == false) printf("Puntero nulo"); // Error. if ((bool) x == false) printf("x falso"); // Ok. if ((bool) y == true) printf("y cierto"); // Ok. if ((bool) iptr == false) printf("Puntero nulo"); // Ok. Las reglas son anlogas a las anteriores: Para convertir un Rvalue tipo bool a un Rvalue numrico: false cero true uno. Para convertir tipos aritmticos (enumeraciones, punteros o punteros a miembros de clases) a un Rvalue de tipo bool, la regla es que cualquier valor cero; puntero nulo, o puntero a miembro de clase nulo, se convierte a false. Cualquier otro valor se convierte a true.

5 Los tipos bool y los literales false y true se pueden utilizar para realizar comprobaciones lgicas. En el ejemplo que sigue se muestra como hacer test booleanos con bool, true, y false: #include <iostream> using std::cout; using std::endl; bool func() { return NULL; return false; } int main() { bool val = false; int i = 1; int *iptr = 0; float j = 1.01; // Func devuelve un tipo bool /* NULL es convertido a Booleano (false) esta sentencia es idntica a la anterior */ // // // // // ============== val es declarada tipo bool e iniciada i es tipo int (no booleano) puntero nulo, equivale a: int *iptr = NULL j es tipo float (no booleano)

// Test para enteros if (i == true) cout << "Cierto: el valor es 1" << endl; if (i == false) cout << "Falso: el valor es 0" << endl; // Test para punteros if ((bool) iptr == false) cout << "Puntero no vlido" << endl; // Para comprobar el valor j se "modela" a tipo bool. if ((bool) j == true) cout << "el Booleano j es cierto" << endl; // Test de funcin devolviendo Booleano val = func(); if (val == false) cout << "func() devuelve falso."; if (val == true) cout << "func() devuelve cierto."; return false; // false es convertido a 0 (int) } Salida del programa: Cierto: el valor es 1 Puntero no vlido el Booleano j es cierto func() devuelve falso

6 El lenguaje C++ tiene operadores y sentencias en las que se exige la presencia de un tipo bool, son las siguientes: Operadores lgicos ( 4.9.8) AND (&&) OR (||) NOT (!). Producen un resultado booleano, y sus operandos son tambin valores lgicos o asimilables a ellos (los valores numricos son asimilados a true o false segn su valor). Operadores relacionales ( 4.9.12) <, >, <=, >=. Aceptan diversos tipos de operando, pero el resultado es de tipo booleano. Expresin condicional en las sentencias if ( 4.10.2) y en las iteraciones for, while y do..while ( 4.10.3). En todos estos casos, la sentencia condicional produce un bool como resultado. El primer operando del operador ? : ( 4.9.6), es convertido a bool.

3.2.1c const
1 Sinopsis La palabra clave const se utiliza para hacer que un objeto-dato, sealado por un identificador, no pueda ser modificado a lo largo del programa (sea constante). El especificador const se puede aplicar a cualquier objeto de cualquier tipo, dando lugar a un nuevo tipo con idnticas propiedades

que el original pero que no puede ser cambiado despus de su inicializacin (se trata pues de un verdadero especificador de tipo). Cuando se utiliza en la definicin de parmetros de funciones tiene significados adicionales especiales. o con miembros de clases ,

2 Sintaxis Existen cuatro formas posibles: const [<tipo-de-variable>] <nombre-de-variable> [ = <valor> ]; <nombre-de-funcin> ( const <tipo>*<nombre-de-variable> ); <nombre-de-metodo> const; const <instanciado de clase>; Nota: observe que no consideramos aqu la forma: const [<tipo-de-variable>] <nombre-de-funcin> (...); Significara una funcin devolviendo un tipo constante. Como el lector puede suponer, la pluralidad de formas sintcticas permitidas trasluce la variedad de significados que, dependiendo de las circunstancias, puede asumir esta palabra clave. Esta variedad se traduce en un comportamiento camalenico que cuesta trabajo asimilar en toda su extensin. Mxime si se le suma el hecho de que su comportamiento puede ser afectado por ciertos especificadores adicionales. 2.1 2.2 2.3 2.4

3 Descripcin La palabra clave const declara que un valor no es modificable por el programa. Puesto que no puede hacerse ninguna asignacin posterior a un identificador especificado como const, esta debe hacerse inevitablemente en el momento de la declaracin, a menos que se trate de una declaracin extern ( 4.1.8d). Ejemplos: const float pi = 3.14; char *const pt1 = "Sol"; char const *pt2 = "Luna"; const char *pt2 = "Luna"; const peso = 45; const float talla; extern const int x // // // // // // // una constante float pt1 puntero constante-a-char pt2 puntero-a-cadena-de-char constante idem. una constante int Error!! no inicializada Ok. declarada extern

4 Es importante insistir en el concepto de que tipo X-const y tipoX son tipos distintos (no se trata solamente de que a dicha variable no se le pueda cambiar el valor). Esto tiene importantes repercusiones prcticas; como veremos ( 4.2.1a), un puntero-a-constante-tipoX no es intercambiable por un puntero-a-tipoX.

5 Cuando no se indica <tipo-de-variable>, en ausencia de otro especificador, const por s misma equivale a int. const *ptr ; cons x = 10; // Puntero a int constante // Entero constante

6 Un carcter entre comillas simples es un carcter constante const char (declaracin implcita), y puede ser asignado a una variable, o utilizado en cualquier lugar que un char normal: const char letra_a = 'a';

7 Cualquier futuro intento de asignacin a una constante origina un error de compilacin (aunque el resultado exacto depende de la implementacin), por lo que segn las declaraciones anteriores , las que siguen son ilegales. pi = 3.0; i = peso++; pt1 = "Marte" // NO! Asigna un valor a una constante. // NO! Incrementa una constante. // NO! Apunta pt1 a otra entidad

8 const y volatile Aunque en principio pueda parecer un contrasentido, el especificador const puede coexistir con el atributo volatile ( 3.2.1d). Por tanto es vlida la expresin: const volatile int x = 33; Para el compilador viene a significar que la variable x no puede aceptar modificaciones a lo largo del programa, pero que su valor inicial 33, puede variar por causas externas, por lo que no se pueden hacer suposiciones sobre su valor actual.

9 const con punteros Puesto que el especificador const crea un nuevo tipo, un puntero a-tipoX es considerado distinto de un puntero a-constante-tipoX. Considere el siguiente ejemplo: int x = 10; int* xptr = &x; cons int y = 10; int* yptr = &y; const int* yptr = &y;

// Ok. // L.4: Error: // Ok.

Observe que const-tipoX* no puede ser asignado a tipoX*, que es el error que se produce en L.4. En este caso, el compilador lo expresa: "Cannot convert 'const int *' to 'int *' ..."; sin embargo, la inversa s es posible, es decir: tipoX* puede ser asignado a const-tipoX*:

int y = 10; const int* yptr = &y;

// Ok. !!

Tenga en cuenta que un puntero declarado como constante no puede ser modificado, pero el objeto al que seala s que puede modificarse (de hecho, el objeto sealado no tiene porqu ser constante). Siguiendo con las definiciones anteriores, puede hacerse: char *const pt1 = "Sol"; char *pt3 = pt1; pt3++; *pt3 = 'a'; cout << pt1; // // // // // pt1 puntero constante-a-char el puntero pt3 seala a la cadena "Sol" el puntero pt3 seala a la cadena "ol" modifica segundo carcter de "Sol" -> "Sal"

Nota: en determinados casos, una constante puede ser indirectamente modificada a travs de un puntero, aunque no sea aconsejable y el resultado dependa de la implementacin. Ver una discusin mas detallada sobre este asunto de punteros y constantes: Puntero constante/a constante ( 4.2.1e).

10 El atributo const puede ser reversible. C++ dispone de un operador especfico que puede poner o quitar este atributo de un objeto ( 4.9.9aoperador const_cast); se trata de una especie de "casting" especfico de la propiedad const, aunque con ciertas limitaciones.

11 const en parmetros de funciones El especificador const puede ser utilizado en la definicin de parmetros de funciones. Esto resulta de especial utilidad en tres casos; en los tres el fin que se persigue es el mismo: indicar que la funcin no podr cambiar dichos argumentos (segundo caso de la sintaxis ): Con parmetros de funciones que sean de tipo matriz (que se pasan por referencia). Ejemplo: int strlen(const char[]); Cuando los parmetros son punteros (a fin de que desde dentro de la funcin no puedan ser modificados los objetos referenciados). Ejemplo: int printf (const char *format, ...); Cuando el argumento de la funcin sea una referencia, previniendo as que la funcin pueda modificar el valor referenciado. Ejemplo: int dimen(const X &x2); En los tres casos sealados, la declaracin de los parmetros como const, garantiza que las respectivas funciones no puedan modificar el contenido de los argumentos.

12 const con miembros de clases El declarador const puede utilizarse con clases de tres formas distintas:

Utilizado en el sentido tradicional (objeto-dato que no puede ser modificado) Aplicado a mtodos de clase (con un sentido muy especial) 12.2 Aplicado a objetos (instancias de clase) asignndoles ciertas caractersticas

12.1 12.3

12.1 En el sentido tradicional del especificador Cuando se aplica a propiedades de clase, indicando que se trata de constantes cuyo valor no puede ser modificado a lo largo de la vida del programa. Ejemplo: class C { const int k; int x; public: int f1(C c1, const C& c2) { c1.x = c1.k = c2.x = return } ... }; Nota: puesto que en el cuerpo de la clase no pueden hacerse asignaciones, C++ proporciona un mtodo especial para inicializar estas constantes (caso de k) ( 4.11.2d3). 3; 4; 5; (x

// declara k constante (private) // declara x no constante (private) declara argumento c2 constante (segundo caso de sintaxis) Ok: objeto c1 modificable Error: Miembro k no modificable Error: objeto c2 NO modificable

// // // // // + k + c1.x + c2.k);

12.2 Aplicado a mtodos de clase Tercer caso de la sintaxis :

<nombre-de-funcin> const; Si se utiliza el especificador const en la declaracin de un mtodo de clase, la funcin no puede modificar ninguna propiedad en la clase. Este uso del especificador no puede utilizarse con funcinoes normales, solo con funciones-miembro de clase. Ejemplo class int int x } ... }; C { x; func(int i) const { = x + i;

// Private por defecto // tercer caso de sintaxis // Error: !

Al compilar este cdigo se produce un error: Cannot modify a const object in function C::func(int) const, avisndonos que la funcin func declarada constante no puede modificar el valor de la variable x (ni ningn otro miembro de C, sea o no constante). Ntese que si la definicin de la funcin est fuera de la definicin de la clase, tambin es necesario el especificador. En el ejemplo anterior: class C { int x; int func(int) const; ... }; ... inline int C::func(int i) const { /* ... */ } Tenga en cuenta que en estos casos, el especificador const forma parte del tipo de la funcin miembro, de forma que: class C { int x; int func(int); ... }; ... inline int C::func(int i) const { /* ... */ } Produce un error de compilacin: 'C::func(int) const' is not a member of 'C' Observe que el especificador const puede aparecer simultneamente en tres posiciones distintas de la declaracin de un mtodo: struct C { int x; const int& foo (const int&) const; }; const int& C::foo(const int& i) const { cout << "xxx " << x * i << endl; return this->x; } ... void func() { C c1 = {3}; int x = 3; foo(x); // -> xxx 30 } El primero significa que el valor devuelto por la funcin no podr ser utilizado para modificar el objeto original. El segundo indica que el argumento recibido por la funcin no podr ser modificado en el cuerpo de esta. El tercero seala que el mtodo C::foo no podr ser utilizado para modificar ningn miembro de la estructura C a la que pertenece.

12.3 Aplicado a instancias de clases Adems del sentido estricto de constante, cuando se utiliza con miembros de clases, const es una caracterstica aadida de seguridad [2]. En efecto: los objetos definidos como const intentan usar las funciones-miembro definidas a su vez como const . En general estos objetos solo puede usar miembros que tambin estn definidos como const, de forma que si un objeto-const invoca un mtodo no-const, el compilador muestra un mensaje de alerta, avisando que un objeto-const est utilizando un miembro no-const. Ejemplo #include <iostream.h> class C { int num; public: C(int i = 0) { num = i; } int func(int i) const { // privado por defecto

// constructor por defecto // func declarada const // tercer caso de la sintaxis cout << "Funcion no-modificativa." << endl; return i++;

} int func(int i) { // versin no-const de func cout << "Modifica datos privados" << endl; return num = i; // modifica miembro num } int f(int i) { // funcin no-const cout << "Funcion no-modificativa llamada con " << i << endl; return i; } }; int main() { C c_nc; const C c_c; c_nc.func(1); c_nc.f(1); c_c.func(1); c_c.f(1); } Salida: Modifica datos privados Funcion no-modificativa llamada con 1 Funcion no-modificativa. Funcion no-modificativa llamada con 1 // // // // // // // // ====================================== Instancia-1 Utilizar miembros no-const Instancia-2 Utilizar miembros const cuarto caso de la sintaxis invoca versin no-const de func Ok. f es funcin no-const invoca versin cons de func Aviso: objeto-const invoca funcin no-const

Observe que la clase C tiene dos versiones de la misma funcin func. No se trata de un caso de polimorfismo, puesto que ambas tienen exactamente la misma definicin. En ausencia del

especificador const en una de ellas, el compilador hubiese dado un error: Multiple declaration for 'C::func(int)', pero la presencia de este modificador hace que el compilador considere que ambos mtodos son distintos. La distincin de cual de las dos se utilizar en cada invocacin de un objeto depende de que el propio objeto sea declarado const o no-const. En el ejemplo se han creado dos instancias de la clase: Una normal, no-const c_nc, y otra const, c_c. Vemos que la invocacinc_nc.func(1); utiliza la versin no-const, mientras que la invocacin c_c.func(1); utiliza la versin constante. Puede comprobarse tambin que el compilador genera un mensaje de aviso en la penltima lnea: Non-const function C::f(int) called for const object in function main(), avisando que una funcin no-constante (f) ha sido invocada por un objeto constante (c_c). Nota: hay forma de saltarse esta restriccin: ver mutable ( 4.1.8e).

Temas relacionados: Constantes ( 3.2.3): Todo lo referente a los diversos tipos de constantes y su declaracin. Cosntantes literales ( 3.2.3f): Un tipo especial de matrices de caracteres. Enumeradores ( 3.2.3g): Un tipo especial de variable cuyo rango es una serie de constantes enteras.

3.2.1d volatile
1 Sinopsis Hemos sealado repetidas veces que una de las premisas de diseo del C++ es la velocidad de ejecucin. En este sentido, los diseadores de estos compiladores adoptan todas las estrategias posibles para garantizarlo. Por ejemplo, consideremos el siguiente trozo de cdigo: ... func (/* ... */); // realiza determinado proceso int limit = 1200; // definicin del lmite para el bucle for (int c=0; c<limit; c++) func(/* ... */); ...

El proceso definido por func se repite mientras que el contador c se mantiene inferior al lmite limit definido en la sentencia anterior. Puesto que en la condicin del for ( 4.10.3) no se altera el valor de la variable limit, el compilador puede asumir que este valor se mantiene constante durante la ejecucin del bucle, y puede que sea almacenado como variable de registro ( 4.1.8b) a fin de ahorrarse lecturas y tenerla rpidamente accesible para la comparacin c<limit, es ms que posible que c tambin sea declarada variable de registro, en cuyo caso las operaciones de incremento unitario c++, y comparacin con limit, seran procesos muy rpidos.

El problema es que, en determinadas circunstancias, este tipo de asunciones no es conveniente. Por ejemplo, supongamos que el programa C++ al que corresponden las sentencias anteriores est controlando un sistema de instrumentacin en el que la variable limit puede ser alterada por un dispositivo o proceso externo (puede ser otro hilo de ejecucin del programa - 1.7-). En tales casos nos encontramos en una situacin justamente opuesta a las constantes ( 3.2.1c). En aquel caso se indica al compilador: "Este valor se mantendr inalterable a lo largo de la ejecucin". En ocasiones como la descrita, necesitamos indicar al compilador justamente lo contrario: "No hacer ninguna suposicin sobre este valor, incluso si se ha ledo en la sentencia anterior". Para conseguir esto, C++ dispone de un indicador especfico; la palabra clave volatile es un modificador que aadido a una variable, advierte al compilador que esta puede ser modificada por una rutina en segundo plano (background), por una rutina de interrupcin, o por un dispositivo de entrada salida. Es decir, que puede sufrir modificaciones fuera del control del programa. La declaracin de un objeto con este atributo, previene al compilador de hacer ninguna asuncin sobre el valor del objeto mientras se ejecutan instrucciones en las que est involucrado, advirtindolo que dicho valor puede cambiar en cualquier momento. Tambin previene al compilador de que no debe hacer de dicho objeto una variable de registro.

2 Sintaxis volatile [<tipo-de-variable>] <nombre-de-variable> [ = <valor> ]; <nombre-de-funcin> ( volatile <tipo> <nombre-de-variable> ); <nombre-de-metodo> volatile; volatile <instanciado de clase>; 3 Ejemplo volatile int ticks; void timer( ) { ticks++; } void wait (int interval) { ticks = 0; while (ticks < interval); } // primer caso de la sintaxis.

// No hacer nada (esperar)

En este ejemplo, las rutinas implementan una espera de ciclos (ticks) especificados por el argumento interval (asumiendo que el reloj est asociado a un dispositivo hardware de interrupciones). Un compilador correctamente optimizado no debera cargar la variable ticks dentro de la rutina de comprobacin del bucle while, dado que el bucle no cambia el valor de dicha variable. Otros ejemplos: 4.9.3; 9.1;

4 volatile y const Como se ha sealado, el especificador const puede coexistir con el atributo volatile ( 3.2.1c)

5 volatile con punteros

Hay que hacer la salvedad de que, contra lo que ocurre con el especificador const, que crea un nuevo tipo, el atributo volatile solo realiza determinadas advertencias al compilador, manteniendo inalterado el tipo de variable sobre la que se aplica. Sin embargo, un puntero a-volatile-tipoX es considerado diferente de un puntero a-tipoX normal, del mismo modo que puntero a-tipoX es considerado distinto de puntero a-constante-tipoX. Considere el siguiente ejemplo: cons int x = 10; int* xptr = &x; const int* xptr = &x; volatile int y = 10; int* yptr = &y; volatile int* yptr = &y; int z; volatile int* zptr = &z

// Error: // Ok. // Error: // Ok. // Error:

6 volatile con miembros de clases El atributo volatile puede ser utilizado con miembros de clases (propiedades y mtodos), de forma parecida al modificador const con miembros de clase ( 3.2.1c). En efecto, puede ser utilizado de tres formas distintas:

6.1 En el sentido tradicional del especificador. Cuando se aplica a propiedades de clase, indicando que se trata de variables cuyo valor puede ser modificado desde fuera del programa. En el ejemplo que sigue podemos encontrar la primera y segunda formas de sintaxis: class C { volatile int x; int f1(int x, volatile int y) { return x + y + C::x; } ... };

// declara x volatile (private). // declara argumento y volatile

6.2 Cuando se aplica a mtodos de clase (tercer caso de la sintaxis). Ejemplo: <nombre-de-metodo> volatile; Si en la declaracin de un mtodo de clase se utiliza volatile, la funcin debe ser invocada por objetos tambin volatile (ver el siguiente caso). Este uso del atributo solo puede utilizarse con funciones-miembro de clase. Ejemplo:

class int int x } ... };

C { x; func(int i) volatile { = x + i;

// Private por defecto // declara func volatile

6.3 Cuando se aplica a instancias de clase. Adems del sentido estricto (variable que puede ser modificada desde fuera del programa), cuando se utiliza con miembros de clases, volatile es una caracterstica aadida de seguridad. En efecto, los objetos volatile intentan usar las funciones miembro definidas a su vez como volatile. Si un objeto volatile invoca una funcin miembro no-volatile, el compilador muestra un mensaje avisando de la circunstancia. Ejemplo: #include <iostream.h> class C { int num; // privado por defecto public: C(int i = 0) { num = i; } // constructor por defecto int func(int i) volatile { // version volatile de func cout << "Funcion volatile" << endl; return num = i; // modifica miembro no-volatile } int func(int i) { // versin no-volatile de func cout << "Funcion no-volatile" << endl; return num = i; // modifica miembro no-volatile } void f(int i) { // funcin no-volatile cout << "Funcion no-volatile llamada con " << i << endl; } }; int main() { C c_nv; volatiles volatile C c_v; volatiles c_nv.func(1); c_nv.f(1); c_v.func(1); c_v.f(2); volatil } Salida: Funcion no-volatile Funcion no-volatile llamada con 1 // ================================= // Instancia-1 Utilizar funciones no// Instancia-2 Utilizar funciones // // // // // cuarto caso de la sintaxis invoca versin no-volatil de func Ok. f es funcin no-volatil invoca versin volatil de func Aviso: objeto-volatil invoca funcin no-

Funcion volatile Funcion no-volatile llamada con 2

Observe que la clase C tiene dos versiones de la misma funcin func, y que no se trata de un caso de polimorfismo, puesto que ambas tienen exactamente la misma definicin. En ausencia del especificador volatile en una de ellas, el compilador hubiese dado un error: Multiple declaration for 'C::func(int)', pero la presencia de este modificador hace que el compilador considere que ambos mtodos son distintos. La distincin de cual de las dos se utilizar en cada invocacin de un objeto depende de que el propio objeto sea declarado volatile o no-volatile. En el ejemplo se han creado dos instancias de la clase: Una normal, no-volatile c_nv, y otra volatile, c_v. Vemos que la invocacinc_nv.func(1); utiliza la versin no-volatile, mientras que la invocacin c_v.func(1); utiliza la versin volatile. Tambin puede comprobarse que el compilador genera un mensaje de aviso en la penltima lnea: Non-volatile function C::f(int) called for volatile object in function main(), avisando que una funcin no-voltil (f) ha sido invocada por un objeto voltil (c_v).

7 El atributo volatile puede ser reversible. C++ dispone de un operador especfico que puede poner o quitar este atributo de un objeto ( 4.9.9aoperador const_cast).

3.2.1e typename
1 Sinopsis La palabra clave typename es un especificador de tipo. Indica justamente eso, que el identificador que la acompaa es un tipo (aunque no se haya definido todava). Una especie de declaracin adelantada para que el compilador no lo rechace (al identificador) y lo tome como tal.

2 Sintaxis Son posibles dos formas: typename identificador template < typename identificador > identificador-de-clase 2a 2b

3 Comentario La forma 2a se utiliza para declarar variables que no han sido definidas todava. De esta forma se evita que el compilador genere un error avisando que es un "tipo no definido". En el siguiente ejemplo se utiliza esta sintaxis para utilizar miembros A de una clase T ( T::A ) que no ha sido definida todava:

template <class T> void f() { typedef typename T::A TA; TA a1; typename T::A a2; TA * ptra; }

// clase T no definida an

// // // //

L.3 declara TA como tipo T::A L.4 declara a1 como de tipo TA declara a2 como de tipo T::A declara ptra como puntero-a-TA

L.3 especifica que cada ocurrencia de TA ser sustituida por typename T::A, lo que significa que por ejemplo, la sentencia L.4 es vista por el compilador como: typename T::A a1;. La sintaxis 2b se utiliza en sustitucin de la palabra clave class en las declaraciones de plantillas ( 4.12.2). El sentido es el mismo; significa "este argumento es cualquier tipo". En el siguiente ejemplo se utiliza esta sintaxis en la declaracin de dos funciones genricas: template <typename T1, typename T2> T2 convert (T1 t1) { return (T2)t1; } template <typename X, class Y> bool isequal (X x, Y y) { if (x==y)return 1; return 0; } // L.1 // L.5

Observe que la definicin L.1 utiliza typename en sustitucin del especificador class, mientras que en L.5 se utilizan indistintamente.

3.2.2 Identificadores
1 Sinopsis Recordemos ( 1.2.1) que un identificador es un conjunto de caracteres alfanumricos de cualquier longitud que sirve para identificar las entidades del programa (clases, funciones, variables, tipos compuestos, Etc.) Los identificadores pueden ser combinaciones de letras y nmeros. Cada lenguaje tiene sus propias reglas que definen como pueden estar construidos. En el caso de C++, son las que se indican a continuacin. Cuando un identificador se asocia a una entidad concreta, entonces es el "nombre" de dicha entidad, y en adelante la representa en el programa. Por supuesto puede ocurrir que varios identificadores se refieran a una misma entidad. Nota: 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.

2 Identificadores C++ Los identificadores C++ pueden contener las letras a a z y A a Z, el guin bajo "_" ("Underscore") y los dgitos 0 a 9.

Caracteres permitidos: abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ _ Dgitos permitidos 0123456789


Solo hay dos restricciones en cuanto a la composicin: El primer carcter debe ser una letra o el guin bajo. El Estndar establece que los identificadores comenzando con guin bajo y mayscula no deben ser utilizados. Este tipo de nombres se reserva para los compiladores y las Libreras Estndar. Tampoco se permite la utilizacin de nombres que contengan dos guiones bajos seguidos. El estndar ANSI establece que como mnimo sern significativos los 31 primeros caracteres, aunque pueden ser ms, segn la implementacin [1]. Es decir, para que un compilador se adhiera al estndar ANSI, debe considerar como significativos, al menos, los 31 primeros caracteres. 4.9) son un caso especial que se

Nota: veremos que los identificadores de los operadores ( aparta de estas reglas.

Los identificadores distinguen maysculas y minsculas, as que Sum, sum y suM son distintos para el compilador. Sin embargo, C++Builder ofrece la opcin de suspender la sensibilidad a maysculas / minsculas, lo que permite la compatibilidad con lenguajes insensibles a esta cuestin, en este caso, las variables globales Sum, sum y suM seran consideradas idnticas, aunque podra resultar un mensaje de aviso "Duplicate symbol" durante el enlazado. 2.1 Con los identificadores del tipo __pascal hay una excepcin a esta regla, ya que son convertidos siempre a maysculas con vistas al enlazado. Los identificadores globales importados desde otros mdulos siguen las mismas reglas que los identificadores normales.

3 Aunque los nombres de los identificadores pueden ser arbitrarios (dentro de las reglas sealadas), se produce un error si se utiliza el mismo identificador dentro del mismo mbito compartiendo el mismo espacio de nombres ( 4.1.11). Los nombres duplicados son legales en diferentes espacios de nombres con independencia de las reglas de mbito. Un identificador no puede coincidir con una palabra clave ( funcin de biblioteca. 3.2.1) o con el de ninguna

4 El estndar ANSI distingue dos tipos de identificadores:

Identificadores internos; los nombres de macros de preprocesado y todas las que no tengan enlazado externo. El estndar establece que sern significativos, al menos, los primeros 31 caracteres. Identificadores externos; los que corresponden a elementos que tengan enlazado externo. En este caso el estndar es ms permisivo. Se acepta que el compilador identifique solo seis caracteres significativos y pueda ignorar la distincin maysculas/minsculas (Enlazado 1.4.4).

5 Reglas de estilo Es bastante frecuente que en la enseanza de C++ (y de cualquier otro lenguaje de programacin) no se subraye suficientemente la importancia de la eleccin de los identificadores. En este sentido, los textos se suelen limitar a sealar las reglas formales que impone el lenguaje para la declaracin de nombres. Sin embargo, como todos los que tienen que ver con la legibilidad del cdigo, el asunto es de capital importancia. Sobre todo, si se trata de algo ms que del consabido programita "Hola mundo", y desde luego resulta crtico en proyectos medianamente grandes en los que puedan trabajar ms de un programador y/o deba ser mantenido por personas distintas de su creador original (lo que antes o despus acaba ocurriendo en la informtica empresarial). C y C++ tienen sus propias reglas no escritas, sancionadas por la costumbre, en cuanto a ciertas formas concretas de usar los identificadores. Por ejemplo: Es costumbre utilizar minsculas para los nombres de variables y funciones (1) (con frecuencia se utilizan combinaciones minsculas/Maysculas - por ejemplo getRvalue o rColor-, aunque la inicial suele ser minscula). Los identificadores de variables automticas lo ms cortos posibles (2); los de estticas y globales ms largos y descriptivos (3). Los nombres de constantes simblicas normalmente en maysculas (4). Ejemplo: void someFunc (int numero, char clave, static tipoCliente = 0; enum formaPago { CONTADO, CREDITO }; ... someFunc(int n, char k, int *ptr) { int z, y, z = 2; } int* puntero_a_clase); // (3) // (3) // (4) // (1) (2) // (2)

Aparte de las manas o hbitos particulares que pueda tener cada programador, la mayora de empresas de software medianamente serias disponen de sus propios "Manuales de estilo" o "Reglas de uso", en los que se recogen las convenciones que deben utilizarse para los identificadores, de forma que se mantenga la mxima homogeneidad posible en el cdigo, lo que a la postre redunda en una mayor legibilidad y facilidad de mantenimiento. En este sentido cabra sealar que, dentro de ciertos lmites, no es tan importante cuales sean estas reglas, sino que existan y se respeten. En determinados entornos existen reglas consagradas por el uso. Por ejemplo, en la programacin C/C++ para las plataformas Windows suelen seguirse determinadas convenciones conocidas como Notacin Hngara [5], en la que el identificador de cada variable comienza con una o varias letras (minsculas) que sealan el tipo de la variable. Por ejemplo, nValor.

Los nombres de clases se preceden siempre con una "C" mayscula, y la siguiente letra tambin es mayscula. Por ejemplo: CAboutDlg,CAprenderApp, CMainFrame, etc. Los nombres de variables comienzan por letras fijas segn su tipo (ver cuadro adjunto), pero cuando se refieren a una propiedad de clase, los dos primeros caracteres son "m_" (ejemplo m_nValor, m_wndStatusBar, etc). Los nombres de funciones miembro de clase comienzan siempre con mayscula, pero la siguiente letra es minscula [6]. Por ejemplo: DoDataExchange(),InitInstance(), OnAppAbout(), etc. Este tipo de notacin presenta la ventaja de con un poco de prctica es mucha la informacin que puede extraerse de la simple lectura del cdigo. En el cuadro adjunto se incluyen algunas de estas convenciones.

Prefijo Tipo de dato/situacin

Ejemplo
aValor, aX, aY bValor, bA, bB byValr, byA, byB cValor, cA, cB, chA, chB clrBgColor, clrFrgColor cxValor, cyValor, cxA, cyA dValor, dA, dB dwValor, dwA, dwB

a b by c / ch clr cx, cy d dw fn h i l m_ n

Matriz (array) BOOL (long) BYTE (unsigned char) carcter (char) COLORREF (unsigned long) [3] Entero (short) [2] double DWORD (unsigned long)

Funcin normal (los mtodos siguen otra fnGetx(), fnGety() regla) Handle [4] Entero (int) LONG (long)
hWind, m_hWind iValor, iX, iY lValor, lX, lY

Propiedades de clases (member variable) m_nValor, m_szString Entero (short int)


nValor, nX, nY

p s sz

Puntero Cadena alfanumrica (string)

pValor, pA, pB sValor, sA, sB szValor, szA, szB

Cadena alfanumrica terminada en carcter nulo al viejo estilo C ( 4.3.4)


Entero (unsigned int) Coordenadas x e y WORD (unsigned short) Clases ( 4.11) la letra siguiente tambin mayscula

u x, y w C

uValor, uA, uB x, y wValor, wA, wB

CDialog, CEditView, CButton

Nota: la programacin Windows utiliza sus propios tipos definidos mediante typedefs ( 3.2.1a). Generalmente son identificadores expresados en maysculas ( Ejemplos).

3.2.3 Constantes
1 Sinopsis La palabra constante tiene en C++ dos connotaciones sutilmente diferentes aunque relacionadas, que conviene distinguir: 1.1 La primera es el sentido normal de la palabra constante en lenguaje natural; es decir, datos (de cualquiera de los tipos posible) cuyos valores se han definido en el momento de escribir el cdigo del programa, y no pueden ser modificados ms tarde en tiempo de ejecucin (lo que significa que sus valores deben ser resueltos en tiempo de compilacin). Dicho en otras palabras: el compilador sabe cual es el valor de los objetos declarados como constantes y en base a este conocimiento puede hacer cuantas suposiciones sean vlidas para conseguir la mayor eficiencia en tiempo de ejecucin. En este sentido, el concepto constante es justamente el opuesto a variable, que corresponde a aquellos objetos-dato que pueden recibir nuevas asignaciones de valor a lo largo del programa. Dicho en otras palabras: entidades cuyo valor solo es conocido en tiempo de ejecucin. 1.2 La segunda connotacin es la de tipo ( 2.1) de objeto-dato. En este sentido podemos afirmar que en C++ los enteros (variables) forman un tipo distinto de los enteros constantes (constantes enteras), y que los caracteres (variables) forman un tipo distinto de las constantes carcter. As pues, distinguimos entre un tipo char y un tipo const char. Como prcticamente todos los tipos de objeto-dato posibles en C++ puede declararse constantes, existe un universo de tipos C++, simtrico al de los tipos de objetos variables, pero de objetos constantes.

Como corolario de lo anterior, tenga en mente que, por ejemplo, un entero y una constante entera son tipos distintos y que una constante entera C++ significa algo ms que un entero al que no se le puede cambiar su valor.

2 Lo que hace el compilador con los objetos declarados inicialmente como constantes depende de la implementacin. Esto significa que no est garantizado que tales objetos tengan un Lvalue ( 2.1.5). Por ejemplo: en const int x = 5; no est garantizado que el compilador le asigne a x un Lvalue, es decir, un espacio en memoria determinado (que se permita modificar o no su valor es otra cuestin). Puede ocurrir que, por razones de eficacia, sea simplemente una especie de " define" [1]. Una especie de nemnico que hace que el compilador lo sustituya por un 5 en cada trozo de cdigo donde aparezca x. Incluso en sitios donde aparezca asociada a otras constantes puede estar resuelto el valor en tiempo de compilacin. Por ejemplo, si en otro sitio del programa aparece: const int z = 7; y ms tarde: int w = x + z + y;puede ocurrir que el compilador establezca directamente: int w = 12 + y. Por esta razn no est garantizado que el operador const_cast ( declarados inicialmente como constantes. 4.9.9a) funcione con objetos

3 Como se ha indicado, en C++ existen tantos tipos de constantes como tipos de variables, pero aparte de las ya mencionadas constantes manifiestas ( 1.4.1a), solo nos detendremos en las que por una u otra razn hay cosas interesantes que puntualizar. Son las siguientes: Expresiones Constantes ( 3.2.3a) Constantes enteras ( 3.2.3b) Constantes fraccionarias ( 3.2.3c) Constantes carcter de varios subtipos, incluyendo elementos aislados y cadenas ( 3.2.3d, 3.3.3h, 3.2.3f) Enumeraciones ( 3.2.3g)

El Estndar C++ denomina "literales" a lo que el Estndar C denomina "constantes", y establece que existen 5 tipos de estos "literales": Constantes enteras ("Integer literal") Constantes carcter ("Character literal") Constantes fraccionarias ("Floating literal") Constantes de cadena ("String literal") Constantes lgicas ("Boolean literal").

4 Por la forma en que estn expresadas en el cdigo pueden considerarse en dos grupos: Directas si estn directamente expresadas. Por ejemplo: const float pi = 3.14159;

Expresiones (

3.2.3a) si su valor est implcito en una expresin. Por ejemplo:

const float k = pi * 2;

5 El tipo de dato correspondiente a una constante es deducido por el compilador en base a indicios implcitos, como el valor numrico y formato usados en el fuente. En algunos casos tambin por ciertos calificadores explcitos. C++ tiene una palabra especfica para este fin: const ( 3.2.1c). Ejemplos: char c = 'X'; const int X = 10; Inicio. // X es una constante tipo char // X es un tipo int-constante

[1] De hecho, en los primitivas versiones del C de K&R, cuando se quera utilizar una constante haba que utilizar un define ( 4.9.10b) en la forma: # define PI = 3.13159; # define G = 9.80665; Etc.

3.2.3a Expresiones constantes


1 Sinopsis Una expresin constante es aquella que solo implica a constantes (queda reducida a una constante), por lo que puede ser evaluada en tiempo de compilacin y debe evaluarse a un valor que est en el rango de valores admitidos para el tipo que se declara. Por ejemplo, si estamos declarando una constante tipo int, la expresin debe reducirse a un int: int const peso = 50 + 20; Los operandos de las expresiones constantes pueden ser constantes enteras ( 3.2.3b); constantes carcter ( 3.2.3d); constantes fraccionarias ( 3.2.3c); constantes de enumeracin ( 3.2.3g); expresiones sizeof ( 4.9.13); expresiones de modelado de tipos ( 4.9.9), e incluso otras expresiones constantes, aunque en ciertos casos se imponen algunas restricciones . Las expresiones constantes se evalan como el resto de las expresiones con variables y pueden utilizarse en cualquier caso en que sea legal el uso de una constante. 2 Sintaxis expresion-constante: Expresion-condicional

3 Ejemplo #define LEAP 1 /* en aos bisiestos */ int days[31+28+LEAP+31+30+31+30+31+31+30+31+30+31];

4 Las expresiones constantes no pueden contener ninguno de los operadores indicados a continuacin a menos que los operadores estn contenidos dentro del operando de un operador sizeof ( 4.9.13). Asignacin Coma Decremento Llamada a funcin Incremento int const k = 2+(kte = 4); int const k = (i=1, 3); int const k = kte--; int const k = func(4); int const k = kte++; // Error // Error // Error // Error // Error

5 Existen mltiples situaciones en las que el compilador C++ exige la presencia de expresiones constantes enteras (que se resuelvan a un entero). Por ejemplo, para establecer el tamao del campo de bits de una estructura ( 4.5.9); el valor de una constante de enumeracin ( 3.2.3g); el tamao de una matriz ( 4.3.1) o el valor de una constante case ( 4.10.2).

6 Expresiones constantes restringidas Las expresiones constantes utilizadas en las directivas de preproceso ( 4.9.10d) estn sujetas a ciertas restricciones adicionales, por lo que son conocidas como expresiones constantes restringidas. Entre estas restricciones se encuentran que no pueden contener constantes fraccionarias; tampoco expresiones sizeof, constantes de enumeracin ni expresiones de modelado de tipo.

3.2.3b Constantes enteras


1 Sinopsis Las constantes enteras representan un int, y pueden estar expresadas en los sistemas de numeracin decimal ; octal o hexadecimal . En ausencia de ningn sufijo , el tipo de una constante entera se deduce de su valor segn se muestra en las tablas que siguen. Observe que las reglas para las constantes decimales son distintas del resto.

2 Decimal Se permiten constantes enteras en formato decimal (base 10, 2.2.4b) dentro del rango 0 a 4,294,967,295; las que excedan este lmite sern truncadas. Observe que las constantes decimales no pueden comenzar con cero, porque seran interpretadas como octales. int i = 10; // decimal 10 int i = 010; // decimal 8 (octal 10)

int i = 0;

// decimal 0 = octal 0

Tipos adoptados por las constantes decimales en funcin del valor declarado (en ausencia de sufijos L o U) 0 a 32,767 32,768 a 2,147,483,647 2,147,483,648 a 4,294,967,295 >4294967295 int long unsigned long truncado

3 Octal Todas las constantes que empiecen por cero se suponen en octal (base 8, 2.2.4b). Si una constante de este tipo contiene dgitos ilegales (8 o 9), se produce un mensaje de error; como se indica en la tabla adjunta, las que exceden del valor mximo 037777777777 son truncadas. Tipos adoptados por las constantes Octales en funcin del valor declarado (en ausencia de sufijos L o U). 00 a 077777 010000 a 0177777 02000000 a 017777777777 020000000000 a 037777777777 > 037777777777 int unsigned int long unsigned long truncado

4 Hexadecimal Cualquier constante que comience por 0x o 0X es considerada hexadecimal [1], base 16 ( 2.2.4b), despus se pueden incluir los dgitos vlidos (0...9, a...f, A...F). Como se indica e el cuadro adjunto, las que excedan del valor mximo 0xFFFFFFFF son truncadas. Tipos adoptados por las constantes hexadecimales en funcin del valor declarado (en ausencia de sufijos L o U). 0x0000 a 0x7FFF 0x8000 a 0xFFFF 0x10000 a 0x7FFFFFFF 0x80000000 a 0xFFFFFFFF >0xFFFFFFFF Ejemplo: long lg1 = 0xFFFF; lg2 = 0xFF; cout << lg1 << endl; // -> 65535 cout << lg2 << endl; // -> 255 int unsigned int long unsigned long truncado

#define CS_VREDRAW 0x0001 #define CS_HREDRAW 0x0002

5 Sufijos: long (L) y unsigned (U) La adicin de sufijos en la definicin de una constante puede forzar que esta sea de un tipo especfico, as pues, los sufijos funcionan como una especie de "casting" para las constantes. Se utilizan las letras U u L l. Estos sufijos se pueden utilizar juntos y en cualquier orden o grafa ( ul, lu, Ul, lU, uL, Lu, LU o UL). Los sufijos L / l despus de una constante la fuerzan a ser declarada como long. Ejemplo: const x = 7L; // x es long

Los sufijos U / u la fuerzan a que sea declarada como unsigned; en este caso, con independencia de la base utilizada, la constante ser considerada unsigned long si el valor del nmero es mayor que el decimal 65,535. Ejemplo: const x = 7U; const y = 66000U; // x es unsigned int // y es unsigned long

En ausencia de alguno de estos sufijos el tipo de la constante es el primero de los que se sealan que pueda albergar su valor. Decimal: Octal: Hexadecimal: int, long int, unsigned long int int, unsigned int, long int, unsigned long int int, unsigned int, long int, unsigned long int

Si la constante tiene un sufijo U o u, su tipo es unsigned int, unsigned long o int (el primero que pueda albergar el valor). Si tiene un sufijo L o l, su tipo es long int o unsigned long int (el primero que pueda albergar el valor). Si la constante tiene ambos sufijos, u e l (ul, lu, Ul, lU, uL, Lu, LU o UL), su tipo de es unsigned long int. Ejemplo: const z = 0UL; // z es unsigned long

Como puede verse, las constantes enteras sin L o U compendian la representacin de constantes enteras en cualquiera de los tres sistemas de numeracin (en C++Builder). Nota: algunos compiladores utilizan los sufijos ll/LL y ull/ULL para referirse a los enteros extendidos ( 3.2.3d) long long y unsigned long long respectivamente.

3.2.3c Constantes fraccionarias


1 Sinopsis Las constantes fraccionarias (tambin llamada "de punto flotante") corresponden al concepto matemtico de nmeros fraccionarios. Es decir, cantidades con cierto nmero de cifras decimales. Su representacin el cdigo fuente puede contener: Parte entera 37.092e-2L Punto decimal [1] 37.092e-2L Parte fraccionaria 37.092e-2L e/E y un entero con signo (exponente) 37.092e-2L. Sufijo (indicador de tipo): f/F l/L 37.092e-2L

Pueden omitirse la parte entera o la decimal (pero no ambas); pueden omitirse el punto decimal y la letra E (e), y el exponente (pero no ambos). Las constantes fraccionarias negativas se forman igual que las positivas, pero precedindolas con el operador unitario menos ( - ).

2 Dos posibles notaciones Las reglas anteriores permiten utilizar para las constantes fraccionarias dos tipos de notacin: Notacin convencional (de punto decimal) Notacin cientfica (con exponente E/e)

Ejemplos: Expresin Valor 23.45e6 23.45 10^6 .0 0 0. 0.0 1. 1.0 -1.23 -1.23 2e-5 2.0 10^-5 3E+10 3.0 10^10 .09E34 0.09 10^34 Nota: la notacin 10^-5 significa 10 elevado a menos 5 ( 10 ). Ms informacin sobre la notacin cientfica en ( 2.2.4a)
-5

3 En ausencia de cualquier sufijo, las constantes fraccionarias se consideran de tipo double, aunque se puede obligar a que sea considerada de tipo float aadindole el sufijo f F. Ejemplo:

x = 1.; y = 1.f;

// L.1: // L.2:

En L.1 la constante 1.0 es considerada double, y podra producir una advertencia del compilador: Warning: Initializacin to 'int' from 'double'. L.2 producira: Warning: Initializacin to 'int' from 'float'. La razn es que, por tradicin del C, en ausencia de una declaracin explcita de tipo, en expresiones como L.1 y L.2, el compilador C++ supone que x e y son tipo int. Ejemplo relacionado: ( 2.2.4a)

4 De forma anloga, el sufijo l / L la fuerza a ser del tipo long double. Ejemplo: long lg1 = 3.0; long lg2 = 3.2L; long double = 4.0L; // L.1: // L.2: // L.3: Ok.

En L.1el compilador puede mostrar un aviso: Warning: initialization to 'long int' from 'double'. En L.2 el aviso sera:Warning: initialization to 'long int' from 'long double'.

5 La tabla adjunta muestra los rangos permitidos para los tres tipos disponibles: float, double y long double. Tipo float double long double Tamao (bits) 32 64 80 Rango 3.4 10^-38 a 3.4 10^38 1.7 10^-308 a 1.7 10^308 3.4 10^-4932 a 1.1 10^4932 .

3.2.3d Constantes carcter


1 Sinopsis Una constante carcter es uno o ms caracteres delimitados por comillas simples, como 'A', '+', o '\n'. En C++, son de un tipo especfico: chardel que existen dos versiones, signed y unsigned [1]. El valor por defecto, el que se supone cuando no se indica nada en concreto (char a secas) depende del compilador, pero puede ser seleccionado.

2 Los tres tipos char Las constantes de un carcter, tales como 'A', '\t' y '007', son representados como valores enteros, int (signed). En estos casos, si el valor es mayor que 127, el bit ms alto es puesto a -1 ( = 0xFF), es decir, las constantes carcter cuyo valor ASCII es mayor de 127 se representan como nmeros negativos, aunque esto puede ser desactivado declarando que los caracteres son unsigned por defecto. Cualquiera de los tres tipos carcter, char, signed char y unsigned char, se almacenan en 8-bits (un byte) [2].

En los programas a C++, una funcin puede ser sobrecargada con argumentos de cualquiera de los tres tipos: char, signed char o unsigned char (porque son tipos distintos para el compilador). Por ejemplo, los siguientes prototipos de funciones son vlidos y distintos: void func(char ch); void func(signed char ch); void func(unsigned char ch);

Sin embargo, si existiera solo uno de dichos prototipos, podra aceptar cualquiera de los tres tipos carcter. Por ejemplo, el cdigo que sigue sera aceptable (se producira una conversin automtica de tipo al pasar el argumento a la funcin): void func(unsigned char ch); void main(void) { signed char ch = 'x'; func(ch); }

3 Constantes de carcter-ancho El tipo de caracteres ancho se utiliza para representar juegos de caracteres que no caben en el espacio (1 byte) proporcionado por los carcter normales (char, signed char o unsigned char). Nota histrica: en C clsico, un carcter ancho ocupa dos bytes, y cualquier constante carcter precedida por L es un carcter ancho, un tipo de dato denominado wchar_t, que en realidad es un typedef definido en <stddef.h>. En Borland C++ wchar_t est definida como: typedef unsigned short wchar_t; para el caso de compilar como C. Recuerde que este compilador puede trabaja indistintamente con cdigo C y C++, y que un unsigned short ocupa 2 bytes ( 2.2.4).

En C++ wchar_t es una palabra clave que representa un tipo especial, el carcter ancho o extendido. Su espacio de almacenamiento depende de la implementacin (ver 2.2.1a1). En el caso de BC++ 5.5 ocupa el mismo espacio que un int, es decir: 4 bytes. En las cadenas de caracteres anchos se ocupan igualmente 4 bytes por carcter.

Ejemplos: wchar_t ch = L'A'; wchar_t *str = L"ABCD"; wchar_t nulo = L'\0'

// carcter nulo ancho

423

You might also like