Punteros II

Punteros a 'void'

Un puntero puede apuntar a un objeto de cualquier tipo, predefinido por el lenguaje o definido por el usuario. 'Void' es el nombre de un tipo, pero su uso esta sujeto a mayores restricciones respecto a otros tipos.
El termino 'void' puede ser usado como tipo de una funcion o de un puntero, pero no para declarar un objeto. Las declaraciones posibles de tipo void, en C y C++, se resumen en el siguiente cuadro.

Declaraciones

C

C++

Objeto de tipo void
Ej:
     void x;
 No permitido. Mensaje de error : "Objeto de sizeof desconocido."

 El compilador no esta en condiciones de determinar el monto de  
 memoria que requiere el objeto.
Retorno de un funcion
Ej:
void func(){
......................
 Significa que la funcion no retorna  ningun valor
 
 ('tipo pseudodevuelto').
Puntero a void
Ej:
void* p;
 Un puntero a void es tratado
 como un puntero a char.
 Conversion implicita.
 Puntero a objeto de tipo desconocido.
 Requiere conversion explicita a otro tipo
 antes de ser utilizado.


En C se accede a bytes no vinculados a ningun tipo mediante punteros a char, esto es natural si se considera que un 'char' ocupa 1 byte de almacenamiento, y de ahi que exista conversion implicita entre ambos tipos.
La funcion C standard "memset()", retorna un puntero a void. El siguiente codigo es aceptable en C:

void f(char*cad, char ch, int n){
char* s;
s = memset(cad,ch,n);             //conversion implicita de void* a char*
}                                 //valido en C, pero no en C++. 

C++ no lo permite debido a su mas estricta comprobacion de tipos. Lanza el mensaje de error, "no se puede convertir void* a char*". Hay otras funciones similares a memset que derivan de C y retornan void, algunas declaradas en "mem.h" y "string.h". Todas pueden ser utilizadas igualmente en C++, pues la cadena afectada por la funcion es enviada como parametro. Si se quisiera utilizar el puntero de retorno seria necesario un puntero a "void" o una conversion explicita, por ejemplo:

s = (char*)memset(cad,ch,n);     //valido en C++

El significado en C++ de un puntero a 'void' es el de un puntero que apunta a una zona de memoria no inicializada, memoria 'en bruto', o en la cual se encuentra almacenado un objeto de tipo desconocido, en general se trata de codigo que trabaja a nivel hardware o relacionado con administracion de memoria.

Las operaciones permitidas y no permitidas, en C++, para un puntero a void se resumen en el siguiente cuadro:

Operaciones
permitidas
1- Asignar a void* un puntero de cualqueir tipo void*=T*
2- Asignar un void a otro void void* = void*
3- Convertir explicitamente un void a otro tipo T* = (T*)void*
4-Comparaciones de igualdad o desigualdad entre void* (void*!=void*)
Operaciones
prohibidas
1-Usar un void (void*)++;
2-Convertir implicitamente un void a otro tipo (no void) T* = void*
3-Desreferenciar un void. *v;
4-Asignar a void punteros a funciones o a miembros

Punteros y 'const'

Un puntero implica la intervencion de dos elementos: el puntero y el objeto apuntado (salvo que sea nulo). El termino reservado "const" puede tener dos significados diferentes segun el sitio que ocupe en la declaracion, haciendo constante al puntero o al objeto apuntado.

1-Puntero constante

El operador '*const', en lugar de '*' solo, declara al puntero como constante, esto significa que la direccion a la que apunta el puntero no puede cambiar en todo el programa. La variable apuntada si puede cambiar. Ejemplo:

int a = 5;               
int *const ptr = &a;       //Puntero constante a int
*ptr = 4;                  //Bien, se modifica la variable
ptr = NULL;                //Error, intento de modificar el puntero constante

Al no poder modificar la direccion a la que apuntan, estos punteros se aproximan al sentido que tiene una referencia

2-Puntero a constante

Aqui el termino "const" afecta al tipo al que apunta el puntero.

int a = 5;
const int* ptr = &a;
ptr = NULL;             //Bien, el puntero puede cambiar, ser reasignado
*ptr = 6;               //Error. No se puede cambiar el objeto apuntado.


Es importante observar que en el ejemplo la variable 'a' no fue declarada originalmente como "const", pero el puntero la toma como "constante". En este sentido, aunque la variable no puede ser modificada a traves de ese puntero, si podria serlo a traves de otro identificador, el propio nombre de la variable u otro puntero que no apunte a const.

Declarar un puntero a const suele ser util al declarar argumentos de funciones, sirve para especificar que el argumento puntero no puede ser modificado dentro de la funcion. La funcion C standars "strcpy", en su declaracion:

char* strcpy (char*p, const char* q);

impide que el segundo argumento sea modificado por la funcion. La funcion copia el contenido de 'q' en 'p', por esa razon el primer argumento, que no es 'const', sera modificado. Esto no significa que al llamar a la funcion el segundo parametro necesite ser una constante, es 'tomado' como constante por el puntero de la funcion.

El siguiente cuadro resume la sintaxis de punteros y 'const':

Entidad Ejemplo Comentario
Puntero constante int *const ptr = &a;  Puntero constante, no puede modificar la direccion a la que
apunta.
Puntero a const const int* ptr = &a;


int const* ptr = &a;
Puntero que apunta a const. No puede modificarse el
objeto apuntado a traves de ese puntero.

Notacion alternativa para puntero a const.
Puntero const
a const
const int *const ptr = &a; No puede modificarse la direccion apuntada ni el objeto
a traves de ese puntero.

Como ya hemos visto, se puede asignar una variable no constante a un puntero a constante, esto por la razon de que no puede producir ningun perjuicio, un puntero a const es un puntero con restricciones. Lo inverso, asignar una variable constante a un puntero que no apunte a const, no esta permitido, pues se perderia el sentido de haber restringido la operatividad de la variable y existiria el peligro de modificar sus datos.

Puntero nulo ("Null pointer")

Algunos autores definen a este puntero como "aquel que no apunta a ningun sitio" y otros como "un puntero que no apunta a ningun objeto" (Stourtrup-1997). La segunda definicion es mas clara, mientras que la primera puede introducir alguna confusion. De hecho no esta claro que podria significar que 'no apuntar a nigun sitio'. El concepto de 'puntero nulo' existe por la necesidad practica de hablar de un puntero que no esta ligado a ningun objeto, muchisimas funciones de las librerias de c/c++ devuelven punteros y entre los posibles valores de retorno cuentan con el de puntero nulo (ej: strchr () ), tambien se presenta cuando solicitamos memoria dinamica, el operador 'new' retorna un puntero nulo si no hay memoria suficiente (no todos los compiladores).

La localidad de memoria donde esta el puntero contiene siempre algun valor (!no existen celdas vacias! cero es un valor!). Un puntero apunta a una direccion, la indicada por el valor que almacena, por lo tanto es logico concluir que un puntero siempre apunta a algun sitio.

Lo que distingue a un puntero nulo no es que 'no apunte a ningun sitio' sino que apunta a alguna localidad de memoria que, por convencion del compilador utilizado, no puede estar asociada a ningun objeto o variable. Ese valor, esa direccion 'prohibida' para almacenar alli algun objeto, varia para diferentes compiladores, para Borland (y la mayoria) es la direccion 0 (cero) del segmento de datos. Cuando el puntero apunta a la localidad 0 el compilador considera que su valor es 'null', o lo que es lo mismo, para este compilador 'null' es equivalente a cero. Esa direccion existe y es el comienzo del segmento de datos-stack, puede ser visualizado, y si no hay errores su valor deberia ser 0.

Existe otro concepto que no debe confundirse con el de puntero nulo, el de "wild pointer". Un puntero nulo apunta a un sitio bien determinado, en cambio un 'wild pointer' puede estar apuntando a cualquier sitio, una direccion indeterminada dentro del segmento.

Puntero a puntero

Un puntero almacena la direccion de un objeto, puesto que ese objeto puede ser otro puntero, es posible declarar un puntero que apunta a puntero. La notacion de puntero a puntero requiere de un doble asterisco, '**', la sola notacion suele generar un efecto de confusion considerable, y es la razon de que Mats Henricson y Erik Nyquist, en Rules and Recommendations on C++, sugieran en lo posible reemplazar punteros a punteros por alguna otra alternativa (una clase con miembro puntero) en su Rec 48.

Sin embargo, como se vera, el concepto en si mismo no es complejo. La relacion entre una variable comun, un puntero y un puntero a puntero se muestra en las siguientes lineas:

int a = 4;
int* pt1 = &a;
int**pt2 = &pt1;

Por un lado tenemos el valor que almacena la variable 'a', el puntero 'pt1' almacena la direccion de esa variable, y el puntero 'pt2' almacena la direccion del puntero 'pt1'. Son tres identificadores, cada uno tiene un doble aspecto: la localidad de memoria donde se asienta, y el valor que almacena en esa localidad de memoria.

 Declaracion e
 inicializacion
 Direccion de memoria 
 (hipotetica)
 Valor que almacena en
 tal direccion de memoria 

int a = 4;

0xfff6

4

int* pt1 = &a;

0xfff4

0xfff6

 int**pt2 = &pt1; 

0xfff2

0xfff4

Es interesante comprobar las diferentes salidas en pantalla de 'pt2' en los siguientes casos:

cout<<pt2;     //Imprime la direccion del propio puntero 'pt2', aqui: "0xfff2"
cout<<*pt2;    //Imprime la direccion almacenada en 'pt2', "0xfff4"
cout<<**pt2;   //Imprime el valor almacenado en '*pt1 = a', "4".

El comportamiento de la salida en pantalla es coherente, pues se cumplen las siguientes igualdades:

   *pt2 == pt1;           //Desreferenciacion de 'pt2'
*(*pt2) == *(pt1);        //Aplicamos '*' a ambos lados 
   *pt1 == a;             //De esto y la linea previa se deduce...
  **pt2 == a;             //...esta igualdad       

Leanse las anteriores lineas como 'igualdades' (comparaciones que dan 'Verdadero') y no como asignaciones.

La estrecha relacion existente entre los conceptos de puntero y array, es la razon de que el asterisco doble (**) pueda ser interpretado indistintamente como puntero a puntero, o bien como un array de punteros.


PRINCIPAL

Free Web Hosting