IT. Expert System.

C#

Pointers in expressions


In an unsafe context, an expression may yield a result of a pointer type, but outside an unsafe context it is a compile-time error for an expression to be of a pointer type. In precise terms, outside an unsafe context a compile-time error occurs if any simple-name , member-access , invocation-expression , or element-access is of a pointer type.

In an unsafe context, the primary-no-array-creation-expression and unary-expression productions permit the following additional constructs:

primary-no-array-creation-expression:
...
pointer-member-access
pointer-element-access
sizeof-expression

unary-expression:
...
pointer-indirection-expression
addressof-expression

These constructs are described in the following sections. The precedence and associativity of the unsafe operators is implied by the grammar.

      1. Pointer indirection

A pointer-indirection-expression consists of an asterisk (*) followed by a unary-expression.

pointer-indirection-expression:
* unary-expression

The unary * operator denotes pointer indirection and is used to obtain the variable to which a pointer points. The result of evaluating *P, where P is an expression of a pointer type T*, is a variable of type T. It is a compile-time error to apply the unary * operator to an expression of type void* or to an expression that isn’t of a pointer type.

The effect of applying the unary * operator to a null pointer is implementation-defined. In particular, there is no guarantee that this operation throws a System.NullReferenceException.

If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined. Among the invalid values for dereferencing a pointer by the unary * operator are an address inappropriately aligned for the type pointed to (see example in §18.4), and the address of a variable after the end of its lifetime.

For purposes of definite assignment analysis, a variable produced by evaluating an expression of the form *P is considered initially assigned .

      1. Pointer member access

A pointer-member-access consists of a primary-expression, followed by a “->” token, followed by an identifier.

pointer-member-access:
primary-expression -> identifier

In a pointer member access of the form P->I, P must be an expression of a pointer type other than void*, and I must denote an accessible member of the type to which P points.

A pointer member access of the form P->I is evaluated exactly as (*P).I. For a description of the pointer indirection operator (*), see §18.5.1. For a description of the member access operator (.), see §7.5.4.

In the example

using System;

struct Point
{
public int x;
public int y;

public override string ToString() {
return "(" + x + "," + y + ")";
}
}

class Test
{
static void Main() {
Point point;
unsafe {
Point* p = &point;
p->x = 10;
p->y = 20;
Console.WriteLine(p->ToString());
}
}
}

the -> operator is used to access fields and invoke a method of a struct through a pointer. Because the operation P->I is precisely equivalent to (*P).I, the Main method could equally well have been written:

class Test
{
static void Main() {
Point point;
unsafe {
Point* p = &point;
(*p).x = 10;
(*p).y = 20;
Console.WriteLine((*p).ToString());
}
}
}

      1. Pointer element access

A pointer-element-access consists of a primary-no-array-creation-expression followed by an expression enclosed in “[” and “]”.

pointer-element-access:
primary-no-array-creation-expression [ expression ]

In a pointer element access of the form P[E], P must be an expression of a pointer type other than void*, and E must be an expression of a type that can be implicitly converted to int, uint, long, or ulong.

A pointer element access of the form P[E] is evaluated exactly as *(P + E). For a description of the pointer indirection operator (*), see §18.5.1. For a description of the pointer addition operator (+), see §18.5.6.

In the example

class Test
{
static void Main() {
unsafe {
char* p = stackalloc char[256];
for (int i = 0; i < 256; i++) p[i] = (char)i;
}
}
}

a pointer element access is used to initialize the character buffer in a for loop. Because the operation P[E] is precisely equivalent to *(P + E), the example could equally well have been written:

class Test
{
static void Main() {
unsafe {
char* p = stackalloc char[256];
for (int i = 0; i < 256; i++) *(p + i) = (char)i;
}
}
}

The pointer element access operator does not check for out-of-bounds errors and the behavior when accessing an out-of-bounds element is undefined. This is the same as C and C++.

      1. The address-of operator

An addressof-expression consists of an ampersand (&) followed by a unary-expression.

addressof-expression:
& unary-expression

Given an expression E which is of a type T and is classified as a fixed variable , the construct &E computes the address of the variable given by E. The type of the result is T* and is classified as a value. A compile-time error occurs if E is not classified as a variable, if E is classified as a read-only local variable, or if E denotes a moveable variable. In the last case, a fixed statement can be used to temporarily “fix” the variable before obtaining its address. As stated in §7.5.4, outside an instance constructor or static constructor for a struct or class that defines a readonly field, that field is considered a value, not a variable. As such, its address cannot be taken. Similarly, the address of a constant cannot be taken.

The & operator does not require its argument to be definitely assigned, but following an & operation, the variable to which the operator is applied is considered definitely assigned in the execution path in which the operation occurs. It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.

In the example

using System;

class Test
{
static void Main() {
int i;
unsafe {
int* p = &i;
*p = 123;
}
Console.WriteLine(i);
}
}

