Sie sind auf Seite 1von 55

4 Conversin entre sistemas multibyte y de caracteres anchos

Debido a que los caracteres anchos se emplean generalmente para representacin interna, y los
multibyte para la representacin externa, las operaciones de entrada/salida suelen implicar un
proceso de conversin entre ambos sistemas de codificacin. Un caso frecuente lo constituyen los
procesos de lectura y escritura de ficheros. Estos ltimos contienen generalmente caracteres
multibyte, y cuando se lee un fichero, sus caracteres deben ser convertidos en caracteres anchos y
situados en un almacenamiento interno donde puedan ser procesados. Evidentemente el sistema
de caracteres anchos debe tener anchura suficiente para contener el carcter ms ancho del
sistema multibyte. En cambio, todas las secuencias de escape pueden ser eliminadas. La figura
adjunta [3] muestra esquemticamente el proceso de convertir un fichero de codificacin JIS al
sistema Unicode de caracteres anchos.

Nota: La Librera Estndar C++ dispone de algunas herramientas para realizar este tipo de
conversiones ( 5.2.1).

2.2.1a1 El carcter ancho
1 Introduccin
Respecto al problema someramente esbozado en el apartado anterior ( 2.2.1a), de representar
los caracteres de lenguas distintas del ingls americano. En los compiladores C y C++ se decidi
utilizar un nuevo tipo, denominado carcter ancho o DBCS ("Double Byte Character Set"), en
atencin a que como veremos a continuacin, la mayora de las veces ocupa dos Bytes.
El carcter ancho se designa con la palabra wchar_t. En C se utiliz un typdef ( 3.2.1a,
<stddef.h>) para definirlo, pero en C++ tom carta de naturaleza como un nuevo
tipo preconstruido en el lenguaje, de forma que de ser un typedef pas a ser palabra clave.
2 wchar_t
En C++ esta palabra clave identifica un tipo especial, el carcter ancho.
Sintaxis:
wchar_t <identificador>;
Comentario
El Estndar C++ nos dice que su tamao es dependiente de la implementacin, pero que debe ser
suficiente para almacenar el juego de caracteres mayor que soporte el compilador. Como
actualmente el juego de caracteres internacional es Unicode ( 2.2.1a2) que utiliza 2 bytes, este
es el tamao mnimo del wchar_t de la mayora de compiladores.
Nota: en el caso de Borland C++ es del mismo tamao, signo y alineacin que el tipo int, es
decir 4 bytes (un wchar_t C es de solo 2 bytes), mientras que el wchar_t de MS Visual C++
es de 2 bytes.

Tenga en cuenta, que la librera Estndar C++ utiliza funciones distintas para manejar caracteres
anchos y normales. Por ejemplo, la funcinprintf para caracteres tipo char, tiene una
versin wprintf cuando se trata de representar caracteres tipo wchar_t; a su vez, strlen ("string
length") tiene la versin wcslen ("wide-character string length") para caracteres anchos. wcout es
la versin de cout para caracteres anchos. Etc. (existen dos versiones de todas aquellas
funciones que manejan cadenas alfanumricas).
Ejemplos
wchar_t ch; // declaracin de carcter ancho
wchar_t* str = L"ABCD"; // puntero a cadena de caracteres anchos
wcout << "La cadena es: " << str << endl; // L.3
wcout << L"La cadena es: " << str << endl; // L.4
wchar_t cnulo = L'\0' // carcter nulo ancho
Comentario:
Las sentencias L.3 y L.4 son equivalentes; la salida en ambos casos es:
La cadena es: ABCD
Observe que en L.3 el compilador es suficientemente inteligente para suponer que el literal "La
cadena es: " es de caracteres anchos.

2.2.1a2 Codificaciones UCS/Unicode
1 Introduccin
Respecto al problema ya esbozado en el captulo anterior ( 2.2.1a), de representar los
caracteres de lenguas distintas del ingls americano, se haban utilizado mltiples soluciones
parciales, pero a finales de los 80 se hicieron intentos para definir un sistema de codificacin
verdaderamente universal. De un lado el denominado UCS ("Universal Character Set"),
preconizado por la organizacin internacional de estandarizacin ISO; de otro el
denominado Unicode, desarrollado por la iniciativa privada (Microsoft principalmente). Esta
compaa haba incorporado en sus sistemas Windows 9x un juego de caracteres bastante
adaptado a la mayora de alfabetos occidentales. Posteriormente implement Unicode en sus
sistemas NT y sucesores.
Tanto UCS como Unicode pretendan eliminar el problema de una vez por todas, estableciendo
un sistema de codificacin que permitiese la incorporacin los caracteres (grafos) de todas las
lenguas escritas del mundo, tanto actuales como pasadas, as como los smbolos utilizados en
matemticas, tipografa (por ejemplo los utilizados en TeX) y otros, como el juego de caracteres
fonticos IPA ("International Phonetic Alphabet"); tipos usados frecuentemente para reconocimiento
ptico de caracteres (OCR), etc. El sistema debera ser adems extensible, de forma que pudieran
aadirse nuevos tipos en el futuro.
Aunque el desarrollo inicial de ambos sistemas fue independiente, los respectivos comits se
dieron cuenta enseguida que este camino seguira manteniendo el cisma que tanto dao haba
causado a la informtica desde los primeros intentos de extender el US-ASCII, de forma que pronto
convergieron, y el sistema Unicode se convirti en un subset del sistema UCS ms general.
2 UCS
El sistema universal de caracteres UCS se concreta en el estndar ISO-10646 y se define como un
juego de caracteres de 31 bits. Por supuesto sus 2
31
posibilidades garantizan que pueda soportar
con creces las amplsimas expectativas sealadas en el prrafo anterior. Sin embargo, como no es
necesario utilizar un potencial tan desmesurado, se han definido subconjuntos. En realidad todos
los espacios utilizados por el momento se agrupan en los 16 primeros bits. Este subconjunto es
conocido como BMP ("Basic Multilingual Plane") o Plano 0, y sus 2
16
(65.536) posibilidades son
suficientes para los caracteres de todas las lenguas conocidas.
Nota: en realidad, lo que se hizo fue copiar el conocidsimo juego de caracteres US-ACII en
las 128 primeras posiciones. A continuacin, en las 128 siguientes, hasta completar 256, se
colocaron los caracteres de la extensin ANSI/ISO 8859-1 (una variacin de los caracteres
mostrados en la tabla 3.2a del captulo anterior). Sucesivamente se fueron reservando zonas
para diversos alfabetos, por ejemplo, para el Griego; el Cirlico (utilizado en la Federacin
Rusa y otros pases limtrofes); Armenio; Hebreo y los ideogramas Kanji (Japoneses), Hangeul
(Coreanos) y del alfabeto Chino.

Los espacios situados fuera de este "Plano" estn reservados para los caracteres utilizados en
aplicaciones muy especiales. Por ejemplo, los estudiosos de culturas antiguas o la notacin
cientfica. El estndar ISO-10646-1 fue establecido en 1993, y le sigui una versin ISO-10646-2
en el 2001. La teora es que pueden aadirse nuevos caracteres segn las necesidades, pero los
ya establecidos no se alterarn nunca ms, de forma que se pretende una plataforma estable.
Adems se supone que no existirn nunca caracteres de ms de 21 bits, ya que mil trillones de
caracteres (2
21
) sern suficientes.
3 Unicode
Tcnicamente Unicode es el ISO 10646-1. As pues cada carcter ocupa 16 bits, lo que significa
que puede estar representado por un carcter ancho (wchar_t 2.2.1a1) de cualquier
compilador C++.
El inconveniente es que evidentemente, los almacenamientos ocupan el doble de espacio que con
los caracteres estndar. La ventaja es que no existe necesidad de ninguna conversin ni
ambigedad. Cada carcter, representado por un nmero, tiene una significacin
inequvoca. Como ejemplo se incluye un trozo de cdigo tomado del fichero winnt.h, donde puede
verse como la definicin de los typedef ( 3.2.1a) TCHAR yPTCHAR se realiza de forma distinta
segn se est utilizando un juego de caracteres normales o Unicode.
#ifdef UNICODE
typedef WCHAR TCHAR, *PTCHAR;
#else
typedef char TCHAR, *PTCHAR;
#endif
Los programas Windows identifican la lengua mediante una constante manifiesta ( 1.4.1a) que
es traducida por el preprocesador a una constante hexadecimal ( 3.2.3b). A su vez, las
variaciones o dialectos locales ("sublenguajes" en la terminologa de estos compiladores) se
identifican mediante otras constantes que son tambin traducidas a nmeros.
Para dar idea de las lenguas soportadas, a continuacin se relacionan las constantes
implementadas en el compilador MS VC++ 6.0
Lengua Variedad
#define LANG_NEUTRAL 0x00
#define LANG_AFRIKAANS
0x36
#define LANG_ALBANIAN 0x1c
#define LANG_ARABIC 0x01
#define LANG_ARMENIAN 0x2b
#define LANG_ASSAMESE 0x4d
#define LANG_AZERI 0x2c
#define LANG_BASQUE 0x2d
#define LANG_BELARUSIAN
0x23
#define LANG_BENGALI 0x45
#define LANG_BULGARIAN
0x02
#define LANG_CATALAN 0x03
#define LANG_CHINESE 0x04
#define LANG_CROATIAN 0x1a
#define LANG_CZECH 0x05
#define LANG_DANISH 0x06
#define SUBLANG_NEUTRAL
0x00 // language neutral
#define SUBLANG_DEFAULT
0x01 // user default
#define SUBLANG_SYS_DEFAULT
0x02 // system default
#define
SUBLANG_ARABIC_SAUDI_ARABIA 0x01
// Arabic (Saudi Arabia)
#define SUBLANG_ARABIC_IRAQ
0x02 // Arabic (Iraq)
#define SUBLANG_ARABIC_EGYPT
0x03 // Arabic (Egypt)
#define SUBLANG_ARABIC_LIBYA
0x04 // Arabic (Libya)
#define SUBLANG_ARABIC_ALGERIA
0x05 // Arabic (Algeria)
#define SUBLANG_ARABIC_MOROCCO
0x06 // Arabic (Morocco)
#define LANG_DUTCH 0x13
#define LANG_ENGLISH 0x09
#define LANG_ESTONIAN 0x25
#define LANG_FAEROESE 0x38
#define LANG_FARSI 0x29
#define LANG_FINNISH 0x0b
#define LANG_FRENCH 0x0c
#define LANG_GEORGIAN 0x37
#define LANG_GERMAN 0x07
#define LANG_GREEK 0x08
#define LANG_GUJARATI 0x47
#define LANG_HEBREW 0x0d
#define LANG_HINDI 0x39
#define LANG_HUNGARIAN
0x0e
#define LANG_ICELANDIC
0x0f
#define LANG_INDONESIAN
0x21
#define LANG_ITALIAN 0x10
#define LANG_JAPANESE 0x11
#define LANG_KANNADA 0x4b
#define LANG_KASHMIRI 0x60
#define LANG_KAZAK 0x3f
#define LANG_KONKANI 0x57
#define LANG_KOREAN 0x12
#define LANG_LATVIAN 0x26
#define LANG_LITHUANIAN
0x27
#define LANG_MACEDONIAN
0x2f
#define LANG_MALAY 0x3e
#define LANG_MALAYALAM
0x4c
#define LANG_MANIPURI 0x58
#define LANG_MARATHI 0x4e
#define LANG_NEPALI 0x61
#define LANG_NORWEGIAN
0x14
#define LANG_ORIYA 0x48
#define LANG_POLISH 0x15
#define LANG_PORTUGUESE
0x16
#define LANG_PUNJABI 0x46
#define LANG_ROMANIAN 0x18
#define LANG_RUSSIAN 0x19
#define LANG_SANSKRIT 0x4f
#define SUBLANG_ARABIC_TUNISIA
0x07 // Arabic (Tunisia)
#define SUBLANG_ARABIC_OMAN
0x08 // Arabic (Oman)
#define SUBLANG_ARABIC_YEMEN
0x09 // Arabic (Yemen)
#define SUBLANG_ARABIC_SYRIA
0x0a // Arabic (Syria)
#define SUBLANG_ARABIC_JORDAN
0x0b // Arabic (Jordan)
#define SUBLANG_ARABIC_LEBANON
0x0c // Arabic (Lebanon)
#define SUBLANG_ARABIC_KUWAIT
0x0d // Arabic (Kuwait)
#define SUBLANG_ARABIC_UAE
0x0e // Arabic (U.A.E)
#define SUBLANG_ARABIC_BAHRAIN
0x0f // Arabic (Bahrain)
#define SUBLANG_ARABIC_QATAR
0x10 // Arabic (Qatar)
#define SUBLANG_AZERI_LATIN
0x01 // Azeri (Latin)
#define SUBLANG_AZERI_CYRILLIC
0x02 // Azeri (Cyrillic)
#define
SUBLANG_CHINESE_TRADITIONAL 0x01
// Chinese (Taiwan)
#define SUBLANG_CHINESE_SIMPLIFIED
0x02 // Chinese (PR China)
#define SUBLANG_CHINESE_HONGKONG
0x03 // Chinese (Hong Kong)
#define SUBLANG_CHINESE_SINGAPORE
0x04 // Chinese (Singapore)
#define SUBLANG_CHINESE_MACAU
0x05 // Chinese (Macau)
#define SUBLANG_DUTCH
0x01 // Dutch
#define SUBLANG_DUTCH_BELGIAN
0x02 // Dutch (Belgian)
#define SUBLANG_ENGLISH_US
0x01 // English (USA)
#define SUBLANG_ENGLISH_UK
0x02 // English (UK)
#define SUBLANG_ENGLISH_AUS
0x03 // English (Australian)
#define SUBLANG_ENGLISH_CAN
0x04 // English (Canadian)
#define LANG_SERBIAN 0x1a
#define LANG_SINDHI 0x59
#define LANG_SLOVAK 0x1b
#define LANG_SLOVENIAN
0x24
#define LANG_SPANISH 0x0a
#define LANG_SWAHILI 0x41
#define LANG_SWEDISH 0x1d
#define LANG_TAMIL 0x49
#define LANG_TATAR 0x44
#define LANG_TELUGU 0x4a
#define LANG_THAI 0x1e
#define LANG_TURKISH 0x1f
#define LANG_UKRAINIAN
0x22
#define LANG_URDU 0x20
#define LANG_UZBEK 0x43
#define LANG_VIETNAMESE
0x2a
#define SUBLANG_ENGLISH_NZ
0x05 // English (New
Zealand)
#define SUBLANG_ENGLISH_EIRE
0x06 // English (Irish)
#define
SUBLANG_ENGLISH_SOUTH_AFRICA 0x07
// English (South Africa)
#define SUBLANG_ENGLISH_JAMAICA
0x08 // English (Jamaica)
#define SUBLANG_ENGLISH_CARIBBEAN
0x09 // English (Caribbean)
#define SUBLANG_ENGLISH_BELIZE
0x0a // English (Belize)
#define SUBLANG_ENGLISH_TRINIDAD
0x0b // English (Trinidad)
#define SUBLANG_ENGLISH_ZIMBABWE
0x0c // English (Zimbabwe)
#define
SUBLANG_ENGLISH_PHILIPPINES 0x0d
// English (Philippines)
#define SUBLANG_FRENCH
0x01 // French
#define SUBLANG_FRENCH_BELGIAN
0x02 // French (Belgian)
#define SUBLANG_FRENCH_CANADIAN
0x03 // French (Canadian)
#define SUBLANG_FRENCH_SWISS
0x04 // French (Swiss)
#define SUBLANG_FRENCH_LUXEMBOURG
0x05 // French (Luxembourg)
#define SUBLANG_FRENCH_MONACO
0x06 // French (Monaco)
#define SUBLANG_GERMAN
0x01 // German
#define SUBLANG_GERMAN_SWISS
0x02 // German (Swiss)
#define SUBLANG_GERMAN_AUSTRIAN
0x03 // German (Austrian)
#define SUBLANG_GERMAN_LUXEMBOURG
0x04 // German (Luxembourg)
#define
SUBLANG_GERMAN_LIECHTENSTEIN 0x05
// German (Liechtenstein)
#define SUBLANG_ITALIAN
0x01 // Italian
#define SUBLANG_ITALIAN_SWISS
0x02 // Italian (Swiss)
#define SUBLANG_KASHMIRI_INDIA
0x02 // Kashmiri (India)
#define SUBLANG_KOREAN
0x01 // Korean
(Extended Wansung)
#define SUBLANG_LITHUANIAN
0x01 // Lithuanian
#define SUBLANG_LITHUANIAN_CLASSIC
0x02 // Lithuanian (Classic)
#define SUBLANG_MALAY_MALAYSIA
0x01 // Malay (Malaysia)
#define
SUBLANG_MALAY_BRUNEI_DARUSSALAM
0x02 // Malay (Brunei Darussalam)
#define SUBLANG_NEPALI_INDIA
0x02 // Nepali (India)
#define SUBLANG_NORWEGIAN_BOKMAL
0x01 // Norwegian (Bokmal)
#define SUBLANG_NORWEGIAN_NYNORSK
0x02 // Norwegian (Nynorsk)
#define SUBLANG_PORTUGUESE
0x02 // Portuguese
#define
SUBLANG_PORTUGUESE_BRAZILIAN 0x01
// Portuguese (Brazilian)
#define SUBLANG_SERBIAN_LATIN
0x02 // Serbian (Latin)
#define SUBLANG_SERBIAN_CYRILLIC
0x03 // Serbian (Cyrillic)
#define SUBLANG_SPANISH
0x01 // Spanish
(Castilian)
#define SUBLANG_SPANISH_MEXICAN
0x02 // Spanish (Mexican)
#define SUBLANG_SPANISH_MODERN
0x03 // Spanish (Modern)
#define SUBLANG_SPANISH_GUATEMALA
0x04 // Spanish (Guatemala)
#define SUBLANG_SPANISH_COSTA_RICA
0x05 // Spanish (Costa Rica)
#define SUBLANG_SPANISH_PANAMA
0x06 // Spanish (Panama)
#define
SUBLANG_SPANISH_DOMINICAN_REPUBLIC
0x07 // Spanish (Dominican
Republic)
#define SUBLANG_SPANISH_VENEZUELA
0x08 // Spanish (Venezuela)
#define SUBLANG_SPANISH_COLOMBIA
0x09 // Spanish (Colombia)
#define SUBLANG_SPANISH_PERU
0x0a // Spanish (Peru)
#define SUBLANG_SPANISH_ARGENTINA
0x0b // Spanish (Argentina)
#define SUBLANG_SPANISH_ECUADOR
0x0c // Spanish (Ecuador)
#define SUBLANG_SPANISH_CHILE
0x0d // Spanish (Chile)
#define SUBLANG_SPANISH_URUGUAY
0x0e // Spanish (Uruguay)
#define SUBLANG_SPANISH_PARAGUAY
0x0f // Spanish (Paraguay)
#define SUBLANG_SPANISH_BOLIVIA
0x10 // Spanish (Bolivia)
#define
SUBLANG_SPANISH_EL_SALVADOR 0x11
// Spanish (El Salvador)
#define SUBLANG_SPANISH_HONDURAS
0x12 // Spanish (Honduras)
#define SUBLANG_SPANISH_NICARAGUA
0x13 // Spanish (Nicaragua)
#define
SUBLANG_SPANISH_PUERTO_RICO 0x14
// Spanish (Puerto Rico)
#define SUBLANG_SWEDISH
0x01 // Swedish
#define SUBLANG_SWEDISH_FINLAND
0x02 // Swedish (Finland)
#define SUBLANG_URDU_PAKISTAN
0x01 // Urdu (Pakistan)
#define SUBLANG_URDU_INDIA
0x02 // Urdu (India)
#define SUBLANG_UZBEK_LATIN
0x01 // Uzbek (Latin)
#define SUBLANG_UZBEK_CYRILLIC
0x02 // Uzbek (Cyrillic)
3 Webografa
UTF-8 and Unicode FAQ for Unix/Linux www.cl.cam.ac.uk
A pesar de su ttulo, esta pgina de Markus Kuhn es una excelente gua acerca de los sistemas de
codificacin en general.
Unicode.org www.unicode.org/unicode/
Sin duda el sitio oficial es la mejor fuente de informacin. En la pgina "Code charts" pueden
obtenerse las tablas de caracteres, ideogramas y signos de todas las lenguas del mundo.

