Подтвердить что ты не робот

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

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

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

#include<stdio.h>

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

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

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

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


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

Разыменование (как вы думаете) Функция указателя означает: доступ к CODE, поскольку это будет ДАННЫЕ память.

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

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

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


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

4b9b3361

Ответ 1

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

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

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

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

Два аналогичных эксперимента, которые вы можете попробовать:

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

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

Надеюсь, что это поможет.

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

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

Ответ 2

С++ 03 §4.3/1:

Значение l типа функции T может быть преобразовано в r-значение типа "указатель на 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.

Ответ 3

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

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

Ответ 4

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

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

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

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