Hasta ahora, se han visto los tipos básicos de datos
char
, int
, float
, y
double
, junto con los especificadores
signed
, unsigned
, short
,
y long
, que se pueden utilizar con los tipos
básicos de datos en casi cualquier combinación. Ahora hemos
añadido los punteros y las referencias, que son lo ortogonal a
los tipos básicos de datos y los especificadores, de modo que
las combinaciones posibles se acaban de triplicar:
//: C03:AllDefinitions.cpp // All possible combinations of basic data types, // specifiers, pointers and references #include <iostream> using namespace std; void f1(char c, int i, float f, double d); void f2(short int si, long int li, long double ld); void f3(unsigned char uc, unsigned int ui, unsigned short int usi, unsigned long int uli); void f4(char* cp, int* ip, float* fp, double* dp); void f5(short int* sip, long int* lip, long double* ldp); void f6(unsigned char* ucp, unsigned int* uip, unsigned short int* usip, unsigned long int* ulip); void f7(char& cr, int& ir, float& fr, double& dr); void f8(short int& sir, long int& lir, long double& ldr); void f9(unsigned char& ucr, unsigned int& uir, unsigned short int& usir, unsigned long int& ulir); int main() {} ///:~
Listado 3.18. C03/AllDefinitions.cpp
Los punteros y las referencias entran en juego también cuando se pasan objetos dentro y fuera de las funciones; aprenderá sobre ello en un capítulo posterior.
Hay otro tipo que funciona con punteros: void
. Si
se establece que un puntero es un void*
, significa
que cualquier tipo de dirección se puede asignar a ese puntero
(en cambio si tiene un int*
, sólo puede asignar la
dirección de una variable int
a ese puntero). Por
ejemplo:
//: C03:VoidPointer.cpp int main() { void* vp; char c; int i; float f; double d; // The address of ANY type can be // assigned to a void pointer: vp = &c; vp = &i; vp = &f; vp = &d; } ///:~
Listado 3.19. C03/VoidPointer.cpp
Una vez que se asigna a un void*
se pierde
cualquier información sobre el tipo de la variables. Esto
significa que antes de que se pueda utilizar el puntero, se debe
moldear al tipo correcto:
//: C03:CastFromVoidPointer.cpp int main() { int i = 99; void* vp = &i; // Can't dereference a void pointer: // *vp = 3; // Compile-time error // Must cast back to int before dereferencing: *((int*)vp) = 3; } ///:~
Listado 3.20. C03/CastFromVoidPointer.cpp
El molde (int*)vp
toma el void*
y le
dice al compilador que lo trate como un int*
, y de
ese modo se puede dereferenciar correctamente. Puede observar
que esta sintaxis es horrible, y lo es, pero es peor que eso -
el void*
introduce un agujero en el sistema de
tipos del lenguaje. Eso significa, que permite, o incluso
promueve, el tratamiento de un tipo como si fuera otro tipo. En
el ejemplo anterior, se trata un int
como un
int
mediante el moldeado de vp
a
int*
, pero no hay nada que indique que no se lo
puede moldear a char*
o double*
, lo
que modificaría una cantidad diferente de espacio que ha sido
asignada al int
, lo que posiblemente provocará que
el programa falle.. En general, los punteros void
deberían ser evitados, y utilizados únicamente en raras
ocasiones, que no se podrán considerar hasta bastante más
adelante en el libro.
No se puede tener una referencia void
, por razones
que se explicarán en el capítulo 11.