2.2.2 Tipos derivados
1 Sinopsis
Aparte de los tipos bsicos ( 2.2.1) y sus variantes, C++ soporta otros tipos de naturaleza
compleja que derivan de aquellos. Dicho en otras palabras: C++ permite al usuario construir otros
tipos a partir de los bsicos. De estos "nuevos" tipos, los de nivel de abstraccin ms alto son
lasclases ( 4.11), incluyendo sus formas particulares (estructuras y uniones); tambin pueden
crearse campos de bits; matrices y una familia de tipos muy importante: los punteros. Hay
punteros a cada uno de los dems tipos: a carcter, a entero, a estructura, a funcin, a puntero,
etc. ( 4.2).
Los tipos derivados se construyen con palabras clave: class, struct, unin u operadores
especiales como los siguientes:
* puntero a
*const puntero constante a
& referencia a
[ ] array[1] de
( ) retorno de funcin
Ejemplos: suponiendo que tipoX un tipo bsico o variante (no void 2.2.1), pueden declararse
tipos derivados tal como se muestra:
tipoX t; // t es un objeto de tipo tipoX
tipoX arr[10]; // arr es una matriz de diez
elementos tipoX
tipoX *ptr; // ptr es un puntero a tipoX
tipoX &ref=t; // ref es una referencia a tipoX
tipoX func(void); // func devuelve un valor tipoX (no
acepta parmetros)
void func(tipoX t); // func1 acepta un
parmetro t tipoX (no devuelve nada)
struct st {tipoX t1; tipoX t2}; // la estructura st alberga dos tipoX
Nota: Las expresiones: tipo& var, tipo &var y tipo & var son equivalentes.

2.2.3 Modificadores de tipo
1 Sinopsis
Hemos sealado ( 2.2.1) que los modificadores opcionales que pueden acompaar a los tipos
bsicos son: con signo, sin signo, largo ycorto; y que se aplican con las palabras
clave. long, short, signed, unsigned. Por otra parte, la creciente implementacin de
procesadores con longitud de palabra de 64 bits, hace suponer que pronto se extendern los tipos
actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos).
2 long
Sintaxis:
long [int] <identificador> ;
[long] double <identificador> ;
Descripcin:
Cuando se utiliza este modificador sobre un int, crea un tipo que dobla el espacio de
almacenamiento utilizado para almacenar un int [1]. Cuando se utiliza para modificar un double,
define un tipo de dato de coma flotante, long double, con 80 bits de precisin, no los 128 (2x64)
que cabra esperar ( 2.2.4 Representacin interna y precisin).
Existen los siguientes tipos de long:
long x; // x es signed long int
long int x; // x es signed long int
signed long x; // x es signed long int
signed long int x; // x es signed long int
unsigned long x; // x es unsigned long int
unsigned long int x; // x es unsigned long int
3 short
Sintaxis:
short int <identificador> ;
Descripcin:
El modificador short se utiliza cuando se desea una variable menor que un int. Este modificador
solo puede aplicarse al tipo base int (si se omite el tipo base, se asume int por defecto).
Existen los siguientes tipos:
short x; // x es signed short int
short int x; // x es signed short int
signed short x; // x es signed short int
signed short int x; // x es signed short int
unsigned short x; // x es unsigned short int
unsigned short int x; // x es unsigned short int
Ejemplos:
short int i;
short i; // equivale a: "short int i;"
4 signed
Sintaxis:
signed <tipo> <identificador> ;
Descripcin:
El modificador de tipo signed define que el valor de una variable numrica puede ser positivo o
negativo. Este modificador puede ser aplicado a los tipos bsicos int, char, long, short y __int64.
Segn se indica en el ejemplo, si no se indica el tipo bsico, el modificador por si solo
suponesigned int.
El efecto que tiene este modificador, es que parte de la capacidad de almacenamiento del tipo
base al que se aplica, se utiliza para especificar el signo, por lo que el mximo valor absoluto que
es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base (
2.2.4Tipos de datos y representacin interna).
Ejemplos:
signed int i; // signed es valor por defecto (no es preciso)
int i; // equivalente al anterior
signed i; // equivalente al anterior
unsigned long int l; // Ok
unsigned long l; // equivale al anterior
signed char ch; // Ok
unsigned char ch; // Ok
5 unsigned
Sintaxis:
unsigned <tipo> <identificador> ;
Descripcin:
Este modificador se utiliza cuando la variable sea siempre positiva. Puesto que no es necesario
almacenar el signo, el valor absoluto puede ser mayor que en las versiones con signo. Puede
aplicarse a los tipos base int, char, long, short e __int64. Cuando no se indica tipo base, por
defecto se supone que se trata de un int (ver los ejemplos).
Ejemplos:
unsigned int i;
unsigned i; // equivale al anterior
unsigned long int l; // Ok
unsigned long l; // Equivale al anterior
unsigned char ch; // Ok
char ch; // Equivale al anterior
6 Tipos enteros extendidos
La progresiva utilizacin de procesadores de 64 bits, de los que pronto existirn versiones para
ordenadores de sobremesa, junto con el crecimiento espectacular de la memoria disponible, tanto
en RAM como en disco, hace que sea razonable esperar versiones de C++ que permitan utilizar
enteros con ms de 32 bits. Entre otras razones, porque rpidamente sern normales
almacenamientos de disco con ms capacidad de la que puede direccionarse directamente con
palabras de 32 bits.
En la actualidad se est trabajando en un estndar, conocido como C9x que se espera sea
adoptado oficialmente en breve (2001). Esta versin incluye nuevos especificadores
opcionales long long en versiones con y sin signo [2].
El cuadro adjunto muestra la propuesta existente para los citados modificadores. Como puede
verse, siguiendo la tradicin, se supone int si no se indica otro tipo base. Adems por defecto long
long se supone con signo.
long long x; // x es signed long long int
long long int x; // x es signed long long int
signed long long x; // x es signed long long int
signed long long int x; // x es signed long long int
unsigned long long x; // x es unsigned long long int
unsigned long long int x; // x es unsigned long long int
7 Extensiones C++Builder
Este compilador C++ de Borland permite especificadores opcionales para los enteros, ms all de
lo especificado en el estndar (alguno en lnea de los nuevos tipos que se esperan). Estos
modificadores opcionales permiten definir el tipo de almacenamiento que se utilizar para el entero.
Para utilizarlos es preciso usar tambin los sufijos que se indican en cada caso:
Tipo Sufijo Ejemplo Almacenamiento
__int8 i8 __int8 c = 127i8; 8 bits
__int16 i16 __int16 s = 32767i16; 16 bits
__int32 i32 __int32 i = 123456789i32; 32 bits
__int64 i64 __int64 big = 12345654321i64; 64 bits
unsigned
__int64
ui64
unsigned __int64
hInt=1234567887654321ui64;
64 bits
2.2.4 Tipos bsicos: representacin interna, rango
1 Sinopsis
El ANSI C reconoce que el tamao y rangos de valor numrico de los tipos bsicos y sus varias
permutaciones ( 2.2.1) dependen de la implementacin y generalmente derivan de la
arquitectura del ordenador. La tabla adjunta muestra los tamaos y rangos de los tipos numricos
de 32-bits de Borland C++ [1].
Nota: precisamente esta circunstancia, que el Estndar C++ impone relativa libertad en cuanto
al tamao de los tipos, es la responsable de que an adhirindose estrictamente al Estndar,
puedan existir problemas de portabilidad entre diversas plataformas de los programas C++
(algo que no ocurre con otros lenguajes de definicin ms estricta. Por ejemplo Java).
Ver en 2.2.4c unas notas adicionales sobre los tipos bsicos.
2 Almacenamiento y rango
Las explicaciones siguientes muestran como se representan internamente estos tipos
(en negrita los tipos bsicos). Los ficheros de cabecera<climits> y <float.h> contienen
definiciones de los rangos de valor de todos los tipos fundamentales.
Tipo bits Rango / Tipo de uso
unsigned char 8 0 <= X <= 255 Nmeros pequeos y juego caracteres del PC.
char (signed) 8 -128 <= X <= 127 Nmeros muy pequeos y juego de caracteres
ASCII [5]
short (signed) 16 -32,768 <= X <= 32,767 Nmeros muy pequeos, control de bucles
pequeos
unsigned
short
16 0 <= X <= 65,535 Nmeros muy pequeos, control de bucles
pequeos
unsigned (int) 32 0 <= X <= 4,294,967,295. Nmeros grandes
int (signed) 32 -2,147,483,648 <= X <= 2,147,483,647 Nmeros pequeos, control
de bucles
unsigned long 32 0 <= X <= 4,294,967,295 Distancias astronmicas
enum 32 -2,147,483,648 <= X <= 2,147,483,647 Conjuntos de valores
ordenados
long (int) 32 -2,147,483,648 <= X <= 2,147,483,647 Nmeros grandes
float 32 1.18e-38 <= |X| <= 3.40e38 Precisin cientfica ( 7-dgitos)
double 64 2.23e-308 <= |X| <= 1.79e308 Precisin cientfica (15-dgitos)
long double 80 3.37e-4932 <= |X| <= 1.18e4932 Precisin cientfica (18-dgitos)
Nota: las cuestiones de almacenamiento interno, como se almacenan los datos en memoria y
cuanto espacio necesitan, estn influidas por otros factores adems de los sealados. Estas
son las que podramos denominar "necesidades mnimas" de almacenamiento. En ocasiones,
especialmente en estructuras y uniones, el compilador realiza determinados "ajustes" o
"alineaciones" con los datos, de forma que las direcciones de memoria se ajustan a
determinadas pautas. El resultado es que en estos casos, el tamao de por ejemplo una
estructura, no corresponde con lo que tericamente se deduce de la suma de los miembros (
4.5.9). Las caractersticas de esta "alineacin" pueden ser controladas mediante opciones
del compilador ( 4.5.9a).

