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

Порядок оценки параметров перед вызовом функции C

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

#include <stdio.h>

int main()
{
   int a[] = {1, 2, 3};
   int * pa; 

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
   /* Result: a[0] = 3  a[1] = 2    a[2] = 2 */

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
   /* Result: a[0] = 2  a[1] = 2     a[2] = 2 */

   pa = &a[0];
   printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
   /* a[0] = 2  a[1] = 2 a[2] = 1 */

}
4b9b3361

Ответ 2

Порядок оценки аргументов функции не указан, из C99 §6.5.2.2p10:

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

Аналогичная формулировка существует на C89.

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

$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function

Ответ 3

Просто чтобы добавить некоторые впечатления. Следующий код:

int i=1;
printf("%d %d %d\n", i++, i++, i);

приводит к

2 1 3 - с помощью g++ 4.2.1 на Linux.i686
1 2 3 - использование SunStudio С++ 5.9 на Linux.i686
2 1 3 - используя g++ 4.2.1 на SunOS.x86pc
1 2 3 - используя SunStudio С++ 5.9 на SunOS.x86pc
1 2 3 - с помощью g++ 4.2.1 на SunOS.sun4u
1 2 3 - используя SunStudio С++ 5.9 на SunOS.sun4u

Ответ 4

Можно ли считать порядок оценки параметров функции при вызове в C?

Нет, нельзя предположить, что это неуказанное поведение, черновик стандарта C99 в разделе 6.5 пункт 3 гласит:

Группирование операторов и операндов обозначается синтаксисом .74) За исключением случаев, указанных в позже (для функций-вызовов(), &, ||,?: и операторов запятой), порядок оценки подвыражений и порядок, в котором происходят побочные эффекты, являются неуточненными.

Он также говорит, что, как указано ниже, и конкретно сайты function-call (), поэтому мы видим, что позже в проекте стандарта в разделе 6.5.2.2 Функция вызывает абзац 10:

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

Эта программа также демонстрирует поведение undefined, поскольку вы изменяете pa более одного раза между точки последовательности. Из черновика стандартного раздела 6.5 paragraph 2:

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

он ссылается на следующие примеры кода как undefined:

i = ++i + 1;
a[i++] = i; 

Важно отметить, что хотя оператор запятой

и по умолчанию clang будет предупреждать с сообщением, аналогичным:

warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
                                            ~         ^

В целом важно понять, как использовать ваши инструменты наиболее эффективным способом, важно знать флаги, доступные для предупреждений, для gcc вы можете найти эту информацию . Некоторые флаги, которые являются полезными и сэкономит вам много проблем в долгосрочной перспективе и являются общими для обоих gcc и clang: -Wextra -Wconversion -pedantic. Для clang может быть очень полезным. Например, -fsanitize=undefined будет захватывать многие экземпляры поведения undefined во время выполнения.

Ответ 5

Как уже говорили другие, порядок, в котором оцениваются аргументы функции, не указан, и нет никакой точки последовательности между их оценкой. Поскольку при изменении каждого аргумента вы меняете pa, вы меняете и читаете pa дважды между двумя точками последовательности. Это на самом деле поведение undefined. Я нашел очень хорошее объяснение в руководстве GCC, которое, я думаю, может быть полезно:

Стандарты C и С++ определяют порядок, в котором выражения в программе C/С++ оцениваются в терминах точек последовательности, которые представляют собой частичное упорядочение между выполнением частей программы: те, которые выполняются перед точкой последовательности, и те, которые выполняются после него. Это происходит после оценки полного выражения (которое не является частью большего выражения) после оценки первого операнда &, ||,?: или, (запятая), перед вызовом функции (но после оценки ее аргументов и выражения, обозначающего вызываемую функцию) и в некоторых других местах. Кроме того, что выражается в правилах последовательности, порядок оценки подвыражений выражения не указан. Все эти правила описывают только частичный порядок, а не полный порядок, поскольку, например, если две функции вызываются внутри одного выражения без точки последовательности между ними, порядок, в котором вызываются функции, не указывается. Тем не менее, комитет по стандартам постановил, что вызовы функций не перекрываются.

Не указывается, когда между точками последовательности вносятся изменения в значения объектов. Программы, поведение которых зависит от этого, имеют поведение undefined; стандарты C и С++ указывают, что "между предыдущей и следующей точками последовательности объект должен иметь значение хранимого значения не более одного раза, оценивая выражение. Кроме того, предыдущее значение должно быть считано только для определения значения, которое должно быть сохранено.". Если программа нарушает эти правила, результаты по любой конкретной реализации полностью непредсказуемы.

Примерами кода с поведением undefined являются a = a ++;, a [n] = b [n ++] и [i ++] = i;. Некоторые более сложные случаи не диагностируются по этому варианту, и это может дать случайный ложноположительный результат, но в целом он оказался достаточно эффективным для обнаружения такого рода проблем в программах.

Стандарт сформулирован смутно, поэтому есть некоторые дебаты по поводу точного значения правил точки последовательности в тонких случаях. Ссылки на обсуждения проблемы, включая предлагаемые формальные определения, можно найти на странице чтения GCC, http://gcc.gnu.org/readings.html.

Ответ 6

Изменение переменной более одного раза в выражении - это поведение undefined. Таким образом, вы можете получить разные результаты для разных компиляторов. Поэтому избегайте модификации переменной более одного раза.

Ответ 7

Ответ на правильный ответ, undefined.

НО,,

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

Это совершенно не переносное и ужасное, но ужасное дело.