05 Programacion Lenguaje c++

71
§4 Conversión entre sistemas multibyte y de caracteres anchos Debido a que los caracteres anchos se emplean generalmente para representación interna, y los multibyte para la representación externa, las operaciones de entrada/salida suelen implicar un proceso de conversión entre ambos sistemas de codificación. 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 carácter más ancho del sistema multibyte. En cambio, todas las secuencias de escape pueden ser eliminadas. La figura adjunta [3 ] muestra esquemáticamente el proceso de convertir un fichero de codificación JIS al sistema Unicode de caracteres anchos. Nota: La Librería Estándar C++ dispone de algunas herramientas para realizar este tipo de conversiones ( 5.2.1 ). 2.2.1a1 El carácter ancho §1 Introducción Respecto al problema someramente esbozado en el apartado anterior ( 2.2.1a ), de representar los caracteres de lenguas distintas del inglés americano. En los compiladores C y C++ se decidió utilizar un nuevo

Transcript of 05 Programacion Lenguaje c++

Page 1: 05 Programacion Lenguaje c++

sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos

Debido a que los caracteres anchos se emplean generalmente para representacioacuten interna y los multibyte para la representacioacuten externa las operaciones de entradasalida suelen implicar un proceso de conversioacuten entre ambos sistemas de codificacioacuten Un caso frecuente lo constituyen los procesos de lectura y escritura de ficheros Estos uacuteltimos 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 caraacutecter maacutes ancho del sistema multibyte En cambio todas las secuencias de escape pueden ser eliminadas La figura adjunta [3] muestra esquemaacuteticamente el proceso de convertir un fichero de codificacioacuten JIS al sistema Unicode de caracteres anchos

Nota La Libreriacutea Estaacutendar C++ dispone de algunas herramientas para realizar este tipo de

conversiones ( 521)

221a1 El caraacutecter ancho

sect1 Introduccioacuten

Respecto al problema someramente esbozado en el apartado anterior ( 221a) de representar los caracteres de lenguas distintas del ingleacutes americano En los compiladores C y C++ se decidioacute utilizar un nuevo tipo denominado caraacutecter ancho o DBCS (Double Byte Character Set) en atencioacuten a que como veremos a continuacioacuten la mayoriacutea de las veces ocupa dos Bytes

El caraacutecter ancho se designa con la palabra wchar_t En C se utilizoacute un typdef ( 321a ltstddefhgt) para definirlo pero en C++ tomoacute carta de naturaleza como un nuevo tipo preconstruido en el lenguaje de forma que de ser un typedef pasoacute a ser palabra clave

sect2 wchar_t

En C++ esta palabra clave identifica un tipo especial el caraacutecter ancho

Sintaxis

wchar_t ltidentificadorgt

Comentario

El Estaacutendar C++ nos dice que su tamantildeo es dependiente de la implementacioacuten 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 ( 221a2) que utiliza 2 bytes este es el tamantildeo miacutenimo del wchar_t de la mayoriacutea de compiladores

Nota en el caso de Borland C++ es del mismo tamantildeo signo y alineacioacuten 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 libreriacutea Estaacutendar C++ utiliza funciones distintas para manejar caracteres anchos y normales Por ejemplo la funcioacutenprintf para caracteres tipo char tiene una versioacuten wprintf cuando se trata de representar caracteres tipo wchar_t a su vez strlen (string length) tiene la versioacuten wcslen (wide-character string length) para caracteres anchos wcout es la versioacuten de cout para caracteres anchos Etc (existen dos versiones de todas aquellas funciones que manejan cadenas alfanumeacutericas)

Ejemplos

wchar_t ch declaracioacuten de caraacutecter anchowchar_t str = LABCD puntero a cadena de caracteres anchoswcout ltlt La cadena es ltlt str ltlt endl L3wcout ltlt LLa cadena es ltlt str ltlt endl L4wchar_t cnulo = L0 caraacutecter nulo ancho

Comentario

Las sentencias L3 y L4 son equivalentes la salida en ambos casos es

La cadena es ABCD

Observe que en L3 el compilador es suficientemente inteligente para suponer que el literal La cadena es es de caracteres anchos

221a2 Codificaciones UCSUnicode

sect1 Introduccioacuten

Respecto al problema ya esbozado en el capiacutetulo anterior ( 221a) de representar los caracteres de lenguas distintas del ingleacutes americano se habiacutean utilizado muacuteltiples soluciones parciales pero a finales de los 80 se hicieron intentos para definir un sistema de codificacioacuten verdaderamente universal De un lado el denominado UCS (Universal Character Set) preconizado por la organizacioacuten internacional de estandarizacioacuten ISO de otro el denominado Unicode desarrollado por la iniciativa privada (Microsoft principalmente) Esta compantildeiacutea habiacutea incorporado en sus sistemas Windows 9x un juego de caracteres bastante adaptado a la mayoriacutea de alfabetos occidentales Posteriormente implementoacute Unicode en sus sistemas NT y sucesores

Tanto UCS como Unicode pretendiacutean eliminar el problema de una vez por todas estableciendo un sistema de codificacioacuten que permitiese la incorporacioacuten los caracteres (grafos) de todas las lenguas escritas del mundo tanto actuales como pasadas asiacute como los siacutembolos utilizados en matemaacuteticas tipografiacutea (por ejemplo los utilizados en TeX) y otros como el juego de caracteres foneacuteticos IPA (International Phonetic Alphabet) tipos usados frecuentemente para reconocimiento oacuteptico de caracteres (OCR) etc El sistema deberiacutea ser ademaacutes extensible de forma que pudieran antildeadirse nuevos tipos en el futuro

Aunque el desarrollo inicial de ambos sistemas fue independiente los respectivos comiteacutes se dieron cuenta enseguida que este camino seguiriacutea manteniendo el cisma que tanto dantildeo habiacutea causado a la informaacutetica desde los primeros intentos de extender el US-ASCII de forma que pronto convergieron y el sistema Unicode se convirtioacute en un subset del sistema UCS maacutes general

sect2 UCS

El sistema universal de caracteres UCS se concreta en el estaacutendar ISO-10646 y se define como un juego de caracteres de 31 bits Por supuesto sus 231 posibilidades garantizan que pueda soportar con creces las ampliacutesimas expectativas sentildealadas en el paacuterrafo 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 216 (65536) posibilidades son suficientes para los caracteres de todas las lenguas conocidas

Nota en realidad lo que se hizo fue copiar el conocidiacutesimo juego de caracteres US-ACII en las 128 primeras posiciones A continuacioacuten en las 128 siguientes hasta completar 256 se colocaron los caracteres de la extensioacuten ANSIISO 8859-1 (una variacioacuten de los caracteres mostrados en la tabla sect32a del capiacutetulo anterior) Sucesivamente se fueron reservando zonas para diversos alfabetos por ejemplo para el Griego el Ciriacutelico (utilizado en la Federacioacuten Rusa y otros paiacuteses limiacutetrofes) Armenio Hebreo y los ideogramas Kanji (Japoneses) Hangeul (Coreanos) y del alfabeto Chino

Los espacios situados fuera de este Plano estaacuten reservados para los caracteres utilizados en aplicaciones muy especiales Por ejemplo los estudiosos de culturas antiguas o la notacioacuten cientiacutefica El estaacutendar ISO-10646-1 fue establecido en 1993 y le siguioacute una versioacuten ISO-10646-2 en el 2001 La teoriacutea es que pueden antildeadirse nuevos caracteres seguacuten las necesidades pero los ya establecidos no se alteraraacuten nunca maacutes de forma que se pretende una plataforma estable Ademaacutes se supone que no existiraacuten nunca caracteres de maacutes de 21 bits ya que mil trillones de caracteres (221) seraacuten suficientes

sect3 Unicode

Teacutecnicamente Unicode es el ISO 10646-1 Asiacute pues cada caraacutecter ocupa 16 bits lo que significa

que puede estar representado por un caraacutecter ancho (wchar_t 221a1) de cualquier compilador C++

El inconveniente es que evidentemente los almacenamientos ocupan el doble de espacio que con los caracteres estaacutendar La ventaja es que no existe necesidad de ninguna conversioacuten ni ambiguumledad Cada caraacutecter representado por un nuacutemero tiene una significacioacuten inequiacutevoca Como ejemplo se incluye un trozo de coacutedigo tomado del fichero winnth donde puede

verse como la definicioacuten de los typedef ( 321a) TCHAR yPTCHAR se realiza de forma distinta seguacuten se esteacute utilizando un juego de caracteres normales o Unicode

ifdef UNICODE typedef WCHAR TCHAR PTCHARelsetypedef char TCHAR PTCHARendif

Los programas Windows identifican la lengua mediante una constante manifiesta ( 141a) que

es traducida por el preprocesador a una constante hexadecimal ( 323b) A su vez las variaciones o dialectos locales (sublenguajes en la terminologiacutea de estos compiladores) se identifican mediante otras constantes que son tambieacuten traducidas a nuacutemeros

Para dar idea de las lenguas soportadas a continuacioacuten se relacionan las constantes implementadas en el compilador MS VC++ 60

Lengua Variedad

define LANG_NEUTRAL 0x00define LANG_AFRIKAANS 0x36define LANG_ALBANIAN 0x1cdefine LANG_ARABIC 0x01define LANG_ARMENIAN 0x2bdefine LANG_ASSAMESE 0x4ddefine LANG_AZERI 0x2cdefine LANG_BASQUE 0x2ddefine LANG_BELARUSIAN 0x23define LANG_BENGALI 0x45define LANG_BULGARIAN 0x02define LANG_CATALAN 0x03define LANG_CHINESE 0x04define LANG_CROATIAN 0x1adefine LANG_CZECH 0x05define LANG_DANISH 0x06define LANG_DUTCH 0x13define LANG_ENGLISH 0x09define LANG_ESTONIAN 0x25

define SUBLANG_NEUTRAL 0x00 language neutraldefine SUBLANG_DEFAULT 0x01 user defaultdefine SUBLANG_SYS_DEFAULT 0x02 system defaultdefine 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 SUBLANG_ARABIC_TUNISIA

define LANG_FAEROESE 0x38define LANG_FARSI 0x29define LANG_FINNISH 0x0bdefine LANG_FRENCH 0x0cdefine LANG_GEORGIAN 0x37define LANG_GERMAN 0x07define LANG_GREEK 0x08define LANG_GUJARATI 0x47define LANG_HEBREW 0x0ddefine LANG_HINDI 0x39define LANG_HUNGARIAN 0x0edefine LANG_ICELANDIC 0x0fdefine LANG_INDONESIAN 0x21define LANG_ITALIAN 0x10define LANG_JAPANESE 0x11define LANG_KANNADA 0x4bdefine LANG_KASHMIRI 0x60define LANG_KAZAK 0x3fdefine LANG_KONKANI 0x57define LANG_KOREAN 0x12define LANG_LATVIAN 0x26define LANG_LITHUANIAN 0x27define LANG_MACEDONIAN 0x2fdefine LANG_MALAY 0x3edefine LANG_MALAYALAM 0x4cdefine LANG_MANIPURI 0x58define LANG_MARATHI 0x4edefine LANG_NEPALI 0x61define LANG_NORWEGIAN 0x14define LANG_ORIYA 0x48define LANG_POLISH 0x15define LANG_PORTUGUESE 0x16define LANG_PUNJABI 0x46define LANG_ROMANIAN 0x18define LANG_RUSSIAN 0x19define LANG_SANSKRIT 0x4fdefine LANG_SERBIAN 0x1adefine LANG_SINDHI 0x59define LANG_SLOVAK 0x1bdefine LANG_SLOVENIAN 0x24define LANG_SPANISH 0x0adefine LANG_SWAHILI 0x41define LANG_SWEDISH 0x1d

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 (UAE)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 Dutchdefine 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 SUBLANG_ENGLISH_NZ

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 2: 05 Programacion Lenguaje c++

sect2 wchar_t

En C++ esta palabra clave identifica un tipo especial el caraacutecter ancho

Sintaxis

wchar_t ltidentificadorgt

Comentario

El Estaacutendar C++ nos dice que su tamantildeo es dependiente de la implementacioacuten 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 ( 221a2) que utiliza 2 bytes este es el tamantildeo miacutenimo del wchar_t de la mayoriacutea de compiladores

Nota en el caso de Borland C++ es del mismo tamantildeo signo y alineacioacuten 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 libreriacutea Estaacutendar C++ utiliza funciones distintas para manejar caracteres anchos y normales Por ejemplo la funcioacutenprintf para caracteres tipo char tiene una versioacuten wprintf cuando se trata de representar caracteres tipo wchar_t a su vez strlen (string length) tiene la versioacuten wcslen (wide-character string length) para caracteres anchos wcout es la versioacuten de cout para caracteres anchos Etc (existen dos versiones de todas aquellas funciones que manejan cadenas alfanumeacutericas)

Ejemplos

wchar_t ch declaracioacuten de caraacutecter anchowchar_t str = LABCD puntero a cadena de caracteres anchoswcout ltlt La cadena es ltlt str ltlt endl L3wcout ltlt LLa cadena es ltlt str ltlt endl L4wchar_t cnulo = L0 caraacutecter nulo ancho

Comentario

Las sentencias L3 y L4 son equivalentes la salida en ambos casos es

La cadena es ABCD

Observe que en L3 el compilador es suficientemente inteligente para suponer que el literal La cadena es es de caracteres anchos

221a2 Codificaciones UCSUnicode

sect1 Introduccioacuten

Respecto al problema ya esbozado en el capiacutetulo anterior ( 221a) de representar los caracteres de lenguas distintas del ingleacutes americano se habiacutean utilizado muacuteltiples soluciones parciales pero a finales de los 80 se hicieron intentos para definir un sistema de codificacioacuten verdaderamente universal De un lado el denominado UCS (Universal Character Set) preconizado por la organizacioacuten internacional de estandarizacioacuten ISO de otro el denominado Unicode desarrollado por la iniciativa privada (Microsoft principalmente) Esta compantildeiacutea habiacutea incorporado en sus sistemas Windows 9x un juego de caracteres bastante adaptado a la mayoriacutea de alfabetos occidentales Posteriormente implementoacute Unicode en sus sistemas NT y sucesores

Tanto UCS como Unicode pretendiacutean eliminar el problema de una vez por todas estableciendo un sistema de codificacioacuten que permitiese la incorporacioacuten los caracteres (grafos) de todas las lenguas escritas del mundo tanto actuales como pasadas asiacute como los siacutembolos utilizados en matemaacuteticas tipografiacutea (por ejemplo los utilizados en TeX) y otros como el juego de caracteres foneacuteticos IPA (International Phonetic Alphabet) tipos usados frecuentemente para reconocimiento oacuteptico de caracteres (OCR) etc El sistema deberiacutea ser ademaacutes extensible de forma que pudieran antildeadirse nuevos tipos en el futuro

Aunque el desarrollo inicial de ambos sistemas fue independiente los respectivos comiteacutes se dieron cuenta enseguida que este camino seguiriacutea manteniendo el cisma que tanto dantildeo habiacutea causado a la informaacutetica desde los primeros intentos de extender el US-ASCII de forma que pronto convergieron y el sistema Unicode se convirtioacute en un subset del sistema UCS maacutes general

sect2 UCS

El sistema universal de caracteres UCS se concreta en el estaacutendar ISO-10646 y se define como un juego de caracteres de 31 bits Por supuesto sus 231 posibilidades garantizan que pueda soportar con creces las ampliacutesimas expectativas sentildealadas en el paacuterrafo 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 216 (65536) posibilidades son suficientes para los caracteres de todas las lenguas conocidas

Nota en realidad lo que se hizo fue copiar el conocidiacutesimo juego de caracteres US-ACII en las 128 primeras posiciones A continuacioacuten en las 128 siguientes hasta completar 256 se colocaron los caracteres de la extensioacuten ANSIISO 8859-1 (una variacioacuten de los caracteres mostrados en la tabla sect32a del capiacutetulo anterior) Sucesivamente se fueron reservando zonas para diversos alfabetos por ejemplo para el Griego el Ciriacutelico (utilizado en la Federacioacuten Rusa y otros paiacuteses limiacutetrofes) Armenio Hebreo y los ideogramas Kanji (Japoneses) Hangeul (Coreanos) y del alfabeto Chino

Los espacios situados fuera de este Plano estaacuten reservados para los caracteres utilizados en aplicaciones muy especiales Por ejemplo los estudiosos de culturas antiguas o la notacioacuten cientiacutefica El estaacutendar ISO-10646-1 fue establecido en 1993 y le siguioacute una versioacuten ISO-10646-2 en el 2001 La teoriacutea es que pueden antildeadirse nuevos caracteres seguacuten las necesidades pero los ya establecidos no se alteraraacuten nunca maacutes de forma que se pretende una plataforma estable Ademaacutes se supone que no existiraacuten nunca caracteres de maacutes de 21 bits ya que mil trillones de caracteres (221) seraacuten suficientes

