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


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

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

#include<stdio.h>

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

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

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

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


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

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

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

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

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


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

4 59

4 ответа:

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

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

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

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

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

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

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

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

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

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

C++03 §4.3 / 1:

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

если вы попытаетесь выполнить недопустимую операцию над ссылкой на функцию, например унарную * оператор, первое, что пытается сделать язык-это стандартное преобразование. Это так же, как преобразование 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.

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

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

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

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

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

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