Компиляторы и порядок аргументов оценки в C ++

StackOverflow https://stackoverflow.com/questions/621542

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

  

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

Другие советы

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

Пункт 5.2.2.8:

  

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

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

Прочитайте это

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top