2012-01-13 2 views
2

Пожалуйста, объясните, почему этот код правильный или почему нет: На мой взгляд, строка ++ * p1 = * p2 ++ имеет неопределенное поведение, поскольку p1 разыменовывается первым, а затем увеличивается.Является ли это неопределенным поведением и почему?

int main() 
{ 
    char a[] = "Hello"; 
    char b[] = "World"; 

    char* p1 = a; 
    char* p2 = b; 

    //*++p1 = *p2++; // is this OK? 
    ++*p1 = *p2++; // is this OK? Or this is UB? 

    std::cout << a << "\n" << b; 

    return 0; 
} 
+6

Интересно, почему все это утро задает вопросы о группировке произвольного '++,' * 'и' = 'в одном ужасно сложной и тяжелой для чтения строки ... – ereOn

+11

Если вы должны попробовать и выясните, будет ли код ужасно взрываться и вызвать апокалипсис или нет, ** не используйте его **. Независимо от того, насколько он определен, это * не хороший код *. – ssube

+1

Если это не легко читаемо и прозрачно, это не нормально. – maligree

ответ

9

Первый нормально

*++p1 = *p2++ // p1++; *p1 = *p2; p2++; 

второй UB с C++, потому что вы изменяете что указываемого p1 дважды (один раз из-за приращения и один раз из-за назначения) и нет последовательности точек, разделяющих два побочных эффекта.

С правилами C++ 0x все по-другому и сложнее объяснить и понять. Если вы пишете намеренно выражение наподобие второго, если оно не для соревнований по коду для гольфа, и если вы работаете на меня, подумайте, что вы уволены (даже если это законно в C++ 0x).

Я не знаю, является ли это законным в C++ 0x, и я не хочу знать. У меня слишком мало нейронов, чтобы тратить их так.

+4

+1: Для« считайте себя уволенным »: это заставило меня рассмеяться. Умность доказана тем, что она умеет писать простой код, а не записывая ужасно сложные строки кода. – ereOn

+1

Второй не является UB как в C++ 03, так и в C++ 11. –

+0

@SergeDundich: Стандарт C++ требует, чтобы «между предыдущей и следующей последовательной точкой скалярный объект должен иметь значение, которое его хранимое значение изменялось не более одного раза путем оценки выражения». Второй случай в вопросе - это одно выражение без точек последовательности и дважды записывает одно и то же значение скалярного значения. – 6502

1

Также не существует не определено поведение. И даже ни один из них не задан или неуказанный. (Все три условия - разные вещи.)

Эти две линии хорошо определены, но они делают разные вещи.

*++p1 = *p2++; 

эквивалентно

_T1* _p1 = ++p1; 
T2_* p2_ = p2++; 
*_p1 = *p2_; 

И

++*p1 = *p2++; 

эквивалентно

T1& x = *p1; 
__T1_& y = ++x; 
T2_* p2_ = p2++; 
y = *p2_; 

Где

  • T1& является типом *p1 выражения,
  • _T1* является типом ++p1 выражения,
  • T2_* является типом p2++ выражения,
  • __T1_& является типом ++(*p1) выражения,

    т.е. всех T1/_T1/__T1_/T2_ являются char в вашем случае.

С ++x (для C++ нативных типов) возвращение ссылки на x в вашем случае x и y являются одной и той же ссылка (т.е. *p1) - но это не может быть в случае, если T1 будет каким-то пользовательский типом с перегруженными operator++().

ADD:

Очевидно приращение значения, а затем присвоить это совершенно бессмысленно.

И да - как и другие люди, которые уже сказали, просто не пишите код, о котором у вас есть такие вопросы. Напишите то, что ясно для вас и должно быть понятным читателям.

ADD2:

Чтобы уточнить спорные моменты я цитирую стандарт.

ИСО/МЭК 14882-2003 5.3.2:

  1. Операнд префикса ++ модифицируется путем добавления 1, или установить истинно, если оно является BOOL (это использование не рекомендуется). Операнд должен быть модифицируемым lvalue. Тип операнда должен быть арифметическим типом или указателем на полностью определенный тип объекта. Значение представляет собой значение операнда; это значение lvalue. Если x не относится к типу bool, выражение ++ x эквивалентно x + = 1.
+0

Во втором выражении вы меняете lvalue '* p1' дважды (приращение и присваивание) в одном выражении без точек последовательности, и это UB в C++. Почему, по вашему мнению, это законно в этом случае? – 6502

+0

@ 6502: «Во втором выражении вы меняете lvalue' * p1' дважды. «Lvalue' * p1' изменяется один раз - на 'operator ++'. И после этого lvalue '++ (* p)' (который является возвращаемым значением 'operator ++') изменен оператором присваивания. –

+0

@ 6502: Кроме того, даже если бы не было точек последовательности между оценкой двух выражений (например, 'f (++ x, ++ x)'), тогда порядок оценки такого выражения ** не указан **, не будет ** неопределенного поведения **. –

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