Disponible la versión 6 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


2.2.1 Tipos básicos

§1 Sinopsis

Los tipos de datos básicos o fundamentales de C++, se caracterizan porque no tienen "descomposición", y tanto sus características y como las operaciones que se pueden realizar con ellos están predefinidos en el lenguaje (la literatura especializada también suele referirse a ellos diciendo que están "preconstruidos" o "intrínsecamente definidos" en el lenguaje).

Nota: al tratar de los tipos abstractos veremos que en realidad, los tipos básicos son considerados también por el compilador como un tipo especial de clases preconstruidas en el lenguaje ( 4.11.2d3).

Se pueden clasificar en tres grandes grupos, y se designan por palabras clave específicas que, en estos casos,  representan especificadores de tipo.

  Asimilables a enteros:
  • carácter  char              ()
  • enteros    int                ()
  • booleanos  bool          ( 3.2.1b)
  • enumeraciones  enum ( 4.7)

Son considerados tipos artiméticos, porque en determinadas circunstancias pueden ser promovidos automáticamente a enteros por el compilador [3]. Los enteros, carácter y booleanos son conocidos como "Integral types" en la literatura inglesa.

  Fraccionarios: dos versiones: float y double ()

  Ausencia de dato, sin valor, void ().

Los asimilables a enteros y los fraccionarios son conocidos conjuntamente como tipos numéricos.

A excepción de void, los tipos básicos pueden tener modificadores opcionales ( 2.2.3), que se usan para alterar el significado del tipo base (en cuanto a rango de valores y espacio de almacenamiento), de forma que dan lugar a variantes que se adecuan a ciertas necesidades específicas. Estos modificadores indican: con signo, sin signo, largo y corto, y se aplican con las palabras clave que se señalan.

  • largo (long)
  • corto (short)
  • con signo (signed)
  • sin signo (unsigned)

§2 Los especificadores de tipo, aislados o acompañados de uno o más de los modificadores opcionales, definen completamente un identificador.  Por ejemplo, pueden darse las siguientes variedades del tipo int:

int i1;

short int i2;

long int i3;

signed int i4;

unsigned int i5;

signed short int i6;

unsigned short int i7;

signed long int i8;

unsigned long int i9;

Estas declaraciones presuponen una definición exacta de la cantidad de memoria necesaria para albergar el tipo de dato; como interpreta el programa el conjunto de bits que se almacena, y cuales son las operaciones permitidas con tales datos. La cuestión del almacenamiento es en muchos casos dependiente de la implementación concreta, es decir, depende del compilador utilizado, porque el estándar ANSI C++ deja la cuestión un tanto en el aire. En cualquier caso, el operador sizeof ( 4.9.13) permite calcular el tamaño en bytes de cualquier tipo de dato, ya sea de los tipos básicos (pre-definidos en el lenguaje) o de los tipos complejos (definidos por el usuario).

Observe que los modificadores opcionales aplicados a un tipo básico definen un nuevo tipo, de forma que por ejemplo, short y unsigned short son tipos distintos.

§3 Enteros

El especificador de tipo int, junto con sus variantes short y long en sus versiones signed y unsigned,  identifican a los enteros.

Sintaxis:

[signed|unsigned] int <identificador> ;

Descripción:

El especificador int se utiliza para definir un dato tipo número entero.  Junto con sus variantes short y long en sus versiones signed y unsigned,  dan lugar a las combinaciones autorizadas que se indican en la tabla, los sinónimos se deben a los valores que (salvo indicación en contrario) se suponen por defecto.

Especificadores de enteros (sinónimos en la misma línea)

int

signed int

 

unsigned

unsigned int

 

short

short int

signed short int

unsigned short

unsigned short int

 

long

long int

signed long int

unsigned long

unsigned log int

 


En este cuadro observe que:

  • signed y unsigned solo pueden usarse con int, short y long.
  • La palabras clave signed y unsigned, usadas aisladamente, significan signed int y unsigned int.
  • Con int solo pueden usarse long o short.
  • Las palabras clave long y short usadas aisladamente significan long int y short int.

