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

Ассоциативность оператора вызова функции в C

Я обсуждал тему ассоциативности операторов C.

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

Например, в выражении a = f(x) + g(x); результат зависит от порядка оценки, а не от ассоциативности двух вызовов функций. Аналогично, вызов f(g(x)) сначала оценит функцию g(), а затем функцию f(). Здесь у нас есть вложенный вызов функции, и снова ассоциативность не играет никакой роли.

Другие операторы C в этой группе приоритетов представляют собой индекс массива [], postfix ++ и postfix --. Но я не мог найти примеров, связанных с комбинацией этих операторов с (), где ассоциативность играет роль в оценке выражений.

Итак, мой вопрос: определяет ли ассоциативность вызова функции как слева направо, какое-либо выражение в C? Может ли кто-нибудь представить пример, когда ассоциативность оператора вызова функции () имеет значение при оценке выражения?

4b9b3361

Ответ 1

Вот пример, где лево-правая ассоциативность оператора вызова функции имеет значение:

#include <stdio.h>

void foo(void)
{
    puts("foo");
}

void (*bar(void))(void) // bar is a function that returns a pointer to a function
{
    puts("bar");
    return foo;
}

int main(void)
{
    bar()();

    return 0;
}

Вызов функции:

bar()();

эквивалентно:

(bar())();

Ответ 2

В дополнение к ответу @GrzegorzSzpetkowski вы также можете иметь следующее:

void foo(void) { }

int main(void) {
    void (*p[1])(void);
    p[0] = foo;
    p[0]();
    return 0;
}

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

Ответ 3

Утверждение, связанное с назначением приложения слева, полностью исключено из определения языка C. Эта "ассоциативность" подразумевается тем фактом, что список аргументов функции отображается справа от самого выражения функции и должен быть заключен в круглые скобки. Это означает, что для данного выражения функции никогда не может быть никаких сомнений в степени его списка аргументов: он простирается от обязательной открывающей скобки после выражения функции до соответствующей закрывающей круглой скобки.

Выражение функции (которое часто является просто идентификатором, но может быть более сложным) может само по себе быть (голой) вызовом функции, как в f(n)(1,0). (Конечно, для этого требуется, чтобы значение, возвращаемое f(n), было чем-то, которое затем можно вызвать с помощью списка аргументов (1,0), для которого C имеет некоторые возможности и С++ намного больше, но это семантические соображения, которые появляются только после синтаксический анализ, поэтому их следует игнорировать для обсуждения ассоциативности.) Это означает, что правило синтаксиса для вызовов функций является леворекурсивным (часть функции сама может быть вызовом функции); это можно сформулировать, сказав, что "вызовы функций связаны слева", но факт очевиден. Напротив, правая часть (список аргументов) не может быть (голой) вызовом функции из-за требуемых круглых скобок, поэтому не может быть правильной рекурсии.

Например, рассмотрим (a)(b)(c) (где я помещаю избыточные круглые скобки, чтобы предложить симметрию и возможную двусмысленность). Здесь само по себе (b)(c) можно принять вызов b (избыточно в скобках) с аргументом c; однако такой вызов не может быть истолкован как аргумент a, поскольку для этого потребуются дополнительные скобки, как в (a)((b)(c)). Поэтому без упоминания ассоциативности ясно, что (a)(b)(c) может означать только то, что a вызывается с аргументом b, а результирующее значение вызывается с аргументом c.