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



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



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



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


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

531   6  

6 ответов:

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

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

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

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

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

пункт 5.2.2.8:

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

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

читать это

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

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

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

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

вычисляются все аргументы. Порядок не определен (согласно стандарту). Но все реализации C / C++ (что я знаю) оценивают аргументы функции от справа налево. EDIT: CLang является исключением (см. комментарий ниже).

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

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

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

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

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

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

Comments

    Ничего не найдено.