Страницы

Поиск по вопросам

суббота, 13 октября 2018 г.

Как реализовано разыменовывание указателя в C/C++?

Почему код int* p = &(*(&n)) корректно работает? Я, представляя себя компилятором, воспроизвожу код так:
&n: возвращаем адрес переменной n *(&n): возвращаем число, лежащее по этому адресу &(*(&n)): возвращаем адрес, по которому хранится число, которое мы временно только что вычислили (?)
Как компилятор узнает, какой адрес у выражения *(&n)?


Ответ

Ошибка кроется в выделенном слове данного предложения
&(*(&n)): возвращаем адрес, по которому хранится число, которое мы временно только что вычислили (?)
Дело в том, что согласно, например, стандарту C++ (5.3.1 Unary operators)
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
никакого временного объекта не создается.
То же самое записано в C стандарте (6.5.3.2 Address and indirection operators)
4 The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object.
Также в этом же разделе стандарта C записано
3 The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.
Так что выражение
&(*(&n))
эквивалентно исходному выражению
&n
с учетом упомянутых ограничений.
Примечание. Упомянутые в цитате ограничения порой существенны. Например, результат этой C-программы где используется связка операторов &*
#include
int main( void ) { int a[10];
printf( "sizeof( a ) = %zu
", sizeof( a ) ); printf( "sizeof( &*a ) = %zu
", sizeof( &*a ) );
return 0; }
будет следующим
sizeof( a ) = 40 sizeof( &*a ) = 4
То есть операнд первого оператора sizeof является lvalue, в то время как операнд второго оператора sizeof будет уже rvalue в связи с применением связки &*. [Конец примечания]
В связи с этим есть забавный побочный эффект. Так как функции в выражениях преобразуются к указателю на функцию, то можно вызов функции записать, например, в следующем виде
#include
void f() { std::cout << "Hello, pointers!" << std::endl; }
int main() { ( **********f )(); //^^^^^^^^^^^^^
return 0; }
Этот прием полезен, когда вашу продуктивность оценивают по числу набранных символов в программе.:)

Комментариев нет:

Отправить комментарий