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.