3 Enteros
En C++ 32-bit, los tipos int y long son equivalentes, ambos usan 32 bits [3]. Las variedades con
signo son todas almacenadas en forma de complemento a dos usando el bit ms significativo como
bit de signo (0 positivo y 1 negativo), lo que explica los rangos indicados en la tabla. En las
versiones sin signo, se usan todos los bits, con lo que el nmero de posibilidades es 2
n
, y el rango
de valores est entre 0 y 2
n
-1, donde n es el nmero de bits de la palabra del procesador, 8, 16 o
32 (uno, dos, o cuatro octetos).
El estndar ANSI C no define el tamao de almacenamiento de los diversos tipos, solamente indica
que la serie short, int y long no es descendente, es decir: short <= int <= long. De hecho,
legalmente los tres tipos pueden ser del mismo tamao.
En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int.
Actualmente, la mayora de las aplicaciones de oficina y personales, con entornos como Windows
o Linux, corren sobre plataformas hardware de 32 bits, de forma que la mayora de los
compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamao que el long).
En cualquier caso, los rangos vienen indicados por las constantes que se sealan (incluidas en
<limits.h>):
signed short: SHRT_MIN <= X <= SHRT_MAX.
Siendo: SHRT_MIN <= -32767 y SHRT_MAX >= 32767. Algunas implementaciones
hacen SHRT_MIN = -32768 pero no es exigido por el estndar.
unsigned short: 0 <= X <= USHRT_MAX.
Siendo: USHRT_MAX >= 65535. Las variedades short deben contener al menos 16 bits para
que pueda cubrirse el rango de valores exigidos.
En la mayora de los compiladores un short es menor que un int, de forma que algunos
programas que deben almacenar grandes matrices de nmeros en memoria o en ficheros pueden
economizar espacio utilizando short en lugar de int, pero siempre que se cumplan dos
condiciones:
1. En la implementacin un short es realmente menor que un int.
2.- Los valores caben en un short.
En algunas arquitecturas el cdigo empleado para manejar los short es ms largo y lento que
el correspondiente para los int. Esto es particularmente cierto en los procesadores Intel x86
ejecutando cdigo de 32 bits en programas para Windows (NT/95/98), Linux y otras versiones
Unix. En estos cdigos, cada instruccin que referencia a un short es un byte ms larga y
generalmente necesita tiempo extra de procesador para ejecutarse.
signed int: INT_MIN <= X <= INT_MAX.
Siendo: INT_MIN <= -32767 y INT_MAX >= 32767. Algunas implementaciones utilizan un
valor INT_MIN = -32768 pero no es exigido en el estndar.
unsigned int: 0 <= X <= UINT_MAX.
Siendo: UINT_MAX >= 65535. Para cubrir esta exigencia, los int deben ser de 16 bits por lo
menos.
El rango exigido para signed int y unsigned int es idntico que para los signed
short y unsigned short. En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel
x86 ejecutando cdigo en modo 16 bits, como bajo MS DOS), normalmente un int es de 16 bits,
exactamente igual que un short. En los compiladores para procesadores de 32 bit y mayores
(incluyendo los Intel x86 ejecutando cdigo de 32 bits como Windows o Linux) generalmente
un int es de 32 bits, exactamente igual que un long.
signed long: LONG_MIN <= X <= LONG_MAX.
Siendo: LONG_MIN <= -2147483647 y LONG_MAX >= 2147483647. Existen implementaciones
que hacen LONG_MIN = -2147483648 pero no es exigido por el estndar.
unsigned long: 0 <= X <= ULONG_MAX.
Siendo: ULONG_MAX >= 4294967295. Para poder cubrir este rango, los tipos long deben ser de
al menos 32 bits.
4 Nuevos tipos numricos
Los rangos previstos para los nuevos tipos ( 3.2.3d) long long. que se proyectan incluir en el
estndar son:
signed long long: LLONG_MIN <= X <= LLONG_MAX.
Siendo: LLONG_MIN <= -9223372036854775807 y LLONG_MAX >= 9223372036854775807.
Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido.
unsigned long long: 0 <= X <= ULLONG_MAX.
Siendo: ULLONG_MAX >= 18446744073709551615. Las variedades long deben ser de al menos
64 bits para poder cubrir el rango exigido.
La diferencia entre enteros con signo y sin signo (signed y unsigned) es que en los primeros el bit
ms significativo se usa para guardar el signo (0 positivo, 1 negativo), esto hace que los enteros
con signo tengan un rango de valores posibles distinto que los unsigned. Vase al respecto el
rango de int y unsigned int.
Los enteros sin signo se mantienen en valores 0 positivos, dentro de la aritmtica de numeracin
de mdulo base 2, es decir 2
n
, donde n es el nmero de bits de almacenamiento del tipo, de forma
que, por ejemplo, si un int se almacena en 32 bits, unsigned int tiene un rango entre 0 y 2
32
-1 =
4,294,967,295 (el valor 0 ocupa una posicin de las 4.294.967.295 posibles).
5 Carcter
La cabecera <limits.h> contiene una macro, CHAR_BIT, que se expande a una constante entera
que indica el nmero de bits de un tipo carcter (char), que se almacenan en 1 byte, es decir,
siempre ocurre que sizeof(char) == 1. Esta es la definicin ANSI de byte en C/C++, es decir, la
memoria requerida para almacenar un carcter, pero este byte no corresponde necesariamente
con el byte de la mquina.
El valor de CHAR_BIT es al menos 8; la mayora de los ordenadores modernos usan bytes de
8 bits (octetos), pero existen algunos con otros tamaos, por ejemplo 9 bits. Adems algunos
procesadores, especialmente de seal (Digital Signal Processors), que no pueden acceder de
forma eficiente a la memoria en tamaos menores que la palabra del preprocesador, tienen
un CHAR_BIT distinto, por ejemplo 24. En estos casos, los tipos char, short e int son todos de 24
bits, y long de 48 bits. Incluso son ms comunes actualmente procesadores de seal donde todos
los tipos enteros incluyendo los long son de 32 bits.
signed char: Valores entre: SCHAR_MIN <= X <= SCHAR_MAX,
Siendo: SCHAR_MIN <= -127 y SCHAR_MAX >= 127. La mayora de los compiladores utilizan
un valor SCHAR_MIN de -128, pero no es exigido por el estndar.
unsigned char: Valores entre 0 <= x <= UCHAR_MAX.
Se exige que UCHAR_MAX >= 255. si CHAR_BIT es mayor que 8, se exige que UCHAR_MAX =
2
CHAR_BIT
- 1. De forma que una implementacin que utilice un carcter de 9 bits puede almacenar
valores entre 0 y 511 en un unsigned char.
char (valor carcter a secas "plain char"). Valores entre CHAR_MIN <= X <= CHAR_MAX
Si los valores char son considerados signed char por
defecto: CHAR_MIN == SCHAR_MIN y CHAR_MAX == SCHAR_MAX.
Si los valores char son considerados unsigned char por defecto: CHAR_MIN == 0
y CHAR_MAX == UCHAR_MAX.
Por ejemplo, un trozo del fichero limits.h que acompaa al compilador Microsoft Visual C++ 2008,
tiene el siguiente aspecto:
#define CHAR_BIT 8 /* number of bits in a char */
#define SCHAR_MIN (-128) /* minimum signed char value */
#define SCHAR_MAX 127 /* maximum signed char value */
#define UCHAR_MAX 0xff /* maximum unsigned char value */
6 Fraccionarios
La representacin y rango de valores de los nmeros fraccionarios depende del compilador. Es
decir, cada implementacin de C++ es libre para definirlos. La mayora utiliza el formato estndar
de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nmeros ( 2.2.4a).
float y double son tipos fraccionarios de 32 y 64 bits respectivamente. El modificador long puede
utilizarse con el tipo double, declarando entonces un nmero fraccionario de 80 bits. En
C++Builder las constantes fraccionarias, que pueden ser float, double y long double, tienen los
rangos que se indican:
Tipo bits Rango
float 32 1.17549e-38 <= |X| <= 3.40282e+38
double 64 2.22507e-308 <= |X| <= 1.79769e+308
long double 80 3.37e-4932 <= |X| <= 1.18e4932

Generalmente los compiladores C++ incluyen de forma automtica la librera matemtica de punto
flotante si el programa utiliza valores fraccionarios [4]. Builder utiliza los siguientes lmites,
definidos en el fichero <values.h>
float. Valores entre: MINFLOAT <= |X| <= MAXFLOAT (valores actuales entre parntesis)
MINFLOAT (1.17549e-38)
MAXFLOAT (3.40282e+38)
FEXPLEN Nmero de bits en el exponente (8)
FMAXEXP Valor mximo permitido para el exponente (38)
FMAXPOWTWO Mxima potencia de dos permitida (127)
FMINEXP Valor mnimo permitido para el exponente (-37)
FSIGNIF Nmero de bits significativos. (24)
double. Valores entre MINDOUBLE <= |X| <= MAXDOUB
double. Valores entre MINDOUBLE <= |X| <= MAXDOUBLE
MINDOUBLE (2.22507e-308)
MAXDOUBLE (1.79769e+308)
DEXPLEN Nmero de bits en el exponente (11)
DMAXEXP Valor mximo permitido para el exponente (308)
DMAXPOWTWO Mxima potencia de dos permitida (1023)
DMINEXP Valor mnimo permitido para el exponente (-307)
DSIGNIF Nmero de bits significativos (53)
7 La clase numeric_limits
La Librera Estndar C++ contiene una clase numeric_limits que contiene informacin sobre los
escalares. Existen subclases para cada tipo fundamental, enteros (incluyendo los booleanos) y
fraccionarios. Esta clase engloba la informacin contenida en los ficheros de cabecera <climits> y
<cfloat>; adems de incluir informacin que no est contenida en ninguna otra cabecera. A
continuacin se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor
de los tipos fundamentales (mantisa), as como la salida proporcionada en un caso concreto.
#include <cstdlib>
#include <iostream>
#include <limits>

int main(int argc, char *argv[]) {
std::cout << "Mantisa de un char: "
<< std::numeric_limits<char>::digits << '\n';
std::cout << "Mantisa de un unsigned char: "
<< std::numeric_limits<unsigned char>::digits << '\n';

std::cout << "Mantisa de un short: "
<< std::numeric_limits<short>::digits << '\n';
std::cout << "Mantisa de un unsigned short: "
<< std::numeric_limits<unsigned short>::digits << '\n';

std::cout << "Mantisa de un int: "
<< std::numeric_limits<int>::digits << '\n';
std::cout << "Mantisa de un unsigned int: "
<< std::numeric_limits<unsigned int>::digits << '\n';

std::cout << "Mantisa de un long: "
<< std::numeric_limits<long>::digits << '\n';
std::cout << "Mantisa de un unsigned long: "
<< std::numeric_limits<unsigned long>::digits << '\n';

std::cout << "Mantisa de un float: "
<< std::numeric_limits<float>::digits << '\n';
std::cout << "Longitud de un double: "
<< std::numeric_limits<double>::digits << '\n';
std::cout << "Longitud de un long double: "
<< std::numeric_limits<long double>::digits << '\n';

std::cout << "Mantisa de un long long: "
<< std::numeric_limits<long long>::digits << '\n';
std::cout << "Mantisa de un unsigned long long: "
<< std::numeric_limits<unsigned long long>::digits << '\n';

return 0;
}
Salida en una mquina Intel con el compilador GNU g++ 3.4.2 para Windows:
Mantisa de un char: 7
Mantisa de un unsigned char: 8
Mantisa de un short: 15
Mantisa de un unsigned short: 16
Mantisa de un int: 31
Mantisa de un unsigned int: 32
Mantisa de un long: 31
Mantisa de un unsigned long: 32
Mantisa de un float: 24
Longitud de un double: 53
Longitud de un long double: 64
Mantisa de un long long: 63
Mantisa de un unsigned long long: 64
Temas relacionados:
Operador sizeof ( 4.9.13)
Alineacin interna ( 4.6.1).

2.2.4a Formas de representacin binaria de las magnitudes numricas
1 Presentacin de un problema
Antes de entrar en detalles, haremos un pequeo inciso para sealar el principal problema que
entraa la representacin de cantidades numricas en los ordenadores digitales.
En el apartado dedicado al Ordenador digital ( 0.1) recordamos que la informacin est
representada en forma digitalizada. Es decir, reducida a cantidades discretas representadas por
nmeros y estos a su vez, expresados en formato binario. Como la serie de los nmeros reales
tiene infinitos nmeros (desde -Infinito a +Infinito [0]), es imposible su representacin completa en
cualquier sistema de representacin. Adems, aunque un nmero puede contener una cantidad
indefinida de cifras, los bits destinados a almacenarlas son necesariamente limitados [3]. Como
consecuencia, en la informtica real solo es posible utilizar un subconjunto finito del conjunto de los
nmeros reales.
El rango y precisin (nmero de cifras) del subconjunto de nmeros que pueden representarse en
una mquina dada, dependen de la arquitectura, y para el lenguaje C++, depende adems del
compilador ( 2.2.4). Puesto que existen ocasiones en que las aplicaciones informticas
necesitan manejar nmeros muy grandes y muy pequeos, se ha derrochado mucho ingenio para
conseguir representaciones binarias con la mxima precisin en el mnimo espacio, y para que
estos formatos puedan ser manipulados por implementaciones hardware lo ms simples posible.
Tambin ha sido necesario ingeniar artificios para detectar y prevenir situaciones en las que un
resultado se sale por arriba o por abajo del rango permitido, al tiempo que se mantiene el mximo
de precisin en los clculos.
Hay que recordar que, incluso manejando cantidades dentro del rango, pueden presentarse
fcilmente situaciones con errores de bulto que seran catastrficas en determinadas
circunstancias. Por ejemplo, en clculos de ingeniera. Supongamos una situacin en que el
compilador C++ tiene que multiplicar una serie de cantidades definidas en la mxima precisin:
long double r = x * y * z;
y que el orden de ejecucin x * y * z es en este caso de izquierda a derecha. Si en un momento
dado los valores de x e y son suficientemente pequeos (prximos al lmite inferior permitido
para long double), el primer producto x * y puede resultar inferior al mnimo que puede
representar el compilador, originndose un "underflow". El resultado intermedio sera cero, y su
producto por z tambin cero, con independencia del valor de esta ltima variable (que suponemos
grande). El valor cero del resultado r podra a su vez propagarse inadvertidamente a otros clculos.
Observe tambin, que si la operacin hubiese sido programada en otro orden. Por ejemplo:
long double r = x * z * y;
Tal vez el error no hubiese llegado a presentarse, dando la sensacin que el cdigo es seguro con
los mismos valores de las variables. No es necesario sealar que este tipo de errores pueden
acarrear consecuencias desastrosas. Por ejemplo, en clculos de ingeniera. Para que el lector
pueda formarse visin ms tangible del problema, le invito a visitar esta interesante pgina (en
ingls) Some disasters attributable to bad numerical computing.
Otros tipos de errores de precisin son ms insidiosos an. Para comprobarlo pruebe el lector este
sencillo programa:
#include <iostream.h>
int main (void) {
float f = 3.0/7.0; // M1:
if (f == 3.0/7.0)
cout << "Igual" << endl; // M2:
else
cout << "Desigual" << endl;
return 0;
}
La salida, con el compilador Borland C++ 5.5 es
Desigual
La explicacin es que aqu las constantes 3.0 y 7.0 han sido consideradas como nmeros de coma
flotante de doble precisin (double), y el resultado de 3.0/7.0, que es del mismo tipo, sufre una
conversin estrechante ( 4.9.9) a float, con prdida de precisin, antes de la asignacin a f en
M1. El mismo valor 3.0/7.0 (double) es comparado en M2 con el de f (float), con el resultado de
que no son iguales.
La comprobacin de las afirmaciones anteriores es muy sencilla: basta modificar la lnea M1 del
programa por alguna de estas dos:
double f = 3.0/7.0; // M1.1:
long double f = 3.0/7.0; // M1.2:
Despus de la sustitucin, en ambos casos la salida es
Igual.
Si deseamos mantener la variable f como un float, una posible solucin sera cambiar la sentencia
M2 (intente encontrar la explicacin por s mismo en 3.2.3c):
if (f == 3.0f/7.0f) cout << "Igual" << endl; // M2.1:

En el apartado que dedicamos a las conversiones estndar ( 2.2.5), encontrar explicacin del
porqu no funcionara ninguna de las versiones siguientes:
if (f == 3.0f/7.0) cout << "Igual" << endl; // M2.2:
if (f == 3.0/7.0f) cout << "Igual" << endl; // M2.3:
2 Formas de representacin binaria
La necesidad de representar no solo enteros naturales (positivos), sino tambin valores negativos
e incluso fraccionarios (racionales), ha dado lugar a diversas formas de representacin binaria de
los nmeros. En lo que respecta a los enteros, se utilizan principalmente cuatro:
Binario sin signo
Binario con signo
Binario en complemento a uno
Binario en complemento a dos
Lo relativo a los fraccionarios se indica ms adelante .
2.1 Cdigo binario sin signo:
Las cantidades se representan de izquierda a derecha (el bit ms significativo a la izquierda y el
menos significativo a la derecha) como en el sistema de representacin decimal. Los bits se
representan por ceros y unos; cero es ausencia de valor, uno es valor. Por ejemplo, la
representacin del decimal 33 es 100001.
Si utilizamos un octeto para representar nmeros pequeos, y mantenemos la costumbre de
separar las cifras en grupos de 4 para mejorar la legibilidad, su representacin es: 0010 0001.
Con este sistema todos los bits estn disponibles para representar una cantidad; por consiguiente,
un octeto puede albergar nmeros de rango
0 <= n <= 255
Nota: aunque la representacin interna (en memoria) suele tener el bit ms significativo a la
izquierda, el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente
igual. Existen casos en los que la representacin externa es justamente al contrario, el bit ms
significativo a la derecha, lo que supone que estos bytes deben ser invertidos durante los
procesos de lectura/escritura. Existen casos en que una misma aplicacin sigue distintos
criterios para la alineacin del bit ms significativo segn el tipo de dato que se escribe en el
disco. Por supuesto la situacin se complica cuando el nmero est representado por ms de
un octeto. En este caso tambin puede jugarse con el orden de escritura de los octetos. Vase
al respecto Orden de Almacenamiento ( 2.2.6a)
2.2 Cdigo binario con signo
Ante la necesidad de tener que representar enteros negativos, se decidi reservar un bit para
representar el signo. Es tradicin destinar a este efecto el bit ms significativo (izquierdo); este bit
es 0 para valores positivos y 1 para los negativos. Por ejemplo, la representacin de 33 y -33
sera:
+33 0010 0001
-33 1010 0001
Como en un octeto solo quedan siete bits para representar la cantidad, con este sistema un Byte
puede representar nmeros en el rango:
- 127 <= n <= 127

El sistema anterior se denomina cdigo binario en magnitud y signo. Aparentemente es el
primero y ms sencillo de los que se pueden discurrir, adems de ser muy simple para codificar y
decodificar. Sin embargo, la circuitera electrnica necesaria para implementar con ellos
operaciones aritmticas es algo complicada, por lo que se dispusieron otros sistemas que se
revelaron ms simples en este sentido.
2.3 Cdigo binario en complemento a uno
En este sistema los nmeros positivos se representan como en el sistema binario en magnitud y
signo, es decir, siguiendo el sistema tradicional, aunque reservando el bit ms significativo, que
debe ser cero. Para los nmeros negativos se utiliza el complemento a uno, que consiste en
tomar la representacin del correspondiente nmero positivo y cambiar los bits 0 por 1 y viceversa
(el bit ms significativo del nmero positivo, que es cero, pasa ahora a ser 1). En captulo dedicado
a los Operadores de manejo de bits ( 4.9.3), veremos que C++ dispone de un operador
especfico para realizar estos complementos a uno.
Como puede verse, en este sistema, el bit ms significativo sigue representando el signo, y es
siempre 1 para los nmeros negativos. Por ejemplo, la representacin de 33 y -33 sera:
+33 0010 0001
-33 1101 1110
2.4 Cdigo binario en complemento a dos:
En este sistema, los nmeros positivos se representan como en el anterior, reservando tambin el
bit ms significativo (que debe ser cero) para el signo. Para los nmeros negativos, se utiliza un
sistema distinto, denominado complemento a dos, en el que se cambian los bits que seran 0 por
1 y viceversa, y al resultado se le suma uno.
Este sistema sigue reservando el bit ms significativo para el signo, que sigue siendo 1 en los
negativos. Por ejemplo, la representacin de 33 y -33 sera:
+33 0010 0001
-33 1101 1110 + 0000 0001 1101 1111
El hardware necesario para implementar operaciones aritmticas con nmeros representados de
este modo es mucho ms sencillo que el del complemento a uno, por lo que es el sistema ms
ampliamente utilizado [8]. Precisamente esta forma de representacin interna es la respuesta al
problema presentado en la pgina
adjunta ( Problema)
Nota: el manual Borland C++
informa que los tipos enteros
con signo, tanto los que
utilizan dos octetos (16 bits)
como los que utilizan una
palabra de 4 Bytes (32 bits),
se representan internamente
en forma de cdigo binario en complemento a dos (Fig. 1).

Precisamente los procesadores Intel 8088, sus descendientes y compatibles, almacenan
internamente los nmeros en esta forma, y para facilitar la rpida identificacin del signo,
disponen de un bit (SF) en el registro de estado ( H3.2) que indica si el resultado de una
operacin tiene a 1 o a 0 el bit ms significativo.
3 Nmeros fraccionarios
A continuacin exponemos brevemente los detalles del formato utilizado para representacin
interna de los nmeros fraccionarios. Es decir, cmo son representados en forma binaria los
nmeros con decimales.
3.1 Notacin cientfica
En ciencias puras y aplicadas, es frecuente tener que utilizar nmeros muy grandes y muy
pequeos. Para facilitar su representacin, se desarroll la denominada notacin
cientfica (tambin denominada engineering notation en la literatura inglesa) en la que el nmero
es representado mediante dos cantidades, la mantisa y la caracterstica, separadas por la
letra E/e.
Nota: en esta notacin las letras E/e no tienen nada que ver con la constante e (2.71828182...)
base de los logaritmos Neperianos. Es meramente un smbolo para separar dos partes de una
expresin (podra haberse utilizado cualquier otro).
La mantisa es la parte significativa del nmero (las cifras significativas que se conocen [5] ).
La caracterstica es un nmero entero con signo, que indica el nmero de posiciones que hay que
desplazar a la derecha o a la izquierda el punto decimal (explcito o implcito). Por la razn
sealada (que la caracterstica indica la posicin del punto decimal), esta representacin es
tambin conocida como de "punto flotante".
La caracterstica puede ser interpretada tambin como la potencia de 10 por la que hay que
multiplicar la mantisa para obtener el nmero. Es decir: si V es el nmero, m la mantisa, y c la
caracterstica, resulta: V = m . 10
c
. Esta notacin (matemtica tradicional) es equivalente a V =
mec= mEc en notacin cientfica
Ejemplos:
Expresin Valor
23.45e6 23.45 10^6 == 23450000
-2e-5 -2.0 10^-5 == -0.00002
3E+10 3.0 10^10 == 30000000000
-.09E34 -0.09 10^34 == -900000000000000000000000000000000
3.1.1 Notacin normalizada
Puede verse que la notacin cientfica permite varias formas para un mismo nmero. Por
ejemplo, para el nmero 12.31 seran, entre otras:
12.31e0
1231e-2
123.1e-1
1.231e1
0.1231e2
0.01231e3
La representacin de nmeros fraccionarios que necesita de una menor cantidad de dgitos en
notacin cientfica, es aquella que utiliza un punto decimal despus de la primera cifra significativa
de la mantisa. Esta forma de representacin se denomina normalizada (el resto de formas
posibles se denominan subnormales). En el caso del nmero anterior, la notacin normalizada
sera: 1.231e1.
Nota: observe que en esta forma el exponente es mnimo, y representa la utilizacin de la
mxima cantidad de cifras significativas en la mantisa, de forma que para una cantidad de
cifras determinada, es la que permite mayor precisin.

Segn lo anterior, la mantisa m de la forma normalizada de un nmero distinto de cero, puede
expresarse como suma de una parte entera j y otra fraccionaria f, m = j + f. Siendo j un dgito
decimal distinto de cero (1-9), y f una cantidad menor que la unidad denominada fraccin
decimal. De forma el nmero puede ser expresado mediante:
V = 0 (j + f) 10
c
7.1.1a
En el caso del ejemplo esta representacin sera: + (1+ 0.231) 10
1
.
Nota: cuando el nmero est representado en binario la mantisa tambin puede ser representada
en la forma m = j + f, siendo ahora j un dgito binario distinto de cero (que solo puede ser 1), el
denominado bit-j. Desde luego f sigue siendo una cantidad menor que la unidad, aunque en este
caso representada en binario (una fraccin binaria). Si asumimos que la representacin est
siempre precedida de un 1, este bit puede suponerse implcito, y ocupar su posicin para expresar
un bit adicional de la fraccin. Esta representacin se denomina designificando normalizado y
supone que solo se almacena la fraccin decimal f de la mantisa (como puede ver, se trata de
aprovechar al mximo el espacio disponible).
La expresin binaria equivalente a la anterior (7.1.1a) es:
V = 0 (1+ f) 2
c
7.1.1b
3.2 Representacin binaria
La informtica, que en sus comienzos estaba nutrida por profesionales de otras disciplinas tcnicas
y cientficas, adopt una variacin de la notacin cientfica para representacin interna (binaria) de
las cantidades fraccionarias. Por esta razn, es costumbre que los nmeros fraccionarios sean
denominados de coma o punto flotante [1] ("floating-point") y a las operaciones aritmticas
realizadas con ellos, operaciones de punto flotante FLOP ("FLoating -point- OPeration").
Para los nmeros de "punto flotante", se ha asignando un bit para el signo; un cierto nmero de
bits para representar el exponente y el resto para representar la parte ms significativa del nmero
(la mantisa), aunque en este caso, la caracterstica no se refiere a una potencia de diez sino de
dos. Es decir: un valor V puede ser representado por su mantisa m y su
caracterstica c mediante: V = m . 2
c
.
As pues, la representacin binaria de los nmeros fraccionarios utiliza tres componentes:
Signo S es un nmero binario de un bit representando el signo (0 == positivo, 1 ==
negativo). Generalmente es el bit ms significativo (de la izquierda).
Exponente c es un nmero binario representando la potencia de 2 por la que hay que
multiplicar la mantisa. Cuanto mayor pueda ser este exponente, mayor ser el valor
absoluto del mayor nmero que puede ser representado.
Mantisa m es un nmero binario que representa las cifras significativas del nmero. Por
supuesto, cuanto mayor sea la precisin deseada (ms cifras significativas conocidas),
mayor debe ser el espacio destinado a contener esta parte.
Consideramos los bits numerados de derecha a izquierda, de 0 a N-1 (siendo N el nmero total de
bits que se utilizar en la representacin). El signo est representado por el ltimo bit (bit N-1). A
continuacin le siguen los bits destinados al significando y finalmente los del exponente. Si se
destinan e bits para contener al exponente (representados E), y m para contener
la mantisa (representados M), el esquema de almacenamiento es:
<--------------- N --------------> Espacio total de almacenamiento (bits)
S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucin
1 <-- e -> <---------- m --------> Longitud de campos
| | | | |
N-1m+e m m-1 0 Numeracin de los bits

Es interesante observar que los desplazamientos ("Shift") izquierdo o derecho ( 4.9.3) de los bits
de la mantisa, equivalen respectivamente a multiplicar o dividir por dos su valor, lo que podra
compensarse disminuyendo o aumentando el valor del exponente en una unidad. Para evitar
ambigedades se recurre a la normalizacin ya sealada , de forma que se minimiza el valor del
exponente, y cualquier valor V (distinto de cero) puede ser representado mediante la fraccin
normalizada f de su mantisa (f # 0), con lo que puede ser representado en la forma:
V = 2
c
(1 + f)

Desgraciadamente
no existe una
absoluta unidad de
criterio respecto a
los detalles. Segn
el Estndar, la
representacin
(interna) y rango de
valores de los
nmeros
fraccionarios
depende del
compilador (
2.2.4). Cada
implementacin C++ es libre para definir los detalles. Por ejemplo, que espacio dedica a almacenar
el exp, y cuanto a la mantisa; como se representa el cero, Etc [2]. Como consecuencia, existen
diferencias en algunos aspectos del comportamiento de los compiladores que pueden llegar a ser
cruciales. Por ejemplo, cuando presentan errores de overflow o undeflow.

Nota: el compilador C++Builder utiliza tres tamaos distintos para los nmeros fraccionarios de
32, 64 y 80 bits respectivamente, segn el formato de la IEEE . La representacin interna es
la indicada en la figura 2.

3.2.1 Problemas de la representacin binaria de las cantidades fraccionarias
La representacin binaria de punto flotante utilizada en los computadores digitales, es muy
eficiente y se adapta bastante bien a la mayora de las circunstancias, especialmente en clculos
tcnicos y cientficos (aritmtica de punto flotante). Sin embargo, no est exenta de problemas,
derivados del hecho de que -como hemos sealado al principio del captulo- las posibilidades
(finitas) de representacin del ordenador no pueden cubrir la totalidad (infinita) de los nmeros
reales. Esta dificultad es especialmente molesta en los clculos denominados "de gestin",
"comerciales" o "financieros" que utilizan nmeros fraccionarios de base 10. Por ejemplo clculos
de precios, de conversin de moneda o del resultado de varias pesadas. Este tipo de aplicaciones
utilizan (o deberan utilizar) lo que se denomina aritmtica decimal (que realizamos habitualmente
con un papel y un lpiz) en la que, por ejemplo, 111.567 - 111 = 0.567.
Cuando en los programas C/C++ se utilizan variables fraccionarias para almacenar este tipo de
variables (nmeros fraccionarios de base 10), se presentan problemas que, en principio, suelen
desconcertar al principiante. Como botn de muestra, incluimos el mensaje de un usuario en un
foro de Visual C++, titulado "A very serious bug in MS Visual C++" (evidentemente el usuario est
bastante desconcertado con los resultados obtenidos y como suele ser normal en estos casos,
echa la culpa al compilador).
Try the next code:

double a=111.567, b=111, c;
c=a-b;
// and you will receive
//
// a=111.56699999999999
// b=111.00000000000000
// c=0.56699999999999307
//
// instead => a=111.567, b=111, c=0.567;
I found more fractional numbers that show a similar error.
The problem is that the fractional numbers and their actions can not be
produced otherwise.
I try this example in all MS Visual C/C++ compilers from version 6.0 to
version 2008 and the bug appears everywhere.
Regards.

Mejor que puedan hacerlo mis palabras, en la pgina Decimal Arithmetic FAQ de Mike Cowlishaw
de IBM, encontrar el lector una amplia explicacin del porqu de estos aparentemente
errneos resultados. Como sntesis indicaremos aqu que, para prevenir estos problemas, algunos
lenguajes incluyen un tipo especial de variable "decimal" y funciones y operadores especficos que
permiten realizar clculos de aritmtica decimal. En lo que respecta a C++, debido a sus
orgenes "cientficos", por el momento no dispone de forma nativa de ningn tipo decimal por lo que
las aplicaciones que necesitan de estos de clculos deben recurrir a libreras especficas.
Nota: aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ningn tipo
decimal, el comit de estandarizacin ya est trabajando en una especificacin que se ajusta
al estndar IEEE 754R (ver Decimal Types for C++). Seguramente se definirn tres tipos
decimales de punto flotante de 32, 64 y 128 bits respectivamente. Tambin es previsible que,
del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para
clculos con nmeros de punto flotante de codificacin binaria, en un futuro prximo se
implementen tambin en hardware unidades para clculos con nmeros de punto flotante de
codificacin decimal, ya que las rutinas software actuales para tratar la aritmtica decimal son
considerablemente ms lentas que las de aritmtica binaria.
3.3 El Estndar IEEE 754
En 1985, el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) public
la norma IEEE 754. Una especificacin relativa a la precisin y formato de los nmeros de "punto
flotante". Incluye una lista de las operaciones que pueden realizarse con dichos nmeros, entre las
que se encuentran las cuatro bsicas: suma, resta, multiplicacin, divisin. As como el resto, la
raz cuadrada y diversas conversiones. Tambin incluye el tratamiento de circunstancias
excepcionales, como manejo de nmeros infinitos y valores no numricos.
Nota: en Junio de 2008 se aprob una revisin de dicho Estndar, conocido como IEEE
754R, que incluye recomendaciones para la aritmtica de punto flotante de codificacin
decimal. La explicacin que sigue se refiere exclusivamente a la codificacin de nmeros de
punto flotante de codificacin binaria (versin inicial del estndar).
Dado que la mayora de compiladores utilizan este formato para la representacin de los nmeros
fraccionarios, es ms que probable que el informtico se tope con ellos en alguna ocasin, por lo
que dedicaremos unas lneas a describir sus caractersticas principales [7].
En realidad, la adopcin de este estndar por parte de los compiladores se debe a que el hardware
que los sustenta tambin lo sigue. De hecho, esta es la representacin interna utilizada por los
procesadores, ya que en la actualidad (2002) prcticamente el 100% de las mquinas que se
fabrican siguen el Estndar en lo que se refiere al tratamiento y operacin de los nmeros de
"punto flotante".
El proceso de estandarizacin de las operaciones de punto flotante comenz paralelamente al
desarrollo por Intel (1976) de lo que seran los coprocesadores aritmticos 8087. A partir de
entonces poda asegurarse que X + (Y + Z) proporcionara el mismo resultado que (X + Y) + Z con
cualquier compilador y cualquier terna de nmeros. No olvidemos que es precisamente a partir de
la aparicin de los coprocesadores matemticos, cuando la realizacin de operaciones con
nmeros fraccionarios se encomiendan al silicio (hardware), en vez de a rutinas software que,
hasta entonces, eran especficas de cada compilador y cada plataforma [9].
Los coprocesadores matemticos, denominados tambin FPUs (Floating-Pount Units) comenzaron
siendo circuitos integrados (opcionales) que se insertaban en la placa base junto al procesador
principal [4]. Por ejemplo los 8087, 80287 y 80387 de Intel (este ltimo fue el primero que
proporcion soporte completo para la versin final del Estndar). A partir del 80486, Intel incorpor
el coprocesador matemtico junto con el principal, con lo que su existencia dej de ser opcional y
se convirti en estndar. Estas "unidades de punto flotante" no solo realizan las operaciones
aritmticas bsicas (suma resta, multiplicacin y divisin). Tambin incluyen operaciones como la
raz cuadrada, redondeo, resto, y funciones trascendentes como seno, coseno, tangente,
cotangente, logaritmacin y exponenciacin.
3.3.1 Formatos
En lo referente a la representacin binaria de los nmeros, el Estndar utiliza tres formatos
denominados de precisin simple (equivalente al floatC++); doble (equivalente al double)
y extendida (que podra corresponder al long double), aunque existe un cuarto, denominado
de cudruple precisin, no contemplado en la norma que es tambin un estndar de facto. Los
tamaos son los siguientes:
Precisin
Bytes bits
Simple
4 32
Doble
8 64
Extendida
>= 10 >= 80
Cudruple
16 128