Los int fueron originariamente pensados para ser del tamaño de la palabra "natural" del procesador, aunque muchos procesadores modernos pueden manejar con igual facilidad palabras de diverso tamaño.


  Por una larga tradición de C, el especificador int podía omitirse en la mayoría de los casos, ya que se suponía por defecto. Además, si no se indicaba otra cosa, se suponía también signed int.  Sin embargo, en C++ es necesario especificarlo siempre, ya que es un lenguaje que se preocupa más de la integridad de los tipos utilizados (lo que puede prevenir muchos problemas).

int x;             // signed int x

unsigned x;        // unsigned int x

short x;           // short signed int x

unsigned short x;  // unsigned short int x

long x;            // long signed int x

unsigned long x;   // unsigned long int x

Nota: por una larga tradición C, los tipos carácter (char ) y los enteros (int) eran intercambiables.

La longitud (bits) y rango son los indicados (para Borland C++). En cualquier caso, los rangos vienen indicados por las constantes que se señalan (incluidas en <limits.h>):

Tipo

Tamaño bits

Rango

int

32

 -2,147,483,648 <= X <= 2,147,483,647

signed short

16

 -32767 <= X <= 32767

unsigned short

16

  0 <= X <= 65535


El Estándar C++ establece que los denominados enteros con signo ("Signed integer types") incluyen: signed char, short intint y long int. Y que cada tipo debe tener almacenamiento suficiente para alojar al precedente en la lista anterior.

También establece otra categoría: los enteros sin signo ("Unsigned integer types") que comprende: unsigned charunsigned short int, unsigned int y unsigned long int, que deben tener el mismo almacenamiento y alineación ( 4.6.1) que los correspondientes con signo.

§4 Carácter

El especificador char se utiliza para definir un dato tipo carácter. Se almacenan en 1 byte, es decir, siempre ocurre que sizeof(char) == 1. Esta es la definición ANSI de byte en C/C++: la memoria requerida para almacenar un carácter [4].

Sintaxis:

[signed|unsigned] char <nombre-de-variable>

Descripción:

Un char puede ser con signo (signed), sin signo (unsigned), o no especificado; por defecto se suponen con signo. Los objetos declarados de tipo carácter (char) pueden contener cualquiera de los caracteres del juego de ASCII básico. Son valores que pueden asimilarse fácilmente a los enteros; de hecho se pueden realizar con ellos operaciones aritméticas, el compilador los trata entonces como valores numéricos. En estos casos puede pensarse en char como un número muy pequeño, por lo que pueden ser usados libremente en expresiones aritméticas, lo que proporciona considerable flexibilidad en cierto tipo de transformaciones con caracteres ( 2.2).

Más sobre los tipos carácter en general ( 2.2.1a)

Ejemplos:

char letra, caracter = 'x';
letra = 'a'      // carácter es 'x'
char s[];        // suponemos una matriz de caracteres
s[i] - '0'       /* valor numérico del carácter en s[] para las

                  cifras del 0 al 9 */


Especificadores de carácter (sinónimos en la misma línea)

char

signed char  (si char es signed por defecto).

unsigned char

 

char

unsigned char  (si char es unsigned por defecto).

signed char

 


Como se ha señalado, C++ permite que char sea signed o unsigned; por defecto (si no se especifica otra cosa) es signed.  Si el defecto se establece a unsigned,  entonces la declaración: char ch; declara ch como unsigned, y para modificar el valor por defecto es necesario consignar: signed char ch;. Análogamente, si se ha establecido signed char por defecto, para declarar un unsigned char es necesario especificar explícitamente: unsigned char ch;.

En C++Builder, el valor que se tomará por defecto puede ser establecido mediante un comando de compilación: -K- establece signed; -K establece unsigned. En los ejecutables construidos con este compilador, el valor que se está tomando por defecto está descrito en una constante manifiesta ( 1.4.1a).


Además de los tradicionales (heredados del C clásico), el ANSI C++ define un nuevo tipo de dato tipo carácter, el denominado carácter ancho wchar_t ( 2.2.1a), adecuado para representar el juego de caracteres Unicode.

§5 Fraccionarios

También llamados de coma flotante; son float  y double.  Solo se permite un modificador adicional (long), dando lugar a los long double, de forma que son en realidad tres tipos: float, double y long double ( 2.2.4 Tipos básicos y representación interna).

Sintaxis:

