Espacios de nombres
Variantes
Acciones

Declaración de punteros

De cppreference.com
< cpp‎ | language
 
 
 
 

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)
1) Declarador de puntero: la declaración S* D; declara a D como un puntero al tipo determinado por la sec-especificadores-de-declaración S.
2) Declarador de puntero a miembro: la declaración S C::* D; declara a 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->*:

struct C { int m; };
 
int main()
{
    int C::* p = &C::m;          // puntero a dato miembro m de la clase C
    C c = {7};
    std::cout << c.*p << '\n';   // imprime 7
    C* cp = &c;
    cp->m = 10;
    std::cout << cp->*p << '\n'; // imprime 10
}

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):

struct Base {};
struct Derived : Base { int m; };
 
int main()
{
    int Derived::* dp = &Derived::m;
    int Base::* bp = static_cast<int Base::*>(dp);
 
    Derived d;
    d.m = 7;
    std::cout << d.*bp << '\n'; // de acuerdo: imprime 7
 
    Base b;
    std::cout << b.*bp << '\n'; // comportamiento indefinido
}

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