Компиляторы и порядок аргументов оценки в C++
хорошо, я знаю, что стандарт диктует, что реализация c++ может выбирать, в каком порядке оцениваются аргументы функции, но есть ли какие-либо реализации, которые фактически "используют" это в сценарии, где это действительно повлияет на программу?
Классический Пример:
int i = 0;
foo(i++, i++);
примечание: Я не ищу кого-то, кто скажет мне, что на порядок оценки нельзя полагаться, я хорошо это знаю. Меня интересует только, есть ли компиляторы на самом деле оценивают слева направо, потому что я предполагаю, что если бы они сделали много плохо написанного кода, сломается (правильно, но они все равно, вероятно, будут жаловаться).
6 ответов
Это зависит от типа аргумента, соглашения о вызове вызываемой функции, архитектуры и компилятора. На x86, то Паскаль соглашение о вызове оценивает аргументы слева направо, тогда как в соглашении о вызове C (__cdecl) это справа налево. Большинство программ, которые работают на нескольких платформах, учитывают соглашения о вызовах, чтобы пропустить сюрпризы.
есть хороший статьи на блоге Raymond Chen, если вы заинтересованный. Вы также можете взглянуть на стек и вызове раздел руководства GCC.
Edit: пока мы разделяем волосы: мой ответ рассматривает это не как языковой вопрос, а как платформу. Языковой стандарт не гарантирует или предпочитает один над другим и оставляет его как нет данных. Обратите внимание на формулировку. Он не говорит, что это неопределенно. Неуказанные в этом смысле означает то, что вы не можете рассчитывать на, непереносимое поведение. У меня нет c spec/draft, но он должен быть похож на мой проект n2798 (C++)
некоторые другие аспекты и операции абстрактной машины описаны в настоящем стандарте как неуказанные (например, порядок оценки аргументов функции). Где это возможно, этот международный стандарт определяет набор допустимых поведений. Они определяют недетерминированные аспекты абстрактной машины. Экземпляр таким образом, абстрактная машина может иметь более одной возможной последовательности выполнения для данной программы и заданного ввода.
Я нашел ответ в стандарты c++.
пункт 5.2.2.8:
порядок оценки аргументов не определен. Все побочные эффекты оценок выражений аргументов вступают в силу до ввода функции. Порядок оценки постфиксного выражения и списка выражений аргументов: неопределенный.
другими словами, это зависит только от компилятора.
Это не точная копия вашего вопроса, но мой ответ (и несколько других) также охватывают ваш вопрос.
есть очень хорошие причины оптимизации, по которым компилятор может не только выбирать справа налево, но и чередовать их.
стандарт не гарантирует последовательный заказ. Это только гарантирует, что при вызове функции все аргументы будут полностью оцененный.
и да, я видел, как несколько версий GCC делают именно это. Для вашего примера будет вызван foo(0,0), и после этого я буду 2. (Я не могу дать вам точный номер версии компилятора. Это было некоторое время назад, но я не удивлюсь, если это поведение появится снова. Это эффективный способ планировать инструкции)
все аргументы вычисляются. Порядок не определен (согласно стандарту). Но все реализации C / C++ (которые я знаю) оценивают аргументы функции из справа налево. EDIT: CLang является исключением (см. комментарий ниже).
Я считаю, что порядок оценки справа налево был очень очень старым (начиная с первых компиляторов C). Конечно, задолго до изобретения C++, и большинство реализаций c++ будут поддерживать тот же порядок оценки, потому что ранние реализации C++ просто переведены на C.
есть некоторые технические причины для оценки аргументов функции справа налево. В архитектуре стека аргументы обычно помещаются в стек. В C/C++, вы можете вызвать функцию с более аргументов, чем указано дополнительных аргументов simiply игнорируется. Если аргументы вычисляются слева направо и нажимаются слева направо, то слот стека справа под указателем стека будет содержать последний аргумент, и функция не может получить смещение какого-либо конкретного аргумента (потому что фактическое количество аргументов зависит от вызывающего).
в порядке нажатия справа налево слот стека справа под указателем стека всегда будет содержать первый аргумент,а следующий слот - второй аргумент и т. д. Смещения аргументов всегда будут детерминированными для функции (которая может быть записана и скомпилирована в другом месте в библиотеку отдельно от того, где она находится называемый.)
теперь порядок нажатия справа налево не требует порядка оценки справа налево, но в ранних компиляторах память ограничена. В порядке оценки справа налево один и тот же стек может использоваться на месте (по существу, после оценки аргумента-который может быть выражением или вызовом funciton! -- возвращаемое значение уже находится в правильном положении в стеке). При оценке слева направо значения аргументов должны храниться отдельно, а возвращаемые в стек-наоборот порядок.
Я ожидаю, что большинство современных компиляторов попытаются чередовать инструкции, вычисляющие аргументы, учитывая, что они требуются стандартом C++, чтобы быть независимыми и, следовательно, не иметь каких-либо взаимозависимостей. Это должно помочь сохранить блоки выполнения процессора с глубоким конвейером и тем самым увеличить пропускную способность. (По крайней мере, я ожидал бы, что компилятор, который утверждает, что является оптимизирующим компилятором, сделает это, когда будут даны флаги оптимизации.)
в последний раз я видел различия между VS2005 и GCC 3.x на оборудовании x86 в 2007 году. Так это (было? весьма вероятная ситуация. Поэтому я больше никогда не полагаюсь на порядок оценки. Может, теперь лучше.