sect3 Unicode

Teacutecnicamente Unicode es el ISO 10646-1 Asiacute pues cada caraacutecter ocupa 16 bits lo que significa

que puede estar representado por un caraacutecter ancho (wchar_t 221a1) de cualquier compilador C++

El inconveniente es que evidentemente los almacenamientos ocupan el doble de espacio que con los caracteres estaacutendar La ventaja es que no existe necesidad de ninguna conversioacuten ni ambiguumledad Cada caraacutecter representado por un nuacutemero tiene una significacioacuten inequiacutevoca Como ejemplo se incluye un trozo de coacutedigo tomado del fichero winnth donde puede

verse como la definicioacuten de los typedef ( 321a) TCHAR yPTCHAR se realiza de forma distinta seguacuten se esteacute utilizando un juego de caracteres normales o Unicode

ifdef UNICODE typedef WCHAR TCHAR PTCHARelsetypedef char TCHAR PTCHARendif

Los programas Windows identifican la lengua mediante una constante manifiesta ( 141a) que

es traducida por el preprocesador a una constante hexadecimal ( 323b) A su vez las variaciones o dialectos locales (sublenguajes en la terminologiacutea de estos compiladores) se identifican mediante otras constantes que son tambieacuten traducidas a nuacutemeros

Para dar idea de las lenguas soportadas a continuacioacuten se relacionan las constantes implementadas en el compilador MS VC++ 60

Lengua Variedad

define LANG_NEUTRAL 0x00define LANG_AFRIKAANS 0x36define LANG_ALBANIAN 0x1cdefine LANG_ARABIC 0x01define LANG_ARMENIAN 0x2bdefine LANG_ASSAMESE 0x4ddefine LANG_AZERI 0x2cdefine LANG_BASQUE 0x2ddefine LANG_BELARUSIAN 0x23define LANG_BENGALI 0x45define LANG_BULGARIAN 0x02define LANG_CATALAN 0x03define LANG_CHINESE 0x04define LANG_CROATIAN 0x1adefine LANG_CZECH 0x05define LANG_DANISH 0x06define LANG_DUTCH 0x13define LANG_ENGLISH 0x09define LANG_ESTONIAN 0x25

define SUBLANG_NEUTRAL 0x00 language neutraldefine SUBLANG_DEFAULT 0x01 user defaultdefine SUBLANG_SYS_DEFAULT 0x02 system defaultdefine 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 SUBLANG_ARABIC_TUNISIA

define LANG_FAEROESE 0x38define LANG_FARSI 0x29define LANG_FINNISH 0x0bdefine LANG_FRENCH 0x0cdefine LANG_GEORGIAN 0x37define LANG_GERMAN 0x07define LANG_GREEK 0x08define LANG_GUJARATI 0x47define LANG_HEBREW 0x0ddefine LANG_HINDI 0x39define LANG_HUNGARIAN 0x0edefine LANG_ICELANDIC 0x0fdefine LANG_INDONESIAN 0x21define LANG_ITALIAN 0x10define LANG_JAPANESE 0x11define LANG_KANNADA 0x4bdefine LANG_KASHMIRI 0x60define LANG_KAZAK 0x3fdefine LANG_KONKANI 0x57define LANG_KOREAN 0x12define LANG_LATVIAN 0x26define LANG_LITHUANIAN 0x27define LANG_MACEDONIAN 0x2fdefine LANG_MALAY 0x3edefine LANG_MALAYALAM 0x4cdefine LANG_MANIPURI 0x58define LANG_MARATHI 0x4edefine LANG_NEPALI 0x61define LANG_NORWEGIAN 0x14define LANG_ORIYA 0x48define LANG_POLISH 0x15define LANG_PORTUGUESE 0x16define LANG_PUNJABI 0x46define LANG_ROMANIAN 0x18define LANG_RUSSIAN 0x19define LANG_SANSKRIT 0x4fdefine LANG_SERBIAN 0x1adefine LANG_SINDHI 0x59define LANG_SLOVAK 0x1bdefine LANG_SLOVENIAN 0x24define LANG_SPANISH 0x0adefine LANG_SWAHILI 0x41define LANG_SWEDISH 0x1d

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 (UAE)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 Dutchdefine 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 SUBLANG_ENGLISH_NZ

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 3: 05 Programacion Lenguaje c++

Respecto al problema ya esbozado en el capiacutetulo anterior ( 221a) de representar los caracteres de lenguas distintas del ingleacutes americano se habiacutean utilizado muacuteltiples soluciones parciales pero a finales de los 80 se hicieron intentos para definir un sistema de codificacioacuten verdaderamente universal De un lado el denominado UCS (Universal Character Set) preconizado por la organizacioacuten internacional de estandarizacioacuten ISO de otro el denominado Unicode desarrollado por la iniciativa privada (Microsoft principalmente) Esta compantildeiacutea habiacutea incorporado en sus sistemas Windows 9x un juego de caracteres bastante adaptado a la mayoriacutea de alfabetos occidentales Posteriormente implementoacute Unicode en sus sistemas NT y sucesores

Tanto UCS como Unicode pretendiacutean eliminar el problema de una vez por todas estableciendo un sistema de codificacioacuten que permitiese la incorporacioacuten los caracteres (grafos) de todas las lenguas escritas del mundo tanto actuales como pasadas asiacute como los siacutembolos utilizados en matemaacuteticas tipografiacutea (por ejemplo los utilizados en TeX) y otros como el juego de caracteres foneacuteticos IPA (International Phonetic Alphabet) tipos usados frecuentemente para reconocimiento oacuteptico de caracteres (OCR) etc El sistema deberiacutea ser ademaacutes extensible de forma que pudieran antildeadirse nuevos tipos en el futuro

Aunque el desarrollo inicial de ambos sistemas fue independiente los respectivos comiteacutes se dieron cuenta enseguida que este camino seguiriacutea manteniendo el cisma que tanto dantildeo habiacutea causado a la informaacutetica desde los primeros intentos de extender el US-ASCII de forma que pronto convergieron y el sistema Unicode se convirtioacute en un subset del sistema UCS maacutes general

sect2 UCS

El sistema universal de caracteres UCS se concreta en el estaacutendar ISO-10646 y se define como un juego de caracteres de 31 bits Por supuesto sus 231 posibilidades garantizan que pueda soportar con creces las ampliacutesimas expectativas sentildealadas en el paacuterrafo 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 216 (65536) posibilidades son suficientes para los caracteres de todas las lenguas conocidas

Nota en realidad lo que se hizo fue copiar el conocidiacutesimo juego de caracteres US-ACII en las 128 primeras posiciones A continuacioacuten en las 128 siguientes hasta completar 256 se colocaron los caracteres de la extensioacuten ANSIISO 8859-1 (una variacioacuten de los caracteres mostrados en la tabla sect32a del capiacutetulo anterior) Sucesivamente se fueron reservando zonas para diversos alfabetos por ejemplo para el Griego el Ciriacutelico (utilizado en la Federacioacuten Rusa y otros paiacuteses limiacutetrofes) Armenio Hebreo y los ideogramas Kanji (Japoneses) Hangeul (Coreanos) y del alfabeto Chino

Los espacios situados fuera de este Plano estaacuten reservados para los caracteres utilizados en aplicaciones muy especiales Por ejemplo los estudiosos de culturas antiguas o la notacioacuten cientiacutefica El estaacutendar ISO-10646-1 fue establecido en 1993 y le siguioacute una versioacuten ISO-10646-2 en el 2001 La teoriacutea es que pueden antildeadirse nuevos caracteres seguacuten las necesidades pero los ya establecidos no se alteraraacuten nunca maacutes de forma que se pretende una plataforma estable Ademaacutes se supone que no existiraacuten nunca caracteres de maacutes de 21 bits ya que mil trillones de caracteres (221) seraacuten suficientes

sect3 Unicode

Teacutecnicamente Unicode es el ISO 10646-1 Asiacute pues cada caraacutecter ocupa 16 bits lo que significa

que puede estar representado por un caraacutecter ancho (wchar_t 221a1) de cualquier compilador C++

El inconveniente es que evidentemente los almacenamientos ocupan el doble de espacio que con los caracteres estaacutendar La ventaja es que no existe necesidad de ninguna conversioacuten ni ambiguumledad Cada caraacutecter representado por un nuacutemero tiene una significacioacuten inequiacutevoca Como ejemplo se incluye un trozo de coacutedigo tomado del fichero winnth donde puede

verse como la definicioacuten de los typedef ( 321a) TCHAR yPTCHAR se realiza de forma distinta seguacuten se esteacute utilizando un juego de caracteres normales o Unicode

ifdef UNICODE typedef WCHAR TCHAR PTCHARelsetypedef char TCHAR PTCHARendif

Los programas Windows identifican la lengua mediante una constante manifiesta ( 141a) que

es traducida por el preprocesador a una constante hexadecimal ( 323b) A su vez las variaciones o dialectos locales (sublenguajes en la terminologiacutea de estos compiladores) se identifican mediante otras constantes que son tambieacuten traducidas a nuacutemeros

Para dar idea de las lenguas soportadas a continuacioacuten se relacionan las constantes implementadas en el compilador MS VC++ 60

Lengua Variedad

define LANG_NEUTRAL 0x00define LANG_AFRIKAANS 0x36define LANG_ALBANIAN 0x1cdefine LANG_ARABIC 0x01define LANG_ARMENIAN 0x2bdefine LANG_ASSAMESE 0x4ddefine LANG_AZERI 0x2cdefine LANG_BASQUE 0x2ddefine LANG_BELARUSIAN 0x23define LANG_BENGALI 0x45define LANG_BULGARIAN 0x02define LANG_CATALAN 0x03define LANG_CHINESE 0x04define LANG_CROATIAN 0x1adefine LANG_CZECH 0x05define LANG_DANISH 0x06define LANG_DUTCH 0x13define LANG_ENGLISH 0x09define LANG_ESTONIAN 0x25

define SUBLANG_NEUTRAL 0x00 language neutraldefine SUBLANG_DEFAULT 0x01 user defaultdefine SUBLANG_SYS_DEFAULT 0x02 system defaultdefine 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 SUBLANG_ARABIC_TUNISIA

define LANG_FAEROESE 0x38define LANG_FARSI 0x29define LANG_FINNISH 0x0bdefine LANG_FRENCH 0x0cdefine LANG_GEORGIAN 0x37define LANG_GERMAN 0x07define LANG_GREEK 0x08define LANG_GUJARATI 0x47define LANG_HEBREW 0x0ddefine LANG_HINDI 0x39define LANG_HUNGARIAN 0x0edefine LANG_ICELANDIC 0x0fdefine LANG_INDONESIAN 0x21define LANG_ITALIAN 0x10define LANG_JAPANESE 0x11define LANG_KANNADA 0x4bdefine LANG_KASHMIRI 0x60define LANG_KAZAK 0x3fdefine LANG_KONKANI 0x57define LANG_KOREAN 0x12define LANG_LATVIAN 0x26define LANG_LITHUANIAN 0x27define LANG_MACEDONIAN 0x2fdefine LANG_MALAY 0x3edefine LANG_MALAYALAM 0x4cdefine LANG_MANIPURI 0x58define LANG_MARATHI 0x4edefine LANG_NEPALI 0x61define LANG_NORWEGIAN 0x14define LANG_ORIYA 0x48define LANG_POLISH 0x15define LANG_PORTUGUESE 0x16define LANG_PUNJABI 0x46define LANG_ROMANIAN 0x18define LANG_RUSSIAN 0x19define LANG_SANSKRIT 0x4fdefine LANG_SERBIAN 0x1adefine LANG_SINDHI 0x59define LANG_SLOVAK 0x1bdefine LANG_SLOVENIAN 0x24define LANG_SPANISH 0x0adefine LANG_SWAHILI 0x41define LANG_SWEDISH 0x1d

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 (UAE)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 Dutchdefine 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 SUBLANG_ENGLISH_NZ

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 4: 05 Programacion Lenguaje c++

sect3 Unicode

Teacutecnicamente Unicode es el ISO 10646-1 Asiacute pues cada caraacutecter ocupa 16 bits lo que significa

que puede estar representado por un caraacutecter ancho (wchar_t 221a1) de cualquier compilador C++

El inconveniente es que evidentemente los almacenamientos ocupan el doble de espacio que con los caracteres estaacutendar La ventaja es que no existe necesidad de ninguna conversioacuten ni ambiguumledad Cada caraacutecter representado por un nuacutemero tiene una significacioacuten inequiacutevoca Como ejemplo se incluye un trozo de coacutedigo tomado del fichero winnth donde puede

verse como la definicioacuten de los typedef ( 321a) TCHAR yPTCHAR se realiza de forma distinta seguacuten se esteacute utilizando un juego de caracteres normales o Unicode

ifdef UNICODE typedef WCHAR TCHAR PTCHARelsetypedef char TCHAR PTCHARendif

Los programas Windows identifican la lengua mediante una constante manifiesta ( 141a) que

es traducida por el preprocesador a una constante hexadecimal ( 323b) A su vez las variaciones o dialectos locales (sublenguajes en la terminologiacutea de estos compiladores) se identifican mediante otras constantes que son tambieacuten traducidas a nuacutemeros

Para dar idea de las lenguas soportadas a continuacioacuten se relacionan las constantes implementadas en el compilador MS VC++ 60

Lengua Variedad

define LANG_NEUTRAL 0x00define LANG_AFRIKAANS 0x36define LANG_ALBANIAN 0x1cdefine LANG_ARABIC 0x01define LANG_ARMENIAN 0x2bdefine LANG_ASSAMESE 0x4ddefine LANG_AZERI 0x2cdefine LANG_BASQUE 0x2ddefine LANG_BELARUSIAN 0x23define LANG_BENGALI 0x45define LANG_BULGARIAN 0x02define LANG_CATALAN 0x03define LANG_CHINESE 0x04define LANG_CROATIAN 0x1adefine LANG_CZECH 0x05define LANG_DANISH 0x06define LANG_DUTCH 0x13define LANG_ENGLISH 0x09define LANG_ESTONIAN 0x25

define SUBLANG_NEUTRAL 0x00 language neutraldefine SUBLANG_DEFAULT 0x01 user defaultdefine SUBLANG_SYS_DEFAULT 0x02 system defaultdefine 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 SUBLANG_ARABIC_TUNISIA

define LANG_FAEROESE 0x38define LANG_FARSI 0x29define LANG_FINNISH 0x0bdefine LANG_FRENCH 0x0cdefine LANG_GEORGIAN 0x37define LANG_GERMAN 0x07define LANG_GREEK 0x08define LANG_GUJARATI 0x47define LANG_HEBREW 0x0ddefine LANG_HINDI 0x39define LANG_HUNGARIAN 0x0edefine LANG_ICELANDIC 0x0fdefine LANG_INDONESIAN 0x21define LANG_ITALIAN 0x10define LANG_JAPANESE 0x11define LANG_KANNADA 0x4bdefine LANG_KASHMIRI 0x60define LANG_KAZAK 0x3fdefine LANG_KONKANI 0x57define LANG_KOREAN 0x12define LANG_LATVIAN 0x26define LANG_LITHUANIAN 0x27define LANG_MACEDONIAN 0x2fdefine LANG_MALAY 0x3edefine LANG_MALAYALAM 0x4cdefine LANG_MANIPURI 0x58define LANG_MARATHI 0x4edefine LANG_NEPALI 0x61define LANG_NORWEGIAN 0x14define LANG_ORIYA 0x48define LANG_POLISH 0x15define LANG_PORTUGUESE 0x16define LANG_PUNJABI 0x46define LANG_ROMANIAN 0x18define LANG_RUSSIAN 0x19define LANG_SANSKRIT 0x4fdefine LANG_SERBIAN 0x1adefine LANG_SINDHI 0x59define LANG_SLOVAK 0x1bdefine LANG_SLOVENIAN 0x24define LANG_SPANISH 0x0adefine LANG_SWAHILI 0x41define LANG_SWEDISH 0x1d

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 (UAE)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 Dutchdefine 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 SUBLANG_ENGLISH_NZ

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 5: 05 Programacion Lenguaje c++

