Компиляторы и порядок аргументов оценки в C ++ - PullRequest
56 голосов
/ 07 марта 2009

Хорошо, я знаю, что стандарт диктует, что реализация C ++ может выбирать, в каком порядке оцениваются аргументы функции, но есть ли реализации, которые на самом деле «используют» это в сценарии, где это фактически повлияет программа?

Классический Пример:

int i = 0;
foo(i++, i++);

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

Ответы [ 6 ]

51 голосов
/ 07 марта 2009

Это зависит от типа аргумента, соглашения о вызове вызываемой функции, архитектуры и компилятора. На x86 соглашение о вызовах Pascal оценивает аргументы слева направо, тогда как в соглашении о вызовах C ( __cdecl ) оно справа налево. Большинство программ, которые работают на нескольких платформах, учитывают соглашения о вызовах, чтобы пропустить сюрпризы.

Есть хорошая статья в блоге Рэймонда Чена, если вам интересно. Вы также можете взглянуть на раздел Stack and Calling в руководстве GCC.

Редактировать: Пока мы раскалываемся: мой ответ рассматривает это не как вопрос языка, а как вопрос платформы. Языковой стандарт не гарантирует и не предпочитает одно другому, а оставляет его как неопределенный . Обратите внимание на формулировку. Это не говорит, что это не определено. Неопределенное в этом смысле означает то, на что вы не можете рассчитывать, непереносимое поведение. У меня нет под рукой спецификации C / draft, но она должна быть похожа на ту, что написана в моем проекте n2798 (C ++)

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

7 голосов
/ 09 января 2012

Я нашел ответ по c ++ стандартам .

Пункт 5.2.2.8:

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

Другими словами, это зависит только от компилятора.

6 голосов
/ 07 марта 2009

Читать это

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

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

Стандарт даже не гарантирует последовательного заказа. only гарантирует, что при вызове функции все аргументы будут полностью оценены.

И да, я видел несколько версий GCC, делающих именно это. Для вашего примера будет вызван foo (0,0), а потом будет 2. (Я не могу дать вам точный номер версии компилятора. Это было некоторое время назад - но я не удивлюсь, если это поведение снова всплывет. Это эффективный способ планирования инструкций)

5 голосов
/ 22 марта 2011

Все аргументы оцениваются. Заказ не определен (согласно стандарту). Но все реализации C / C ++ (которые я знаю) оценивают аргументы функции от справа налево . РЕДАКТИРОВАТЬ: CLang является исключением (см. Комментарий ниже).

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

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

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

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

2 голосов
/ 07 марта 2009

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

2 голосов
/ 07 марта 2009

В последний раз я видел различия между VS2005 и GCC 3.x на оборудовании x86 в 2007 году. Так что это (было?) Очень вероятная ситуация. Поэтому я больше не полагаюсь на порядок оценки. Может быть, теперь лучше.

...