2015-07-27 2 views
2

Если я запустил следующий код, graph[0][0] получает 1, а graph[0][1] получает 4.Странное поведение от простой программы С

Другими словами, линия graph[0][++graph[0][0]] = 4; ставит 1 в graph[0][0] и 4 в graph[0][1].

Я был бы очень признателен, если кто-нибудь может предложить разумные объяснения.

Я наблюдал это с Visual C++ 2015, а также с компилятором Android C (CppDriod).

static int graph[10][10]; 
void main(void) 
{ 
    graph[0][++graph[0][0]] = 4; 
} 
+3

Прочитайте [undefined behavior] (https://en.wikipedia.org/wiki/Undefined_behavior) и избегайте писать такой нечитаемый код. –

+5

@BasileStarynkevitch Сэр это действительно UB? Разве мы не работаем на разных элементах? –

+5

@BasileStarynkevitch. Этот выглядит корректно, потому что он использует pre-increment. Если бы это был 'graph [0] [graph [0] [0] ++] = 4', это был бы UB. Аналогично, сохранение '-1' в' graph [0] [0] 'приведет к UB с предварительным приращением. – dasblinkenlight

ответ

5

Давайте разбить его:

++graph[0][0] 

Эта предварительная увеличивает значение на graph[0][0], что означает, что теперь graph[0][0] = 1, а затем значение выражения 1 (потому что это конечное значение от graph[0][0]).

Затем

graph[0][/*previous expression = 1*/] = 4; 

Так в основном, graph[0][1] = 4;

Вот так! Теперь graph[0][0] = 1 и graph[0][1] = 4.

+0

Спасибо, Jahaszun! Ответ Yous разрешил проблему. –

+1

@ InKwonPark если так, пожалуйста, «примите» этот ответ. –

+0

@AnT Не могли бы вы объяснить это? Что вы подразумеваете под «наложением заказа, где не существует порядка»? Это не неопределенное поведение. – Jashaszun

1

Вы добавили один к graph[0][0], выполнив ++graph[0][0]. А затем установите graph[0][1] на 4. Возможно, вы хотите сделать graph[0][graph[0][0]+1] = 4

1

Сначала ваша переменная graph[10][10] статична, поэтому она будет инициализирована значением 0.

Тогда линия graph[0][++graph[0][0]] = 4 ; здесь граф [0] [0] = 0 в выражении, то вы просто приращением значение графа [0] [0] поэтому в основном вы назначая graph[0][1] = 4; себя

Обратите внимание, что вам использовали метод pre-increment (++ x), чтобы он сначала увеличивался, а значение менялось, но если бы вы использовали оператор post-increment (x ++), то graph[0][0] = 4; сам

2

Сначала посмотрим, что такое унарный (префикс) оператор инкремента.

Значение операнда оператора prefix ++ увеличивается. Результатом является новое значение операнда после инкремента.

Таким образом, в случае

graph[0][++graph[0][0]] = 4; 

первого, значение graph[0][0] увеличивается на 1, а затем значение используется в индексации.

Теперь, является статической глобальной переменной из-за неявной инициализации, все элементы в массиве инициализируются по умолчанию 0.Таким образом, ++graph[0][0] увеличивает значение graph[0][0] до 1 и возвращает значение 1.

Затем simpllified версия instrucion выглядит

graph[0][1] = 4; 

Таким образом, вы получаете

  • graph[0][0] как 1
  • graph[0][1] в 4.

Кроме того, FWIW , рекомендуемая подпись main() - int main(void). линия

+0

Это неправильно. Где это «первое» в «... во-первых, значение« graph [0] [0] 'увеличивается на 1 ...»? В этом выражении нет последовательности, по этой причине никто не может сказать, когда значение 'graph [0] [0]' увеличивается. – AnT

+0

@ ANT сэр, разве это не похоже на операцию индекса нижнего индекса массива? Я имею в виду, сначала будет оценено выражение в индексе массива. Пожалуйста, поправьте меня, если я ошибаюсь. –

+0

Да, вы правы: сначала будет оценено выражение в индексе массива. И он произведет '1', как результат. Но это не имеет никакого отношения к тому, чтобы на самом деле написать «1» обратно в «graph [0] [0]». Модификация 'graph [0] [0]' не является частью «оценки выражения в индексе массива». Это побочный эффект. Этот побочный эффект позволяет материализоваться гораздо позже. – AnT

0

Давай до фактов об этом выражении

graph[0][++graph[0][0]] = 4; 
  • Per 6.5.1, вычислении индекса массива ++graph[0][0] секвенировала перед вычислением массива элемента graph[0][++graph[0][0]], который, в свою очередь, секвенировал перед тем вычисление всего оператора присваивания.

  • Значение ++graph[0][0] должно быть 1. Обратите внимание, что это не означает, что весь предварительный приращение вместе с его побочными эффектами должен «произойти первым». Это просто означает, что результат этого предварительного приращения (который равен 1) должен быть вычислен первым. Фактическая модификация graph[0][0] (т. Е. Изменение graph[0][0] от 0 до 1) может произойти намного позже. Никто не знает, когда это произойдет точно (когда-то до конца заявления).

  • Это означает, что элемент, изменяемый оператором присваивания, равен graph[0][1]. Это то, к чему должен идти 4. Назначение 4 на graph[0][1] также является побочным эффектом оператора =, который произойдет где-то до конца заявления.

Обратите внимание, что в этом случае мы могли бы окончательно установить, что ++ изменяет graph[0][0], в то время как = изменяет graph[0][1]. У нас есть два непредсказуемых побочных эффекта (что опасно), но они действуют на два разных объекта (что делает их безопасными). Это именно то, что спасает нас от неопределенного поведения в этом случае.

Однако это зависит от начального значения массива . Если вы попробуете это, то поведение сразу же станет неопределенным, хотя само выражение выглядит одинаково. В этом случае побочный эффект ++ и побочный эффект = применяются к тому же элементу решетки graph[0][0]. Побочные эффекты не связаны друг с другом, что означает, что поведение не определено.

Смежные вопросы