define LANG_FAEROESE 0x38define LANG_FARSI 0x29define LANG_FINNISH 0x0bdefine LANG_FRENCH 0x0cdefine LANG_GEORGIAN 0x37define LANG_GERMAN 0x07define LANG_GREEK 0x08define LANG_GUJARATI 0x47define LANG_HEBREW 0x0ddefine LANG_HINDI 0x39define LANG_HUNGARIAN 0x0edefine LANG_ICELANDIC 0x0fdefine LANG_INDONESIAN 0x21define LANG_ITALIAN 0x10define LANG_JAPANESE 0x11define LANG_KANNADA 0x4bdefine LANG_KASHMIRI 0x60define LANG_KAZAK 0x3fdefine LANG_KONKANI 0x57define LANG_KOREAN 0x12define LANG_LATVIAN 0x26define LANG_LITHUANIAN 0x27define LANG_MACEDONIAN 0x2fdefine LANG_MALAY 0x3edefine LANG_MALAYALAM 0x4cdefine LANG_MANIPURI 0x58define LANG_MARATHI 0x4edefine LANG_NEPALI 0x61define LANG_NORWEGIAN 0x14define LANG_ORIYA 0x48define LANG_POLISH 0x15define LANG_PORTUGUESE 0x16define LANG_PUNJABI 0x46define LANG_ROMANIAN 0x18define LANG_RUSSIAN 0x19define LANG_SANSKRIT 0x4fdefine LANG_SERBIAN 0x1adefine LANG_SINDHI 0x59define LANG_SLOVAK 0x1bdefine LANG_SLOVENIAN 0x24define LANG_SPANISH 0x0adefine LANG_SWAHILI 0x41define LANG_SWEDISH 0x1d

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 (UAE)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 Dutchdefine 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 SUBLANG_ENGLISH_NZ

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 6: 05 Programacion Lenguaje c++

define LANG_TAMIL 0x49define LANG_TATAR 0x44define LANG_TELUGU 0x4adefine LANG_THAI 0x1edefine LANG_TURKISH 0x1fdefine LANG_UKRAINIAN 0x22define LANG_URDU 0x20define LANG_UZBEK 0x43define LANG_VIETNAMESE 0x2a

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 Frenchdefine 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 Germandefine 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 Italiandefine SUBLANG_ITALIAN_SWISS 0x02 Italian (Swiss)

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 7: 05 Programacion Lenguaje c++

define SUBLANG_KASHMIRI_INDIA 0x02 Kashmiri (India)define SUBLANG_KOREAN 0x01 Korean (Extended Wansung)define SUBLANG_LITHUANIAN 0x01 Lithuaniandefine 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 Portuguesedefine 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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 8: 05 Programacion Lenguaje c++

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 Swedishdefine 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)

sect3 Webografiacutea

UTF-8 and Unicode FAQ for UnixLinux wwwclcamacuk

A pesar de su tiacutetulo esta paacutegina de Markus Kuhn es una excelente guiacutea acerca de los sistemas de codificacioacuten en general

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 9: 05 Programacion Lenguaje c++

Unicodeorg wwwunicodeorgunicode

Sin duda el sitio oficial es la mejor fuente de informacioacuten En la paacutegina Code charts pueden obtenerse las tablas de caracteres ideogramas y signos de todas las lenguas del mundo

222 Tipos derivados

sect1 Sinopsis

Aparte de los tipos baacutesicos ( 221) 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 baacutesicos De estos nuevos tipos los de nivel de abstraccioacuten maacutes alto son

lasclases ( 411) incluyendo sus formas particulares (estructuras y uniones) tambieacuten pueden crearse campos de bits matrices y una familia de tipos muy importante los punteros Hay punteros a cada uno de los demaacutes tipos a caraacutecter a entero a estructura a funcioacuten a puntero

etc ( 42)

Los tipos derivados se construyen con palabras clave class struct unioacuten u operadores especiales como los siguientes

puntero a const puntero constante a amp referencia a [ ] array[1] de ( ) retorno de funcioacuten

Ejemplos suponiendo que tipoX un tipo baacutesico o variante (no void 221) pueden declararse tipos derivados tal como se muestra

tipoX t t es un objeto de tipo tipoXtipoX arr[10] arr es una matriz de diez elementos tipoXtipoX ptr ptr es un puntero a tipoXtipoX ampref=t ref es una referencia a tipoXtipoX func(void) func devuelve un valor tipoX (no acepta paraacutemetros)void func(tipoX t) func1 acepta un paraacutemetro t tipoX (no devuelve nada)struct st tipoX t1 tipoX t2 la estructura st alberga dos tipoX

Nota Las expresiones tipoamp var tipo ampvar y tipo amp var son equivalentes

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 10: 05 Programacion Lenguaje c++

223 Modificadores de tipo

sect1 Sinopsis

Hemos sentildealado ( 221) que los modificadores opcionales que pueden acompantildear a los tipos baacutesicos son con signo sin signo largo ycorto y que se aplican con las palabras clave long short signed unsigned Por otra parte la creciente implementacioacuten de procesadores con longitud de palabra de 64 bits hace suponer que pronto se extenderaacuten los tipos

actuales incluyendo versiones extra-largas de los enteros ( Tipos extendidos)

sect2 long

Sintaxis

long [int] ltidentificadorgt [long] double ltidentificadorgt

Descripcioacuten

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 precisioacuten no los 128 (2x64)

que cabriacutea esperar ( 224 Representacioacuten interna y precisioacuten)

Existen los siguientes tipos de long

long x x es signed long intlong int x x es signed long intsigned long x x es signed long intsigned long int x x es signed long intunsigned long x x es unsigned long intunsigned long int x x es unsigned long int

sect3 short

Sintaxis

short int ltidentificadorgt

Descripcioacuten

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 intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 11: 05 Programacion Lenguaje c++

short x x es signed short intshort int x x es signed short intsigned short x x es signed short intsigned short int x x es signed short intunsigned short x x es unsigned short intunsigned short int x x es unsigned short int

Ejemplos

short int ishort i equivale a short int i

sect4 signed

Sintaxis

signed lttipogt ltidentificadorgt

Descripcioacuten

El modificador de tipo signed define que el valor de una variable numeacuterica puede ser positivo o negativo Este modificador puede ser aplicado a los tipos baacutesicos int char long short y __int64 Seguacuten se indica en el ejemplo si no se indica el tipo baacutesico 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 maacuteximo valor absoluto que

es posible guardar es menor que el correspondiente valor unsigned del mismo tipo base ( 224Tipos de datos y representacioacuten interna)

Ejemplos

signed int i signed es valor por defecto (no es preciso)int i equivalente al anteriorsigned i equivalente al anteriorunsigned long int l Okunsigned long l equivale al anteriorsigned char ch Okunsigned char ch Ok

sect5 unsigned

Sintaxis

unsigned lttipogt ltidentificadorgt

Descripcioacuten

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 12: 05 Programacion Lenguaje c++

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 iunsigned i equivale al anteriorunsigned long int l Okunsigned long l Equivale al anteriorunsigned char ch Okchar ch Equivale al anterior

sect6 Tipos enteros extendidos

La progresiva utilizacioacuten de procesadores de 64 bits de los que pronto existiraacuten 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 maacutes de 32 bits Entre otras razones porque raacutepidamente seraacuten normales almacenamientos de disco con maacutes capacidad de la que puede direccionarse directamente con palabras de 32 bits

En la actualidad se estaacute trabajando en un estaacutendar conocido como C9x que se espera sea adoptado oficialmente en breve (2001) Esta versioacuten 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 tradicioacuten se supone int si no se indica otro tipo base Ademaacutes por defecto long long se supone con signo

long long x x es signed long long intlong long int x x es signed long long intsigned long long x x es signed long long intsigned long long int x x es signed long long intunsigned long long x x es unsigned long long intunsigned long long int x x es unsigned long long int

sect7 Extensiones C++Builder

Este compilador C++ de Borland permite especificadores opcionales para los enteros maacutes allaacute de lo especificado en el estaacutendar (alguno en liacutenea de los nuevos tipos que se esperan) Estos modificadores opcionales permiten definir el tipo de almacenamiento que se utilizaraacute para el entero Para utilizarlos es preciso usar tambieacuten 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

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 13: 05 Programacion Lenguaje c++

__int16 i16 __int16 s = 32767i16 16 bits

__int32 i32 __int32 i = 123456789i32 32 bits

__int64 i64 __int64 big = 12345654321i64 64 bits

unsigned __int64

ui64unsigned __int64 hInt=1234567887654321ui64 64 bits

224 Tipos baacutesicos representacioacuten interna rango

sect1 Sinopsis

El ANSI C reconoce que el tamantildeo y rangos de valor numeacuterico de los tipos baacutesicos y sus varias

permutaciones ( 221) dependen de la implementacioacuten y generalmente derivan de la arquitectura del ordenador La tabla adjunta muestra los tamantildeos y rangos de los tipos numeacutericos de 32-bits de Borland C++ [1]

Nota precisamente esta circunstancia que el Estaacutendar C++ impone relativa libertad en cuanto al tamantildeo de los tipos es la responsable de que auacuten adhirieacutendose estrictamente al Estaacutendar puedan existir problemas de portabilidad entre diversas plataformas de los programas C++ (algo que no ocurre con otros lenguajes de definicioacuten maacutes estricta Por ejemplo Java)

Ver en 224c unas notas adicionales sobre los tipos baacutesicos

sect2 Almacenamiento y rango

Las explicaciones siguientes muestran como se representan internamente estos tipos (en negrita los tipos baacutesicos) Los ficheros de cabeceraltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

Tipo bits Rango Tipo de uso

unsigned char 8 0 lt= X lt= 255 Nuacutemeros pequentildeos y juego caracteres del PC

char (signed) 8 -128 lt= X lt= 127 Nuacutemeros muy pequentildeos y juego de caracteres ASCII [5]

short (signed) 16 -32768 lt= X lt= 32767 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned short 16 0 lt= X lt= 65535 Nuacutemeros muy pequentildeos control de bucles pequentildeos

unsigned (int) 32 0 lt= X lt= 4294967295 Nuacutemeros grandes

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 14: 05 Programacion Lenguaje c++

int (signed) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros pequentildeos control de bucles

unsigned long 32 0 lt= X lt= 4294967295 Distancias astronoacutemicas

enum 32 -2147483648 lt= X lt= 2147483647 Conjuntos de valores ordenados

long (int) 32 -2147483648 lt= X lt= 2147483647 Nuacutemeros grandes

float 32 118e-38 lt= |X| lt= 340e38 Precisioacuten cientiacutefica ( 7-diacutegitos)

double 64 223e-308 lt= |X| lt= 179e308 Precisioacuten cientiacutefica (15-diacutegitos)

long double 80 337e-4932 lt= |X| lt= 118e4932 Precisioacuten cientiacutefica (18-diacutegitos)

Nota las cuestiones de almacenamiento interno como se almacenan los datos en memoria y cuanto espacio necesitan estaacuten influidas por otros factores ademaacutes de los sentildealados Estas son las que podriacuteamos denominar necesidades miacutenimas 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 tamantildeo de por ejemplo una estructura no corresponde con lo que teoacutericamente se deduce de la suma de los miembros (

459) Las caracteriacutesticas de esta alineacioacuten pueden ser controladas mediante opciones

del compilador ( 459a)

sect3 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 maacutes 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 nuacutemero de posibilidades es 2n y el rango de valores estaacute entre 0 y 2n-1 donde n es el nuacutemero de bits de la palabra del procesador 8 16 o 32 (uno dos o cuatro octetos)

El estaacutendar ANSI C no define el tamantildeo de almacenamiento de los diversos tipos solamente indica que la serie short int y long no es descendente es decir short lt= int lt= long De hecho legalmente los tres tipos pueden ser del mismo tamantildeo

En muchas (pero no todas) las implementaciones de C y C++ un long es mayor que un int Actualmente la mayoriacutea de las aplicaciones de oficina y personales con entornos como Windows o Linux corren sobre plataformas hardware de 32 bits de forma que la mayoriacutea de los compiladores para estas plataformas utilizan un int de 32 bits (del mismo tamantildeo que el long)

En cualquier caso los rangos vienen indicados por las constantes que se sentildealan (incluidas en ltlimitshgt)

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 15: 05 Programacion Lenguaje c++

signed short SHRT_MIN lt= X lt= SHRT_MAX

Siendo SHRT_MIN lt= -32767 y SHRT_MAX gt= 32767 Algunas implementaciones hacen SHRT_MIN = -32768 pero no es exigido por el estaacutendar

unsigned short 0 lt= X lt= USHRT_MAX

Siendo USHRT_MAX gt= 65535 Las variedades short deben contener al menos 16 bits para que pueda cubrirse el rango de valores exigidos

En la mayoriacutea de los compiladores un short es menor que un int de forma que algunos programas que deben almacenar grandes matrices de nuacutemeros en memoria o en ficheros pueden economizar espacio utilizando short en lugar de int pero siempre que se cumplan dos condiciones

1 En la implementacioacuten un short es realmente menor que un int

2- Los valores caben en un short

En algunas arquitecturas el coacutedigo empleado para manejar los short es maacutes largo y lento que el correspondiente para los int Esto es particularmente cierto en los procesadores Intel x86 ejecutando coacutedigo de 32 bits en programas para Windows (NT9598) Linux y otras versiones Unix En estos coacutedigos cada instruccioacuten que referencia a un short es un byte maacutes larga y generalmente necesita tiempo extra de procesador para ejecutarse

signed int INT_MIN lt= X lt= INT_MAX

Siendo INT_MIN lt= -32767 y INT_MAX gt= 32767 Algunas implementaciones utilizan un valor INT_MIN = -32768 pero no es exigido en el estaacutendar

unsigned int 0 lt= X lt= UINT_MAX

Siendo UINT_MAX gt= 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 ideacutentico que para los signed short y unsigned short En compiladores para procesadores de 8 y 16 bits (incluyendo los Intel x86 ejecutando coacutedigo 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 coacutedigo de 32 bits como Windows o Linux) generalmente un int es de 32 bits exactamente igual que un long

signed long LONG_MIN lt= X lt= LONG_MAX

Siendo LONG_MIN lt= -2147483647 y LONG_MAX gt= 2147483647 Existen implementaciones que hacen LONG_MIN = -2147483648 pero no es exigido por el estaacutendar

unsigned long 0 lt= X lt= ULONG_MAX

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 16: 05 Programacion Lenguaje c++

Siendo ULONG_MAX gt= 4294967295 Para poder cubrir este rango los tipos long deben ser de al menos 32 bits

sect4 Nuevos tipos numeacutericos

Los rangos previstos para los nuevos tipos ( 323d) long long que se proyectan incluir en el estaacutendar son

signed long long LLONG_MIN lt= X lt= LLONG_MAX

Siendo LLONG_MIN lt= -9223372036854775807 y LLONG_MAX gt= 9223372036854775807 Algunas implementaciones hacenLLONG_MIN = -9223372036854775808 pero no es exigido

unsigned long long 0 lt= X lt= ULLONG_MAX

Siendo ULLONG_MAX gt= 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 maacutes 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 Veacutease al respecto el rango de int y unsigned int

Los enteros sin signo se mantienen en valores 0 oacute positivos dentro de la aritmeacutetica de numeracioacuten de moacutedulo base 2 es decir 2n donde n es el nuacutemero 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 232-1 = 4294967295 (el valor 0 ocupa una posicioacuten de las 4294967295 posibles)

sect5 Caraacutecter

La cabecera ltlimitshgt contiene una macro CHAR_BIT que se expande a una constante entera que indica el nuacutemero de bits de un tipo caraacutecter (char) que se almacenan en 1 byte es decir siempre ocurre que sizeof(char) == 1 Esta es la definicioacuten ANSI de byte en CC++ es decir la memoria requerida para almacenar un caraacutecter pero este byte no corresponde necesariamente con el byte de la maacutequina

El valor de CHAR_BIT es al menos 8 la mayoriacutea de los ordenadores modernos usan bytes de 8 bits (octetos) pero existen algunos con otros tamantildeos por ejemplo 9 bits Ademaacutes algunos procesadores especialmente de sentildeal (Digital Signal Processors) que no pueden acceder de forma eficiente a la memoria en tamantildeos 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 maacutes comunes actualmente procesadores de sentildeal donde todos los tipos enteros incluyendo los long son de 32 bits

signed char Valores entre SCHAR_MIN lt= X lt= SCHAR_MAX

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 17: 05 Programacion Lenguaje c++

Siendo SCHAR_MIN lt= -127 y SCHAR_MAX gt= 127 La mayoriacutea de los compiladores utilizan un valor SCHAR_MIN de -128 pero no es exigido por el estaacutendar

unsigned char Valores entre 0 lt= x lt= UCHAR_MAX

Se exige que UCHAR_MAX gt= 255 si CHAR_BIT es mayor que 8 se exige que UCHAR_MAX = 2 CHAR_BIT - 1 De forma que una implementacioacuten que utilice un caraacutecter de 9 bits puede almacenar valores entre 0 y 511 en un unsigned char

