Почему sum1 = 46 и sum2 = 48 - PullRequest
0 голосов
/ 31 января 2020

В коде, указанном ниже, переменная sum1 получает 46 в качестве ответа, когда приоритет операторов идет слева направо. Но в sum2 ответ получает значение 48, а приоритет - справа налево. Почему эти ответы становятся разными.

#include <stdio.h>

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

void main() {
    int i = 10, j = 10, sum1, sum2;
    sum1 = (i / 2) + func(&i);
    sum2 = func(&j)+(j/2);
    printf("%d\n",sum1);
    printf("%d",sum2);
}

Ответы [ 3 ]

1 голос
/ 01 февраля 2020

В выражении (i / 2) + func(&i) компилятор (или реализация C в целом) может свободно вычислять либо i / 2 сначала, либо func(&i) сначала. Точно так же в func(&j) + (j/2) компилятор может сначала оценить func(&j) или j/2.

Приоритет не имеет значения. Приоритет говорит нам, как структурировано выражение, но не полностью определяет порядок, в котором оно оценивается. Приоритет говорит нам, что в a * b + c * d структура должна быть (a * b) + (c * d). В a + b + c приоритет в форме связи слева направо для + говорит нам, что структура должна быть (a + b) + c. Это не говорит о том, что a должно быть оценено до c. Например, в a() + b() + c() структура имеет вид (a() + b()) + c(), но компилятор может вызывать функции в любом порядке, сохраняя их результаты во временных регистрах, если это необходимо, и затем добавлять результаты.

В func(&j)+(j/2), нет приоритета справа налево или ассоциации. Ни одно из правил в стандарте C не гласит, что j/2 должно быть оценено до func(&j).

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

Вопросы о секвенировании

Стандарт C содержит правило в C 2018 6.5 2 это говорит о том, что если модификация объекта, как происходит с i в операторе *k+=4;, не секвенирована относительно вычисления значения с использованием того же объекта, что происходит для i в i / 2, то поведение не определено. Тем не менее, эта проблема не возникает в этом коде, потому что модификация и вычисление значения являются неопределенно последовательными, а не последовательными: 6.5.2.2 10 говорит «… Каждая оценка в вызывающей функции (включая другие вызовы функций), которая иным образом специально не упорядочена перед или после выполнения тела вызываемой функции выполняется неопределенная последовательность действий относительно выполнения вызываемой функции ». C 5.1.2.3 3 гласит: «… оценки A и B имеют неопределенную последовательность , когда A последовательно или до, или после B, но не определено, какая…»

0 голосов
/ 31 января 2020

Символ & предшествующий имени переменной (& i или & j) отправляет указатель, означающий, что любые изменения в переменной сохраняются. В этом случае вызов fun c берет переменную и добавляет к ней 4, а затем возвращает некоторое значение на основе этой переменной. Функция все еще изменила значение переменной, и поскольку уравнение обрабатывается слева направо, каждое последующее использование переменной отражает это изменение.

sum1 = (i/2) +func(&i) (where i = 10)
--> sum1 = 5 + func(&i)
--> sum1 = 5 + 41 (and now i = 14)

sum2 = func(&j) + (j/2) (where j = 10)
--> sum2 = 41 + (j/2) (and now j = 14)
--> sum2 = 41 + 7
0 голосов
/ 31 января 2020

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

Обратите внимание, что в выражении присваивания переменные i и j изменяются, и эти изменения не упорядочены. i / 2 или j / 2 могут быть оценены перед вызовом функции или наоборот.

...