3.4.7. Punteros y Referencias como modificadores

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.