Как происходит разыменование указателя функции?

почему и как разыменование указателя функции просто"ничего не делает"?

вот о чем я говорю:

#include<stdio.h>

void hello() { printf("hello"); }

int main(void) { 
    (*****hello)(); 
}

из комментария над здесь:

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


и ответ здесь:

разыменование (как вы думаете) a указатель функции означает: доступ к Память кода, как это было бы данные память.

указатель функции не должен быть разыменование в ту сторону. Вместо этого призванный.

Я бы использовал имя" разыменование " сторона рядом с "звонком". Все в порядке.

в любом случае: C разработан таким образом что идентификатор имени функции как также, как переменная функция удерживания указатель означают одно: адрес кодировать память. И это позволяет перейти к этому память с помощью синтаксиса call () на идентификаторе или переменной.


как ровно работает ли разыменование указателя функции?

4 ответов


Это не совсем правильный вопрос. Для C, по крайней мере, правильный вопрос

что происходит со значением функции в контексте rvalue?

(контекст rvalue находится в любом месте, где появляется имя или другая ссылка, где она должна использоваться в качестве значения, а не местоположения-в основном в любом месте, кроме левой стороны назначения. Само название происходит от правостороны уступки.)

ОК, так что происходит значение функции в значение rvalue контексте? Он немедленно и неявно преобразуется в указатель на исходное значение функции. Если вы разыменовать этот указатель с *, вы снова получаете то же значение функции, которое немедленно и неявно преобразуется в указатель. И ты можешь делать это столько раз, сколько захочешь.

два подобных эксперимента вы можете попробовать:

  • что произойдет, если вы разыменовать указатель на функцию в lvalue context-левая сторона задания. (Ответ будет о том, что вы ожидаете, если вы помните, что функции неизменны.)

  • значение массива также преобразуется в указатель в контексте lvalue, но оно преобразуется в указатель на элемент тип, а не указатель на массив. Поэтому разыменование даст вам элемент, а не массив, и безумие, которое вы показываете, не происходить.

надеюсь, что это помогает.

P. S. Как к почему значение функции неявно преобразуется в указатель, ответ заключается в том, что для тех, кто использует указатели на функции, это очень удобно, не нужно использовать &'ы везде. Существует также двойное удобство: указатель функции в позиции вызова автоматически преобразуется в значение функции, поэтому вам не нужно писать * для вызова через функцию указатель.

P. P. S. В отличие от функций C, функции C++ могут быть перегружены, и я не компетентен комментировать, как семантика работает в C++.


C++03 §4.3 / 1:

значение lvalue типа функции T может быть преобразовано в значение rvalue типа "указатель на T." результатом является указатель на функцию.

если вы попытаетесь выполнить недопустимую операцию над ссылкой на функцию, такую как unary * оператор, первое, что пытается сделать язык, это стандартное преобразование. Это так же, как преобразование int при добавлении его в float. Используя * ссылка на функцию заставляет язык принимать его указатель, что в вашем примере это квадрат 1.

другой случай, когда это применимо при назначении указатель на функцию.

void f() {
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr.
    recurse(); // call operator is defined for pointers
}

обратите внимание, что это не работа в другую сторону.

void f() {
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref.
    recurse(); // OK - call operator is *separately* defined for references
}

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

в C99 разыменование функции указатель дает обозначение функции. §6.3.2.1/4:

обозначение функции-это выражение, имеющее тип функции. За исключением случаев, когда это операнд оператора sizeof или унарного оператора&, обозначение функции с типом ‘функция, возвращающая тип’ преобразуется в выражение с типом ‘указатель на функцию, возвращающую тип’.

это больше похоже на ответ Нормана, но, в частности, C99 не имеет понятия rvalues.


поставьте себя на место автора компилятора. Указатель функции имеет четко определенное значение, это указатель на blob байтов, которые представляют машинный код.

Что вы делаете, когда программист разыменовывает указатель на функцию? Берете ли вы первый (или 8) байт машинного кода и интерпретируете его как указатель? Шансы примерно 2 миллиарда к одному, что это не сработает. Вы объявляете UB? Много чего уже происходит. Или вы просто игнорируете попытку? Ты знаешь ответ.


как именно работает разыменование указателя функции?

два шага. Первый шаг-во время компиляции, второй-во время выполнения.

на первом шаге компилятор видит, что у него есть указатель и контекст, в котором этот указатель разыменован (например,(*pFoo)() ) Так он генерирует код для этой ситуации, код, который будет использоваться на Шаге 2.

на Шаге 2 во время выполнения выполняется код. Указатель содержит несколько байтов, указывающих, какая функция должна быть казнен следующим. Эти байты каким-то образом загружаются в CPU. Общим случаем является CPU с явным CALL [register] инструкция. В таких системах указатель функции может быть просто адресом функции в памяти, а код разыменования не делает ничего, кроме загрузки этого адреса в регистр, за которым следует CALL [register] инструкция.