2013-11-22 1 views
2

я наткнулся на этот кусок кода в C:Последовательное назначение указателя с постинкремента

*p++=*q++=*r++; 

На мой взгляд, это ошибочное, потому что я не могу вспомнить какое-либо правило в C, указывающий когда ++ после ц должен быть фактически проведен - до назначения между *p++ и *q++, или после?

Поэтому у меня сложилось впечатление, что это может очень вероятно, будет неопределенное поведение, но не мог точно определить, где именно в C стандартов (C89 или C99) это объясняется.

Здесь очень ценят помощь от языковых полицейских. Прошу пролить свет.

+0

Не имеет значения: значение 'q ++' является значением 'q' перед приращением. 'q' - указатель, назначение - на то, что указывал q. Похож на внутренний цикл симпатичного li'l two-fer 'strcpy()', 'while (* p ++ = * q ++ = * r ++) {}' будет делать две копии строки 'r' на' p' и 'q'. – jthill

ответ

0

Если я не ошибаюсь, пост-инкремент всегда выполняется в конце задания

т.е.

прирост * г

* д < - * г

Приращение в * q

* p < - * q

прирост * р

«It is important to note that a postfix increment or decrement expression evaluates to the value of the expression prior to application of the respective operator.»

+0

пост-инкремент применяется когда-то между оценкой и следующей точкой последовательности; «в конце задания» звучит слишком специфично. – Potatoswatter

2

Поскольку вы увеличивающиеся три переменные-указатели, общее поведение вполне определенной (предполагается, что указатели все это указывает на действительные места хранения). Не указано, имеет ли приращение r до или после присвоения p, но это также не влияет на результат.

Для удобства предположим, что мы имеем:

char buffer1[10]; 
char buffer2[10]; 
char buffer3[] = "abc"; 
char *p = buffer1; 
char *q = buffer2; 
char *r = buffer3; 

*p++ = *q++ = *r++; 

После того как это выполнено, все эти утверждения являются безопасными:

assert(p == buffer1 + 1); // Or p == &buffer1[1]; 
assert(q == buffer2 + 1); // Or q == &buffer2[1]; 
assert(r == buffer3 + 1); // Or r == &buffer3[1]; 
assert(*r == 'b'); 
assert(buffer1[0] == 'a'); 
assert(buffer2[0] == 'a'); 
+0

Предполагая, что указатели указывают на ** разные ** допустимые места хранения – Potatoswatter

+0

Пожалуйста, объясните неопределенное поведение, если (1) 'p',' q' и 'r' указывают на одно и то же место, и (2) если' p' и 'q' указывают на то же место, а' r' указывает где-то в другом месте. ISO/IEC 9899: 2011 §6.5.2. Выражения ¶2 _ Если побочный эффект скалярного объекта не зависит от другого побочного эффекта для одного и того же скалярного объекта или вычисления значения с использованием значения одного и того же скалярного объекта, поведение не определено. Если имеется несколько допустимых порядков подвыражений выражения, поведение не определено, если такой какой-либо побочный эффект происходит в любом из порядков. –

+0

Хм, но он спрашивал в терминах C89 и C99, плохие старые дни точки последовательности, где (если я помню), изменяя любой объект дважды между точками последовательности, был UB. +1 в любом случае. – Potatoswatter

3

Поведение этого кода не зависит вообще от того, когда ++ физически выполнено. Поведение четко определено.

Тот факт, что постфикса форма приращения используется просто означает, что оригинальные значения указателей используются в качестве результатов приращения. То естьвсе это эквивалентно

old_p = p; 
old_q = q; 
old_r = r; 

*old_p = *old_q = *old_r; 

p = p + 1; 
q = q + 1; 
r = r + 1; 

Обратите внимание, что вы можете свободно перемещать физические приращения вокруг в совершенно произвольным образом, как и в

old_p = p; 
old_q = q; 
old_r = r; 

p = p + 1; 
q = q + 1; 

*old_p = *old_q = *old_r; 

r = r + 1; 

семантика кода не изменится до тех пор, как оригинал Значения указателя используются в задании.

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

p = p + 1; 
q = q + 1; 

*(p - 1) = *(q - 1) = *r; 

r = r + 1; 
1

уступки lower precedence чем как * и ++ (как поста и предварительно), так что назначение произойдет после этого, но порядок вычисления операндов в = не определен которые не должны иметь значения в этом случае, если r, p или q указывают на одно и то же место, в этом случае у вас будет undefined behavior, так как вы будете изменять один и тот же объект более одного раза в одной и той же точке последовательности.

Проект C99 стандартного раздел 6.5.16операторов присваивания пункта говорит:

[...] Побочный эффект обновления сохраненного значения левого операнда должен происходить между предыдущим и . следующий пункт последовательности [...]

и пункт говорит:

Порядок оценки операндов не указан. [...]