char (valor caraacutecter a secas plain char) Valores entre CHAR_MIN lt= X lt= 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 limitsh que acompantildea 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

sect6 Fraccionarios

La representacioacuten y rango de valores de los nuacutemeros fraccionarios depende del compilador Es decir cada implementacioacuten de C++ es libre para definirlos La mayoriacutea utiliza el formato estaacutendar

de la IEEE (Institute of Electrical and Electronics Engineers) para este tipo de nuacutemeros ( 224a)

float y double son tipos fraccionarios de 32 y 64 bits respectivamente El modificador long puede utilizarse con el tipo double declarando entonces un nuacutemero 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 117549e-38 lt= |X| lt= 340282e+38

double 64 222507e-308 lt= |X| lt= 179769e+308

long double 80 337e-4932 lt= |X| lt= 118e4932

Generalmente los compiladores C++ incluyen de forma automaacutetica la libreriacutea matemaacutetica de punto

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 18: 05 Programacion Lenguaje c++

flotante si el programa utiliza valores fraccionarios [4] Builder utiliza los siguientes liacutemites definidos en el fichero ltvalueshgt

float Valores entre MINFLOAT lt= |X| lt= MAXFLOAT (valores actuales entre pareacutentesis)

MINFLOAT (117549e-38)

MAXFLOAT (340282e+38)

FEXPLEN Nuacutemero de bits en el exponente (8)

FMAXEXP Valor maacuteximo permitido para el exponente (38)

FMAXPOWTWO Maacutexima potencia de dos permitida (127)

FMINEXP Valor miacutenimo permitido para el exponente (-37)

FSIGNIF Nuacutemero de bits significativos (24) double Valores entre MINDOUBLE lt= |X| lt= MAXDOUB

double Valores entre MINDOUBLE lt= |X| lt= MAXDOUBLE

MINDOUBLE (222507e-308)

MAXDOUBLE (179769e+308)

DEXPLEN Nuacutemero de bits en el exponente (11)

DMAXEXP Valor maacuteximo permitido para el exponente (308)

DMAXPOWTWO Maacutexima potencia de dos permitida (1023)

DMINEXP Valor miacutenimo permitido para el exponente (-307)

DSIGNIF Nuacutemero de bits significativos (53)

sect7 La clase numeric_limits

La Libreriacutea Estaacutendar C++ contiene una clase numeric_limits que contiene informacioacuten sobre los escalares Existen subclases para cada tipo fundamental enteros (incluyendo los booleanos) y fraccionarios Esta clase engloba la informacioacuten contenida en los ficheros de cabecera ltclimitsgt y ltcfloatgt ademaacutes de incluir informacioacuten que no estaacute contenida en ninguna otra cabecera A continuacioacuten se incluye un ejemplo que muestra el espacio disponible (bits) para codificar el valor de los tipos fundamentales (mantisa) asiacute como la salida proporcionada en un caso concreto

include ltcstdlibgtinclude ltiostreamgtinclude ltlimitsgt

int main(int argc char argv[]) stdcout ltlt Mantisa de un char ltlt stdnumeric_limitsltchargtdigits ltlt n stdcout ltlt Mantisa de un unsigned char ltlt stdnumeric_limitsltunsigned chargtdigits ltlt n

stdcout ltlt Mantisa de un short ltlt stdnumeric_limitsltshortgtdigits ltlt n stdcout ltlt Mantisa de un unsigned short ltlt stdnumeric_limitsltunsigned shortgtdigits ltlt n

stdcout ltlt Mantisa de un int

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 19: 05 Programacion Lenguaje c++

ltlt stdnumeric_limitsltintgtdigits ltlt n stdcout ltlt Mantisa de un unsigned int ltlt stdnumeric_limitsltunsigned intgtdigits ltlt n

stdcout ltlt Mantisa de un long ltlt stdnumeric_limitsltlonggtdigits ltlt n stdcout ltlt Mantisa de un unsigned long ltlt stdnumeric_limitsltunsigned longgtdigits ltlt n

stdcout ltlt Mantisa de un float ltlt stdnumeric_limitsltfloatgtdigits ltlt n stdcout ltlt Longitud de un double ltlt stdnumeric_limitsltdoublegtdigits ltlt n stdcout ltlt Longitud de un long double ltlt stdnumeric_limitsltlong doublegtdigits ltlt n

stdcout ltlt Mantisa de un long long ltlt stdnumeric_limitsltlong longgtdigits ltlt n stdcout ltlt Mantisa de un unsigned long long ltlt stdnumeric_limitsltunsigned long longgtdigits ltlt n

return 0

Salida en una maacutequina Intel con el compilador GNU g++ 342 para Windows

Mantisa de un char 7Mantisa de un unsigned char 8Mantisa de un short 15Mantisa de un unsigned short 16Mantisa de un int 31Mantisa de un unsigned int 32Mantisa de un long 31Mantisa de un unsigned long 32Mantisa de un float 24Longitud de un double 53Longitud de un long double 64Mantisa de un long long 63Mantisa de un unsigned long long 64

Temas relacionados

Operador sizeof ( 4913)

Alineacioacuten interna ( 461)

224a Formas de representacioacuten binaria de las magnitudes numeacutericas

sect1 Presentacioacuten de un problema

Antes de entrar en detalles haremos un pequentildeo inciso para sentildealar el principal problema que entrantildea la representacioacuten de cantidades numeacutericas en los ordenadores digitales

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 20: 05 Programacion Lenguaje c++

En el apartado dedicado al Ordenador digital ( 01) recordamos que la informacioacuten estaacute representada en forma digitalizada Es decir reducida a cantidades discretas representadas por nuacutemeros y estos a su vez expresados en formato binario Como la serie de los nuacutemeros reales tiene infinitos nuacutemeros (desde -Infinito a +Infinito [0]) es imposible su representacioacuten completa en cualquier sistema de representacioacuten Ademaacutes aunque un nuacutemero puede contener una cantidad indefinida de cifras los bits destinados a almacenarlas son necesariamente limitados [3] Como consecuencia en la informaacutetica real solo es posible utilizar un subconjunto finito del conjunto de los nuacutemeros reales

El rango y precisioacuten (nuacutemero de cifras) del subconjunto de nuacutemeros que pueden representarse en una maacutequina dada dependen de la arquitectura y para el lenguaje C++ depende ademaacutes del

compilador ( 224) Puesto que existen ocasiones en que las aplicaciones informaacuteticas necesitan manejar nuacutemeros muy grandes y muy pequentildeos se ha derrochado mucho ingenio para conseguir representaciones binarias con la maacutexima precisioacuten en el miacutenimo espacio y para que estos formatos puedan ser manipulados por implementaciones hardware lo maacutes simples posible Tambieacuten 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 maacuteximo de precisioacuten en los caacutelculos

Hay que recordar que incluso manejando cantidades dentro del rango pueden presentarse faacutecilmente situaciones con errores de bulto que seriacutean catastroacuteficas en determinadas circunstancias Por ejemplo en caacutelculos de ingenieriacutea Supongamos una situacioacuten en que el compilador C++ tiene que multiplicar una serie de cantidades definidas en la maacutexima precisioacuten

long double r = x y z

y que el orden de ejecucioacuten x y z es en este caso de izquierda a derecha Si en un momento dado los valores de x e y son suficientemente pequentildeos (proacuteximos al liacutemite inferior permitido para long double) el primer producto x y puede resultar inferior al miacutenimo que puede representar el compilador originaacutendose un underflow El resultado intermedio seriacutea cero y su producto por z tambieacuten cero con independencia del valor de esta uacuteltima variable (que suponemos grande) El valor cero del resultado r podriacutea a su vez propagarse inadvertidamente a otros caacutelculos Observe tambieacuten que si la operacioacuten 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 sensacioacuten que el coacutedigo es seguro con los mismos valores de las variables No es necesario sentildealar que este tipo de errores pueden acarrear consecuencias desastrosas Por ejemplo en caacutelculos de ingenieriacutea Para que el lector pueda formarse visioacuten maacutes tangible del problema le invito a visitar esta interesante paacutegina (en ingleacutes) Some disasters attributable to bad numerical computing

Otros tipos de errores de precisioacuten son maacutes insidiosos auacuten Para comprobarlo pruebe el lector este sencillo programa

include ltiostreamhgtint main (void) float f = 3070 M1 if (f == 3070) cout ltlt Igual ltlt endl M2 else cout ltlt Desigual ltlt endl return 0

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 21: 05 Programacion Lenguaje c++

La salida con el compilador Borland C++ 55 es

Desigual

La explicacioacuten es que aquiacute las constantes 30 y 70 han sido consideradas como nuacutemeros de coma flotante de doble precisioacuten (double) y el resultado de 3070 que es del mismo tipo sufre una

conversioacuten estrechante ( 499) a float con peacuterdida de precisioacuten antes de la asignacioacuten a f en M1 El mismo valor 3070 (double) es comparado en M2 con el de f (float) con el resultado de que no son iguales

La comprobacioacuten de las afirmaciones anteriores es muy sencilla basta modificar la liacutenea M1 del programa por alguna de estas dos

double f = 3070 M11long double f = 3070 M12

Despueacutes de la sustitucioacuten en ambos casos la salida es

Igual

Si deseamos mantener la variable f como un float una posible solucioacuten seriacutea cambiar la sentencia

M2 (intente encontrar la explicacioacuten por siacute mismo en 323c)

if (f == 30f70f) cout ltlt Igual ltlt endl M21

En el apartado que dedicamos a las conversiones estaacutendar ( 225) encontraraacute explicacioacuten del porqueacute no funcionariacutea ninguna de las versiones siguientes

if (f == 30f70) cout ltlt Igual ltlt endl M22if (f == 3070f) cout ltlt Igual ltlt endl M23

sect2 Formas de representacioacuten binaria

La necesidad de representar no solo enteros naturales (positivos) sino tambieacuten valores negativos e incluso fraccionarios (racionales) ha dado lugar a diversas formas de representacioacuten binaria de los nuacutemeros 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 maacutes adelante

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 22: 05 Programacion Lenguaje c++

sect21 Coacutedigo binario sin signo

Las cantidades se representan de izquierda a derecha (el bit maacutes significativo a la izquierda y el menos significativo a la derecha) como en el sistema de representacioacuten decimal Los bits se representan por ceros y unos cero es ausencia de valor uno es valor Por ejemplo la representacioacuten del decimal 33 es 100001

Si utilizamos un octeto para representar nuacutemeros pequentildeos y mantenemos la costumbre de separar las cifras en grupos de 4 para mejorar la legibilidad su representacioacuten es 0010 0001

Con este sistema todos los bits estaacuten disponibles para representar una cantidad por consiguiente un octeto puede albergar nuacutemeros de rango

0 lt= n lt= 255

Nota aunque la representacioacuten interna (en memoria) suele tener el bit maacutes significativo a la izquierda el almacenamiento en dispositivos externos (disco) no tiene que ser forzosamente igual Existen casos en los que la representacioacuten externa es justamente al contrario el bit maacutes significativo a la derecha lo que supone que estos bytes deben ser invertidos durante los procesos de lecturaescritura Existen casos en que una misma aplicacioacuten sigue distintos criterios para la alineacioacuten del bit maacutes significativo seguacuten el tipo de dato que se escribe en el disco Por supuesto la situacioacuten se complica cuando el nuacutemero estaacute representado por maacutes de un octeto En este caso tambieacuten puede jugarse con el orden de escritura de los octetos Veacutease

al respecto Orden de Almacenamiento ( 226a)

sect22 Coacutedigo binario con signo

Ante la necesidad de tener que representar enteros negativos se decidioacute reservar un bit para representar el signo Es tradicioacuten destinar a este efecto el bit maacutes significativo (izquierdo) este bit es 0 para valores positivos y 1 para los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+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 nuacutemeros en el rango

- 127 lt= n lt= 127

El sistema anterior se denomina coacutedigo binario en magnitud y signo Aparentemente es el primero y maacutes sencillo de los que se pueden discurrir ademaacutes de ser muy simple para codificar y decodificar Sin embargo la circuiteriacutea electroacutenica necesaria para implementar con ellos operaciones aritmeacuteticas es algo complicada por lo que se dispusieron otros sistemas que se revelaron maacutes simples en este sentido

sect23 Coacutedigo binario en complemento a uno

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 23: 05 Programacion Lenguaje c++

En este sistema los nuacutemeros positivos se representan como en el sistema binario en magnitud y signo es decir siguiendo el sistema tradicional aunque reservando el bit maacutes significativo que debe ser cero Para los nuacutemeros negativos se utiliza el complemento a uno que consiste en tomar la representacioacuten del correspondiente nuacutemero positivo y cambiar los bits 0 por 1 y viceversa (el bit maacutes significativo del nuacutemero positivo que es cero pasa ahora a ser 1) En capiacutetulo dedicado

a los Operadores de manejo de bits ( 493) veremos que C++ dispone de un operador especiacutefico para realizar estos complementos a uno

Como puede verse en este sistema el bit maacutes significativo sigue representando el signo y es siempre 1 para los nuacutemeros negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110

sect24 Coacutedigo binario en complemento a dos

En este sistema los nuacutemeros positivos se representan como en el anterior reservando tambieacuten el bit maacutes significativo (que debe ser cero) para el signo Para los nuacutemeros negativos se utiliza un sistema distinto denominado complemento a dos en el que se cambian los bits que seriacutean 0 por 1 y viceversa y al resultado se le suma uno

Este sistema sigue reservando el bit maacutes significativo para el signo que sigue siendo 1 en los negativos Por ejemplo la representacioacuten de 33 y -33 seriacutea

+33 0010 0001

-33 1101 1110 + 0000 0001 1101 1111

El hardware necesario para implementar operaciones aritmeacuteticas con nuacutemeros representados de este modo es mucho maacutes sencillo que el del complemento a uno por lo que es el sistema maacutes ampliamente utilizado [8] Precisamente esta forma de representacioacuten interna es la respuesta al

problema presentado en la paacutegina 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 coacutedigo binario en complemento a dos (Fig 1)

Precisamente los procesadores Intel 8088 sus descendientes y compatibles almacenan internamente los nuacutemeros en esta forma y para facilitar la raacutepida identificacioacuten del signo

disponen de un bit (SF) en el registro de estado ( H32) que indica si el resultado de una operacioacuten tiene a 1 o a 0 el bit maacutes significativo

sect3 Nuacutemeros fraccionarios

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 24: 05 Programacion Lenguaje c++

A continuacioacuten exponemos brevemente los detalles del formato utilizado para representacioacuten interna de los nuacutemeros fraccionarios Es decir coacutemo son representados en forma binaria los nuacutemeros con decimales

sect31 Notacioacuten cientiacutefica

En ciencias puras y aplicadas es frecuente tener que utilizar nuacutemeros muy grandes y muy pequentildeos Para facilitar su representacioacuten se desarrolloacute la denominada notacioacuten cientiacutefica (tambieacuten denominada engineering notation en la literatura inglesa) en la que el nuacutemero es representado mediante dos cantidades la mantisa y la caracteriacutestica separadas por la letra Ee

Nota en esta notacioacuten las letras Ee no tienen nada que ver con la constante e (271828182) base de los logaritmos Neperianos Es meramente un siacutembolo para separar dos partes de una expresioacuten (podriacutea haberse utilizado cualquier otro)

La mantisa es la parte significativa del nuacutemero (las cifras significativas que se conocen [5] ) La caracteriacutestica es un nuacutemero entero con signo que indica el nuacutemero de posiciones que hay que desplazar a la derecha o a la izquierda el punto decimal (expliacutecito o impliacutecito) Por la razoacuten sentildealada (que la caracteriacutestica indica la posicioacuten del punto decimal) esta representacioacuten es tambieacuten conocida como de punto flotante

La caracteriacutestica puede ser interpretada tambieacuten como la potencia de 10 por la que hay que multiplicar la mantisa para obtener el nuacutemero Es decir si V es el nuacutemero m la mantisa y c la caracteriacutestica resulta V = m 10c Esta notacioacuten (matemaacutetica tradicional) es equivalente a V = mec= mEc en notacioacuten cientiacutefica

Ejemplos

Expresioacuten Valor 2345e6 2345 10^6 == 23450000-2e-5 -20 10^-5 == -0000023E+10 30 10^10 == 30000000000-09E34 -009 10^34 == -900000000000000000000000000000000

sect311 Notacioacuten normalizada

Puede verse que la notacioacuten cientiacutefica permite varias formas para un mismo nuacutemero Por ejemplo para el nuacutemero 1231 seriacutean entre otras

1231e01231e-21231e-11231e101231e2001231e3