En todos los casos se utilizan tres campos para describir el nmero: El signo S, el exponente k y
el significando (mantisa) n, que se almacenan en ese orden en memoria (no en los registros del
procesador).
El signo S, se almacena como es usual en un bit (0 significa positivo, 1 negativo).
El exponente k se almacena en forma de un nmero binario con signo segn una regla
que, como veremos a continuacin, depende del rango y del formato.
El significando n se almacena en forma normalizada, salvo cuando se representan
significados especiales (ver a continuacin).

El esquema de la distribucin utilizada para los de simple y doble precisin es el indicado.

Espacio (bits): 1 <-- 8 -> <-------- 23 --------->
Simple precisin: S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
posicin: 31 30 23 22 0

Espacio (bits): 1 <--- 11 --> <-------------------- 52 ---------------------
----->
Doble precisin: S EEEEEEEEEEE
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
posicin: 63 62 52
51 0

Como veremos a continuacin, la interpretacin de los patrones de bits contenidos en
el exponente y en el significando sigue reglas algo complicadas. El motivo es que del espacio
total de posibilidades se han reservado algunas para significados especiales y circunstancias
excepcionales, que es necesario considerar para prevenir los errores e imprecisiones aludidas al
principio del captulo . Por ejemplo, se considera la existencia de valores especiales: +Infinito; -
Infinito; NaN ("Not a Number") y una representacin especial para el valor cero, lo que ha obligado
a definir reglas especiales de aritmtica cuando estos valores intervienen en operaciones con
valores normales o entre ellos . A lo anterior se aade que existen dos tipos de representacin
para los valores no especiales, cada uno con sus reglas; son las denominadas formas
normalizadas y subnormales.
Empezaremos por la representacin de los significados especiales.
3.3.2 Significados especiales
Definicin del cero: puesto que el significando se supone almacenado en forma
normalizada , no es posible representar el cero (se supone siempre precedido de
un 1). Por esta razn se convino que el cero se representara con valores 0 en
el exponente y en elsignificando. Ejemplo:
0 00000000 00000000000000000000000 = +0
1 00000000 00000000000000000000000 = -0
Observe que en estas condiciones el bit de signo S an permite distinguir +0 de -0. De
hecho, el compilador lo hace as, permitiendo distinguir divisiones por cero con resultado
+4 y -4 . Sin embargo el Estndar establece que al comparar ambos "ceros" el
resultado debe indicar que son iguales.
Infinitos: se ha convenido que cuando todos los bits del exponente estn a 1 y todos los
del significando a 0, el valor es +/- infinito (segn el valor S). Esta distincin ha permitido
al Estndar definir procedimientos para continuar las operaciones despus que se ha
alcanzado uno de estos valores (despus de un overflow). Ejemplo:
0 11111111 00000000000000000000000 = +Infinito
1 11111111 00000000000000000000000 = -Infinito
Valores no-normalizados (denominados tambin "subnormales"). En estos casos no se
asume que haya que aadir un 1 al significado para obtener su valor. Se identifican porque
todos los bits del exponente son 0 pero el significado presenta un valor distinto de cero (en
caso contrario se tratara de un cero). Ejemplo:
1 00000000 00100010001001010101010
Valores no-numricos: Denominados NaN ("Not-a-number"). Se identifican por
un exponente con todos sus valores a 1, y unsignificando distinto de cero. Existen dos
tipos QNaN ("Quiet NaN") y SNaN ("Signalling NaN"), que se distinguen dependiendo del
valor 0/1 del bit ms significativo del significando. QNaN tiene el primer bit a 1, y significa
"Indeterminado", SNaN tiene el primer bit a 0 y significa "Operacin no-vlida". Ejemplo:
0 11111111 10000100000000000000000 = QNaN
1 11111111 00100010001001010101010 = SNaN


3.3.3 Significados normales
La representacin de nmeros no incluidos en los casos especiales (distintos de cero que no sean
infinitos ni valores no-numricos), sigue reglas distintas segn la precisin y el tipo de
representacin (normal o subnormal).
Para calcular el valor V de un nmero binario IEEE 754 de exponente E y mantisa M, debe
recordarse que esta ltima representa una fraccin binaria (no decimal ;-) en notacin
normalizada . Es decir, hay que sumarle una unidad. En estas condiciones, si por ejemplo, el
contenido de la mantisa es 0.254 se supone que M = 1 + 0.254. Por su parte el clculo de la
fraccin binaria es anlogo al de la fraccin decimal. Recordemos que la fraccin decimal 1304
(0.1304) equivale a 1/10
1
+ 3/10
2
+ 0/10
3
+ 4/10
4
. Del mismo modo, la fraccin binaria 1101
(0.1101) equivale a 1/2
1
+ 1/2
2
+ 0/2
3
+ 1/2
4
= 0.8125.
Teniendo en cuenta estas observaciones, el valor decimal V de una representacin binaria
estndar, puede calcularse mediante las siguientes frmulas:
Nota: en las frmulas que siguen puede suponerse sustituido el signo por la expresin (-
1)
S
. Donde S es el valor del bit de signo; cero si es positivo y 1 si es negativo.

Si es un problema real, en el que es preciso calcular el valor correspondiente a un binario que
sigue el estndar (por ejemplo, los datos recibidos de un instrumento de medida), adems de
las consideraciones anteriores, tambin hay que tener en cuenta el orden ("Endianness") en
que pueden recibirse los datos ( 2.2.6a).
3.3.3a Simple precisin, representacin normalizada:
V == (1 + M) 2
E-127

Es evidente que en estos casos E es un nmero tal que 0 < E < 255 (2
8
- 2 posibilidades), ya que
en caso contrario se estara en alguno de los significados especiales (todos los bits del exponente
a 0 o a 1). As pues, E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un
rango entre 2
-126
y 2
127
.
Ejemplos:
0 00001100 11010000000000000000000
Signo = +; E = 12; M = 1/2
1
+ 1/2
2
+ 0/2
3
+ 1/2
4
+ 0 + 0 + ... = 0.8125
V = + (1 + 0.8125) 2
12-127
= 1.8125 2
-115
= 4.3634350 10
-35

1 10001101 01101000000000000000000
Signo = -; E = 141; M = 0/2
1
+ 1/2
2
+ 1/2
3
+ 0/2
4
+ 1/2
5
+ 0 + ... = 0.40625
V = - (1 + 0.40625) 2
141
= - 1.40625 2
14
= - 23040
3.3.3b Simple precisin, representacin subnormal:
V == (0 + M) 2
-127

Como se ha sealado , en estos casos es E = 0, y M es distinto de cero. La operatoria es
anloga al caso anterior.
Ejemplo:
0 00000000 11010000000000000000000
Signo = +; E = 0; M = 1/2
1
+ 1/2
2
+ 0/2
3
+ 1/2
4
+ 0 + 0 + ... = 0.8125
V = + 0.8125 2
-127
= 4.77544580 10
-39

3.3.3c Doble precisin, representacin normalizada:
V == (1 + M) 2
E-1023

En estos casos es 0 < E < 2047. En caso contrario se estara en alguno de los significados
especiales (todos los bits del exponente a 0 o a 1). La operatoria es anloga a la de simple
precisin, con la diferencia de que en este caso se dispone de ms espacio para representar la
mantisa M y el exponente E (52 y 11 bits respectivamente).
3.3.3d Doble precisin, representacin subnormal:
V == (0 + M) 2
-1023

En estos casos es E = 0, y M es distinto de cero. La operatoria es anloga a la sealada en casos
anteriores.
3.3.4 Conversor automtico de formatos
Con objeto de facilitar al lector la realizacin de algunos ejemplos que le permitan terminar de
comprender y comprobar estas reglas, en la pgina adjunta se incluye un convertidor automtico
de formatos que permite introducir un nmero en formato decimal (incluso en notacin cientfica), y
comprobar el aspecto de su almacenamiento binario segn el Estndar IEEE 754
en simple y doble precisin ( Conversor).
Nota: en la librera de ejemplos ( 9.4.1) se incluye un programa C++ que realiza la misma
conversin que el anterior (realizado en javascript), aunque est limitado a la representacin
de nmeros en precisin simple.
3.3.5 Operaciones con nmeros especiales
La tabla adjunta establece las reglas que, segn el Estndar IEEE 754, rigen las operaciones en
que intervienen magnitudes de significado especial.
Operacin: Resultado
cualquiera con NaN NaN
n / Infinito 0
Infinito * Infinito Infinito
Infinito + Infinito Infinito
Infinito - Infinito NaN
Infinito * 0 NaN
Infinito / Infinito NaN
0 / 0 NaN
n / 0 Infinito
3.3.6 Rango de la representacin IEEE 754
Exceptuando los valores especiales infinitos, est claro que para la simple precisin, los valores
mnimos y mximos que pueden representarse de forma estandarizada son:
Vmin = - (0 + M) 2
-127
, donde M sea el valor mnimo de la mantisa distinto de cero. Su
representacin es
1 00000000 00000000000000000000001
Traduccin:
Signo = -
E = 0
M = 1/2
23
= 2
-23
= 1.19209289551 10
-7

Vmin = 2
-23
2
-127
= 2
-150
= 7.00649232163 10
-46

En la prctica solo se consideran las representaciones normales, de forma que la forma normal
ms pequea corresponde a la siguiente representacin binaria:
1 00000001 00000000000000000000001
Traduccin:
Signo = -
E = 1
M = 1/2
23
= 2
-23

V
min
= -(1 + 2
-23
) 2
1-127
= -(1 + 2
-23
) 2
-126
= -1.17549449 10
-38

Es significativo que el prximo valor en escala ascendente es
1 00000001 00000000000000000000010
Signo = -;
E = 1
M = 1/2
22
= 2
-22

V = -(1 + 2
-22
) 2
-126

La diferencia entre ambos es: I
min
= V - V
min
= 2
-22
- 2
-23
= 1.192092 10
-7
, lo que representa algo
ms de una parte en 10 millones. Es importante recordar que esta ser la mejor precisin que
podr alcanzarse en los procesos con nmeros de coma flotante de simple precisin. En la prctica
la precisin alcanzada ser an menor, dependiendo de la suerte que tengamos y del nmero de
operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden
a anularse entre s- o acumulativos).

El valor mximo en la representacin normal, corresponde a la forma binaria
0 11111110 11111111111111111111111
Signo = +
E = 254
M = 1/2
1
+ 1/2
2
+ ... + 1/2
23
= 0.9999999999
V
max
= (1 + 0.999999) 2
254-127
= (1.99999999) 2
127
= 3.40282346 10
38


2.2.4b Formas de representacin simblica
1 Sinopsis
En el epgrafe dedicado al Ordenador Electrnico Digital ( 0.1), se seal que para la
representacin de los datos textuales (alfanumricos) se utilizan los sistemas de codificacin Us-
ASCII y Unicode. El lenguaje y el sistema de escritura varan, pero desde el punto de vista del
programador C++, el texto de sus programas fuente es siempre texto plano (sin formatear)
codificado en Us-ASCII ( 2.2.1a), que es el sistema exigido como entrada por los compiladores (
1.4).
Sin embargo, la representacin simblica de datos numricos (como aparecen representados
estos nmeros en el texto del cdigo fuente), no siempre ocurre en formato decimal, el sistema de
numeracin "Occidental", como cabra esperar. Por una larga tradicin informtica, de cuando las
consolas de entrada de los ordenadores eran exclusivamente numricas, adems del
sistema decimal, se conservan otras dos formas de codificacin numrica: hexadecimal y octal.
Cualquier cantidad numrica entera, puede ser representada en el texto del programa C++ en
cualquiera de los sistemas citados. Adems, las funciones de salida de la propia Librera Estndar,
tambin permite que tales cantidades puedan ser expresadas en cualquiera de estos formatos. Sin
embargo, salvo caso de programas antiguos o que se trate de direcciones de memoria, es raro
encontrar otras formas de expresin distintas de la decimal que es mucho ms legible.
Por su parte, las cantidades numricas fraccionarias (de punto flotante) se representan siempre en
formato decimal.
Nota: en la exposicin que sigue, nos referimos exclusivamente a la representacin de
cantidades numricas en el Fuente (desde el punto de vista del programador). Cuestin esta
que no tiene nada que ver con el formato de entrada/salida para las cantidades numricas en
tiempo de ejecucin (como las ve el usuario del programa).
2 Formato decimal
Poco hay que decir respecto a este formato, de base 10, utiliza las cifras 0 a 9. Las cantidades
fraccionarias utilizan el punto en vez de la coma. Salvo el propio cero (0), las cantidades
expresadas no pueden empezar por cero, porque seran confundidas con el formato octal
(afortunadamente el cero octal y el decimal coinciden).
Ejemplos:
int x = 12, y = 0;
float y = 3.14, z = .16;
En ocasiones, cuando hay posibilidad de confusin, los textos informticos aaden una "d" al final
de las cantidades enteras decimales. Por ejemplo, 125d; 0125 y 125h son cantidades distintas (ver
a continuacin).
Cuando se trata de representar cantidades decimales muy grandes o muy pequeas, es posible
tambin utilizar la notacin decimal cientficacomentada en el captulo anterior ( 2.2.4a). Por
ejemplo:
float f = 2.54E20;
double d = -1.55E-200;
long double ld = 2.33E-480;
3 Formato hexadecimal
Este sistema de codificacin numrica utiliza un sistema de numeracin de base 16 ( E0.1w2).
Como el sistema arbigo solo posee diez cifras (del 0 al 9), las restantes se complementan con
letras del alfabeto, de la 'A' a la 'F'. C++ permite la utilizacin indistinta de maysculas y minsculas
para representar cantidades en este formato, aunque es ms frecuente la utilizacin de
maysculas. Es la forma tradicional de representar direcciones de memoria.
La representacin de estos nmeros debe ir precedido de 0x 0X, para indicar al compilador que
lo que sigue es formato hexadecimal. Tambin es costumbre representar estas cantidades en
grupos de 8 dgitos (aadiendo ceros a la izquierda).
Ejemplo:
int x = 0xFF, y = 0x000000FF;

