Pointer declaration
Puntero es un tipo de objeto que hace referencia a una función o a un objeto de otro tipo, posiblemente añadiendo calificadores. El puntero también puede no hacer referencia a nada, lo cual se indica mediante el valor especial de puntero nulo.
Contenidos |
Sintaxis
En la gramática de declaraciones de una declaración de puntero, la secuencia type-specifier designa el tipo apuntado (que puede ser tipo función o tipo objeto y puede estar incompleto), y el declarator tiene la forma:
*
attr-spec-seq
(opcional)
qualifiers
(opcional)
declarator
|
|||||||||
donde declarator puede ser el identificador que nombra el puntero que se está declarando, incluyendo otro declarador de puntero (lo que indicaría un puntero a un puntero):
float *p, **pp; // p es un puntero a float // pp es un puntero a un puntero a float int (*fp)(int); // fp es un puntero a función con tipo int(int)
Los
calificadores
que aparecen entre
*
y el identificador (u otro declarador anidado) califican el tipo del puntero que se está declarando:
int n; const int * pc = &n; // pc es un puntero no constante a un int constante // *pc = 2; // Error: n no puede ser modificado a través de pc sin un cast pc = NULL; // OK: pc mismo puede ser modificado int * const cp = &n; // cp es un puntero constante a un int no constante *cp = 2; // OK modificar n a través de cp // cp = NULL; // Error: cp mismo no puede ser modificado int * const * pcp = &cp; // puntero no constante a puntero constante a int no constante
La attr-spec-seq (C23) es una lista opcional de atributos , aplicados al puntero declarado.
Explicación
Los punteros se utilizan para la indirección, que es una técnica de programación ubicua; pueden usarse para implementar semántica de paso por referencia, para acceder a objetos con storage duration dinámico, para implementar tipos "opcionales" (usando el valor de puntero nulo), relación de agregación entre estructuras, callbacks (usando punteros a funciones), interfaces genéricas (usando punteros a void), y mucho más.
Punteros a objetos
Un puntero a objeto puede ser inicializado con el resultado del operador de dirección aplicado a una expresión de tipo objeto (que puede ser incompleto):
int n; int *np = &n; // puntero a int int *const *npp = &np; // puntero no constante a puntero constante a int no constante int a[2]; int (*ap)[2] = &a; // puntero a arreglo de int struct S { int n; } s = {1} int* sp = &s.n; // puntero al int que es miembro de s
Los punteros pueden aparecer como operandos del
operador de indirección
(operador unario
*
), que devuelve
el lvalue
que identifica el objeto apuntado:
int n; int* p = &n; // el puntero p está apuntando a n *p = 7; // almacena 7 en n printf("%d\n", *p); // la conversión de lvalue a rvalue lee el valor desde n
Los punteros a objetos de tipo
struct
y
union
también pueden aparecer como operandos izquierdos del operador de
acceso a miembro a través de puntero
->
.
Debido a la conversión implícita de array-a-puntero , el puntero al primer elemento de un array puede ser inicializado con una expresión de tipo array:
int a[2]; int *p = a; // puntero a a[0] int b[3][3]; int (*row)[3] = b; // puntero a b[0]
Ciertos operadores de suma, resta , asignación compuesta , incremento y decremento están definidos para punteros a elementos de arrays.
Operadores de comparación se definen para punteros a objetos en algunas situaciones: dos punteros que representan la misma dirección se comparan iguales, dos valores de puntero nulo se comparan iguales, los punteros a elementos del mismo array se comparan igual que los índices del array de esos elementos, y los punteros a miembros de struct se comparan en orden de declaración de esos miembros.
Muchas implementaciones también proporcionan strict total ordering de punteros de origen aleatorio, por ejemplo, si están implementados como direcciones dentro de un espacio de direcciones virtual continuo ("flat").
Punteros a funciones
Un puntero a función puede inicializarse con la dirección de una función. Debido a la conversión función-a-puntero , el operador dirección-de es opcional:
void f(int); void (*pf1)(int) = &f; void (*pf2)(int) = f; // igual que &f
A diferencia de las funciones, los punteros a funciones son objetos y, por lo tanto, pueden almacenarse en arrays, copiarse, asignarse, pasarse a otras funciones como argumentos, etc.
Un puntero a función puede ser usado en el lado izquierdo del operador de llamada a función ; esto invoca la función apuntada:
#include <stdio.h> int f(int n) { printf("%d\n", n); return n * n; } int main(void) { int (*p)(int) = f; int x = p(7); }
Desreferenciar un puntero a función produce el designador de función para la función apuntada:
int f(); int (*p)() = f; // el puntero p está apuntando a f (*p)(); // función f invocada a través del designador de función p(); // función f invocada directamente a través del puntero
Operadores de comparación de igualdad están definidos para punteros a funciones (comparan igual si apuntan a la misma función).
Debido a que la compatibilidad de tipos de función ignora los calificadores de nivel superior de los parámetros de función, los punteros a funciones cuyos parámetros solo difieren en sus calificadores de nivel superior son intercambiables:
int f(int), fc(const int); int (*pc)(const int) = f; // CORRECTO int (*p)(int) = fc; // CORRECTO pc = p; // CORRECTO
Punteros a void
El puntero a objeto de cualquier tipo puede ser convertido implícitamente a puntero a void (opcionalmente const o volatile -calificado), y viceversa:
int n=1, *p=&n; void* pv = p; // de int* a void* int* p2 = pv; // de void* a int* printf("%d\n", *p2); // imprime 1
Los punteros a void se utilizan para pasar objetos de tipo desconocido, lo cual es común en interfaces genéricas: malloc retorna void * , qsort espera un callback proporcionado por el usuario que acepte dos argumentos const void * . pthread_create espera un callback proporcionado por el usuario que acepte y retorne void * . En todos los casos, es responsabilidad del llamante convertir el puntero al tipo correcto antes de su uso.
Punteros nulos
Los punteros de cada tipo tienen un valor especial conocido como null pointer value de ese tipo. Un puntero cuyo valor es null no apunta a un objeto o a una función (desreferenciar un puntero null es comportamiento indefinido), y se compara igual a todos los punteros del mismo tipo cuyo valor también es null .
Para inicializar un puntero a null o asignar el valor null a un puntero existente, se puede usar una constante de puntero null ( NULL , o cualquier otra constante entera con valor cero). static initialization también inicializa los punteros a sus valores null.
Los punteros nulos pueden indicar la ausencia de un objeto o pueden usarse para indicar otros tipos de condiciones de error. En general, una función que recibe un argumento puntero casi siempre necesita verificar si el valor es nulo y manejar ese caso de manera diferente (por ejemplo, free no hace nada cuando se pasa un puntero nulo).
Notas
Aunque cualquier puntero a objeto puede convertirse a puntero a objeto de un tipo diferente, desreferenciar un puntero a un tipo distinto del tipo declarado del objeto casi siempre es comportamiento indefinido. Consulte strict aliasing para más detalles.
|
Es posible indicar a una función que accede a objetos a través de punteros que esos punteros no generan alias. Consulte restrict para más detalles. |
(since C99) |
Las expresiones lvalue de tipo array, cuando se utilizan en la mayoría de contextos, experimentan una conversión implícita al puntero del primer elemento del array. Consulte array para más detalles.
char *str = "abc"; // "abc" es un array char[4], str es un puntero a 'a'
Los punteros a char a menudo se utilizan para representar cadenas . Para representar una cadena de bytes válida, un puntero debe apuntar a un char que sea un elemento de un array de char, y debe existir un char con valor cero en algún índice mayor o igual al índice del elemento referenciado por el puntero.
Referencias
- Estándar C23 (ISO/IEC 9899:2024):
-
- 6.7.6.1 Declaradores de puntero (p: TBD)
- Estándar C17 (ISO/IEC 9899:2018):
-
- 6.7.6.1 Declaradores de puntero (p: 93-94)
- Estándar C11 (ISO/IEC 9899:2011):
-
- 6.7.6.1 Declaradores de puntero (p: 130)
- Estándar C99 (ISO/IEC 9899:1999):
-
- 6.7.5.1 Declaradores de puntero (p: 115-116)
- Estándar C89/C90 (ISO/IEC 9899:1990):
-
- 3.5.4.1 Declaradores de puntero
Véase también
|
Documentación de C++
para
Declaración de puntero
|