La representacioacuten de nuacutemeros fraccionarios que necesita de una menor cantidad de diacutegitos en notacioacuten cientiacutefica es aquella que utiliza un punto decimal despueacutes de la primera cifra significativa de la mantisa Esta forma de representacioacuten se denomina normalizada (el resto de formas posibles se denominan subnormales) En el caso del nuacutemero anterior la notacioacuten normalizada seriacutea 1231e1

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 25: 05 Programacion Lenguaje c++

Nota observe que en esta forma el exponente es miacutenimo y representa la utilizacioacuten de la maacutexima cantidad de cifras significativas en la mantisa de forma que para una cantidad de cifras determinada es la que permite mayor precisioacuten

Seguacuten lo anterior la mantisa m de la forma normalizada de un nuacutemero distinto de cero puede expresarse como suma de una parte entera j y otra fraccionaria f m = j + f Siendo j un diacutegito decimal distinto de cero (1-9) y f una cantidad menor que la unidad denominada fraccioacuten decimal De forma el nuacutemero puede ser expresado mediante

V = plusmn 0 (j + f) 10c sect711a

En el caso del ejemplo esta representacioacuten seriacutea + (1+ 0231) 101

Nota cuando el nuacutemero estaacute representado en binario la mantisa tambieacuten puede ser representada en la forma m = j + f siendo ahora j un diacutegito 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 fraccioacuten binaria) Si asumimos que la representacioacuten estaacute siempre precedida de un 1 este bit puede suponerse impliacutecito y ocupar su posicioacuten para expresar un bit adicional de la fraccioacuten Esta representacioacuten se denomina designificando normalizado y supone que solo se almacena la fraccioacuten decimal f de la mantisa (como puede ver se trata de aprovechar al maacuteximo el espacio disponible)

La expresioacuten binaria equivalente a la anterior (sect711a) es

V = plusmn 0 (1+ f) 2c sect711b

sect32 Representacioacuten binaria

La informaacutetica que en sus comienzos estaba nutrida por profesionales de otras disciplinas teacutecnicas y cientiacuteficas adoptoacute una variacioacuten de la notacioacuten cientiacutefica para representacioacuten interna (binaria) de las cantidades fraccionarias Por esta razoacuten es costumbre que los nuacutemeros fraccionarios sean denominados de coma o punto flotante [1] (floating-point) y a las operaciones aritmeacuteticas realizadas con ellos operaciones de punto flotante FLOP (FLoating -point- OPeration)

Para los nuacutemeros de punto flotante se ha asignando un bit para el signo un cierto nuacutemero de bits para representar el exponente y el resto para representar la parte maacutes significativa del nuacutemero (la mantisa) aunque en este caso la caracteriacutestica 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 caracteriacutestica c mediante V = m 2c

Asiacute pues la representacioacuten binaria de los nuacutemeros fraccionarios utiliza tres componentes

Signo S es un nuacutemero binario de un bit representando el signo (0 == positivo 1 == negativo) Generalmente es el bit maacutes significativo (de la izquierda)

Exponente c es un nuacutemero binario representando la potencia de 2 por la que hay que multiplicar la mantisa Cuanto mayor pueda ser este exponente mayor seraacute el valor absoluto del mayor nuacutemero que puede ser representado

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 26: 05 Programacion Lenguaje c++

Mantisa m es un nuacutemero binario que representa las cifras significativas del nuacutemero Por supuesto cuanto mayor sea la precisioacuten deseada (maacutes 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 nuacutemero total de bits que se utilizaraacute en la representacioacuten) El signo estaacute representado por el uacuteltimo bit (bit N-1) A continuacioacuten 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

lt--------------- N --------------gt Espacio total de almacenamiento (bits)S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM Distribucioacuten1 lt-- e -gt lt---------- m --------gt Longitud de campos| | | | |N-1m+e m m-1 0 Numeracioacuten de los bits

Es interesante observar que los desplazamientos (Shift) izquierdo o derecho ( 493) de los bits de la mantisa equivalen respectivamente a multiplicar o dividir por dos su valor lo que podriacutea compensarse disminuyendo o aumentando el valor del exponente en una unidad Para evitar

ambiguumledades se recurre a la normalizacioacuten ya sentildealada de forma que se minimiza el valor del exponente y cualquier valor V (distinto de cero) puede ser representado mediante la fraccioacuten normalizada f de su mantisa (f 0) con lo que puede ser representado en la forma

V = plusmn 2c (1 + f)

Desgraciadamente no existe una absoluta unidad de criterio respecto a los detalles Seguacuten el Estaacutendar la representacioacuten (interna) y rango de valores de los nuacutemeros fraccionarios

depende del compilador ( 224) Cada implementacioacuten 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 tamantildeos distintos para los nuacutemeros fraccionarios de 32

64 y 80 bits respectivamente seguacuten el formato de la IEEE La representacioacuten interna es la indicada en la figura 2

sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 27: 05 Programacion Lenguaje c++

La representacioacuten binaria de punto flotante utilizada en los computadores digitales es muy eficiente y se adapta bastante bien a la mayoriacutea de las circunstancias especialmente en caacutelculos teacutecnicos y cientiacuteficos (aritmeacutetica de punto flotante) Sin embargo no estaacute exenta de problemas derivados del hecho de que -como hemos sentildealado al principio del capiacutetulo- las posibilidades (finitas) de representacioacuten del ordenador no pueden cubrir la totalidad (infinita) de los nuacutemeros reales Esta dificultad es especialmente molesta en los caacutelculos denominados de gestioacuten comerciales o financieros que utilizan nuacutemeros fraccionarios de base 10 Por ejemplo caacutelculos de precios de conversioacuten de moneda o del resultado de varias pesadas Este tipo de aplicaciones utilizan (o deberiacutean utilizar) lo que se denomina aritmeacutetica decimal (que realizamos habitualmente con un papel y un laacutepiz) en la que por ejemplo 111567 - 111 = 0567

Cuando en los programas CC++ se utilizan variables fraccionarias para almacenar este tipo de variables (nuacutemeros fraccionarios de base 10) se presentan problemas que en principio suelen desconcertar al principiante Como botoacuten 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 estaacute 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=111567 b=111 c c=a-b and you will receive a=11156699999999999 b=11100000000000000 c=056699999999999307 instead =gt a=111567 b=111 c=0567I found more fractional numbers that show a similar errorThe problem is that the fractional numbers and their actions can not be produced otherwiseI try this example in all MS Visual CC++ compilers from version 60 to version 2008 and the bug appears everywhereRegards

Mejor que puedan hacerlo mis palabras en la paacutegina Decimal Arithmetic FAQ de Mike Cowlishaw de IBM encontraraacute el lector una amplia explicacioacuten del porqueacute de estos aparentemente erroacuteneos resultados Como siacutentesis indicaremos aquiacute que para prevenir estos problemas algunos lenguajes incluyen un tipo especial de variable decimal y funciones y operadores especiacuteficos que permiten realizar caacutelculos de aritmeacutetica decimal En lo que respecta a C++ debido a sus oriacutegenes cientiacuteficos por el momento no dispone de forma nativa de ninguacuten tipo decimal por lo que las aplicaciones que necesitan de estos de caacutelculos deben recurrir a libreriacuteas especiacuteficas

Nota aunque por el momento (Septiembre 2008) el lenguaje C++ no dispone de ninguacuten tipo decimal el comiteacute de estandarizacioacuten ya estaacute trabajando en una especificacioacuten que se ajusta al estaacutendar IEEE 754R (ver Decimal Types for C++) Seguramente se definiraacuten tres tipos decimales de punto flotante de 32 64 y 128 bits respectivamente Tambieacuten es previsible que del mismo modo que los procesadores modernos incluyen unidades hardware (FPU) para caacutelculos con nuacutemeros de punto flotante de codificacioacuten binaria en un futuro proacuteximo se implementen tambieacuten en hardware unidades para caacutelculos con nuacutemeros de punto flotante de codificacioacuten decimal ya que las rutinas software actuales para tratar la aritmeacutetica decimal son considerablemente maacutes lentas que las de aritmeacutetica binaria

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 28: 05 Programacion Lenguaje c++

sect33 El Estaacutendar IEEE 754

En 1985 el IEEE (Institute of Electrical and Electronics Engineers IEEE Standards Site) publicoacute la norma IEEE 754 Una especificacioacuten relativa a la precisioacuten y formato de los nuacutemeros de punto flotante Incluye una lista de las operaciones que pueden realizarse con dichos nuacutemeros entre las que se encuentran las cuatro baacutesicas suma resta multiplicacioacuten divisioacuten Asiacute como el resto la raiacutez cuadrada y diversas conversiones Tambieacuten incluye el tratamiento de circunstancias excepcionales como manejo de nuacutemeros infinitos y valores no numeacutericos

Nota en Junio de 2008 se aproboacute una revisioacuten de dicho Estaacutendar conocido como IEEE 754R que incluye recomendaciones para la aritmeacutetica de punto flotante de codificacioacuten decimal La explicacioacuten que sigue se refiere exclusivamente a la codificacioacuten de nuacutemeros de punto flotante de codificacioacuten binaria (versioacuten inicial del estaacutendar)

Dado que la mayoriacutea de compiladores utilizan este formato para la representacioacuten de los nuacutemeros fraccionarios es maacutes que probable que el informaacutetico se tope con ellos en alguna ocasioacuten por lo que dedicaremos unas liacuteneas a describir sus caracteriacutesticas principales [7]

En realidad la adopcioacuten de este estaacutendar por parte de los compiladores se debe a que el hardware que los sustenta tambieacuten lo sigue De hecho esta es la representacioacuten interna utilizada por los procesadores ya que en la actualidad (2002) praacutecticamente el 100 de las maacutequinas que se fabrican siguen el Estaacutendar en lo que se refiere al tratamiento y operacioacuten de los nuacutemeros de punto flotante

El proceso de estandarizacioacuten de las operaciones de punto flotante comenzoacute paralelamente al desarrollo por Intel (1976) de lo que seriacutean los coprocesadores aritmeacuteticos 8087 A partir de entonces podiacutea asegurarse que X + (Y + Z) proporcionariacutea el mismo resultado que (X + Y) + Z con cualquier compilador y cualquier terna de nuacutemeros No olvidemos que es precisamente a partir de la aparicioacuten de los coprocesadores matemaacuteticos cuando la realizacioacuten de operaciones con nuacutemeros fraccionarios se encomiendan al silicio (hardware) en vez de a rutinas software que hasta entonces eran especiacuteficas de cada compilador y cada plataforma [9]

Los coprocesadores matemaacuteticos denominados tambieacuten 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 uacuteltimo fue el primero que proporcionoacute soporte completo para la versioacuten final del Estaacutendar) A partir del 80486 Intel incorporoacute el coprocesador matemaacutetico junto con el principal con lo que su existencia dejoacute de ser opcional y se convirtioacute en estaacutendar Estas unidades de punto flotante no solo realizan las operaciones aritmeacuteticas baacutesicas (suma resta multiplicacioacuten y divisioacuten) Tambieacuten incluyen operaciones como la raiacutez cuadrada redondeo resto y funciones trascendentes como seno coseno tangente cotangente logaritmacioacuten y exponenciacioacuten

sect331 Formatos

En lo referente a la representacioacuten binaria de los nuacutemeros el Estaacutendar utiliza tres formatos denominados de precisioacuten simple (equivalente al floatC++) doble (equivalente al double) y extendida (que podriacutea corresponder al long double) aunque existe un cuarto denominado de cuaacutedruple precisioacuten no contemplado en la norma que es tambieacuten un estaacutendar de facto Los tamantildeos son los siguientes

Precisioacuten Bytes bits

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 29: 05 Programacion Lenguaje c++

Simple 4 32

Doble 8 64

Extendida gt= 10 gt= 80

Cuaacutedruple 16 128

En todos los casos se utilizan tres campos para describir el nuacutemero 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 nuacutemero binario con signo seguacuten una regla que como veremos a continuacioacuten depende del rango y del formato

El significando n se almacena en forma normalizada salvo cuando se representan significados especiales (ver a continuacioacuten)

El esquema de la distribucioacuten utilizada para los de simple y doble precisioacuten es el indicado

Espacio (bits) 1 lt-- 8 -gt lt-------- 23 ---------gt

Simple precisioacuten S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 31 30 23 22 0

Espacio (bits) 1 lt--- 11 --gt lt-------------------- 52 --------------------------gt

Doble precisioacuten S EEEEEEEEEEE MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

posicioacuten 63 62 52 51 0

Como veremos a continuacioacuten la interpretacioacuten 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 capiacutetulo Por ejemplo se considera la existencia de valores especiales +Infinito -Infinito NaN (Not a Number) y una representacioacuten especial para el valor cero lo que ha obligado a definir reglas especiales de aritmeacutetica cuando estos valores intervienen en operaciones con

valores normales o entre ellos A lo anterior se antildeade que existen dos tipos de representacioacuten para los valores no especiales cada uno con sus reglas son las denominadas formas normalizadas y subnormales

Empezaremos por la representacioacuten de los significados especiales

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 30: 05 Programacion Lenguaje c++

sect332 Significados especiales

Definicioacuten 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 razoacuten se convino que el cero se representariacutea 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 auacuten permite distinguir +0 de -0 De hecho el compilador lo hace asiacute permitiendo distinguir divisiones por cero con resultado

+4 y -4 Sin embargo el Estaacutendar establece que al comparar ambos ceros el resultado debe indicar que son iguales

Infinitos se ha convenido que cuando todos los bits del exponente estaacuten a 1 y todos los del significando a 0 el valor es +- infinito (seguacuten el valor S) Esta distincioacuten ha permitido al Estaacutendar definir procedimientos para continuar las operaciones despueacutes que se ha alcanzado uno de estos valores (despueacutes de un overflow) Ejemplo

0 11111111 00000000000000000000000 = +Infinito

1 11111111 00000000000000000000000 = -Infinito

Valores no-normalizados (denominados tambieacuten subnormales) En estos casos no se asume que haya que antildeadir 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 tratariacutea de un cero) Ejemplo

1 00000000 00100010001001010101010

Valores no-numeacutericos 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 01 del bit maacutes significativo del significando QNaN tiene el primer bit a 1 y significa

Indeterminado SNaN tiene el primer bit a 0 y significa Operacioacuten no-vaacutelida Ejemplo

0 11111111 10000100000000000000000 = QNaN

1 11111111 00100010001001010101010 = SNaN

sect333 Significados normales

La representacioacuten de nuacutemeros no incluidos en los casos especiales (distintos de cero que no sean infinitos ni valores no-numeacutericos) sigue reglas distintas seguacuten la precisioacuten y el tipo de representacioacuten (normal o subnormal)

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 31: 05 Programacion Lenguaje c++

Para calcular el valor V de un nuacutemero binario IEEE 754 de exponente E y mantisa M debe recordarse que esta uacuteltima representa una fraccioacuten binaria (no decimal -) en notacioacuten

normalizada Es decir hay que sumarle una unidad En estas condiciones si por ejemplo el contenido de la mantisa es 0254 se supone que M = 1 + 0254 Por su parte el caacutelculo de la fraccioacuten binaria es anaacutelogo al de la fraccioacuten decimal Recordemos que la fraccioacuten decimal 1304 (01304) equivale a 1101 + 3102 + 0103 + 4104 Del mismo modo la fraccioacuten binaria 1101 (01101) equivale a 121+ 122 + 023 + 124 = 08125

Teniendo en cuenta estas observaciones el valor decimal V de una representacioacuten binaria estaacutendar puede calcularse mediante las siguientes foacutermulas

Nota en las foacutermulas que siguen puede suponerse sustituido el signo plusmn por la expresioacuten (-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 estaacutendar (por ejemplo los datos recibidos de un instrumento de medida) ademaacutes de las consideraciones anteriores tambieacuten hay que tener en cuenta el orden (Endianness) en

que pueden recibirse los datos ( 226a)

sect333a Simple precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-127

Es evidente que en estos casos E es un nuacutemero tal que 0 lt E lt 255 (28 - 2 posibilidades) ya que en caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) Asiacute pues E se mueve en el intervalo 1 a 254 (ambos inclusive) Al restarle 127 queda un rango entre 2-126 y 2127

Ejemplos

0 00001100 11010000000000000000000

Signo = + E = 12 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + (1 + 08125) 212-127 = 18125 middot 2-115 = 43634350 middot 10-35

1 10001101 01101000000000000000000

Signo = - E = 141 M = 021 + 122 + 123 + 024 + 125 + 0 + = 040625

V = - (1 + 040625) 2141 = - 140625 middot 214 = - 23040

sect333b Simple precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-127

Como se ha sentildealado en estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga al caso anterior

Ejemplo

