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

249
26 ноября 2016, 19:07

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

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

Как компилятор узнает, какой адрес у выражения *(&n)?

Answer 1

Ошибка кроется в выделенном слове данного предложения

&(*(&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 <stdio.h>
int main( void ) 
{
    int a[10];
    printf( "sizeof( a ) = %zu\n", sizeof( a ) );
    printf( "sizeof( &*a ) = %zu\n", sizeof( &*a ) );
    return 0;
}

будет следующим

sizeof( a ) = 40
sizeof( &*a ) = 4

То есть операнд первого оператора sizeof является lvalue, в то время как операнд второго оператора sizeof будет уже rvalue в связи с применением связки &*. [Конец примечания]

В связи с этим есть забавный побочный эффект. Так как функции в выражениях преобразуются к указателю на функцию, то можно вызов функции записать, например, в следующем виде

#include <iostream>
void f() { std::cout << "Hello, pointers!" << std::endl; }
int main() 
{
    ( **********f )();
    //^^^^^^^^^^^^^
    return 0;
} 

Этот прием полезен, когда вашу продуктивность оценивают по числу набранных символов в программе.:)

READ ALSO
Как получить результат SQL запроса?

Как получить результат SQL запроса?

выполняю вот такой запрос

228
SIGSEGV при сборке приложения в режиме релиза?

SIGSEGV при сборке приложения в режиме релиза?

Проблема заключается в том что я не могу понять почему я получаю ошибку SIGSEGVЕсли я собираю в режиме Debug программа работает, все хорошо, все...

212
Что за значение [System process] возвращает Process32First

Что за значение [System process] возвращает Process32First

Что это обозначает и зачем оно вообще нужно? Может где то используется?

285
Почему выбивает на указанной строке?

Почему выбивает на указанной строке?

Поясните мне мою ошибку в данной функции на выделение памятиКак только доходит до строчки numb[i] = new char[50]; - сразу ошибка и в отладчике пишет...

173