En ocasiones los textos informticos aaden una "h" al final de las cantidades hexadecimales. Por
ejemplo, 125h sera equivalente a 0x125, aunque la primera notacin no puede ser utilizada en los
fuentes de los programas C++.
4 Formato octal
Utiliza un sistema de numeracin de base 8, por lo que utiliza las cifras del sistema arbigo 0 a
7. Cualquier representacin octal que utilice los dgitos 8 o 9 es un error. La representacin octal
de estos nmeros debe ir precedido por el 0 (cero), para indicar al compilador que lo que sigue es
octal.
Ejemplo:
int x = 0377, y = 0377634; // ojo: cantidades en octal
5 Ejemplo resumen
#include <iostream.h>

int main() {
int x = 255, y = 0377, z = 0x000000FF;
cout << "Direccion de x: " << &x << endl; // L.4:
cout << "Direccion de x: " << long(&x) << endl; // L.5:
cout << "Valor de x: " << x << endl;
cout << "Valor de y: " << y << endl;
cout << "Valor de z: " << z << endl;
}
Salida:
Direccion de x: 0065FE00
Direccion de x: 6684160
Valor de x: 255
Valor de y: 255
Valor de z: 255
Como puede verse en L.4, la forma estndar utilizada por el compilador para presentar direcciones
de memoria, es hexadecimal y con maysculas; en L.5 se ha incluido un "casting" ( 4.9.9) para
forzar una salida en formato decimal (ms legible) de la misma direccin.
Nota: en el captulo dedicado a la representacin de Constantes Numricas ( 3.2.3b) se
incluyen detalles adicionales sobre la forma de utilizar estos formatos.

Tamao de los tipos bsicos C++
1 Sinopsis
En lo tocante al tamao de los tipos bsicos, el Estndar C++ es bastante liberal y establece muy
pocas directivas al respecto. Cosa que no ocurre en otros lenguajes. Por ejemplo, Java. Es
precisamente esta falta de concrecin, uno de los puntos ms oscuros en cuanto a la portabilidad
del lenguaje.
Una de las razones de esta "permisividad" es que en el diseo del C y C++ se prim sobre todo la
velocidad de ejecucin. Esta libertad para elegir dentro de ciertos lmites el tamao de los tipos,
facilita que los constructores de compiladores puedan adecuar los tipos a las caractersticas de
cada hardware. Por ejemplo, el tamao de un char se supone que es el ms adecuado para
manipular caracteres en una mquina determinada, mientras que el de un int debera ser el ms
adecuado para almacenar y manipular enteros en la misma mquina.
Los tamaos se definen siempre como mltiplos del tamao de un char, as que el tamao de este
es siempre 1, sizeof (char) == 1, y no existen tamaos del tipo 3.5 char por ejemplo. As pues, en
lo que se refiere al tamao de los tipos en C++, la unidad de medida es el tamao de char. En las
expresiones que siguen 1 significa justamente esto.
Respecto al tamao de los tipos bsicos C++, en realidad, las nicas asunciones ciertas que se
pueden hacer son las siguientes:
1 == char <= short <= int <= long
1 <= bool <= long
char <= wchar_t <= long
float <= double <= long double
X == signed X == unsigned X
donde X puede ser: char, short int, int o long int.
Ademsse garantiza que:
8 bits <= char
16 bits <= int
32 bits <= long
Los aspectos especficos de los tipos bsicos en cada implementacin estn contenidos en la
plantilla numeric_limits que puede encontrarse en el fichero <limits>.
Los ficheros de cabecera <climits> y <float.h> contienen definiciones de los rangos de valor de
todos los tipos fundamentales.


2.2.5 Conversiones estndar
1 Presentacin
El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++.
Una divisin de tipos no excesivamente rgida, o simplemente permisiva como la del C++ tiene sus
ventajas, aunque tambin sus inconvenientes. Hemos sealado ( 1.2) que despus de la
premisa fundamental de diseo: Potencia y velocidad de proceso, otra de las caractersticas de su
antecesor C, es la de ser permisivo; "Intentando hacer algo razonable con lo que se haya escrito",
lo que incluye naturalmente el asunto de los tipos. Aunque C++ dispone de mecanismos de
comprobacin ms robustos en este sentido, de alguna forma "hereda" la tradicin de su
antecesor. El resultado es un nuevo frente para el programador que debe prestar atencin al
asunto. En especial porque muchas de estas conversiones de tipo son realizadas por el compilador
sin que el programador tenga constancia explcita de ello. En ocasiones este "automatismo" es
realmente una comodidad; en otras es origen de problemas y quebraderos de cabeza.
2 Conversiones estndar
Se denominan conversiones estndar a determinadas conversiones de tipo que en ocasiones
realiza espontneamente el compilador para ajustar el tipo utilizado por el programador con las
necesidades del momento. Estas conversiones se refieren casi siempre a tipos bsicos
preconstruidos en el lenguaje ( 2.2), y pueden clasificarse en alguno de los supuestos que se
relacionan a continuacin (existen unas pocas conversiones que afectan a los tipos abstractos y
son tratadas en el siguiente captulo 2.2.5a). Algunas de ellas, denominadas conversiones
triviales, se realizan entre tipos que son muy parecidos, hasta el extremo que para ciertas
cuestiones no se consideran tipos distintos. Por ejemplo, para la sobrecarga de funciones (
4.4.1a).
Conversin nula: no existe conversin.
Conversiones triviales
o Conversin de tipo a referencia ( T T&)
o Conversin de referencia a tipo ( T& T)
o Conversin de matriz a puntero ( T[ ] T*) .
o Conversin de funcin a puntero-a-funcin ( T(arg) T(*)(arg) )
o Conversin de calificacin de tipo ( 2.2)
Tipo a constante ( T const T )
Tipo a volatile ( T volatile T )
Puntero-a-tipo a puntero-a-tipo constante ( T* cons T* )
puntero-a-tipo a puntero-a-tipo volatile ( T* volatile T* )
Conversin de Lvalue a Rvalue.
Conversiones y promociones entre tipos numricos
Conversiones a puntero
Conversiones a booleano .

Ejemplo: cuando se utiliza una expresin aritmtica, como a + b, donde a y b son tipos numricos
distintos, el compilador realiza espontneamente ciertas conversiones de tipo antes de evaluar la
expresin. Estas conversiones incluyen la promocin de los operandos de tipo ms bajo a tipos
ms altos, a fin de mejorar la homogeneidad y la precisin del resultado ( 2.2.4 Precisin y
rango).
En ocasiones la conversin de un tipo a otro exige la realizacin de una secuencia de varias de las
conversiones estndar anteriores. Ejemplo: en la definicin
char* cptr = "ABC";
para el compilador la expresin de la derecha es de tipo matriz-de-const char ( 3.2.3f), que es
convertida a puntero-a-const char. Posteriormente, una segunda conversin (de calificacin)
transforma el puntero-a-cons char en puntero-a-char.
Las conversiones estndar se realizan siempre porque las circunstancias exigen un tipo (de
destino o final), y los tipos disponibles son distintos. Esto puede ocurrir en diversos contextos:
Cuando se realizan sobre los operandos de operadores son las exigencias del operador las
que dictan el tipo de destino.
Cuando se realizan en la expresin de condicin de una sentencia if ( 4.10.2) o de
iteracin do...while ( 4.10.3) el tipo de destino es un booleano ( 3.2.1b).
Cuando se realizan en sentencias switch de seleccin ( 4.10.2) el tipo de destino es un
entero.
Cuando se utiliza en el Rvalue de una asignacin, el tipo de destino es el del Lvalue.
Cuando se utiliza en los argumentos de una funcin o en el valor devuelto por esta, el tipo
de destino es el establecido en la declaracin de la funcin.
A su vez existen contextos en los que las conversiones automticas se impiden expresamente. Por
ejemplo, la conversin de Lvalue a Rvalue no se realiza en el operando del operador & ( 4.9.11)
de referencia.

Para que una expresin exp pueda ser convertida implcitamente a un tipo T, es condicin
necesaria que pueda existir un objeto temporal t tal que la asignacin T t = exp sea correcta.
3 Conversiones entre tipos numricos
Dentro de este epgrafe consideramos en realidad varios tipos de conversiones:
Promociones a entero
Promociones a fraccionario .
Conversiones entre asimilables a entero .
Conversiones entre tipos fraccionarios .
Conversiones fraccionario entero .
3.1 Promociones a entero.
Comprende las siguientes conversiones:
Un Rvalue de los tipos char, signed char, unsigned char, short int, o unsigned short
int puede ser convertido a un Rvalue de tipo int si en la implementacin un int puede
contener todos los valores de los tipos a convertir. En caso contrario son convertidos
a unsigned int.
Un Rvalue del tipo wchar_t ( 2.2.1a1) o un enumerador ( 3.2.3g) pueden ser
convertidos a un Rvalue del primero de los tipos: int;unsigned int; long, o unsigned
long, que pueda representar el valor correspondiente.
Un Rvalue de tipo campo de bits ( 4.6) puede ser convertido al primero de los
tipos int o unsigned int capaz de representar el rango de valores posibles del campo de
bits. En caso contrario no se realiza ninguna promocin.
Un Rvalue de tipo lgico (bool) puede ser promovido a un Rvalue tipo int. La regla es
que false se transforma en cero, y true en 1 ( 3.2.1b).
3.2 Promocin a tipo fraccionario
Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double. Este tipo de
promocin se denomina tambin de punto flotante.
3.3 Conversiones entre asimilables a entero
Cualquiera de los asimilables a entero ( 2.2.1) pueden ser convertido a otro tipo asimilable a
entero. Las conversiones permitidas bajo el epgrafe anterior (promociones a entero) estan
excluidas de las que se consideran aqu.
Un Rvalue de tipo enumeracin puede ser convertido a un Rvalue de tipo entero.
La conversin de un entero largo a entero corto trunca los bits de orden superior,
manteniendo sin cambios el resto.
La conversin de un entero corto a largo, pone a cero los bits extra del entero largo y/o los
correspondientes al signo, dependiendo que el entero corto fuese con o sin signo.
La asignacin de un carcter con signo (signed char) a un entero, origina la adopcin del
signo. Los caracteres con signo siempre utilizan signo.
Los caracteres sin signo (unsigned char) siempre ponen a cero el bit ms significativo
cuando son asignados a enteros.
Si el tipo de destino es signed, el valor origen permanece sin cambio si puede ser
representado en el tipo destino (manteniendo el ancho del campo de bits). En caso
contrario, el valor depende de la implementacin [3].
Si el tipo de destino es bool la conversin se efecta segn se indica ms adelante . Si
por el contrario el tipo origen es bool, las reglas son las indicadas en la promocin a
entero: false se transforma en cero, y true en 1.
3.4 Conversiones fraccionario <=> entero
Los tipos fraccionarios (de punto flotante) pueden ser promovidos a cualquier tipo asimilable a
entero. Para ello se elimina la parte fraccionaria (decimal). Si la parte entera no cabe en el tipo de
destino, el resultado es indefinido. Si el tipo de destino es un bool se siguen las pautas indicadas
.
A su vez los tipos enteros y las constantes de enumeracin pueden ser promovidos a fraccionarios.
Si la conversin es posible (lo que ocurre efectivamente en la mayora de las implementaciones) el
resultado es exacto. En algunos casos el valor del entero no puede ser representado exactamente
por el fraccionario, lo que acarrea una prdida de precisin. En tal caso, el valor fraccionario
adoptado es uno de los dos valores ms prximos posibles (por arriba y por abajo) del valor entero.
Si el tipo origen es un booleano, false se transforma en cero, y true en 1.
3.5 Conversiones aritmticas estndar, reglas de conversin
A continuacin se exponen los pasos que sigue C++ durante la conversin de operandos en las
expresiones aritmticas. El resultado de la expresin es del mismo tipo que uno de los
operandos:
1.- Cualquier tipo entero es convertido segn se muestra en la tabla.
Tipo convierte a Mtodo de conversin seguido
char int Con o sin signo (dependiente del tipo char por defecto)
unsigned char int Siempre rellena con cero el byte ms significativo
signed char int Siempre un signed int
short int Mismo valor; signed int
unsigned short unsigned int Mismo valor; rellena con ceros el byte ms significativo
enum int El mismo valor
2.- Despus de esto, cualquier par de valores asociados con un operador son:
Un int (incluyendo sus variedades long y unsigned),
Un fraccionario de cualquiera de sus tres variedades: double, float o long double.
3.- A partir de este momento, la homogenizacin de tipos se realiza ahora siguiendo los patrones
que se indican (en el orden sealado)
Algn operando es long double el otro es convertido en long double.
Algn operando es double el otro es convertido en double.
Algn operando es float el otro es convertido en float.
Algn operando es unsigned long el otro es convertido en unsigned long.
Algn operando es long el otro es convertido en long.
Algn operando es unsigned el otro es convertido en unsigned.
Ambos aperandos son de tipo int.
Observaciones:
Generalmente las funciones matemticas (como las incluidas en <math.h>) esperan argumentos
en doble precisin (double 2.2.1), pero hay que tener en cuenta que las variables float no son
convertidas automticamente a double, y por supuesto, los double tampoco son convertidos
automticamente a float (supondra una prdida de precisin). Ver un ejemplo comentado en (
2.2.4a).
Sobre la forma de convertir double a float, o cualquier tipo a otro, ver el operador de modelado de
tipos ( 4.9.9).
3.6 Precauciones
Las conversiones aritmticas son unos de los puntos en que el programador C++ debe prestar
especial atencin si no quiere dispararse accidentalmente en los pies ( 1), y donde el lenguaje
puede gastarnos insidiosas jugarretas. Como ejemplo mostramos una funcin prevista para
calcular la inversa de cualquier entero que se pase como argumento:
void inverso (int x) {
float f = 1/x;
cout << "X = " << x << " 1/x = " << f << endl;
}