0 00000000 11010000000000000000000

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 32: 05 Programacion Lenguaje c++

Signo = + E = 0 M = 121 + 122 + 023 + 124 + 0 + 0 + = 08125

V = + 08125 middot 2-127 = 477544580 middot 10-39

sect333c Doble precisioacuten representacioacuten normalizada

V == plusmn (1 + M) 2E-1023

En estos casos es 0 lt E lt 2047 En caso contrario se estariacutea en alguno de los significados especiales (todos los bits del exponente a 0 o a 1) La operatoria es anaacuteloga a la de simple precisioacuten con la diferencia de que en este caso se dispone de maacutes espacio para representar la mantisa M y el exponente E (52 y 11 bits respectivamente)

sect333d Doble precisioacuten representacioacuten subnormal

V == plusmn (0 + M) 2-1023

En estos casos es E = 0 y M es distinto de cero La operatoria es anaacuteloga a la sentildealada en casos anteriores

sect334 Conversor automaacutetico de formatos

Con objeto de facilitar al lector la realizacioacuten de algunos ejemplos que le permitan terminar de comprender y comprobar estas reglas en la paacutegina adjunta se incluye un convertidor automaacutetico de formatos que permite introducir un nuacutemero en formato decimal (incluso en notacioacuten cientiacutefica) y comprobar el aspecto de su almacenamiento binario seguacuten el Estaacutendar IEEE 754

en simple y doble precisioacuten ( Conversor)

Nota en la libreriacutea de ejemplos ( 941) se incluye un programa C++ que realiza la misma conversioacuten que el anterior (realizado en javascript) aunque estaacute limitado a la representacioacuten de nuacutemeros en precisioacuten simple

sect335 Operaciones con nuacutemeros especiales

La tabla adjunta establece las reglas que seguacuten el Estaacutendar IEEE 754 rigen las operaciones en que intervienen magnitudes de significado especial

Operacioacuten Resultado

cualquiera con NaN NaN

n plusmn Infinito plusmn 0

plusmn Infinito plusmnInfinito plusmn Infinito

Infinito + Infinito Infinito

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 33: 05 Programacion Lenguaje c++

Infinito - Infinito NaN

plusmn Infinito 0 NaN

plusmn Infinito plusmn Infinito NaN

plusmn0 plusmn0 NaN

plusmnn plusmn0 plusmn Infinito

sect336 Rango de la representacioacuten IEEE 754

Exceptuando los valores especiales infinitos estaacute claro que para la simple precisioacuten los valores miacutenimos y maacuteximos que pueden representarse de forma estandarizada son

Vmin = - (0 + M) 2-127 donde M sea el valor miacutenimo de la mantisa distinto de cero Su representacioacuten es

1 00000000 00000000000000000000001

TraduccioacutenSigno = -E = 0M = 1223 = 2-23 = 119209289551 middot 10-7 Vmin = 2-23 middot 2-127 = 2-150 = 700649232163 middot 10-46

En la praacutectica solo se consideran las representaciones normales de forma que la forma normal maacutes pequentildea corresponde a la siguiente representacioacuten binaria

1 00000001 00000000000000000000001

TraduccioacutenSigno = -E = 1M = 1223 = 2-23 Vmin = -(1 + 2-23) 21-127 = -(1 + 2-23) 2-126 = -117549449 middot 10-38

Es significativo que el proacuteximo valor en escala ascendente es

1 00000001 00000000000000000000010 Signo = -E = 1M = 1222 = 2-22 V = -(1 + 2-22) 2-126

La diferencia entre ambos es Imin = V - Vmin = 2-22 - 2-23 = 1192092 middot 10-7 lo que representa algo maacutes de una parte en 10 millones Es importante recordar que esta seraacute la mejor precisioacuten que podraacute alcanzarse en los procesos con nuacutemeros de coma flotante de simple precisioacuten En la praacutectica la precisioacuten alcanzada seraacute auacuten menor dependiendo de la suerte que tengamos y del nuacutemero de operaciones encadenadas que se hayan realizado (los errores pueden ser aleatorios -que tienden a anularse entre siacute- o acumulativos)

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 34: 05 Programacion Lenguaje c++

El valor maacuteximo en la representacioacuten normal corresponde a la forma binaria

0 11111110 11111111111111111111111 Signo = +E = 254M = 121 + 122 + + 1223 = 09999999999Vmax = (1 + 0999999) 2254-127 = (199999999) 2127 = 340282346 middot 1038

224b Formas de representacioacuten simboacutelica

sect1 Sinopsis

En el epiacutegrafe dedicado al Ordenador Electroacutenico Digital ( 01) se sentildealoacute que para la representacioacuten de los datos textuales (alfanumeacutericos) se utilizan los sistemas de codificacioacuten Us-ASCII y Unicode El lenguaje y el sistema de escritura variacutean 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 ( 221a) que es el sistema exigido como entrada por los compiladores (

14)

Sin embargo la representacioacuten simboacutelica de datos numeacutericos (como aparecen representados estos nuacutemeros en el texto del coacutedigo fuente) no siempre ocurre en formato decimal el sistema de numeracioacuten Occidental como cabriacutea esperar Por una larga tradicioacuten informaacutetica de cuando las consolas de entrada de los ordenadores eran exclusivamente numeacutericas ademaacutes del sistema decimal se conservan otras dos formas de codificacioacuten numeacuterica hexadecimal y octal

Cualquier cantidad numeacuterica entera puede ser representada en el texto del programa C++ en cualquiera de los sistemas citados Ademaacutes las funciones de salida de la propia Libreriacutea Estaacutendar tambieacuten 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 expresioacuten distintas de la decimal que es mucho maacutes legible

Por su parte las cantidades numeacutericas fraccionarias (de punto flotante) se representan siempre en formato decimal

Nota en la exposicioacuten que sigue nos referimos exclusivamente a la representacioacuten de cantidades numeacutericas en el Fuente (desde el punto de vista del programador) Cuestioacuten esta que no tiene nada que ver con el formato de entradasalida para las cantidades numeacutericas en tiempo de ejecucioacuten (como las ve el usuario del programa)

sect2 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 seriacutean confundidas con el formato octal (afortunadamente el cero octal y el decimal coinciden)

Ejemplos

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 35: 05 Programacion Lenguaje c++

int x = 12 y = 0float y = 314 z = 16

En ocasiones cuando hay posibilidad de confusioacuten los textos informaacuteticos antildeaden una d al final de las cantidades enteras decimales Por ejemplo 125d 0125 y 125h son cantidades distintas (ver a continuacioacuten)

Cuando se trata de representar cantidades decimales muy grandes o muy pequentildeas es posible

tambieacuten utilizar la notacioacuten decimal cientiacuteficacomentada en el capiacutetulo anterior ( 224a) Por ejemplo

float f = 254E20double d = -155E-200long double ld = 233E-480

sect3 Formato hexadecimal

Este sistema de codificacioacuten numeacuterica utiliza un sistema de numeracioacuten de base 16 ( E01w2) Como el sistema araacutebigo 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 utilizacioacuten indistinta de mayuacutesculas y minuacutesculas para representar cantidades en este formato aunque es maacutes frecuente la utilizacioacuten de mayuacutesculas Es la forma tradicional de representar direcciones de memoria

La representacioacuten de estos nuacutemeros debe ir precedido de 0x oacute 0X para indicar al compilador que lo que sigue es formato hexadecimal Tambieacuten es costumbre representar estas cantidades en grupos de 8 diacutegitos (antildeadiendo ceros a la izquierda)

Ejemplo

int x = 0xFF y = 0x000000FF

En ocasiones los textos informaacuteticos antildeaden una h al final de las cantidades hexadecimales Por ejemplo 125h seriacutea equivalente a 0x125 aunque la primera notacioacuten no puede ser utilizada en los fuentes de los programas C++

sect4 Formato octal

Utiliza un sistema de numeracioacuten de base 8 por lo que utiliza las cifras del sistema araacutebigo 0 a 7 Cualquier representacioacuten octal que utilice los diacutegitos 8 o 9 es un error La representacioacuten octal de estos nuacutemeros 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

sect5 Ejemplo resumen

include ltiostreamhgt

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 36: 05 Programacion Lenguaje c++

int main() int x = 255 y = 0377 z = 0x000000FF cout ltlt Direccion de x ltlt ampx ltlt endl L4 cout ltlt Direccion de x ltlt long(ampx) ltlt endl L5 cout ltlt Valor de x ltlt x ltlt endl cout ltlt Valor de y ltlt y ltlt endl cout ltlt Valor de z ltlt z ltlt endl

Salida

Direccion de x 0065FE00Direccion de x 6684160Valor de x 255Valor de y 255Valor de z 255

Como puede verse en L4 la forma estaacutendar utilizada por el compilador para presentar direcciones

de memoria es hexadecimal y con mayuacutesculas en L5 se ha incluido un casting ( 499) para forzar una salida en formato decimal (maacutes legible) de la misma direccioacuten

Nota en el capiacutetulo dedicado a la representacioacuten de Constantes Numeacutericas ( 323b) se incluyen detalles adicionales sobre la forma de utilizar estos formatos

Tamantildeo de los tipos baacutesicos C++

sect1 Sinopsis

En lo tocante al tamantildeo de los tipos baacutesicos el Estaacutendar 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 concrecioacuten uno de los puntos maacutes oscuros en cuanto a la portabilidad del lenguaje

Una de las razones de esta permisividad es que en el disentildeo del C y C++ se primoacute sobre todo la velocidad de ejecucioacuten Esta libertad para elegir dentro de ciertos liacutemites el tamantildeo de los tipos facilita que los constructores de compiladores puedan adecuar los tipos a las caracteriacutesticas de cada hardware Por ejemplo el tamantildeo de un char se supone que es el maacutes adecuado para manipular caracteres en una maacutequina determinada mientras que el de un int deberiacutea ser el maacutes adecuado para almacenar y manipular enteros en la misma maacutequina

Los tamantildeos se definen siempre como muacuteltiplos del tamantildeo de un char asiacute que el tamantildeo de este es siempre 1 sizeof (char) == 1 y no existen tamantildeos del tipo 35 char por ejemplo Asiacute pues en lo que se refiere al tamantildeo de los tipos en C++ la unidad de medida es el tamantildeo de char En las expresiones que siguen 1 significa justamente esto

Respecto al tamantildeo de los tipos baacutesicos C++ en realidad las uacutenicas asunciones ciertas que se pueden hacer son las siguientes

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 37: 05 Programacion Lenguaje c++

1 == char lt= short lt= int lt= long

1 lt= bool lt= long

char lt= wchar_t lt= long

float lt= double lt= long double

X == signed X == unsigned X

donde X puede ser char short int int o long int

Ademaacutesse garantiza que

8 bits lt= char

16 bits lt= int

32 bits lt= long

Los aspectos especiacuteficos de los tipos baacutesicos en cada implementacioacuten estaacuten contenidos en la plantilla numeric_limits que puede encontrarse en el fichero ltlimitsgt

Los ficheros de cabecera ltclimitsgt y ltfloathgt contienen definiciones de los rangos de valor de todos los tipos fundamentales

225 Conversiones estaacutendar

sect1 Presentacioacuten

El tema de las conversiones de tipo es uno de los puntos que generalmente se le reprochan a C++ Una divisioacuten de tipos no excesivamente riacutegida o simplemente permisiva como la del C++ tiene sus

ventajas aunque tambieacuten sus inconvenientes Hemos sentildealado ( 12) que despueacutes de la premisa fundamental de disentildeo Potencia y velocidad de proceso otra de las caracteriacutesticas 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 comprobacioacuten maacutes robustos en este sentido de alguna forma hereda la tradicioacuten de su antecesor El resultado es un nuevo frente para el programador que debe prestar atencioacuten al asunto En especial porque muchas de estas conversiones de tipo son realizadas por el compilador sin que el programador tenga constancia expliacutecita de ello En ocasiones este automatismo es realmente una comodidad en otras es origen de problemas y quebraderos de cabeza

sect2 Conversiones estaacutendar

Se denominan conversiones estaacutendar a determinadas conversiones de tipo que en ocasiones realiza espontaacuteneamente el compilador para ajustar el tipo utilizado por el programador con las necesidades del momento Estas conversiones se refieren casi siempre a tipos baacutesicos

preconstruidos en el lenguaje ( 22) y pueden clasificarse en alguno de los supuestos que se

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 38: 05 Programacion Lenguaje c++

relacionan a continuacioacuten (existen unas pocas conversiones que afectan a los tipos abstractos y

son tratadas en el siguiente capiacutetulo 225a) 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 ( 441a)

Conversioacuten nula no existe conversioacuten

Conversiones triviales

o Conversioacuten de tipo a referencia ( T Tamp)

o Conversioacuten de referencia a tipo ( Tamp T)

o Conversioacuten de matriz a puntero ( T[ ] T)

o Conversioacuten de funcioacuten a puntero-a-funcioacuten ( T(arg) T()(arg) )

o Conversioacuten de calificacioacuten de tipo ( 22)

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 )

Conversioacuten de Lvalue a Rvalue

Conversiones y promociones entre tipos numeacutericos

Conversiones a puntero

Conversiones a booleano

Ejemplo cuando se utiliza una expresioacuten aritmeacutetica como a + b donde a y b son tipos numeacutericos distintos el compilador realiza espontaacuteneamente ciertas conversiones de tipo antes de evaluar la expresioacuten Estas conversiones incluyen la promocioacuten de los operandos de tipo maacutes bajo a tipos

maacutes altos a fin de mejorar la homogeneidad y la precisioacuten del resultado ( 224 Precisioacuten y rango)

En ocasiones la conversioacuten de un tipo a otro exige la realizacioacuten de una secuencia de varias de las conversiones estaacutendar anteriores Ejemplo en la definicioacuten

char cptr = ABC

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 39: 05 Programacion Lenguaje c++

para el compilador la expresioacuten de la derecha es de tipo matriz-de-const char ( 323f) que es convertida a puntero-a-const char Posteriormente una segunda conversioacuten (de calificacioacuten) transforma el puntero-a-cons char en puntero-a-char

Las conversiones estaacutendar 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 expresioacuten de condicioacuten de una sentencia if ( 4102) o de

iteracioacuten dowhile ( 4103) el tipo de destino es un booleano ( 321b)

Cuando se realizan en sentencias switch de seleccioacuten ( 4102) el tipo de destino es un entero

Cuando se utiliza en el Rvalue de una asignacioacuten el tipo de destino es el del Lvalue

Cuando se utiliza en los argumentos de una funcioacuten o en el valor devuelto por esta el tipo de destino es el establecido en la declaracioacuten de la funcioacuten

A su vez existen contextos en los que las conversiones automaacuteticas se impiden expresamente Por

ejemplo la conversioacuten de Lvalue a Rvalue no se realiza en el operando del operador amp ( 4911) de referencia

Para que una expresioacuten exp pueda ser convertida impliacutecitamente a un tipo T es condicioacuten necesaria que pueda existir un objeto temporal t tal que la asignacioacuten T t = exp sea correcta

sect3 Conversiones entre tipos numeacutericos

Dentro de este epiacutegrafe 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

sect31 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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 40: 05 Programacion Lenguaje c++

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 implementacioacuten 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 ( 221a1) o un enumerador ( 323g) pueden ser convertidos a un Rvalue del primero de los tipos intunsigned int long o unsigned long que pueda representar el valor correspondiente

Un Rvalue de tipo campo de bits ( 46) 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 promocioacuten

Un Rvalue de tipo loacutegico (bool) puede ser promovido a un Rvalue tipo int La regla es

que false se transforma en cero y true en 1 ( 321b)

sect32 Promocioacuten a tipo fraccionario

Los Rvalues de tipo float o long pueden ser promovidos a Rvalue de tipo double Este tipo de promocioacuten se denomina tambieacuten de punto flotante

sect33 Conversiones entre asimilables a entero

Cualquiera de los asimilables a entero ( 221) pueden ser convertido a otro tipo asimilable a entero Las conversiones permitidas bajo el epiacutegrafe anterior (promociones a entero) estan excluidas de las que se consideran aquiacute

Un Rvalue de tipo enumeracioacuten puede ser convertido a un Rvalue de tipo entero

La conversioacuten de un entero largo a entero corto trunca los bits de orden superior manteniendo sin cambios el resto

La conversioacuten de un entero corto a largo pone a cero los bits extra del entero largo yo los correspondientes al signo dependiendo que el entero corto fuese con o sin signo

La asignacioacuten de un caraacutecter con signo (signed char) a un entero origina la adopcioacuten del signo Los caracteres con signo siempre utilizan signo

Los caracteres sin signo (unsigned char) siempre ponen a cero el bit maacutes 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 implementacioacuten [3]

