Operadores relacionados con el puntero: tome la dirección de las variables, las ubicaciones de almacenamiento de desreferencia y las ubicaciones de memoria de acceso
Los operadores de puntero permiten tomar la dirección de una variable (&
), desreferenciar un puntero (*
), comparar valores de puntero y agregar o restar punteros e enteros.
Puede usar los siguientes operadores para trabajar con punteros:
- Operador unario
&
(address-of): para obtener la dirección de una variable - Operador unario
*
(direccionamiento indirecto del puntero): para obtener la variable a la que apunta un puntero - Operadores
->
(acceso a miembros) y[]
(acceso de elemento) - Operadores aritméticos
+
,-
,++
y--
- Operadores de comparación
==
,!=
,<
,>
,<=
y>=
Para obtener información sobre los tipos de punteros, vea Tipos de puntero.
Nota
Cualquier operación con punteros requiere un contexto unsafe. El código que contenga bloques no seguros se tendrá que compilar con la opción del compilador AllowUnsafeBlocks.
Operador Address-of y
El operador unario &
devuelve la dirección de su operando:
unsafe
{
int number = 27;
int* pointerToNumber = &number;
Console.WriteLine($"Value of the variable: {number}");
Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4
El operando del operador &
debe ser una variable fija. Las variables fijas son las que residen en ubicaciones de almacenamiento que no se ven afectadas por el funcionamiento del recolector de elementos no utilizados. En el ejemplo anterior, la variable local number
es una variable fija, ya que reside en la pila. Las variables que residen en ubicaciones de almacenamiento que pueden verse afectadas por el recolector de elementos no utilizados (por ejemplo, reubicadas) se denominan variables móviles. Los campos de objeto y los elementos de matriz son ejemplos de variables móviles. Puede obtener la dirección de una variable móvil si la "fija" o "ancla" con una instrucción fixed
. La dirección obtenida solo es válida dentro del bloque de una instrucción fixed
. En el ejemplo siguiente se muestra cómo usar una instrucción fixed
y el operador &
:
unsafe
{
byte[] bytes = { 1, 2, 3 };
fixed (byte* pointerToFirst = &bytes[0])
{
// The address stored in pointerToFirst
// is valid only inside this fixed statement block.
}
}
No se puede obtener la dirección de una constante o un valor.
Para obtener más información sobre las variables fijas y móviles, vea la sección Variables fijas y móviles de Especificación del lenguaje C#.
El operador binario &
calcula el AND lógico de sus operandos booleanos o el AND lógico bit a bit de sus operandos enteros.
Operador de direccionamiento indirecto del puntero *
El operador unario de direccionamiento indirecto del puntero *
obtiene la variable a la que apunta su operando. También se conoce como operador de desreferencia. El operando del operador *
debe ser un tipo de puntero.
unsafe
{
char letter = 'A';
char* pointerToLetter = &letter;
Console.WriteLine($"Value of the `letter` variable: {letter}");
Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");
*pointerToLetter = 'Z';
Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z
No se puede aplicar el operador *
a una expresión de tipo void*
.
El operador binario *
calcula la multiplicación de sus operandos numéricos.
Operador de acceso a miembros de puntero ->
El operador ->
combina el direccionamiento indirecto del puntero y el acceso a miembros. Es decir, si x
es un puntero de tipo T*
y y
es un miembro accesible de tipo T
, una expresión con el formato
x->y
es equivalente a
(*x).y
En el siguiente ejemplo se muestra el uso del operador ->
:
public struct Coords
{
public int X;
public int Y;
public override string ToString() => $"({X}, {Y})";
}
public class PointerMemberAccessExample
{
public static unsafe void Main()
{
Coords coords;
Coords* p = &coords;
p->X = 3;
p->Y = 4;
Console.WriteLine(p->ToString()); // output: (3, 4)
}
}
No se puede aplicar el operador ->
a una expresión de tipo void*
.
Operador de acceso de elemento de puntero []
En el caso de una expresión p
de un tipo de puntero, un acceso de elemento de puntero con el formato p[n]
se evalúa como *(p + n)
, donde n
debe ser de un tipo convertible de forma implícita en int
, uint
, long
o ulong
. Para obtener información sobre el comportamiento del operador +
con punteros, vea la sección Suma o resta de un valor entero en un puntero.
En el ejemplo siguiente se muestra cómo acceder a los elementos de matriz con un puntero y el operador []
:
unsafe
{
char* pointerToChars = stackalloc char[123];
for (int i = 65; i < 123; i++)
{
pointerToChars[i] = (char)i;
}
Console.Write("Uppercase letters: ");
for (int i = 65; i < 91; i++)
{
Console.Write(pointerToChars[i]);
}
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ
En el ejemplo anterior, una expresión stackalloc
asigna un bloque de memoria en la pila.
Nota
El operador de acceso de elemento de puntero no busca errores fuera de límites.
No puede usar []
para el acceso de elemento de puntero con una expresión de tipo void*
.
También puede usar el operador []
para acceso de elemento de matriz o indizador.
Operadores aritméticos de puntero
Puede realizar las siguientes operaciones aritméticas con punteros:
- Agregar o restar un valor entero en un puntero
- Restar dos punteros
- Incrementar o reducir un puntero
No es posible realizar esas operaciones con punteros de tipo void*
.
Para obtener información sobre las operaciones aritméticas admitidas con tipos numéricos, vea Operadores aritméticos.
Suma o resta de un valor entero en un puntero
En el caso de un puntero p
de tipo T*
y una expresión n
de un tipo convertible de forma implícita en int
, uint
, long
o ulong
, la suma y la resta se definen de este modo:
- Ambas expresiones
p + n
yn + p
generan un puntero de tipoT*
que resulta de agregarn * sizeof(T)
a la dirección proporcionada porp
. - La expresión
p - n
genera un puntero de tipoT*
que resulta de restarn * sizeof(T)
a la dirección proporcionada porp
.
El operador sizeof
obtiene el tamaño de un tipo en bytes.
En el siguiente ejemplo se muestra el uso del operador +
con un puntero:
unsafe
{
const int Count = 3;
int[] numbers = new int[Count] { 10, 20, 30 };
fixed (int* pointerToFirst = &numbers[0])
{
int* pointerToLast = pointerToFirst + (Count - 1);
Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
}
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144
Resta de puntero
En el caso de dos punteros p1
y p2
de tipo T*
, la expresión p1 - p2
genera la diferencia entre las direcciones proporcionadas por p1
y p2
dividida por sizeof(T)
. El tipo del resultado es long
. Es decir, p1 - p2
se calcula como ((long)(p1) - (long)(p2)) / sizeof(T)
.
El ejemplo siguiente muestra la resta de puntero:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
int* p1 = &numbers[1];
int* p2 = &numbers[5];
Console.WriteLine(p2 - p1); // output: 4
}
Incremento y decremento de puntero
El operador de incremento ++
agrega 1 a su operando de puntero. El operador de decremento --
resta 1 a su operando de puntero.
Ambos operadores se admiten con dos formatos: postfijo (p++
y p--
) y prefijo (++p
y --p
). El resultado de p++
y p--
es el valor de p
antes de la operación. El resultado de ++p
y --p
es el valor de p
después de la operación.
El ejemplo siguiente muestra el comportamiento de los operadores de incremento postfijo y prefijo:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2 };
int* p1 = &numbers[0];
int* p2 = p1;
Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516
Operadores de comparación de puntero
Puede usar los operadores ==
, !=
, <
, >
, <=
y >=
para comparar los operandos de cualquier tipo de puntero, incluido void*
. Esos operadores comparan las direcciones proporcionadas por los dos operandos como si fueran enteros sin signo.
Para obtener información sobre el comportamiento de esos operadores para operandos de otros tipos, vea los artículos Operadores de igualdad y Operadores de comparación.
Prioridad de operadores
En la lista siguiente se ordenan los operadores relacionados con el puntero desde la prioridad más alta a la más baja:
- Operadores de incremento
x++
y decrementox--
postfijos y operadores->
y[]
- Operadores de incremento
++x
y decremento--x
prefijos y operadores&
y*
- Operadores
+
y-
de suma - Operadores de comparación
<
,>
,<=
y>=
- Operadores de igualdad
==
y!=
Use paréntesis, ()
, para cambiar el orden de evaluación impuesto por la prioridad de los operadores.
Para obtener la lista completa de los operadores de C# ordenados por nivel de prioridad, vea la sección Prioridad de operadores del artículo Operadores de C#.
Posibilidad de sobrecarga del operador
Un tipo definido por el usuario no puede sobrecargar los operadores relacionados con el puntero &
, *
, ->
y []
.
Especificación del lenguaje C#
Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:
- Variables fijas y móviles
- El operador address-of
- Direccionamiento indirecto del puntero
- Acceso a miembros de puntero
- Acceso de elemento de puntero
- Aritmética de puntero
- Incremento y decremento de puntero
- Comparación de punteros