La funcin se obstina en devolver siempre cero como resultado de la inversa de cualquier entero.
El compilador Borland C++ no muestra la menor advertencia de que estemos haciendo nada mal y
aparentemente el valor 1/x debe ser promovido a float, con lo que tenemos garantizado que el
resultado puede ser fraccionario. Si una cuestin como esta se presenta cualquier da que estemos
especialmente cansados, puede mandarnos directamente a limpiar cochineras a Carolina del
Norte. Con un poco de suerte y descanso, quizs caigamos en la cuenta que la promocin se
produce "despus" que se haya efectuado la divisin, y que esta considera todava como "enteros"
a los miembros implicados (la constante 1 y el argumento x), con lo que el cociente, que es
siempre menor que la unidad [1], es redondeado a cero, y este valor (int) es el que es promovido
afloat.
Una solucin inmediata y obvia (?) permite resolver la situacin (ver: Modelado de tipos 4.9.9):
void inverso (int x) {
float f = float(1)/float(x);
cout << "X = " << x << " 1/x = " << f << endl;
}
Una solucin un poco ms elegante:
void inverso (int x) {
float f = float(1)/x;
cout << "X = " << x << " 1/x = " << f << endl;
}
En este caso, el compilador realiza automticamente la promocin de x a float antes de efectuar la
divisin (ver reglas anteriores ).
Una solucin an ms elegante que tambin produce resultados correctos:
void inverso (int x) {
float f = 1.0/x;
cout << "X = " << x << " 1/x = " << f << endl;
4 Conversiones a puntero
Un Rvalue que sea una expresin constante ( 3.2.3a) que se resuelva a 0, puede ser convertida
a puntero de cualquier tipo T. Se transforma entonces en una constante-puntero nulo ("Null pointer
constant"), y su valor es el valor del puntero nulo del tipo T*.
Para entender estos conceptos considere que en C++, dos punteros son distintos si apuntan a
tipos distintos. Por ejemplo, un puntero-a-int (int*) es distinto de un puntero-a-char (char*), y
sus valores son de tipo distinto. Resulta as que el valor (0) del puntero-a-int nulo es de tipo
distinto del valor (0) del puntero-a-char nulo. Si representamos ambos valores
por 0
i
y 0
c
respectivamente, diramos que:
0
i
es el valor del puntero nulo de int* (puntero-a-int)
0
c
es el valor del puntero nulo de char* (puntero-a-char)
Ejemplo:
int const nulo = 0; // L1:
int* pint = nulo; // L2:
En L1 nulo es un objeto tipo int calificado const ( 2.2), cuyo Rvelue es 0. En L2 este objeto
sufre una conversin estndar y se convierte al tipo int*; en este momento su valor no es ya un 0
"pelado" ("plain 0"); es el valor del puntero nulo del tipo int*. A continuacin su Rvalue es copiado
a la direccin del objeto pint, que toma as su valor.
Observe que si a la expresin L1 anterior se le suprime el calificador const:
int nulo = 0; // L1a:
int* pint= nulo; // L2: Error!!
se obtiene un error de compilacin en L2. La causa es que la conversin estndar no puede
realizarse porque, aunque nulo sigue siendo un int de valor 0, le falta el calificador const.
Considere ahora otra variacin del ejemplo anterior:
int const nulo = 0; // L1:
const int* pi1 = nulo; // L2:
int const* pi2 = nulo; // L3:
int* const pi3 = nulo; // L4:
Los nuevos objetos son tambin punteros, aunque ahora pi1 y pi2 son punteros-a-int constante
(L2 y L3 son equivalentes); el objeto al que sealan no puede cambiar su valor. Su tipo es const
int*.
Por su parte, pi3 es tambin puntero-a-int, aunque con el calificador const. Su tipo int* no se
distingue del de pint en el caso anterior. En este caso el objeto nulo sufre una conversin
estndar a tipo int* calificado. La norma nos avisa que esta conversin del objeto const al
tipo int*calificado es una sola conversin, y no una conversin a int* seguida de una
calificacin.
5 Conversiones de constantes de enumeracin
Para las conversiones de las constantes de enumeracin ver Enumeraciones ( 4.8).
6 Conversiones de matriz a puntero
El compilador puede realizar expontneamente la conversin de una matriz-de-elementos-tipoX a
puntero-a-tipoX ( 4.3.2). Este tipo de conversin es la que permite que la etiqueta de una
matriz M pueda ser tomada en determinados contextos como un puntero a su primer elemento:
M &M[0] pM
Este tipo de conversin tambin ocurren en las asignaciones del tipo:
char* cptr = "ABC";
7 Conversin a booleano
Los Rvelues de tipo numrico ( 2.2.1), las constante de enumeracin, los punteros y los
punteros a miembro, pueden ser convertidos a Rvelues de tipo bool ( 3.2.1b). La regla es que
un valor cero o un puntero nulo son convertidos a false. Cualquier otro valor es convertido a true.
8 Conversiones de funcin a puntero-a-funcin
Esta conversin permite que el nombre de una funcin F pueda ser tomada en caso necesario
como su puntero ( 4.2.4a) [2]. En realidad, para el compilador, el tipo de una funcin es puntero-
a-funcin, de forma que en lo tocante a este atributo, no distingue entre ambas entidades (
Ejemplo comentado).
Temas relacionados:
Modelado de tipos ( 4.9.9)
Bsqueda de nombres ( Name-lookup)
Congruencia estndar de argumentos ( 4.4.1a)
Conversiones definidas por el usuario ( 4..9.18k)




2.2.5a Conversiones estndar con tipos abstractos
1 Sinopsis
Adems de las conversiones estndar realizadas con los tipos bsicos ( 2.2.5), existe ocasiones
en que el compilador realiza espontneamente ciertas adaptaciones de tipo para que puedan
realizarse determinadas operaciones con objetos abstractos, cuando tales objetos pertenecen a
jerarquas de clases.
Nota: las conversiones que se relacionan exigen que la superclase o subclase sean
accesibles, y que en casos de herencia mltiple, puedan puedan realizarse sin ambigedad.
2 Conversin de referencias
En las jerarquas de clases, las referencias a subclases pueden ser promovidas a referencias a la
superclase. El resultado de la conversin es una referencia al subobjeto de la superclase contenido
en el objeto de la clase derivada (miembros heredados 4.11.2b). Ejemplo:
class Bas { ... };
class Der : public Bas { ... };

void foo(Bas&);
Der d;
Der& rd = d; // referenica-a-d (objeto de subclase)
En este contexto, aunque foo espera una referencia a la superclase, es legal la invocacin:
foo(rd);
El compilador se encarga de realizar una conversin al tipo requerido, de forma que la invocacin
es transformada en:
foo( (Bas&)rd );
3 Conversin de punteros a clase
En las jerarquas de clases, los objetos de las clases derivadas pueden utilizarse con punteros a la
superclase. En realidad, cuando se manipulan mediante punteros, los objetos de la clase derivada
pueden tratarse como si fuesen objetos de la superclase. Ejemplo:
class Bas { ... };
class Der : public Bas { ... };

Bas* bptr; // puntero-a-superclase
Der d; // instancia de sub-clase
En este contexto, aunque bptr es puntero-a-superclase, puede ser asignado con la direccin de
un objeto de la subclase. Es legal la asignacin:
bptr = &d;
El compilador se encarga de realizar una conversin al tipo requerido, de forma que la asignacin
es transformada en:
bptr = &( (Bas)d );
Este tipo de conversin Sub-clase* Super-clase* es realizada automticamente por el
compilador en determinadas circunstancias (congruencia estndar de argumentos 4.4.1a).
Nota: cuando se acceden a travs de punteros objetos de clases que pertenecen a una
jerarqua, es importante tener en cuenta las precauciones indicadas en "Consideraciones
sobre punteros en jerarquas de clases" ( 4.11.2b1).
4 Conversin de punteros a miembro
Con los punteros a miembro ocurre una conversin que, en cierta forma, es inversa de la
anterior: los punteros a miembro de una superclase pueden tratarse como si fuesen punteros a
objetos de una subclase. Ejemplo:
class Bas {
public: int bi;
};

class Der : public Bas {
public: int di;
};

int Bas::* bpi = &Bas::bi; // puntero-a-miembro de superclase
int Der::* dpi = &Der::di; // puntero-a-miembro de subclase
En este contexto, el puntero puede ser utilizado con objetos de la subclase, en cuyo caso sealar
al miembro heredado:
Der d;
Der* dp = &d;

d.*bpi = 2; // Ok. d.bi = 2
dp->*bpi = 3; // Ok. d.bi = 3

d.*dpi = 2; // OK. d.di = 2
dp->*dpi = 3; // Ok. d.di = 3

Bas b;
b.*dpi = 2; // Error!! b NO posee un miembro dpi



2.2.6 Almacenamiento
Recordemos que al describir la estructura de un programa, se dedic un
captulo a explicar las formas de almacenamiento de algoritmos y datos (
1.3.2). Aqu nos referimos exclusivamente al almacenamiento de datos. En
especial a aquellos aspectos del soporte fsico que tienen repercusiones de
inters para el programador.
1 Sinopsis
El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de
vista; trataremos aqu dos de ellos: uno fsico, y otro lgico. Desde el punto de vista fsico existen
cinco zonas de almacenamiento: los registros, el segmento de datos, el montn y la pila.
Pila ("Stack")
Montn ("Heap")
Segmento de datos ("Data segment" en el PC)
Registros ("Registers")
2 Caractersticas fsicas
Cada zona tiene unas caractersticas propias que imprimen carcter a la informacin almacenada
en ellas.
Pila: a menos que se especifique lo contrario las variables locales se almacenan aqu,
tambin los parmetros, es decir: las variables automticas ( 1.3.2).
Los elementos almacenados en esta zona son de naturaleza automtica; esto significa que
el compilador se encarga de crearlas y destruirlas automticamente cuando salen de
mbito.
Montn: es utilizado para asignacin dinmica de bloques de memoria de tamao variable
( 1.3.2). Muchas estructuras de datos, como rboles y listas, lo utilizan como sitio de
almacenamiento. Esta zona est bajo el control del programador con new, malloc y free.
Los elementos almacenados en esta zona se asocian a una existencia
persistente [3]. Esto significa que se crean y destruyen bajo directo control del
programador que debe preocuparse de su destruccin cuando ya no son necesarios para
liberar la memoria y permitir que pueda ser usada por otros objetos.
Segmento de datos: es una zona de memoria utilizada generalmente por las variables
estticas y globales.
Registros: son espacios de almacenamiento en el interior del procesador, por lo que su
nmero depende de la arquitectura del mismo. Los programas C++ no pueden garantizar
que una variable se almacene en un registro (variable de registro), aunque podemos
solicitarlo ( 4.1.8b).
Es la zona de memoria de ms rpido acceso, por lo que se utiliza para guardar
contadores de bucle y usos parecidos en los que la velocidad sea determinante, sin
embargo son un recurso escaso (hay pocos). Los objetos almacenados aqu son tambin
de naturaleza automtica; generalmente de tipos asimilables a entero ( 2.2.1).
Nota: los trminos automtico versus persistente, que en la prctica son respectivamente
sinnimos de existencia en la pila/registros o en el montn, son conceptos que se utilizan
constantemente en C++, por lo que es vital entender sus diferencias y las consecuencias que
de ello se derivan.
Tema relacionado: formas de representacin binaria de las magnitudes numricas (
2.2.4a)
Nota: en lo que sigue, el trmino identificador ( 3.2.2) se refiere al nombre arbitrario
(dentro de ciertas reglas) que se da a una entidad (clase, objeto, funcin, variable, etc) en el
cdigo de un programa. Posteriormente pueden ser transformados por la accin del
compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable.
3 Caractersticas lgicas
Desde el punto de vista lgico, existen tres aspectos bsicos a tener en cuenta en el
almacenamiento de los objetos: mbito, visibilidad (scope) yduracin (lifetime).
mbito o campo de accin de un identificador es la parte del programa en que es
conocido por el compilador ( 4.1.3).
Visibilidad de un identificador es la regin de cdigo fuente desde la que se puede
acceder al objeto asociado al identificador sin utilizar especificadores adicionales de
acceso (simplemente con el identificador 4.1.4).
Duracin: define el periodo durante el que la entidad relacionada con el identificador tiene
existencia real, es decir, un objeto fsicamente alojado en memoria ( 4.1.5).
Nota: observe que los dos primeros, mbito y visibilidad, se refieren al identificador y al fuente,
decimos que son propiedades de tiempo de compilacin. El tercero, la duracin, se refiere a
objetos reales en memoria. Decimos que es una propiedad de runtime.

Tanto las caractersticas fsicas (donde se almacena) como lgicas (mbito, visibilidad y duracin),
estn determinadas por dos atributos de los objetos: clase de almacenamiento y tipo de
dato (abreviadamente conocido como tipo 2.1). El compilador C++ deduce estos atributos a
partir del cdigo, bien de forma implcita bien mediante declaraciones explcitas.
Las declaraciones explcitas de clase de
almacenamiento son: auto; register; static; extern; typedef y mutable (
4.1.8 Especificadores de clase de almacenamiento).
Las declaraciones explcitas de tipo de dato son: char; int; float; double y void (
2.2.1 Tipos bsicos), a estos se pueden aadir matices utilizando ciertos modificadores
opcionales: signed; unsigned; long y short ( 2.2.3 Modificadores de tipo).
4 El concepto "esttico"
El concepto esttico ("Static") tiene en C++ varias connotaciones distintas; algunas de ellas son
herencia del C clsico; otras son significados aadidos en la parte POO del lenguaje.
Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen
absolutamente ninguna relacin entre si y se refieren a conceptos distintos.
Las diversas connotaciones del concepto, podramos resumirlas del siguiente modo:
Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de
compilacin, y como consecuencia directa de esto, el lugar de almacenamiento del objeto,
ya que los objetos cuyos valores son conocidos por el compilador se almacenan en sitio
distinto que los que solo son conocidos en tiempo de ejecucin ( 1.3.2).
Relativa al enlazado de funciones; cuando una llamada a funcin puede traducirse en una
direccin concreta en tiempo de compilacin ( 1.4.4), el enlazado (esttico) es diferente
del que se realiza cuando esta direccin solo es conocida en tiempo de ejecucin
(dinmico).
Relativa a la duracin o permanencia de un objeto.
Relativa a la visibilidad de un objeto, lo que est relacionado directamente con otro
concepto: el tipo de enlazado ( 1.4.4), que se refiere a las variables que puede ver el
enlazador.

Refirindonos a la primera de ellas, esttico (versus dinmico), significa que el compilador conoce
los valores en tiempo de compilacin (frente a tiempo de ejecucin -runtime-). Por tanto, puede
asignar zonas predeterminadas de memoria para estos objetos (variables y constantes). Por el
contrario, para los objetos dinmicos se asigna y desecha espacio de memoria en tiempo de
ejecucin, lo que significa que se crean y se destruyen con cada llamada de la funcin en que han
sido declaradas. Esto explica por ejemplo, que cada llamada recursiva a una funcin pueda
generar su propio conjunto de variables locales (dinmicas). Si el espacio fuese asignado de forma
fija en tiempo de compilacin, la recursin sera imposible, pues cada nueva invocacin de la
funcin machacara los valores anteriores.
Nota: Si la "profundidad" de la recursin se pudiese conocer en tiempo de compilacin, el
compilador podra asignar espacio a los sucesivos juegos de variables, pero tngase en
cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucin.
Por ejemplo, no es lo mismo calcular el factorial de 5 que el de 50 [2].

En principio las variables globales (definidas fuera de una funcin) son estticas (en este sentido)
y las locales son dinmicas (de la variedad llamada automtica) es decir: las primeras pueden
conservar su valor entre llamadas y las segundas no.
En este orden de cosas, la declaracin como static de una variable local, definida dentro de una
funcin, le confiere permanencia entre las sucesivas llamadas a dicha funcin (igual que las
globales). Desafortunadamente [1], la declaracin static de una variable global (que debera ser
redundante e innecesaria), supone una declaracin de visibilidad, en el sentido de que dicha
variable global (aparte de su estaticidad), solo ser conocida por las funciones dentro del fichero
en que se ha declarado.
Resulta as que, desgraciadamente, la palabra clave static tiene un doble sentido (y uso), el
primero est relacionado con la duracin ( 4.1.5), el segundo con la visibilidad ( 4.1.4).
Finalmente, cuando el modificador static se utiliza para miembros de clase adquiere una
peculiaridades especficas ( 4.11.7 Miembros estticos).
5 Resumen:
Con el fin de aclarar un poco este pequeo galimatas semntico, resumimos lo dicho:
Automtico versus Persistente

Propiedad de los objetos de crearse/destruirse automticamente (al entrar y salir del
bloque de cdigo) o bajo control directo del programador mediante sentencias especficas
de creacin y destruccin (new y delete). Existen respectivamente en la Pila/Montn.
Tanto los objetos automticos como los persistentes son de naturaleza dinmica.
Esttico versus Dinmico

Caracterstica de ser conocido en tiempo de compilacin o en tiempo de ejecucin, lo que
significa que el compilador puede reservar almacenamiento desde el principio o este debe
ser creado y destruido en tiempo de ejecucin.
6 Ejemplo:
Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un
sencillo programita:
#include <iostream.h>

void func(int); // prototipo
char* version = "V.0.0"; // L.4:

int main() { // =============
int x = 1;
char* mensaje = "Programa demo ";
cout << mensaje << endl;
cout << "Introduzca numero de salidas (0 para terminar): ";
while ( x != 0) {
cin >> x ;
func(x);
cout << "Otra vez? (numero): " << endl;
}
return 0; // L.15:
}
void func(int i) { // L.17: definicion
static int j = 1;
cout << "Se han solicitado: " << i << " salidas." << endl;
int* v = new int; // L.20:
*v = 1;
register int n; // L.22:
for (n = 1; n <= i; n++) {
cout << " - " << *v << "/" << i << " total efectuadas: " << j <<
" salidas." << endl;
j++; (*v)++; // L.26:
}
cout << version << endl; // L.28:
delete v; // L.29:
}
Volcado de pantalla con la salida del programa despus de marcar 3 y 2 como valores de entrada:
Programa demo
Introduzca numero de salidas (0 para terminar): 3
Se han solicitado: 3 salidas.
- 1/3 total efectuadas: 1 salidas.
- 2/3 total efectuadas: 2 salidas.
- 3/3 total efectuadas: 3 salidas.
V.0.0
Otra vez? (numero):
2
Se han solicitado: 2 salidas.
- 1/2 total efectuadas: 4 salidas.
- 2/2 total efectuadas: 5 salidas.
V.0.0
Comentario
Cuando se inicia el programa, el SO reserva un nmero determinado de bloques del total de
memoria disponible para uso del nuevo ejecutable [4]. Este espacio es exclusivo del programa y
no puede ser violado por otra aplicacin ni an intencionadamente; de esto se encarga el propio
SO. Por ejemplo, si un puntero de una aplicacin se descontrola y seala una zona de memoria
que no le pertenece, surge el conocido mensaje Windows: "La aplicacin ha efectuado
una operacin no vlida y ser detenido...". Si es Linux, el clsico error fatal con
volcado de memoria.
Si el programa lo necesita, el espacio destinado inicialmente puede crecer; el SO puede seguir
asignando nuevos bloques de memoria. Cuando se acaba la memoria fsica disponible, los
modernos SO empiezan a asignar memoria virtual ( H5.1) haciendo constante intercambio con
el disco de las partes que no pueden estar simultneamente en la memoria central (RAM). Este
proceso ("Swapping") es totalmente transparente para el programa usuario, y puede crecer hasta el
lmite del almacenamiento disponible en disco. Por supuesto, antes que se alcance este punto, el
programa se muestra especialmente perezoso, ya que estos intercambios entre el disco y la RAM
son comparativamente lentos.
La ejecucin del programa comienza por el mdulo de inicio ( 1.5), que crea e inicia las
variables estticas y globales. En este caso, la cadena de caracteres, "V.0.0", accesible mediante
el puntero version, y la variable j de la funcin func. Salvo indicacin en contrario, j se habra
inicializado a cero, pero en este caso se instruye al compilador (L.18) que se inicialice a 1, que es
el valor inicial que queremos para este contador. Observe que esta asignacin solo ocurre una vez
durante la vida del programa (en el mdulo de inicio), no con cada invocacin defunc. A partir de
este momento, esta variable conserva su valor entre cada invocacin sucesiva a la funcin, aunque
va siendo incrementado progresivamente en L.26.
Tanto el puntero version como la cadena sealada por l, permanecen constantes a lo largo de
toda la vida del programa, adems este nemnico es visible desde todos los puntos (tiene
visibilidad global), por eso puede ser utilizado desde el interior de func, en L.28. La variable j, el
punteroversion y la propia cadena "V.0.0" son creados en el segmento ( ).
Al llegar a L.15, se inicia la secuencia de finalizacin ( 1.5). En este momento se destruyan las
variables globales anteriormente descritas, as como las locales de la propia funcin main. El SO
recibe un entero como valor devuelto por el programa que termina. Generalmente el valor 0 es
sinnimo de terminacin correcta; cualquier otro valor significa terminacin anormal. En este
momento, el SO recupera el espacio de memoria asignada al programa que queda disponible para
nuevas aplicaciones, y borra del disco el posible fichero imagen de memoria virtual que hubiera
utilizado.
Observe que adems de las constantes literales ( 3.2.3f) sealadas por los
punteros version y mensaje, el programa utiliza otra serie de literales: "Introduzca
numero..."; "Otra vez..."; "Se han solicitado:"; etc. Todas ellas son constantes
conocidas en tiempo de compilacin [5]; se trata por tanto de objetos estticos, mientras que el
resto son dinmicos, ya que sus valores solo son conocidos durante la ejecucin.
Al ejecutarse la funcin main, se van creando e iniciando sucesivamente las variables (dinmicas);
en este caso el entero x que recibe un valor inicial 1, y una constante de valor cero [5] en la
sentencia return (L.15).
Cada invocacin a func provoca la creacin de un juego de variables dinmicas. En este caso el
entero i (argumento recibido por la funcin), variable local de func que recibe el mismo valor que
tiene la variable x de main; el puntero-a-int v y el entero n.
Preste atencin a que (suponiendo que el compilador atienda la peticin en L.22 4.1.8b), n se
crea en el registro ( ), mientras que i se crea en la pila ( ). Ambas son de naturaleza
automtica, por lo que son destruidas al salir de mbito la funcin, cosa que ocurre al llegar al
corchete de cierre ( } ) en L.30. Sin embargo, observe que el entero sealado por el puntero v, se
crea en el montn ( ), lo que le confiere existencia persistente; esto hace que el espacio
reservado (4 bytes en este caso 2.2.4) tenga que ser especficamente desasignado (en L.29),
pues de lo contrario cada invocacin de func supondra la prdida irrecuperable (para el
programa) de 4 bytes de memoria. Suponiendo que estuvisemos corriendo el programa en un
servidor, seramos directamente responsables de una progresiva ralentizacin del sistema
(posiblemente hasta que el "Sysmanager" descubriera una utilizacin inusual de recursos por
nuestra parte y nos desconectara).

2.2.6a Orden de almacenamiento (endianness)
1 Sinopsis
Adems de las cuestiones relativas a la zona en que se almacenan los datos, que fueron objeto del
epgrafe anterior ( 2.2.6), existe otro aspecto que tambin puede ser de inters para el
programador C++; es la cuestin del orden en que se almacenan en memoria los objetos multibyte.
Por ejemplo, como se almacenan los Bytes de un long ( 2.2.4) o de un wchar_t ( 2.2.1a1).
Nota: la cuestin no se refiere solo al orden de almacenamiento en la memoria interna. Puede
ser tambin el caso de en un volcado de memoria a disco, o como se reciben los datos en una
lnea de comunicacin.

La cuestin no es tan trivial como pudiera parecer a primera vista. Lo mismo que en el mundo real,
donde donde existen sistemas de escritura que se leen de izquierda a derecha (el que est
utilizando ahora) y otros que se leen en sentido contrario, tambin en el mundo de las
computadoras existen sistemas que leen y escriben los Bytes de cada palabra en un sentido u otro.
Naturalmente en el interior de la mquina no existe el concepto de izquierda o derecha, pero s
puede utilizarse un orden u otro para colocar los Bytes respecto al sentido ascendente de las
posiciones de memoria, o respecto al orden de salida en una lnea de transmisin.
Para concretar un ejemplo, tomemos los unsigned short, que en el compilador Linux GCC, en
Borland C++ 5.5 y en MS Visual C++ 6.0 ocupan 2 Bytes. Supongamos ahora que una
variable X de este tipo adopta el valor 255. La representacin binaria convencional para los
lectores humanos occidentales (que escribimos de izquierda a derecha) es del tipo 00000000
11111111. Al octeto de valor cero (0h) lo denominamos Byte ms significativo, o byte alto (high
byte) y al otro (FFh), Byte menos significativo, o byte bajo (low byte). Para su almacenamiento
interno caben dos posibilidades: que se coloque primero el ms significativo y a continuacin el
otro, o a la inversa (suponiendo el orden creciente de posiciones de memoria). Desgraciadamente
no ha habido acuerdo entre los fabricantes respecto al sistema a adoptar y existen dispositivos
hardware de ambos tipos.
Es tradicin informtica que la primera disposicin se denomina big-endian y la segunda little-
endian [1]. Si leemos la memoria desde las posiciones ms bajas a las ms altas, la zona que
contiene el nmero X en una mquina que siga la convencin big-endian contendr los
valores00h FFh, mientras que en una little-endian los valores encontrados sern FFh 00h. En
concreto, las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras
que las plataformas Sun's SPARC, Motorola, e IBM PowerPC utilizan la convencin big-endian. En
lo que respecta al software, Java utiliza el formato big-endian con independencia de la plataforma
utilizada (es un lenguaje con una clara vocacin hacia Internet, y los protocolos TCP/IP utilizan
esta convencin). Por contra, C y C++ utilizan la convencin dictada por el Sistema Operativo. Los
sistemas Windows utilizan la convencin little-endian, mientras que la mayora de plataformas Unix
utilizan big-endian.
Nota: es tradicin, que cuando se trata de cantidades de 32 bits. Por ejemplo, un long, la
mitad ms significativa se denomine palabra alta (high word), y la menos significativa palabra
baja (low word). Lo que supone evidentemente que nos referimos a palabras de 16 bits.
2 Tratamiento
Normalmente el programador no debe preocuparse por estas cuestiones de orden ("endianness")
mientras trabaja en una plataforma determinada, pero debe estar prevenido si maneja datos
provenientes de otras plataformas o que deben ser compartidos con ellas [2].
Un ejemplo paradigmtico es el de las comunicaciones TCP/IP. Este conjunto de protocolos utiliza
la convencin big-endian en todas sus estructuras. De forma que, por ejemplo, las direcciones IP,
que son nmeros de multiBytes (de 4 octetos), se construyen colocando primero el Byte ms
significativo. Este es el orden en que se transmiten, viajan y son recibidos las magnitudes multibyte
en las comunicaciones de Internet (el denominado "network-byte order"). En caso de utilizar un
equipo con hardware little-endian. Por ejemplo con un procesador Intel x86, la representacin
interna (el denominado "host-byte order") seguir esta convencin y ser preciso recolocar los
Bytes en el orden adecuado, tanto en los flujos de entrada como en los de salida, para que los
datos puedan ser interpretados correctamente.
2.1 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (
4.9.3). Por ejemplo, si uShort es ununsigned short (de 2 Bytes) y debemos invertir el orden
de sus octetos, pueden utilizarse las siguientes expresiones:
uShort // Valor original a cambiar (por ejemplo big-endian)
unsigned short uS1 = uShort >> 8; // valor del byte ms significativo
unsigned short uS2 = uShort << 8; // valor del byte menos significativo
+ 255
unsigned short uSwap = uS2 | uS1; // valor little-endian
El resultado puede obtenerse en una sentencia:
unsigned short uSwap = (uShort << 8) | (uShort >>8);
Tambin mediante una directiva de preproceso ( 4.9.10b):
#define SWAPSHORT(US) ((US << 8) | (US >>8))
...
unsigned short uSwap = SWAPSHORT(uShort); // valor little-endian

2.2 El procedimiento puede hacerse extensivo para los valores de 4 Bytes. Por ejemplo,
supongamos un unsigned long uLong cuyo valor es 4000967017 (puede ser cualquier otro). Su
mapa de bits big-endian tiene el siguiente esquema:
11101110 01111001 11101001 01101001
Para colocarlos en posicin invertida, aislamos sus 4 Bytes con el auxilio de unos patrones que
responden a los siguientes valores:
unsigned long k = 0xFF; 00000000 00000000 00000000
11111111
unsigned long k1 = k | k << 8 | k << 16;
00000000 11111111 11111111
11111111
unsigned long k2 = k | k << 8 | k << 24;
11111111 00000000 11111111
11111111
unsigned long k3 = k | k << 16 | k << 24;
11111111 11111111 00000000
11111111
unsigned long k4 = k << 8 | k << 16 | k <<
24;
11111111 11111111 11111111
00000000
Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 4.9.3a):
unsigned long B1 = (uLong ^ k1 & uLong) >>
24;
00000000 00000000-00000000
11101110
unsigned long B2 = (uLong ^ k2 & uLong) >>
16;
00000000 00000000-00000000
01111001
unsigned long B3 = (uLong ^ k3 & uLong) >>
8;
00000000 00000000-00000000
11101001
unsigned long B4 = uLong ^ k4 & uLong;
00000000 00000000-00000000
01101001
A partir de aqu es trivial construir el valor deseado, con los Bytes en orden little-endian o en
cualquier otro, mediante desplazamientos combinados con el operador OR inclusivo.
unsigned long uLong_Swap = B4 << 24 | B3 << 16 | B2 << 8 | B1;
Observe que es posible simplificar algo las expresiones anteriores aprovechando que los
desplazamientos derecha + izquierda de B2 y B3 pueden ser combinados en uno solo.
2.3 El procedimiento puede hacerse extensivo a cualquier valor value, expresado por una
sucesin de n bytes. De forma que su representacin big-endian puede expresarse:
value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) | ... | byte[n-1];