Si el tipo de destino es bool la conversioacuten se efectuacutea seguacuten se indica maacutes adelante Si por el contrario el tipo origen es bool las reglas son las indicadas en la promocioacuten a entero false se transforma en cero y true en 1

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 41: 05 Programacion Lenguaje c++

sect34 Conversiones fraccionario lt=gt 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 enumeracioacuten pueden ser promovidos a fraccionarios Si la conversioacuten es posible (lo que ocurre efectivamente en la mayoriacutea 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 peacuterdida de precisioacuten En tal caso el valor fraccionario adoptado es uno de los dos valores maacutes proacuteximos 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

sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten

A continuacioacuten se exponen los pasos que sigue C++ durante la conversioacuten de operandos en las

expresiones aritmeacuteticas El resultado de la expresioacuten es del mismo tipo que uno de los operandos

1ordm- Cualquier tipo entero es convertido seguacuten se muestra en la tabla

Tipo convierte a Meacutetodo de conversioacuten seguido

char int Con o sin signo (dependiente del tipo char por defecto)

unsigned char int Siempre rellena con cero el byte maacutes 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 maacutes significativo

enum int El mismo valor

2ordm- Despueacutes 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

3ordm- A partir de este momento la homogenizacioacuten de tipos se realiza ahora siguiendo los patrones que se indican (en el orden sentildealado)

Alguacuten operando es long double el otro es convertido en long double

Alguacuten operando es double el otro es convertido en double

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 42: 05 Programacion Lenguaje c++

Alguacuten operando es float el otro es convertido en float

Alguacuten operando es unsigned long el otro es convertido en unsigned long

Alguacuten operando es long el otro es convertido en long

Alguacuten operando es unsigned el otro es convertido en unsigned Ambos aperandos son de tipo int

Observaciones

Generalmente las funciones matemaacuteticas (como las incluidas en ltmathhgt) esperan argumentos

en doble precisioacuten (double 221) pero hay que tener en cuenta que las variables float no son convertidas automaacuteticamente a double y por supuesto los double tampoco son convertidos

automaacuteticamente a float (supondriacutea una peacuterdida de precisioacuten) Ver un ejemplo comentado en ( 224a)

Sobre la forma de convertir double a float o cualquier tipo a otro ver el operador de modelado de

tipos ( 499)

sect36 Precauciones

Las conversiones aritmeacuteticas son unos de los puntos en que el programador C++ debe prestar

especial atencioacuten si no quiere dispararse accidentalmente en los pies ( 1) y donde el lenguaje puede gastarnos insidiosas jugarretas Como ejemplo mostramos una funcioacuten prevista para calcular la inversa de cualquier entero que se pase como argumento

void inverso (int x) float f = 1x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

La funcioacuten 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 1x debe ser promovido a float con lo que tenemos garantizado que el resultado puede ser fraccionario Si una cuestioacuten como esta se presenta cualquier diacutea que estemos especialmente cansados puede mandarnos directamente a limpiar cochineras a Carolina del Norte Con un poco de suerte y descanso quizaacutes caigamos en la cuenta que la promocioacuten se produce despueacutes que se haya efectuado la divisioacuten y que esta considera todaviacutea 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 solucioacuten inmediata y obvia () permite resolver la situacioacuten (ver Modelado de tipos 499)

void inverso (int x) float f = float(1)float(x) cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 43: 05 Programacion Lenguaje c++

Una solucioacuten un poco maacutes elegante

void inverso (int x) float f = float(1)x cout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

En este caso el compilador realiza automaacuteticamente la promocioacuten de x a float antes de efectuar la

divisioacuten (ver reglas anteriores )

Una solucioacuten auacuten maacutes elegante que tambieacuten produce resultados correctos

void inverso (int x) float f = 10xcout ltlt X = ltlt x ltlt 1x = ltlt f ltlt endl

sect4 Conversiones a puntero

Un Rvalue que sea una expresioacuten constante ( 323a) 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 asiacute 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 0i y 0c respectivamente diriacuteamos que

0i es el valor del puntero nulo de int (puntero-a-int)

0c es el valor del puntero nulo de char (puntero-a-char)

Ejemplo

int const nulo = 0 L1int pint = nulo L2

En L1 nulo es un objeto tipo int calificado const ( 22) cuyo Rvelue es 0 En L2 este objeto

sufre una conversioacuten estaacutendar 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 continuacioacuten su Rvalue es copiado

a la direccioacuten del objeto pint que toma asiacute su valor

Observe que si a la expresioacuten L1 anterior se le suprime el calificador const

int nulo = 0 L1aint pint= nulo L2 Error

se obtiene un error de compilacioacuten en L2 La causa es que la conversioacuten estaacutendar no puede realizarse porque aunque nulo sigue siendo un int de valor 0 le falta el calificador const

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 44: 05 Programacion Lenguaje c++

Considere ahora otra variacioacuten del ejemplo anterior

int const nulo = 0 L1const int pi1 = nulo L2int const pi2 = nulo L3int const pi3 = nulo L4

Los nuevos objetos son tambieacuten punteros aunque ahora pi1 y pi2 son punteros-a-int constante

(L2 y L3 son equivalentes) el objeto al que sentildealan no puede cambiar su valor Su tipo es const int

Por su parte pi3 es tambieacuten 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 conversioacuten

estaacutendar a tipo int calificado La norma nos avisa que esta conversioacuten del objeto const al

tipo intcalificado es una sola conversioacuten y no una conversioacuten a int seguida de una calificacioacuten

sect5 Conversiones de constantes de enumeracioacuten

Para las conversiones de las constantes de enumeracioacuten ver Enumeraciones ( 48)

sect6 Conversiones de matriz a puntero

El compilador puede realizar expontaacuteneamente la conversioacuten de una matriz-de-elementos-tipoX a

puntero-a-tipoX ( 432) Este tipo de conversioacuten 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 ampM[0] pM

Este tipo de conversioacuten tambieacuten ocurren en las asignaciones del tipo

char cptr = ABC

sect7 Conversioacuten a booleano

Los Rvelues de tipo numeacuterico ( 221) las constante de enumeracioacuten los punteros y los

punteros a miembro pueden ser convertidos a Rvelues de tipo bool ( 321b) La regla es que un valor cero o un puntero nulo son convertidos a false Cualquier otro valor es convertido a true

sect8 Conversiones de funcioacuten a puntero-a-funcioacuten

Esta conversioacuten permite que el nombre de una funcioacuten F pueda ser tomada en caso necesario

como su puntero ( 424a) [2] En realidad para el compilador el tipo de una funcioacuten es puntero-

a-funcioacuten de forma que en lo tocante a este atributo no distingue entre ambas entidades (Ejemplo comentado)

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 45: 05 Programacion Lenguaje c++

Temas relacionados

Modelado de tipos ( 499)

Buacutesqueda de nombres ( Name-lookup)

Congruencia estaacutendar de argumentos ( 441a)

Conversiones definidas por el usuario ( 4918k)

225a Conversiones estaacutendar con tipos abstractos

sect1 Sinopsis

Ademaacutes de las conversiones estaacutendar realizadas con los tipos baacutesicos ( 225) existe ocasiones en que el compilador realiza espontaacuteneamente ciertas adaptaciones de tipo para que puedan realizarse determinadas operaciones con objetos abstractos cuando tales objetos pertenecen a jerarquiacuteas de clases

Nota las conversiones que se relacionan exigen que la superclase o subclase sean accesibles y que en casos de herencia muacuteltiple puedan puedan realizarse sin ambiguumledad

sect2 Conversioacuten de referencias

En las jerarquiacuteas de clases las referencias a subclases pueden ser promovidas a referencias a la superclase El resultado de la conversioacuten es una referencia al subobjeto de la superclase contenido

en el objeto de la clase derivada (miembros heredados 4112b) Ejemplo

class Bas class Der public Bas void foo(Basamp)Der dDeramp rd = d referenica-a-d (objeto de subclase)

En este contexto aunque foo espera una referencia a la superclase es legal la invocacioacuten

foo(rd)

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la invocacioacuten es transformada en

foo( (Basamp)rd )

sect3 Conversioacuten de punteros a clase

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 46: 05 Programacion Lenguaje c++

En las jerarquiacuteas 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-superclaseDer d instancia de sub-clase

En este contexto aunque bptr es puntero-a-superclase puede ser asignado con la direccioacuten de un objeto de la subclase Es legal la asignacioacuten

bptr = ampd

El compilador se encarga de realizar una conversioacuten al tipo requerido de forma que la asignacioacuten es transformada en

bptr = amp( (Bas)d )

Este tipo de conversioacuten Sub-clase Super-clase es realizada automaacuteticamente por el

compilador en determinadas circunstancias (congruencia estaacutendar de argumentos 441a)

Nota cuando se acceden a traveacutes de punteros objetos de clases que pertenecen a una jerarquiacutea es importante tener en cuenta las precauciones indicadas en Consideraciones

sobre punteros en jerarquiacuteas de clases ( 4112b1)

sect4 Conversioacuten de punteros a miembro

Con los punteros a miembro ocurre una conversioacuten 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 = ampBasbi puntero-a-miembro de superclaseint Der dpi = ampDerdi puntero-a-miembro de subclase

En este contexto el puntero puede ser utilizado con objetos de la subclase en cuyo caso sentildealaraacute al miembro heredado

Der dDer dp = ampd dbpi = 2 Ok dbi = 2dp-gtbpi = 3 Ok dbi = 3

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 47: 05 Programacion Lenguaje c++

ddpi = 2 OK ddi = 2dp-gtdpi = 3 Ok ddi = 3 Bas bbdpi = 2 Error b NO posee un miembro dpi

226 Almacenamiento

Recordemos que al describir la estructura de un programa se dedicoacute un

capiacutetulo a explicar las formas de almacenamiento de algoritmos y datos ( 132) Aquiacute nos referimos exclusivamente al almacenamiento de datos En especial a aquellos aspectos del soporte fiacutesico que tienen repercusiones de intereacutes para el programador

sect1 Sinopsis

El almacenamiento de los datos de un programa puede ser considerado desde varios puntos de vista trataremos aquiacute dos de ellos uno fiacutesico y otro loacutegico Desde el punto de vista fiacutesico existen cinco zonas de almacenamiento los registros el segmento de datos el montoacuten y la pila

Pila (Stack)

Montoacuten (Heap)

Segmento de datos (Data segment en el PC)

Registros (Registers)

sect2 Caracteriacutesticas fiacutesicas

Cada zona tiene unas caracteriacutesticas propias que imprimen caraacutecter a la informacioacuten almacenada en ellas

Pila a menos que se especifique lo contrario las variables locales se almacenan aquiacute

tambieacuten los paraacutemetros es decir las variables automaacuteticas ( 132)

Los elementos almacenados en esta zona son de naturaleza automaacutetica esto significa que el compilador se encarga de crearlas y destruirlas automaacuteticamente cuando salen de aacutembito

Montoacuten es utilizado para asignacioacuten dinaacutemica de bloques de memoria de tamantildeo variable

( 132) Muchas estructuras de datos como aacuterboles y listas lo utilizan como sitio de almacenamiento Esta zona estaacute 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 destruccioacuten 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 estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 48: 05 Programacion Lenguaje c++

Segmento de datos es una zona de memoria utilizada generalmente por las variables estaacuteticas y globales

Registros son espacios de almacenamiento en el interior del procesador por lo que su nuacutemero 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 ( 418b)

Es la zona de memoria de maacutes raacutepido 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 aquiacute son tambieacuten

de naturaleza automaacutetica generalmente de tipos asimilables a entero ( 221)

Nota los teacuterminos automaacutetico versus persistente que en la praacutectica son respectivamente sinoacutenimos de existencia en la pilaregistros o en el montoacuten 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 representacioacuten binaria de las magnitudes numeacutericas ( 224a)

Nota en lo que sigue el teacutermino identificador ( 322) se refiere al nombre arbitrario (dentro de ciertas reglas) que se da a una entidad (clase objeto funcioacuten variable etc) en el coacutedigo de un programa Posteriormente pueden ser transformados por la accioacuten del compilador y enlazador hasta quedar total o parcialmente irreconocibles en el ejecutable

sect3 Caracteriacutesticas loacutegicas

Desde el punto de vista loacutegico existen tres aspectos baacutesicos a tener en cuenta en el almacenamiento de los objetos aacutembito visibilidad (scope) yduracioacuten (lifetime)

Aacutembito o campo de accioacuten de un identificador es la parte del programa en que es

conocido por el compilador ( 413)

Visibilidad de un identificador es la regioacuten de coacutedigo fuente desde la que se puede acceder al objeto asociado al identificador sin utilizar especificadores adicionales de

acceso (simplemente con el identificador 414)

Duracioacuten define el periodo durante el que la entidad relacionada con el identificador tiene

existencia real es decir un objeto fiacutesicamente alojado en memoria ( 415)

Nota observe que los dos primeros aacutembito y visibilidad se refieren al identificador y al fuente decimos que son propiedades de tiempo de compilacioacuten El tercero la duracioacuten se refiere a objetos reales en memoria Decimos que es una propiedad de runtime

Tanto las caracteriacutesticas fiacutesicas (donde se almacena) como loacutegicas (aacutembito visibilidad y duracioacuten) estaacuten determinadas por dos atributos de los objetos clase de almacenamiento y tipo de

dato (abreviadamente conocido como tipo 21) El compilador C++ deduce estos atributos a partir del coacutedigo bien de forma impliacutecita bien mediante declaraciones expliacutecitas

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 49: 05 Programacion Lenguaje c++

Las declaraciones expliacutecitas de clase de

almacenamiento son auto register static extern typedef y mutable ( 418 Especificadores de clase de almacenamiento)

Las declaraciones expliacutecitas de tipo de dato son char int float double y void ( 221 Tipos baacutesicos) a estos se pueden antildeadir matices utilizando ciertos modificadores

opcionales signed unsigned long y short ( 223 Modificadores de tipo)

sect4 El concepto estaacutetico

El concepto estaacutetico (Static) tiene en C++ varias connotaciones distintas algunas de ellas son herencia del C claacutesico otras son significados antildeadidos en la parte POO del lenguaje Desafortunadamente (sobre todo para el principiante) algunos de los significados no tienen absolutamente ninguna relacioacuten entre si y se refieren a conceptos distintos

Las diversas connotaciones del concepto podriacuteamos resumirlas del siguiente modo

Relativa al conocimiento o no del compilador de los valores de un objeto en tiempo de compilacioacuten 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 ejecucioacuten ( 132) Relativa al enlazado de funciones cuando una llamada a funcioacuten puede traducirse en una

direccioacuten concreta en tiempo de compilacioacuten ( 144) el enlazado (estaacutetico) es diferente del que se realiza cuando esta direccioacuten solo es conocida en tiempo de ejecucioacuten (dinaacutemico)

Relativa a la duracioacuten o permanencia de un objeto Relativa a la visibilidad de un objeto lo que estaacute relacionado directamente con otro

concepto el tipo de enlazado ( 144) que se refiere a las variables que puede ver el enlazador

Refirieacutendonos a la primera de ellas estaacutetico (versus dinaacutemico) significa que el compilador conoce los valores en tiempo de compilacioacuten (frente a tiempo de ejecucioacuten -runtime-) Por tanto puede asignar zonas predeterminadas de memoria para estos objetos (variables y constantes) Por el contrario para los objetos dinaacutemicos se asigna y desecha espacio de memoria en tiempo de ejecucioacuten lo que significa que se crean y se destruyen con cada llamada de la funcioacuten en que han sido declaradas Esto explica por ejemplo que cada llamada recursiva a una funcioacuten pueda generar su propio conjunto de variables locales (dinaacutemicas) Si el espacio fuese asignado de forma fija en tiempo de compilacioacuten la recursioacuten seriacutea imposible pues cada nueva invocacioacuten de la funcioacuten machacariacutea los valores anteriores

Nota Si la profundidad de la recursioacuten se pudiese conocer en tiempo de compilacioacuten el compilador podriacutea asignar espacio a los sucesivos juegos de variables pero teacutengase en cuenta que este es precisamente un valor que a veces solo se conoce en tiempo de ejecucioacuten 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 funcioacuten) son estaacuteticas (en este sentido) y las locales son dinaacutemicas (de la variedad llamada automaacutetica) es decir las primeras pueden conservar su valor entre llamadas y las segundas no

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 50: 05 Programacion Lenguaje c++

En este orden de cosas la declaracioacuten como static de una variable local definida dentro de una funcioacuten le confiere permanencia entre las sucesivas llamadas a dicha funcioacuten (igual que las globales) Desafortunadamente [1] la declaracioacuten static de una variable global (que deberiacutea ser redundante e innecesaria) supone una declaracioacuten de visibilidad en el sentido de que dicha variable global (aparte de su ldquoestaticidadrdquo) solo seraacute conocida por las funciones dentro del fichero en que se ha declarado

