int TYPE[2]
;int TYPE
, int *TYPE
, etc., for which the operations =, &, * and others are not redefined and denote ordinary things; int x; int *y = &x; // "&". int z = *y; // "*". ,
sizeof (char) == 1
(but standards do not guarantee that the byte contains exactly 8 bits :)). Further, if we add a number to a pointer to a type T, then the real numerical value of this pointer will increase by that number multiplied by sizeof (T)
. That is, if p is of type T *TYPE
, then p + 3
equivalent to (T *)((char *)p + 3 * sizeof (T))
. Similar considerations apply to subtraction. int x; int &y = x; int z = y;
a[i + 2]
, some_struct.some_field
, *ptr
, *(ptr + 3)
are also lvalue.int x
. Now x is a variable of type int TYPE
and no other. This is int and that's it. But if I now write x + 2
or x = 3
, then in these expressions the subexpression x
is of type int &TYPE
. Because otherwise, this x would be no different from, say, 10, and he (like the top ten) could not be assigned anything.int x
declared, then the expression x has the type int &TYPE
. If now this expression (or any other expression of the link type) is to the left of the equal sign, then it is used as a link, in almost all other cases (for example, in the situation x + 2
) x is automatically converted to the type int TYPE
(another operation , next to which the link is not converted into its object, is &, as we will see later). To the left of the equal sign can only be a link. Only a link can initialize a (non-constant) link.int *x
and int &x
. Thus, the principle of “announcement prompts the use of” is once again confirmed: the declaration of the pointer reminds how to turn it into a link, and the declaration of the link is the opposite.&*EXPR
(here EXPR is an arbitrary expression, not necessarily a single identifier) ​​is equivalent to EXPR whenever it makes sense (that is, always when EXPR is a pointer), and *&EXPR
also equivalent to EXPR whenever it has meaning (i.e., when EXPR is a link). int x[5];
5 * sizeof (int)
size, in which our entire array is located. No need to think that this code declared a pointer, which points to a memory located somewhere far away, in a heap. No, we declared an array, the real one. Here on the stack.sizeof (x)
? Of course, it will be equal to the size of our array, i.e. 5 * sizeof (int)
. If we write struct foo { int a[5]; int b; };
&x
), and it will be a real pointer to the place where this array is located. The type of the &x
expression, as is easily understood, will be int (*TYPE)[5]
. At the beginning of the array its zero element is located, therefore the address of the array itself and the address of its zero element are numerically the same. That is, &x
and &(x[0])
numerically equal (here I famously wrote the expression &(x[0])
, in fact, it is not so simple, we'll return to this). But these expressions have a different type - int (*TYPE)[5]
and int *TYPE
, so comparing them with == will not work. But you can use the trick with void *
: the following expression will be true: (void *)&x == (void *)&(x[0])
.int x[5]
. If we now write x + 0
, then this will convert our x (which was of type int TYPE[5]
, or, more precisely, int (&TYPE)[5]
) to &(x[0])
, i.e. pointer to the zero element of the array x. Now our x is of type int *TYPE
.void *
or applying == to it also leads to a preliminary conversion of this name into a pointer to the first element, therefore: &x == x // , : int (*TYPE)[5] int *TYPE (void *)&x == (void *)x // x == x + 0 // x == &(x[0]) //
a[b]
record is always equivalent to *(a + b)
(recall that we do not consider overriding operator[]
and other operations). Thus, x[2]
means the following:x[2]
equivalent to *(x + 2)
x + 2
refers to those operations in which the array name is converted to a pointer to its first element, so this happensx + 2
equivalent to (int *)((char *)x + 2 * sizeof (int))
, i.e. x + 2
means "move the pointer x to two ints" x // int (&TYPE)[5], : int *TYPE x + 2 // int *TYPE *(x + 2) // int &TYPE x[2] // int &TYPE
(x + 2)[3]
, and this will be equivalent to x[5]
. I also note that *a
and a[0]
always equivalent, as in the case when a is an array, and when a is a pointer.&(x[0])
. It is now clear that in this expression, first x is converted to a pointer, then [0]
is applied to this pointer in accordance with the above algorithm and the result is a value of type int &TYPE
, and finally, using & it is converted to type int *TYPE
. Therefore, to explain with the help of this complex expression (inside which an array is converted to a pointer) a slightly simpler notion of converting an array to a pointer is done - it was a bit mulling.&x + 1
? Well, &x
is a pointer to the entire array, + 1
leads to a step to the whole array. That is, &x + 1
is (int (*)[5])((char *)&x + sizeof (int [5]))
, i.e. (int (*)[5])((char *)&x + 5 * sizeof (int))
(here, int (*)[5]
is int (*TYPE)[5]
). So &x + 1
numerically equal to x + 5
, not x + 1
, as one might think. Yes, as a result, we point to a memory that is outside the array (immediately after the last element), but who cares? After all, C still does not check whether the array goes beyond the bounds. Also note that the expression *(&x + 1) == x + 5
true. You can also write it like this: (&x)[1] == x + 5
. It will also be true *((&x)[1]) == x[5]
, or, equivalently, (&x)[1][0] == x[5]
(unless we seize the segmentation fault, of course for trying to turn beyond our memory :)).int x[2]
or int x[]
in the function header, it will be equivalent to int *x
and a pointer will always be passed to the function (the sizeof from the passed variable will be the same as the pointer). In this case, the size of the array specified in the header will be ignored. You can easily specify int x[2]
in the header and pass an array of length 3 there. void f (int (&x)[5]) { // sizeof (x) 5 * sizeof (int) } int main (void) { int x[5]; f (x); // OK f (x + 0); // int y[7]; f (y); // , }
// template <typename t, size_t n> size_t len (t (&a)[n]) { return n; }
int (*a)[2]; // . . int (*TYPE)[2] int b[2]; int *c = b; // . . int *d = new int[4]; // .
b + 0
) are pointers to arrays.int x[5][7]
declared, then x is not an array of length 5 of some pointers pointing somewhere far away. No, x now is a single monolithic block of size 5 x 7 placed on the stack. sizeof (x)
is 5 * 7 * sizeof (int)
. The elements are located in the memory as follows: x[0][0]
, x[0][1]
, x[0][2]
, x[0][3]
, x[0][4]
, x[0][5]
, x[0][6]
, x[1][0]
and so on. When we write x[0][0]
, events happen like this: x // int (&TYPE)[5][7], : int (*TYPE)[7] x[0] // int (&TYPE)[7], : int *TYPE x[0][0] // int &TYPE
**x
. I note that in expressions, say, x[0][0] + 3
and **x + 3
in reality, retrieving from memory occurs only once (despite the presence of two asterisks), at the time of converting the final reference like int &TYPE
just int TYPE
. That is, if we looked at the assembler code that is generated from the expression **x + 3
, we would see in it that the operation of extracting data from memory is performed there only once. **x + 3
can also be written differently as *(int *)x + 3
. int **y = new int *[5]; for (int i = 0; i != 5; ++i) { y[i] = new int[7]; }
7 * sizeof (int)
, which can be far from each other. What is y[0][0]
? y // int **&TYPE y[0] // int *&TYPE y[0][0] // int &TYPE
y[0][0] + 3
, retrieving from memory occurs two times: retrieving from the array y and then retrieving from the array y[0]
, which may be far from the array y. The reason for this is that there is no conversion of the array name to a pointer to its first element, unlike the example with the multidimensional array x. Therefore **y + 3
is not equivalent here *(int *)y + 3
.x[2][3]
equivalent to *(*(x + 2) + 3)
. And y[2][3]
equivalent to *(*(y + 2) + 3)
. But in the first case, our task is to find the “third element in the second row” in a single block of size 5 x 7 (of course, the elements are numbered from zero, so this third element will be in some sense the fourth :)). The compiler calculates that in fact the necessary element is located at 2 * 7 + 3
th place in this block and extracts it. That is, x[2][3]
is equivalent to ((int *)x)[2 * 7 + 3]
, or, equivalently, *((int *)x + 2 * 7 + 3)
. In the second case, first retrieves the 2nd element in the array y, and then the 3rd element in the resulting array.x + 2
, we immediately shift by 2 * sizeof (int [7])
, i.e. by 2 * 7 * sizeof (int)
. In the second case, y + 2
is a shift by 2 * sizeof (int *)
.(void *)x
and (void *)*x
(and (void *)&x
!) Is the same pointer, in the second it is not.Source: https://habr.com/ru/post/251091/
All Articles