What are all the common undefined behaviours that

2018-12-31 00:48发布

What are all the common undefined behaviours that a C++ programmer should know about?

Say, like:

a[i] = i++;

11条回答
牵手、夕阳
2楼-- · 2018-12-31 01:22

Besides undefined behaviour, there is also the equally nasty implementation-defined behaviour.

Undefined behaviour occurs when a program does something the result of which is not specified by the standard.

Implementation-defined behaviour is an action by a program the result of which is not defined by the standard, but which the implementation is required to document. An example is "Multibyte character literals", from Stack Overflow question Is there a C compiler that fails to compile this?.

Implementation-defined behaviour only bites you when you start porting (but upgrading to new version of compiler is also porting!)

查看更多
萌妹纸的霸气范
3楼-- · 2018-12-31 01:24

The compiler is free to re-order the evaluation parts of an expression (assuming the meaning is unchanged).

From the original question:

a[i] = i++;

// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)

// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:

int rhs  = i++;
int lhs& = a[i];
lhs = rhs;

// or
int lhs& = a[i];
int rhs  = i++;
lhs = rhs;

Double Checked locking. And one easy mistake to make.

A* a = new A("plop");

// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'

// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.

// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        a = new A("Plop");  // (Point A).
    }
}
a->doStuff();

// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
//           Remember (c) has been done thus 'a' is not NULL.
//           But the memory has not been initialized.
//           Thread 2 now executes doStuff() on an uninitialized variable.

// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
    Lock   lock(mutex);
    if (a == null)
    {
        A* tmp = new A("Plop");  // (Point A).
        a = tmp;
    }
}
a->doStuff();

// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
查看更多
不流泪的眼
4楼-- · 2018-12-31 01:25

My favourite is "Infinite recursion in the instantiation of templates" because I believe it's the only one where the undefined behaviour occurs at compile time.

查看更多
忆尘夕之涩
5楼-- · 2018-12-31 01:29

Namespace-level objects in a different compilation units should never depend on each other for initialization, because their initialization order is undefined.

查看更多
旧人旧事旧时光
6楼-- · 2018-12-31 01:37

Assigning to a constant after stripping constness using const_cast<>:

const int i = 10; 
int *p =  const_cast<int*>( &i );
*p = 1234; //Undefined
查看更多
登录 后发表回答