Declaración de punteros
Declara una variable de tipo puntero o puntero a miembro.
Contenido |
[editar] Sintaxis
Una declaración de puntero es cualquier declaración simple cuyo declarador tiene la forma
* atrib(opcional) cv(opcional) declarador
|
(1) | ||||||||
especificador-de-nombre-anidado * atrib(opcional) cv(opcional) declarador
|
(2) | ||||||||
D
como un puntero al tipo determinado por la sec-especificadores-de-declaración S
.D
como un puntero a un miembro no estático de C
del tipo determinado por la sec-especificadores-de-declaración S
.declarador | - | Cualquier declarador excepto un declarador de referencia (no existen punteros a referencias). Puede ser otro declarador de puntero (se permiten los punteros a punteros). |
atrib(C++11) | - | Lista opcional de atributos |
cv | - | Calificación const/volatile que se aplica al puntero que está siendo declarado (no al tipo al que apunta el puntero, cuyas calificaciones son parte de la sec-especificadores-de-declaración) |
especificador-de-nombre-anidado | - | Una secuencia de nombres y operadores de resolución de ámbito ::
|
No existen punteros a referencias y no existen punteros a campos de bits. Habitualmente, las menciones de "punteros" sin mayor elaboración no incluye los punteros a miembros (no estáticos).
[editar] Punteros
Cada valor de un tipo puntero es uno de los siguientes:
- un puntero a un objeto o función (en cuyo caso se dice que el puntero apunta al objeto o apunta a la función), o
- un puntero después del final de un objeto, o
- el valor del puntero nulo para ese tipo, o
- un valor de puntero inválido.
Un puntero que apunta a un objeto representa la dirección del primer byte en memoria ocupado por el objeto. Un puntero después del final de un objeto representa la dirección del primer byte en memoria después del final del almacenamiento ocupado por el objeto.
No obstante, observa que los dos punteros que representan la misma dirección pueden tener valores distintos.
struct C { int x, y; } c; int* px = &c.x; // el valor de px es "puntero a c.x" int* pxe= px + 1; // el valor de pxe es "puntero después del final de c.x" int* py = &c.y; // el valor de py es "puntero a c.y" assert(pxe == py); // == prueba si dos punteros representan la misma dirección // puede o no disparar la aserción *pxe = 1; // comportamiento indefinido aun si la aserción no se dispara
La indirección a través de un valor de puntero inválido y el paso de un valor de puntero inválido a una función de desasignación tienen un comportamiento indefinido. Cualquier otro uso de un valor de puntero inválido tiene un comportamiento definido por la implementación.
[editar] Punteros a objetos
Un puntero a objeto puede inicializarse con el valor de retorno del operador dirección de aplicado a cualquier expresión de tipo objeto, incluyendo otro tipo puntero:
int n; int* np = &n; // puntero a int int* const* npp = &np; // puntero no-const a puntero const a int no-const int a[2]; int (*ap)[2] = &a; // puntero a array de int struct S { int n; }; S s = {1}; int* sp = &s.n; // puntero al int que es un miembro de s
Los punteros pueden aparecer como operandos del operador de indirección integrado (operator*
unario), que devuelve la expresión l-valor que identifica el objeto al que se apunta:
int n; int* p = &n; // puntero a n int& r = *p; // la referencia se vincula a la expresión l-valor que identifica a n r = 7; // almacena el int 7 en n std::cout << *p; // conversión implícita de l-valor a r-valor lee el valor desde n
Los punteros a objetos clase también pueden aparecer como los operandos del lado izquierdo de los operadores de acceso a miembro operator-> y operator->*.
Debido a la conversión implícita de array a puntero, un puntero al primer elemento de un array puede inicializarse con una expresión de tipo array:
int a[2]; int* p1 = a; // puntero al primer elemento a[0] (un int) del array a int b[6][3][8]; int (*p2)[3][8] = b; // puntero al primer elemento b[0] del array b, // que es un array de 3 arrays de 8 ints
Debido a la conversión implícita de clase derivada a clase base para punteros, un puntero a una clase base puede inicializarse con la dirección de una clase derivada:
struct Base {}; struct Derived : Base {}; Derived d; Base* p = &d;
Si Derived
es polimórfica, tal puntero puede usarse para hacer llamadas a funciones virtuales.
Ciertos operadores de adición, sustracción, incremento y decremento se definen para punteros a elementos de arrays: tales punteros satisfacen los requerimientos de RandomAccessIterator y permiten a algoritmos de la biblioteca de C++ funcionar con arrays sin formato.
Los operadores de comparación se definen para punteros a objetos en algunas situaciones: dos punteros que representan la misma dirección se comparan iguales, los valores de puntero nulo se comparan iguales, punteros a elementos del mismo array se comparan iguales que los índices del array de esos elementos, y punteros a datos miembros no estáticos con el mismo acceso a miembro se comparan en el orden de declaración de esos miembros.
Varias implementaciones también proporcionan un orden total estricto de punteros de origen aleatorio. Por ejemplo, si se implementan como direcciones dentro del espacio de direcciones virtuales continuo. Aquellas implementaciones que no lo hacen (por ejemplo, cuando no todos los bits del puntero son parte de una dirección de memoria y deben ignorarse para la comparación, o se requiere un cálculo adicional o de lo contrario el puntero y el entero no son una relación 1 a 1), proporcionan una especialización de std::less para punteros que tienen esa garantía. Esto hace posible utilizar todos los punteros de origen aleatorio como claves en contenedores asociativos estándar como std::set o std::map.
[editar] Punteros a void
Un puntero a objeto de cualquier tipo puede ser convertido implícitamente a un puntero a void (opcionalmente calificado-cv); el valor del puntero no cambia. La condición inversa, que requiere static_cast o explicit cast, produce el valor del puntero original:
int n = 1; int* p1 = &n; void* pv = p1; int* p2 = static_cast<int*>(pv); std::cout << *p2 << '\n'; // imprime 1
Si el puntero original apunta a un subobjeto de clase base dentro de un objeto de algún tipo polimórfico, se puede usar dynamic_cast para obtener un void*
que apunta al objeto completo del tipo más derivado.
Los punteros a void
se utilizan para pasar objetos de tipo desconocido, lo cual es común en interfaces de C: std::malloc devuelve void*, std::qsort espera una función de devolución de llamada proporcionada por el usuario que acepta dos argumentos const void*. pthread_create espera una función de devolución de llamada proporcionada por el usuario que acepta y devuelve void*. En todos los casos, es responsabilidad del código que llama convertir el puntero al tipo correcto antes de usarlo.
[editar] Punteros a funciones
Un puntero a función se puede inicializar con una dirección de una función no miembro o una función miembro estática. Debido a la conversión implícita de función a puntero, el operador dirección de es opcional:
void f(int); void (*p1)(int) = &f; void (*p2)(int) = f; // igual que &f
A diferencia de las funciones y referencias a funciones, los punteros a funciones son objetos y por lo tanto pueden almacenarse en arrays, copiarse, asignarse, etc.
Sintaxis | Significado |
---|---|
int (* f)() | f es un puntero a una función con prototipo int func(). |
int (* f())() | f es una función que no recibe datos de entrada y devuelve un puntero a una función con prototipo int func(). |
int * f() | f es una función que devuelve un puntero a int. |
int (* a[])() | a es un array de punteros a funciones, cada una con un prototipo int func(). |
int (* f())[] | f es una función que devuelve un puntero a un array. |
int (f[])() | Prohibido. No se puede tener un array de funciones. |
int * const *(*g)(float) | g es un puntero a una función con prototipo int * const * func(float) donde su valor de retorno int * const * es un puntero de solo lectura a int. |
Véase también declaraciones para mayor información acerca de cómo leer declaraciones de funciones, punteros y punteros a función.
Un puntero a función puede usarse como el operando del lado izquierdo del operador de llamada a función, esto invoca la función a la que se apunta:
int f(int n) { std::cout << n << '\n'; return n * n; } int main() { int (*p)(int) = f; int x = p(7); }
Desreferenciar un puntero a función produce el l-valor que identifica la función a la que se apunta:
int f(); int (*p)() = f; // el puntero p apunta a f int (&r)() = *p; // el l-valor que identifica a f se vincula a una referencia r(); // la función f invocada a través de una referencia l-valor (*p)(); // la función f invocada a través de un l-valor de función p(); // la función f invocada directamente a través del puntero
Un puntero a función se puede inicializar desde un conjunto de sobrecarga que puede incluir funciones, especializaciones de plantilla de función y plantillas de función, si solo una sobrecarga coincide con el tipo de puntero (véase dirección de una función sobrecargada para más detalles):
template<typename T> T f(T n) { return n; } double f(double n) { return n; } int main() { int (*p)(int) = f; // ejempla y selecciona a f<int> }
Los operadores de comparación de igualdad se definen para punteros a función (se comparan iguales si apuntan a la misma función).
[editar] Punteros a miembros
[editar] Punteros a datos miembro
Un puntero a un objeto miembro no estático m
que es miembro de la clase C
se puede inicializar con la expresión &C::m
exactamente. Expresiones como &(C::m)
o &m
dentro de la función miembro de C no forman punteros a miembros.
Dicho puntero se puede utilizar como el operando de la derecha de las operaciones de accesso de puntero a miembro operator.*
y operator->*
:
El puntero al dato miembro de una clase base no virtual no ambigua accesible se puede convertir implícitamente al puntero al mismo dato miembro de una clase derivada:
struct Base { int m; }; struct Derived : Base {}; int main() { int Base::* bp = &Base::m; int Derived::* dp = bp; Derived d; d.m = 1; std::cout << d.*dp << ' ' << d.*bp << '\n'; // imprime 1 1 }
La conversión en la dirección opuesta, de un puntero a un dato miembro de una clase derivada a un puntero a un dato miembro de una clase base no virtual inequívoca, se permite con explicit cast y explicit cast , incluso si la clase base no tiene ese miembro (pero la clase más derivada sí, cuando se usa el puntero para acceso):
El tipo apuntado de un puntero a miembro puede ser un puntero a miembro en sí mismo: los punteros a miembro pueden ser multinivel y pueden calificarse con cv de manera diferente en cada nivel. También se permiten combinaciones mixtas de varios niveles de punteros y punteros a miembros:
struct A { int m; // puntero const a miembro no-const int A::* const p; }; int main() { // un puntero no-const a dato miembro que es un puntero const a un miembro no-const int A::* const A::* p1 = &A::p; const A a = {1, &A::m}; std::cout << a.*(a.*p1) << '\n'; // imprime 1 // puntero no-const a un puntero a miembro const regular int A::* const* p2 = &a.p; std::cout << a.**p2 << '\n'; // imprime 1 }
[editar] Punteros a funciones miembro
Un puntero a una función miembro no estática f
que es miembro de la clase C
se puede inicializar con la expresión &C::f
exactamente. Expresiones como &(C::f)
o &f
dentro de la función miembro de C no forman punteros a funciones miembro.
Dicho puntero puede usarse como el operando de la derecha de los operadores de acceso a puntero a miembro operator.*
y operator->*
. La expresión resultante solo se puede usar como el operando de la izquierda de un operador de llamada a función:
struct C { void f(int n) { std::cout << n << '\n'; } }; int main() { void (C::* p)(int) = &C::f; // puntero a función miembro f de la clase C C c; (c.*p)(1); // imprime 1 C* cp = &c; (cp->*p)(2); // imprime 2 }
Un puntero a función miembro de una clase base se puede convertir implícitamente a un puntero a la misma función miembro de una clase derivada:
struct Base { void f(int n) { std::cout << n << '\n'; } }; struct Derived : Base {}; int main() { void (Base::* bp)(int) = &Base::f; void (Derived::* dp)(int) = bp; Derived d; (d.*dp)(1); (d.*bp)(2); }
La conversión en la dirección opuesta, de un puntero a una función miembro de una clase derivada a un puntero a una función miembro de una clase base no virtual inequívoca, se permite con static_cast y explicit cast , incluso si la clase base no tiene esa función miembro (pero la clase más derivada sí la tiene, cuando se usa el puntero para acceso):
struct Base {}; struct Derived : Base { void f(int n) { std::cout << n << '\n'; } }; int main() { void (Derived::* dp)(int) = &Derived::f; void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp); Derived d; (d.*bp)(1); // de acuerdo: imprime 1 Base b; (b.*bp)(2); // comportamiento indefinido }
Los punteros a funciones miembro pueden usarse como devoluciones de llamada o como objetos función, frecuentemente después de aplicar std::mem_fn o std::bind:
#include <iostream> #include <string> #include <algorithm> #include <functional> int main() { std::vector<std::string> v = {"a", "ab", "abc"}; std::vector<std::size_t> l; transform(v.begin(), v.end(), std::back_inserter(l), std::mem_fn(&std::string::size)); for(std::size_t n : l) std::cout << n << ' '; }
Salida:
1 2 3
[editar] Punteros nulos
Los punteros de cada tipo tienen un valor especial conocido como "valor de puntero nulo" de ese tipo. Un puntero cuyo valor es nulo no apunta a un objeto o función (desreferenciar un puntero nulo es un comportamiento indefinido), y se compara igual a todos los punteros del mismo tipo cuyo valor también es "nulo".
Para inicializar un puntero a nulo o asignar el valor nulo a un puntero existente, puede usarse el literal de puntero nulo nullptr, la constante de puntero nulo NULL, o la conversión implícita del valor entero 0.
La inicialización cero y la inicialización de un valor también inicializan los punteros a sus valores nulos.
Se pueden usar punteros nulos para indicar la ausencia de un objeto (por ejemplo, function::target()) o como otros indicadores de condición de error (por ejemplo, dynamic_cast ). En general, una función que recibe un argumento de puntero casi siempre necesita verificar si el valor es nulo y manejar ese caso de manera diferente (por ejemplo, la expresión delete
no hace nada cuando se pasa un puntero nulo).
[editar] Constantitud
- Si cv aparece antes que
*
en la declaración de puntero, es parte de la sec-especificadores-de-declaración y se aplica al objeto al que se apunta. - Si cv aparece después que
*
en la declaración de puntero, es parte del declarador y se aplica al puntero que está siendo declarado.
Sintaxis | Significado |
---|---|
const T* | Puntero a objeto constante |
T const* | Puntero a objeto constante |
T* const | Puntero constante a objeto |
const T* const | Puntero constante a objeto constante |
T const* const | Puntero constante a objeto constante |
// pc es un puntero no-const a const int // cpc es un puntero const a const int // ppc es un puntero no-const a un puntero no-const a const int const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; // p es un puntero no-const a no-const int // cp es un puntero const a no-const int int i, *p, *const cp = &i; i = ci; // de acuerdo: el valor de const int se copia a no-const int *cp = ci; // de acuerdo: no-const int (apuntado por puntero const ) puede cambiarse pc++; // de acuerdo: puntero no-const (a const int) puede cambiarse pc = cpc; // de acuerdo: puntero no-const (a const int) puede cambiarse pc = p; // de acuerdo: puntero no-const (a const int) puede cambiarse ppc = &pc; // de acuerdo: dirección de puntero a const int es un puntero a puntero a const int ci = 1; // ERROR: const int no puede cambiarse ci++; // ERROR: const int no puede cambiarse *pc = 2; // ERROR: const int al que se apunta no puede cambiarse cp = &ci; // ERROR: puntero const (a no-const int) no puede cambiarse cpc++; // ERROR: puntero const (a const int) no puede cambiarse p = pc; // ERROR: puntero a no-const int no puede apuntar a const int ppc = &p; // ERROR: puntero a puntero a const int no puede apuntar a // puntero a no-const int
En general, la conversión implícita de un puntero multinivel a otro sigue las reglas descritas en las conversiones de calificación y en los operadores de comparación de punteros.
[editar] Véase también
Documentación de C para Declaración de punteros
|