Resulta asiacute que desgraciadamente la palabra clave static tiene un doble sentido (y uso) el

primero estaacute relacionado con la duracioacuten ( 415) el segundo con la visibilidad ( 414)

Finalmente cuando el modificador static se utiliza para miembros de clase adquiere una

peculiaridades especiacuteficas ( 4117 Miembros estaacuteticos)

sect5 Resumen

Con el fin de aclarar un poco este pequentildeo galimatiacuteas semaacutentico resumimos lo dicho

Automaacutetico versus Persistente

Propiedad de los objetos de crearsedestruirse automaacuteticamente (al entrar y salir del bloque de coacutedigo) o bajo control directo del programador mediante sentencias especiacuteficas de creacioacuten y destruccioacuten (new y delete) Existen respectivamente en la PilaMontoacuten Tanto los objetos automaacuteticos como los persistentes son de naturaleza dinaacutemica

Estaacutetico versus Dinaacutemico

Caracteriacutestica de ser conocido en tiempo de compilacioacuten o en tiempo de ejecucioacuten lo que significa que el compilador puede reservar almacenamiento desde el principio o este debe ser creado y destruido en tiempo de ejecucioacuten

sect6 Ejemplo

Intentaremos aclarar los conceptos anteriores comentando el ciclo vital de los elementos en un sencillo programita

include ltiostreamhgt

void func(int) prototipochar version = V00 L4

int main() =============int x = 1char mensaje = Programa demo cout ltlt mensaje ltlt endlcout ltlt Introduzca numero de salidas (0 para terminar) while ( x = 0) cin gtgt x func(x) cout ltlt Otra vez (numero) ltlt endlreturn 0 L15void func(int i) L17 definicion

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 51: 05 Programacion Lenguaje c++

static int j = 1cout ltlt Se han solicitado ltlt i ltlt salidas ltlt endlint v = new int L20v = 1register int n L22for (n = 1 n lt= i n++) cout ltlt - ltlt v ltlt ltlt i ltlt total efectuadas ltlt j ltlt salidas ltlt endl j++ (v)++ L26cout ltlt version ltlt endl L28delete v L29

Volcado de pantalla con la salida del programa despueacutes de marcar 3 y 2 como valores de entrada

Programa demoIntroduzca numero de salidas (0 para terminar) 3Se han solicitado 3 salidas- 13 total efectuadas 1 salidas- 23 total efectuadas 2 salidas- 33 total efectuadas 3 salidasV00Otra vez (numero)2Se han solicitado 2 salidas- 12 total efectuadas 4 salidas- 22 total efectuadas 5 salidasV00

Comentario

Cuando se inicia el programa el SO reserva un nuacutemero 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 aplicacioacuten ni auacuten intencionadamente de esto se encarga el propio SO Por ejemplo si un puntero de una aplicacioacuten se descontrola y sentildeala una zona de memoria que no le pertenece surge el conocido mensaje Windows La aplicacioacuten ha efectuado una operacioacuten no vaacutelida y seraacute detenido Si es Linux el claacutesico 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 fiacutesica disponible los

modernos SO empiezan a asignar memoria virtual ( H51) haciendo constante intercambio con el disco de las partes que no pueden estar simultaacuteneamente en la memoria central (RAM) Este proceso (Swapping) es totalmente transparente para el programa usuario y puede crecer hasta el liacutemite 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 ejecucioacuten del programa comienza por el moacutedulo de inicio ( 15) que crea e inicia las variables estaacuteticas y globales En este caso la cadena de caracteres V00 accesible mediante el puntero version y la variable j de la funcioacuten func Salvo indicacioacuten en contrario j se habriacutea inicializado a cero pero en este caso se instruye al compilador (L18) que se inicialice a 1 que es

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 52: 05 Programacion Lenguaje c++

el valor inicial que queremos para este contador Observe que esta asignacioacuten solo ocurre una vez durante la vida del programa (en el moacutedulo de inicio) no con cada invocacioacuten defunc A partir de este momento esta variable conserva su valor entre cada invocacioacuten sucesiva a la funcioacuten aunque va siendo incrementado progresivamente en L26

Tanto el puntero version como la cadena sentildealada por eacutel permanecen constantes a lo largo de toda la vida del programa ademaacutes este nemoacutenico es visible desde todos los puntos (tiene visibilidad global) por eso puede ser utilizado desde el interior de func en L28 La variable j el

punteroversion y la propia cadena V00 son creados en el segmento ( )

Al llegar a L15 se inicia la secuencia de finalizacioacuten ( 15) En este momento se destruyan las variables globales anteriormente descritas asiacute como las locales de la propia funcioacuten main El SO recibe un entero como valor devuelto por el programa que termina Generalmente el valor 0 es sinoacutenimo de terminacioacuten correcta cualquier otro valor significa terminacioacuten 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 ademaacutes de las constantes literales ( 323f) sentildealadas 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 compilacioacuten [5] se trata por tanto de objetos estaacuteticos mientras que el resto son dinaacutemicos ya que sus valores solo son conocidos durante la ejecucioacuten

Al ejecutarse la funcioacuten main se van creando e iniciando sucesivamente las variables (dinaacutemicas) en este caso el entero x que recibe un valor inicial 1 y una constante de valor cero [5] en la sentencia return (L15)

Cada invocacioacuten a func provoca la creacioacuten de un juego de variables dinaacutemicas En este caso el entero i (argumento recibido por la funcioacuten) 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 atencioacuten a que (suponiendo que el compilador atienda la peticioacuten en L22 418b) n se

crea en el registro ( ) mientras que i se crea en la pila ( ) Ambas son de naturaleza automaacutetica por lo que son destruidas al salir de aacutembito la funcioacuten cosa que ocurre al llegar al corchete de cierre ( ) en L30 Sin embargo observe que el entero sentildealado por el puntero v se

crea en el montoacuten ( ) lo que le confiere existencia persistente esto hace que el espacio

reservado (4 bytes en este caso 224) tenga que ser especiacuteficamente desasignado (en L29) pues de lo contrario cada invocacioacuten de func supondriacutea la peacuterdida irrecuperable (para el programa) de 4 bytes de memoria Suponiendo que estuvieacutesemos corriendo el programa en un servidor seriacuteamos directamente responsables de una progresiva ralentizacioacuten del sistema (posiblemente hasta que el Sysmanager descubriera una utilizacioacuten inusual de recursos por nuestra parte y nos desconectara)

226a Orden de almacenamiento (endianness)

sect1 Sinopsis

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 53: 05 Programacion Lenguaje c++

Ademaacutes de las cuestiones relativas a la zona en que se almacenan los datos que fueron objeto del

epiacutegrafe anterior ( 226) existe otro aspecto que tambieacuten puede ser de intereacutes para el programador C++ es la cuestioacuten del orden en que se almacenan en memoria los objetos multibyte

Por ejemplo como se almacenan los Bytes de un long ( 224) o de un wchar_t ( 221a1)

Nota la cuestioacuten no se refiere solo al orden de almacenamiento en la memoria interna Puede ser tambieacuten el caso de en un volcado de memoria a disco o como se reciben los datos en una liacutenea de comunicacioacuten

La cuestioacuten 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 estaacute utilizando ahora) y otros que se leen en sentido contrario tambieacuten 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 maacutequina no existe el concepto de izquierda o derecha pero siacute 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 liacutenea de transmisioacuten

Para concretar un ejemplo tomemos los unsigned short que en el compilador Linux GCC en Borland C++ 55 y en MS Visual C++ 60 ocupan 2 Bytes Supongamos ahora que una variable X de este tipo adopta el valor 255 La representacioacuten 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 maacutes 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 maacutes significativo y a continuacioacuten 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 tradicioacuten informaacutetica que la primera disposicioacuten se denomina big-endian y la segunda little-endian [1] Si leemos la memoria desde las posiciones maacutes bajas a las maacutes altas la zona que contiene el nuacutemero X en una maacutequina que siga la convencioacuten big-endian contendraacute los valores00h FFh mientras que en una little-endian los valores encontrados seraacuten FFh 00h En concreto las arquitecturas x86 de Intel y los procesadores Alpha de DEC son little-endian mientras que las plataformas Suns SPARC Motorola e IBM PowerPC utilizan la convencioacuten 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 vocacioacuten hacia Internet y los protocolos TCPIP utilizan esta convencioacuten) Por contra C y C++ utilizan la convencioacuten dictada por el Sistema Operativo Los sistemas Windows utilizan la convencioacuten little-endian mientras que la mayoriacutea de plataformas Unix utilizan big-endian

Nota es tradicioacuten que cuando se trata de cantidades de 32 bits Por ejemplo un long la mitad maacutes 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

sect2 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 paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 54: 05 Programacion Lenguaje c++

Un ejemplo paradigmaacutetico es el de las comunicaciones TCPIP Este conjunto de protocolos utiliza la convencioacuten big-endian en todas sus estructuras De forma que por ejemplo las direcciones IP que son nuacutemeros de multiBytes (de 4 octetos) se construyen colocando primero el Byte maacutes 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 representacioacuten interna (el denominado host-byte order) seguiraacute esta convencioacuten y seraacute 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

sect21 Una forma de realizar estas manipulaciones en C++ es recurriendo a los operadores de bit (

493) 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 gtgt 8 valor del byte maacutes significativounsigned short uS2 = uShort ltlt 8 valor del byte menos significativo + 255unsigned short uSwap = uS2 | uS1 valor little-endian

El resultado puede obtenerse en una sentencia

unsigned short uSwap = (uShort ltlt 8) | (uShort gtgt8)

Tambieacuten mediante una directiva de preproceso ( 4910b)

define SWAPSHORT(US) ((US ltlt 8) | (US gtgt8))unsigned short uSwap = SWAPSHORT(uShort) valor little-endian

sect22 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 posicioacuten 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 ltlt 8 | k ltlt 16 00000000 11111111 11111111 11111111

unsigned long k2 = k | k ltlt 8 | k ltlt 24 11111111 00000000 11111111 11111111

unsigned long k3 = k | k ltlt 16 | k ltlt 24 11111111 11111111 00000000 11111111

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 55: 05 Programacion Lenguaje c++

unsigned long k4 = k ltlt 8 | k ltlt 16 | k ltlt 24

11111111 11111111 11111111 00000000

Con ellos podemos construir las expresiones que proporcionan los Bytes individuales ( 493a)

unsigned long B1 = (uLong ^ k1 amp uLong) gtgt 24

00000000 00000000-00000000 11101110

unsigned long B2 = (uLong ^ k2 amp uLong) gtgt 16

00000000 00000000-00000000 01111001

unsigned long B3 = (uLong ^ k3 amp uLong) gtgt 8

00000000 00000000-00000000 11101001

unsigned long B4 = uLong ^ k4 amp uLong 00000000 00000000-00000000 01101001

A partir de aquiacute 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 ltlt 24 | B3 ltlt 16 | B2 ltlt 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

sect23 El procedimiento puede hacerse extensivo a cualquier valor value expresado por una sucesioacuten de n bytes De forma que su representacioacuten big-endian puede expresarse

value = (byte[0] ltlt 8(n-1)) | (byte[1] ltlt 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 mecaacutenica utilizada a continuacioacuten se incluyen algunas muy frecuentes en la programacioacuten Windows

define LOWORD(x) ((WORD) (l))define HIWORD(x) ((WORD) (((DWORD) (l) gtgt 16) amp 0xFFFF))

Con estas definiciones y sabiendo que a su vez WORD y DWORD estaacuten 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 faacutecilmente determinados con las expresiones siguientes

WORD alto = LOWORD(param)WORD ancho = HIWORD(param)

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento
Page 56: 05 Programacion Lenguaje c++

Otras expresiones utilizadas en el compilador MS Visual C++ (BYTE estaacute definida como unsigned char y LONG es long)

define MAKEWORD(a b) ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) ltlt 8))define MAKELONG(a b) ((LONG)(((WORD)(a)) | ((DWORD)((WORD)(b))) ltlt 16))define LOBYTE(w) ((BYTE)(w))define HIBYTE(w) ((BYTE)(((WORD)(w) gtgt 8) amp 0xFF))

Como el lector puede comprobar en todos estos casos si se modifican las condiciones de entorno la adaptacioacuten de las aplicaciones resulta muy faacutecil ya que se limita a modificar adecuadamente los ficheros de cabecera

  • sect4 Conversioacuten entre sistemas multibyte y de caracteres anchos
  • 221a1 El caraacutecter ancho
    • sect1 Introduccioacuten
    • sect2 wchar_t
      • 221a2 Codificaciones UCSUnicode
        • sect1 Introduccioacuten
        • sect2 UCS
        • sect3 Unicode
        • sect3 Webografiacutea
          • 222 Tipos derivados
            • sect1 Sinopsis
              • 223 Modificadores de tipo
                • sect1 Sinopsis
                • sect2 long
                • sect3 short
                • sect4 signed
                • sect5 unsigned
                • sect6 Tipos enteros extendidos
                • sect7 Extensiones C++Builder
                  • 224 Tipos baacutesicos representacioacuten interna rango
                    • sect1 Sinopsis
                    • sect2 Almacenamiento y rango
                    • sect3 Enteros
                    • sect4 Nuevos tipos numeacutericos
                    • sect5 Caraacutecter
                    • sect6 Fraccionarios
                    • sect7 La clase numeric_limits
                    • Temas relacionados
                      • 224a Formas de representacioacuten binaria de las magnitudes numeacutericas
                        • sect1 Presentacioacuten de un problema
                        • sect2 Formas de representacioacuten binaria
                        • sect21 Coacutedigo binario sin signo
                        • sect22 Coacutedigo binario con signo
                        • sect23 Coacutedigo binario en complemento a uno
                        • sect24 Coacutedigo binario en complemento a dos
                        • sect3 Nuacutemeros fraccionarios
                        • sect31 Notacioacuten cientiacutefica
                        • sect311 Notacioacuten normalizada
                        • sect32 Representacioacuten binaria
                        • sect321 Problemas de la representacioacuten binaria de las cantidades fraccionarias
                        • sect33 El Estaacutendar IEEE 754
                        • sect331 Formatos
                        • sect332 Significados especiales
                        • sect333 Significados normales
                        • sect333a Simple precisioacuten representacioacuten normalizada
                        • sect333b Simple precisioacuten representacioacuten subnormal
                        • sect333c Doble precisioacuten representacioacuten normalizada
                        • sect333d Doble precisioacuten representacioacuten subnormal
                        • sect334 Conversor automaacutetico de formatos
                        • sect335 Operaciones con nuacutemeros especiales
                        • sect336 Rango de la representacioacuten IEEE 754
                          • 224b Formas de representacioacuten simboacutelica
                            • sect1 Sinopsis
                            • sect2 Formato decimal
                            • sect3 Formato hexadecimal
                            • sect4 Formato octal
                            • sect5 Ejemplo resumen
                              • Tamantildeo de los tipos baacutesicos C++
                                • sect1 Sinopsis
                                  • 225 Conversiones estaacutendar
                                    • sect1 Presentacioacuten
                                    • sect2 Conversiones estaacutendar
                                    • sect3 Conversiones entre tipos numeacutericos
                                    • sect31 Promociones a entero
                                    • sect32 Promocioacuten a tipo fraccionario
                                    • sect33 Conversiones entre asimilables a entero
                                    • sect34 Conversiones fraccionario lt=gt entero
                                    • sect35 Conversiones aritmeacuteticas estaacutendar reglas de conversioacuten
                                    • Observaciones
                                    • sect36 Precauciones
                                    • sect4 Conversiones a puntero
                                    • sect5 Conversiones de constantes de enumeracioacuten
                                    • sect6 Conversiones de matriz a puntero
                                    • sect7 Conversioacuten a booleano
                                    • sect8 Conversiones de funcioacuten a puntero-a-funcioacuten
                                      • 225a Conversiones estaacutendar con tipos abstractos
                                        • sect1 Sinopsis
                                        • sect2 Conversioacuten de referencias
                                        • sect3 Conversioacuten de punteros a clase
                                        • sect4 Conversioacuten de punteros a miembro
                                          • 226 Almacenamiento
                                            • sect1 Sinopsis
                                            • sect2 Caracteriacutesticas fiacutesicas
                                            • sect3 Caracteriacutesticas loacutegicas
                                            • sect4 El concepto estaacutetico
                                            • sect5 Resumen
                                              • sect6 Ejemplo
                                              • Comentario
                                                  • 226a Orden de almacenamiento (endianness)
                                                    • sect1 Sinopsis
                                                    • sect2 Tratamiento