Чому визначення покажчиків функцій працюють з будь-якою кількістю амперсандів '&' або зірочок '*'?


216

Чому наступні роботи?

void foo() {
    cout << "Foo to you too!\n";
};

int main() {
    void (*p1_foo)() = foo;
    void (*p2_foo)() = *foo;
    void (*p3_foo)() = &foo;
    void (*p4_foo)() = *&foo;
    void (*p5_foo)() = &*foo;
    void (*p6_foo)() = **foo;
    void (*p7_foo)() = **********************foo;

    (*p1_foo)();
    (*p2_foo)();
    (*p3_foo)();
    (*p4_foo)();
    (*p5_foo)();
    (*p6_foo)();
    (*p7_foo)();
}

Відповіді:


225

Є кілька деталей, які дозволяють всім цим комбінаціям операторів працювати однаково.

Основною причиною, чому всі ці роботи є, є те, що функція (як foo) неявно конвертується у покажчик на функцію. Ось чому void (*p1_foo)() = foo;працює: fooнеявно перетворюється на вказівник на себе і йому вказуєтьсяp1_foo .

Унарний &, застосовуваний до функції, видає покажчик на функцію, як і адресу об'єкта, коли він застосовується до об'єкта. Для покажчиків на звичайні функції він завжди є надлишковим через неявне перетворення функції в функцію-покажчик. У будь-якому випадку, саме тому void (*p3_foo)() = &foo;працює.

Унарний *, застосовуваний до покажчика на функцію, дає функцію, на яку спрямовано, так само, як і об'єкт, на яку спрямовано, коли він застосовується до звичайного покажчика на об'єкт.

Ці правила можна поєднувати. Розглянемо ваш останній і останній приклад **foo:

  • По-перше, fooнеявно перетворюється на покажчик на себе, а перший *застосовується до цього покажчика функції, що fooзнову дає функцію .
  • Потім результат знову неявно перетворюється на покажчик на себе і застосовується другий *, знову отримуючи функціюfoo .
  • Потім він неявно перетворюється на покажчик функції знову і присвоюється змінній.

Ви можете додати скільки *завгодно s, результат завжди однаковий. Більше* s, тим веселіше.

Ми також можемо розглянути ваш п'ятий приклад &*foo:

  • По-перше, fooнеявно перетворюється на покажчик на себе; одинарний *застосовується, поступаючисьfoo знову .
  • Потім, значення &застосовується до foo, отримуючи покажчик на foo, який присвоюється змінній.

Однак &функцію can можна застосувати лише до функції, але не до функції, яка була перетворена на покажчик функції (якщо, звичайно, покажчик функції не є змінною, в цьому випадку результатом є покажчик на вказівник на to-a-function; наприклад, ви можете додати до свого списку void (**pp_foo)() = &p7_foo;).

Ось чому &&fooне працює: &fooне є функцією; це покажчик функції, який є значенням r. Однак &*&*&*&*&*&*fooце спрацювало б, як би &******&foo, оскільки в обох цих виразах значення &завжди застосовується до функції, а не до покажчика функції rvalue.

Зауважте також, що *для здійснення виклику за допомогою покажчика на функцію не потрібно використовувати одинарне ; обидва (*p1_foo)();і (p1_foo)();мають однаковий результат, знову ж таки через перетворення покажчика функція на функцію.


2
@Jimmy: Це не посилання на покажчики на функції, це лише покажчики на функції. &fooприймає адресу foo, в результаті чого вказівник на функцію вказує foo, як і слід було очікувати.
Dennis Zickefoose

2
Ви також не можете зв’язати &оператори для об’єктів: задано int p;, &pдає вказівник на pі є виразом rvalue; &оператор вимагає називає вирази.
James McNellis

12
Я не погоджуюсь. Чим більше *, тим менше весело .
Сет Карнегі,

28
Будь ласка, не редагуйте синтаксис моїх прикладів. Я дуже конкретно підібрав приклади, щоб продемонструвати особливості мови.
Джеймс МакНелліс,

7
Як додаткове зауваження, стандарт C прямо вказує, що комбінація &*взаємного скасування (6.5.3.2): "The unary & operator yields the address of its operand."/ - / "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.".
Лундін,

11

Я також думаю, що корисно пам’ятати, що C - це просто абстракція для базової машини, і це одне з місць, де ця абстракція витікає.

З точки зору комп'ютера, функція - це просто адреса пам'яті, яка у разі виконання виконує інші інструкції. Отже, функція в C сама змодельована як адреса, що, ймовірно, призводить до того, що функція "така сама", як і адреса, на яку вона вказує.


0

&і *є ідемпотентними операціями над символом, оголошеним як функція в C, що означаєfunc == *func == &func == *&func і тому*func == **func

Це означає, що тип int ()такий самий, int (*)()як і параметр функції, і визначена функція може передаватися як *func, funcабо &func. (&func)()це те саме, що func(). Годболт ланка.

Функція насправді є адресою, тому *і& не має ніякого сенсу, і замість того , щоб видавати помилку, то вибирає компілятор , щоб інтерпретувати його як адреса FUNC.

&на символ, оголошений як покажчик функції, однак отримає адресу вказівника (оскільки він тепер має окреме призначення), тоді як funcpі *funcpбуде ідентичним

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.