порядок оценки в C с присвоением переменной функцией, которая изменяет переменную в том же присваивании

Сразу скажу, это связано с домашним заданием, с которым мне нужно руководство. Мне не нужен код или что-то еще, но это сводит меня с ума, и мне нужно некоторое разъяснение. Я даже не задаю вопрос в книге.

У меня есть следующий код:

int fun(int *c){
*c += 10;
return *c ;
}

void main(){

int a, b;
a = 10;
b = a + fun(&a);
printf("value of a is %d\n", a);
printf("With the function call on the right, ");
printf(" b is: %d\n", b);
a = 10;
b = fun(&a) + a;
printf("With the function call on the left, ");
printf(" b is: %d\n", b);
}

Когда я запускаю его, я получаю «значение a равно 20» и «с вызовом функции справа, 40» и «с вызовом функции слева, 40».

Что меня смущает, так это то, что у меня был очень похожий вопрос прямо перед

#include<stdio.h>

int fun(int *k) {
*k += 4;
return 3 * (*k) - 1;
}

void main() {
int i = 10, j = 10, sum1, sum2;
sum1 = (i / 2) + fun(&i);
sum2 = fun(&j) + (j / 2);

printf("%d", sum1);
}

И ответ получается 46 для sum1 и 48 для sum2, что, на мой взгляд, имеет смысл с оценкой слева направо в компиляторе codepad.org. Но в компиляторе Pelles C получается 48. Вы можете видеть, что код из первой задачи устроен почти точно так же.

Я нашел эту ссылку: Порядок оценки параметров перед вызовом функции C, что, кажется, объясняет это несоответствие, но я хочу убедиться, что не ошибаюсь в своих рассуждениях. Так можно ли с уверенностью сказать, что это зависит от компилятора и от того, что компилятор считает наиболее эффективным?


person nationalgrammarrodeo    schedule 25.11.2014    source источник
comment
Узнайте о точках последовательности. Нет точки последовательности, например. выражение a + fun(&a), поэтому компилятор может сначала оценить либо a, либо fun(&a).   -  person Some programmer dude    schedule 25.11.2014
comment
У вас неопределенное поведение   -  person Gopi    schedule 25.11.2014
comment
Это в основном дубликат вопроса, на который вы ссылались, ответ на этот вопрос тот же. Мой ответ на этот вопрос будет в основном перефразировать мой ответ на вопрос, который вы связали.   -  person Shafik Yaghmour    schedule 25.11.2014
comment
@Gopi: не указано, а не определено.   -  person rici    schedule 25.11.2014
comment
Другой вопрос был похожим, но это было не то же самое, поэтому я хотел убедиться, что не упустил какой-то фундаментальной концепции, когда дело доходит до оценки этой проблемы с функциями и параметрами.   -  person nationalgrammarrodeo    schedule 25.11.2014


Ответы (1)


В обоих этих выражениях

sum1 = (i / 2) + fun(&i);
sum2 = fun(&j) + (j / 2);

у вас неопределенное поведение, потому что порядок вычисления подвыражений + не указан. Компилятор может оценивать (i/2) и (j/2) до или после вызова, как считает нужным.

В отличие от операторов && и ||, которые принудительно оценивают свою левую часть перед правой, +, -, *, /, % и все побитовые операторы позволяют компилятору выбирать наиболее удобный порядок вычисления в зависимости от его стратегии оптимизации. Это означает, что разные компиляторы могут по-разному оценивать стороны +.

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

sum1 = i/2;
sum1 += fun(&i);

or

sum2 = sun(&j);
sum2 += j/2;
person Sergey Kalinichenko    schedule 25.11.2014
comment
хорошо, я на самом деле играл с принудительной оценкой выражений именно так, как вы упомянули, пока я работал над этим. меня просто поражает, что что-то подобное может быть неопределенным, и я думал, что упускаю какую-то большую фундаментальную концепцию. - person nationalgrammarrodeo; 25.11.2014
comment
@nationalgrammarrodeo Я думаю, что это было философское решение: принудительное выполнение любого определенного порядка оценки ограничивает разработчиков компиляторов определенной стратегией, что может повлиять на производительность. Разработчики стандарта, с другой стороны, старались избегать всего, что снижало бы производительность, иногда за счет запутанного поведения. Вы нашли отличную иллюстрацию того, как быстро может запутаться неопределенный порядок. - person Sergey Kalinichenko; 25.11.2014
comment
Привет, я столкнулся с подобным вопросом, но я и другой ответчик не согласны с поведением, если оно не определено или не указано. Если вам интересно и у вас есть время, ознакомьтесь с этим вопросом: stackoverflow.com/questions/37597855/ Я хотел бы услышать ваше мнение. - person 2501; 07.06.2016
comment
@ 2501 Думаю, вы правы, побочный эффект внутри fun делает это неопределенным, а не просто неуказанным. Спасибо! - person Sergey Kalinichenko; 07.06.2016
comment
@dasblinkenlight ? На самом деле я утверждал, что результат не указан, а поведение определено. stackoverflow.com/a/37599439/4082723 Назначение внутри функции, окруженной двумя точками последовательности. - person 2501; 07.06.2016
comment
@ 2501 Я перепроверил стандартный проект C11, и похоже, что здесь есть только одна точка последовательности - между вычислением указателя fun с его фактическими аргументами и вызовом самого fun. Назначение i внутри fun не упорядочено по отношению к оценке i/2 (раздел 6.5.5.2.10). После вызова fun нет точки следования, потому что это не библиотечная функция. - person Sergey Kalinichenko; 07.06.2016
comment
; также является точкой следования. Вот один в функции: *c += 10; - person 2501; 07.06.2016
comment
@ 2501 Вы уверены, что точка с запятой внутри функции считается точкой следования в контексте вызывающего объекта? Язык стандарта предполагает, что присваивание внутри функции остается непоследовательным по отношению к оценке аргумента вне функции: каждая оценка в вызывающей функции [...], которая иначе не упорядочена до или после выполнения тела вызываемой функции находится в неопределенной последовательности относительно выполнения вызываемой функции. - person Sergey Kalinichenko; 07.06.2016
comment
Я точно уверен. Неопределенная последовательность — это не то же самое, что непоследовательность. Пожалуйста, прочтите: 5.1.2.3 Program execution, paragraph 3. Процитированный вами абзац имеет отношение к объяснению того, что выполнение функций не чередуется, см. сноску. В этом тексте также говорится: это не секвенировано каким-либо иным образом. В данном случае через точки следования в функции. Точка следования между A и B гарантирует, что A предшествует B. - person 2501; 07.06.2016
comment
@ 2501 Думаю, вы правы, спасибо за разъяснения! - person Sergey Kalinichenko; 07.06.2016