Generalmente, estas cuestiones de "endianness" son manejadas mediante directivas de
preproceso (#derfine) existentes al efecto en los ficheros de cabecera. De esta forma, las
aplicaciones son independientes de la plataforma (para adaptar el compilador a otra plataforma
solo hay que modificar las directivas correspondientes). Para que el lector tenga una idea de la
mecnica utilizada, a continuacin se incluyen algunas muy frecuentes en la programacin
Windows.
#define LOWORD(x) ((WORD) (l))
#define HIWORD(x) ((WORD) (((DWORD) (l) >> 16) & 0xFFFF))
Con estas definiciones, y sabiendo que a su vez, WORD y DWORD estn definidas como unsigned
short y unsigned long respectivamente, supongamos que dos valores, ancho y alto de cierta
propiedad, se reciben codificados en las mitades superior e inferior de un long, al que
llamaremos param. En este contexto, ambos valores pueden ser fcilmente determinados con las
expresiones siguientes:
WORD alto = LOWORD(param);
WORD ancho = HIWORD(param);
Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE est definida como unsigned
char, y LONG es long):
#define MAKEWORD(a, b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8))
#define MAKELONG(a, b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) << 16))
#define LOBYTE(w) ((BYTE)(w))
#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
Como el lector puede comprobar en todos estos casos, si se modifican las condiciones de entorno,
la adaptacin de las aplicaciones resulta muy fcil, ya que se limita a modificar adecuadamente los
ficheros de cabecera.
3 Identificacin del endianness del sistema
323

Das könnte Ihnen auch gefallen