i is considered definitely assigned following the &i operation used to initialize p. The assignment to *p in effect initializes i, but the inclusion of this initialization is the responsibility of the programmer, and no compile-time error would occur if the assignment was removed.

The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. For example, many external APIs take a pointer to a structure which is filled in by the API. Calls to such APIs typically pass the address of a local struct variable, and without the rule, redundant initialization of the struct variable would be required.

      1. Pointer increment and decrement

In an unsafe context, the ++ and ‑‑ operators can be applied to pointer variables of all types except void*. Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator ++(T* x);

T* operator --(T* x);

The operators produce the same results as x + 1 and x - 1, respectively . In other words, for a pointer variable of type T*, the ++ operator adds sizeof(T) to the address contained in the variable, and the ‑‑ operator subtracts sizeof(T) from the address contained in the variable.

If a pointer increment or decrement operation overflows the domain of the pointer type, the result is implementation-defined, but no exceptions are produced.

      1. Pointer arithmetic

In an unsafe context, the + and - operators can be applied to values of all pointer types except void*. Thus, for every pointer type T*, the following operators are implicitly defined:

T* operator +(T* x, int y);
T* operator +(T* x, uint y);
T* operator +(T* x, long y);
T* operator +(T* x, ulong y);

T* operator +(int x, T* y);
T* operator +(uint x, T* y);
T* operator +(long x, T* y);
T* operator +(ulong x, T* y);

T* operator –(T* x, int y);
T* operator –(T* x, uint y);
T* operator –(T* x, long y);
T* operator –(T* x, ulong y);

long operator –(T* x, T* y);

Given an expression P of a pointer type T* and an expression N of type int, uint, long, or ulong, the expressions P + N and N + P compute the pointer value of type T* that results from adding N * sizeof(T) to the address given by P. Likewise, the expression P - N computes the pointer value of type T* that results from subtracting N * sizeof(T) from the address given by P.

Given two expressions, P and Q, of a pointer type T*, the expression P – Q computes the difference between the addresses given by P and Q and then divides that difference by sizeof(T). The type of the result is always long. In effect, P - Q is computed as ((long)(P) - (long)(Q)) / sizeof(T).

For example:

using System;

class Test
{

static void Main() {
unsafe {
int* values = stackalloc int[20];
int* p = &values[1];
int* q = &values[15];
Console.WriteLine("p - q = {0}", p - q);
Console.WriteLine("q - p = {0}", q - p);
}
}
}

which produces the output:

p - q = -14
q - p = 14

If a pointer arithmetic operation overflows the domain of the pointer type, the result is truncated in an implementation-defined fashion, but no exceptions are produced.

      1. Pointer comparison

In an unsafe context, the ==, !=, <, >, <=, and => operators can be applied to values of all pointer types. The pointer comparison operators are:

bool operator ==(void* x, void* y);

bool operator !=(void* x, void* y);

bool operator <(void* x, void* y);

bool operator >(void* x, void* y);

bool operator <=(void* x, void* y);

bool operator >=(void* x, void* y);

Because an implicit conversion exists from any pointer type to the void* type, operands of any pointer type can be compared using these operators. The comparison operators compare the addresses given by the two operands as if they were unsigned integers.

      1. The sizeof operator

The sizeof operator returns the number of bytes occupied by a variable of a given type. The type specified as an operand to sizeof must be an unmanaged-type .

sizeof-expression:
sizeof ( unmanaged-type )

The result of the sizeof operator is a value of type int. For certain predefined types, the sizeof operator yields a constant value as shown in the table below.


Expression

Result

sizeof(sbyte)

1

sizeof(byte)

1

sizeof(short)

2

sizeof(ushort)

2

sizeof(int)

4

sizeof(uint)

4

sizeof(long)

8

sizeof(ulong)

8

sizeof(char)

2

sizeof(float)

4

sizeof(double)

8

sizeof(bool)

1


For all other types, the result of the sizeof operator is implementation-defined and is classified as a value, not a constant.

The order in which members are packed into a struct is unspecified.

For alignment purposes, there may be unnamed padding at the beginning of a struct, within a struct, and at the end of the struct. The contents of the bits used as padding are indeterminate.

When applied to an operand that has struct type, the result is the total number of bytes in a variable of that type, including any padding.



Content

Android Reference

Java basics

Java Enterprise Edition (EE)

Java Standard Edition (SE)

SQL

HTML

PHP

CSS

Java Script

MYSQL

JQUERY

VBS

REGEX

C

C++

C#

Design patterns

RFC (standard status)

RFC (proposed standard status)

RFC (draft standard status)

RFC (informational status)

RFC (experimental status)

RFC (best current practice status)

RFC (historic status)

RFC (unknown status)

IT dictionary

License.
All information of this service is derived from the free sources and is provided solely in the form of quotations. This service provides information and interfaces solely for the familiarization (not ownership) and under the "as is" condition.
Copyright 2016 © ELTASK.COM. All rights reserved.
Site is optimized for mobile devices.
Downloads: 145 / 158765815. Delta: 0.03907 с