float <nombre-de-variable>

[signed|unsigned] double <nombre-de-variable>

Ejemplos:

float x = 3.14;

double y = 6.2;

long double z = 66.01;

Tanto el especificador float como el double son palabras clave que definen un identificador como de tipo numérico de coma flotante (fraccionario). La razón principal de usar floats es economizar espacio o tiempo, cuando se almacenan grandes arrays de números en máquinas donde el uso de double es muy costoso en términos de tiempo de computación. En ciertos casos los tipos float pueden ser promovidos a double de forma automática por el compilador ( 4.4.1a).

   El almacenamiento interno y rango de los tipos fraccionarios es dependiente de la implementación. Es decir, cada compilador C++ es libre de definirlos a su manera ( 2.2.4).  El Estándar solo establece para ellos las siguientes condiciones [5]:

  • El tipo double permitirá al menos tanta precisión como el float.
  • El tipo long double permitirá al menos tanta precisión como el double.
  • El rango de valores de float será un subconjunto del rango de los double.
  • El rango de valores de double será un subconjunto del rango de los long double.
§6 void

void es palabra reservada, indicador de un tipo especial: ausencia de valor.

Sintaxis:

void identificador

Es importante señalar que void es tratado por el compilador como un tipo, del mismo modo que pueden serlo int, char, double, etc. Aunque de tipo un poco especial (un tipo incompleto 4.1.2).  El hecho de que signifique ausencia de valor no es inconveniente para que sea un tipo con todas sus características. Podríamos suponer que representa al conjunto de los tipos lo que el cero al conjunto de los números. La única diferencia respecto a cualquier tipo normal es que tiene una sola instancia o representante; él mismo. Dicho en otras palabras:

int entero;        // Ok. entero es una instancia de la clase int

char caracter;     // Ok. caracter es una instancia de la clase char

void novalor;      // Error. uso no permitido.

En cambio si están permitidas con él otras expresiones propias de los tipos:

return void(0);    // Ok. modelado de tipo

void* puntero;     // Ok. puntero es puntero-a-void

void func();       // Ok. tipo devuelto por función


void se usa de varias formas:

  • Para indicar que una función no devuelve ningún valor o no acepta ningún parámetro ( 4.4.7).
  • Para crear punteros genéricos ( 4.2.1d).
  • Para modelado de tipo ( 4.9.9).

Ejemplos:

int x;

void* p;    // define un puntero-a-nulo (genérico)

p = &x;     // p señala a x (pasa a ser puntero-a-int)

Uso de void para señalar que una función no acepta ningún parámetro:

int main (void) {

  ...

}

Uso de void para para definir una función que no devuelve ningún valor [2]:

void saludo(char *nombre) {

  printf("Hola, %s.",nombre);

}

Uso de void para devolver void (se trata en realidad de un modelado de tipo)

void saludo(char *nombre) {

  printf("Hola, %s.",nombre);

  return (void)0;

}

Uso de void para modelado de tipo [1]

#ifdef NDEBUG
#define assert(p) ((void)0)
#else
#define assert(p) ((p) ? (void)0 : _assert(#p, __FILE__, __LINE__))
#endif


  Inicio.


[1] Definición de la función assert() de la Librería Estándar en <assert.h>

[2] Existen un tipo de métodos (funciones miembros de clases), que representan una excepción sintáctica respecto a lo anterior: no devuelven ningún valor, ni siquiera void, son los constructores y destructores ( 4.11.2d).  En C/C++ devolver no-valor es distinto de no-devolver-nada (cosas de los informáticos ;-)

[3] Ver congruencia estándar de argumentos ( 4.4.1a).

[4]  Carácter significa aquí un conjunto de símbolos, que generalmente corresponden a letras y dígitos, que el compilador es capaz de manejar e interpretar (por ejemplo, los "fuentes" están escritos con estos caracteres). El compilador reconoce un conjunto ("character set") de estos caracteres, generalmente el conjunto US-ASCII ( 2.2.1a).

[5]  Como consecuencia de esta permisividad, pueden existir implementaciones en las que, por ejemplo, los tipos float, double, y long double tengan el mismo rango e incluso la misma representación interna. Sin embargo, el Estándar establece que aún en estos casos sus tipos serán considerados distintos.