English 中文(简体)
这种不明确的行为和为什么?
原标题:Is this undefined behaviour and why?
  • 时间:2012-01-13 09:29:27
  •  标签:
  • c++

Please, explain why this code is correct or why not: In my opinion, line ++*p1 = *p2++ has undefined behaviour, because p1 is dereferenced first and then incrementing.

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 << "
" << b;

   return 0;
}
最佳回答

第一是ok

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

第二类是C++,因为你正在修改<代码>p1两倍(由于加固和一次由于派任而产生的)的标语,而且没有分两部分效果的顺序点。

With C++0x rules things are different and more complex to explain and to understand. If you write intentionally expressions like the second one, if it s not for a code golf competition and if you are working for me then consider yourself fired (even if that is legal in C++0x).

我不知道C++0x是否合法,我不想知道。 我用这种方式去除神经。

问题回答

在现代C++(至少是C++2011年及以后)中,两者都不是未界定的行为。 即便不是,实施的定义是unspecised。 (所有三个术语都是不同的。)

These two lines are both well defined (but they do different things). When you have pointers p1 以及 p2 to scalar types then

*++p1 = *p2++;

等于

p1 = p1 + 1;
*p1 = *p2;
p2 = p2 + 1;

(^^^this is also true for C++ 1998/2003)

以及

++*p1 = *p2++;

等于

*p1 = *p1 + 1;
*p1 = *p2;
p2 = p2 + 1;

(C++ 1998/2003年) 或不可(如下文所述)

Obviously in case 2 incrementing value 以及 then assigning to it (thus overwriting just incremented value) is pointless - but there may be similar examples that make sense (e.g. += instead of =).

BUT like many people point out - just don t write the code that looks ambiguous or unreasonably complex. Write the code that is clear to you 以及 supposed to be clear to the readers.

Old C++ 1998/2003 第二种表述是一个奇怪的问题:

在阅读预先加固操作员的说明之后,首先:

<>strongISO>IEC 14882-2003 5.3.2:

  1. The oper以及 of prefix ++ is modified by adding 1, or set to true if it is bool (this use is deprecated). The oper以及 shall be a modifiable lvalue. The type of the oper以及 shall be an arithmetic type or a pointer to a completely-defined object type. The value is the new value of the oper以及; it is an lvalue. If x is not of type bool, the expression ++x 等于 x+=1.

I personally have a strong feeling that everything is perfectly defined 以及 obvious 以及 the same as above for C++ 2011 以及 later. At least in the sense that every reasonable C++ implementation will behave in exact same well defined way (including old ones).

Why it should be otherwise if we always intuitively rely on a general rule that in any simple operator evaluation within a complex expression we evaluate its oper以及s first 以及 after that apply the operator to the values of those oper以及s. Right? Breaking this intuitive expectation would be extremely stupid for any programming language.

So for the full expression ++*p1 = *p2++; we have oper以及s: 1 - ++*p1 evaluated as already incremented lvalue (as defined in the above quote from C++ 2003) 以及 2 - *p2++ that is an rvalue stored at pointer p2 before its increment. It doesn t look ambiguous at all. Of course in this case - no reason to increment a value you are overwriting anyway BUT if there was double increment instead - ++(++*p1); OR other kind of assignment like +=/-=/&=/*=/etc instead of simple assignment THAT would not be unreasonable at all.

Unfortunately all the intuition 以及 logic is messed up by this:

<>strongISO>IEC 14882-2003 - 5 expression:

  1. Except where noted, the order of evaluation of oper以及s of individual operators 以及 subexpressions of individual expressions, 以及 the order in which side effects take place, is unspecified. Between the previous 以及 next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
  [Example:
      i = v[i++];         // the behavior is unspecified
      i = 7, i++, i++;    // i becomes 9
      i = ++i + 1;        // the behavior is unspecified
      i = i + 1;          // the value of i is incremented
  —end example]

So this wording if interpreted in a paranoid way seems to imply that modification of a value stored in a specific location more than once without intervening sequence point is explicitly forbidden by this rule 以及 the last sentence declares that failing to comply with every requirement is Undefined Behavior. AND our expression seems to modify the same location more that once (?) with no sequence point until the full expression evaluated. (This arbitrary 以及 unreasonable limitation is reinforced further by example 3 - i = ++i + 1; though it says // the behavior is unspecified - not undefined as in the wording before - which only adds more confusion.)

BUT on the other h以及... If we ignore the example 3. (Maybe i = ++i + 1; is a typo 以及 there should have been postfix increment instead - i = i++ + 1;? Who knows... Anyway examples are not part of formal specification.) If we interpret this wording in the most permissive way - we can see that in each allowed order of evaluation of subexpressions of the whole expression - preincrement ++*p1 must be evaluated to an LVALUE (which is something that allows further modification) BEFORE applying assignment operator so the only valid final value at that location is the one that is stored with assignment operator. ALSO NOTE that conforming C++ implementation have no obligation to actually modify that location more than once 以及 may instead store only final result - that is both reasonable optimization allowed by the st以及ard 以及 may be actual dem以及 of this article.

Which one of those interpretations is correct? Paranoid or permissive? Universally applicable logic or some suspicious 以及 ambiguous words in a document almost nobody really ever read? Blue pill or Red pill?

谁知道...... 它看起来像一个灰色区域,需要作不明确的解释。

If we interpret the quote from C++ 2003 st以及ard above in a paranoid way then it looks like this code may be Undefined Behavior:

#include <iostream>

#define INC(x) (++(x))

int main()
{
    int a = 5;

    INC(INC(a));
    std::cout << a;
    return 0;
}

while this code is perfectly legitimate 以及 well defined:

#include <iostream>

template<class T> T& INC(T& x) // sequence point after evaluation of the arguments
{                              // 以及 before execution of the function body
    return ++x;
}

int main()
{
    int a = 5;

    INC(INC(a));
    std::cout << a;
    return 0;
}

真的?

All this looks very much like a defect of the old C++ st以及ard.

Fortunately this has been addressed in newer C++ st以及ards (starting with C++ 2011) as there is no such concept as sequence point anymore. Instead there is a relation - something sequenced before something. And of course the natural guarantee that evaluation of the argument expressions of any operator is sequenced before evaluation of the result of the operator is there.

ISO/IEC 14882-2011 - 1.9 Program execution

  1. Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (1.10), which induces a partial order among those evaluations. Given any two evaluations A 以及 B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B 以及 B is not sequenced before A, then A 以及 B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. — end note ] Evaluations A 以及 B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. — end note ]

  2. Every value computation 以及 side effect associated with a full-expression is sequenced before every value computation 以及 side effect associated with the next full-expression to be evaluated.

  3. Except where noted, evaluations of oper以及s of individual operators 以及 of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced 以及 indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. — end note ] The value computations of the oper以及s of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either anotherside effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

  [ Example:
      void f(int, int);
      void g(int i, int* v) {
      i = v[i++];         // the behavior is undefined
      i = 7, i++, i++;    // i becomes 9
      i = i++ + 1;        // the behavior is undefined
      i = i + 1;          // the value of i is incremented
      f(i = -1, i = -1);  // the behavior is undefined
  }
  — end example ]

(Also NOTE, How C++ 2003 prefix increment example i = ++i + 1;, 改为“职位固定增减范例i = i++ + 1; in this C++ 2011 quote.) iii





相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签