John Harrison said:
....
Off the top of my head here are a few common causes of undefined behaviour ....
1) dereferencing a null pointer
or any pointer to an address that has not been properly obtained ( i.e.
*(int*)454 = 0; )
2) accessing outside the bounds of an array
3) deleting the same memory twice
or calling delete on an object addresss that was not allocated with
new.
3b) Using delete[] to release memory allocated with new,
or delete on the address returned by new[].
4) dereferencing a pointer after it has been deleted
5) dereferencing a pointer which points to a destroyed object
6) accessing an uninitialised variable
or a variable that has been destroyed.
(NB: this also applies to global variables).
7) signed integer overflow
8) modifying a const object
Two important additions I can think of:
9) modifying a variable twice between sequence points (or accessing the
value being modified).
e.g. a = ++i + ++i; or a = i + ++i;
10) deleting a derived class through a pointer to a base class whose
destructor is not virtual.
Off the top of my head too, I think that these would be the most common
causes, but I'm sure the list can be extended.
Furthermore, UB may be triggered by causing library functions
to perform one of the above actions, for example by passing an
insufficiently large or invalid output buffer to functions such as sprintf
or strcpy.
Some standard library functions have explicit restrictions on the parameters
they
can receive (e.g. calling memcpy with overlapping memory ranges).
So it is important, for writing correct code, to understand the behavior
and restrictions of the functions you are calling. And it's not an obvious
thing.
No doubt I've missed many others
So do I...
The each of the C and C++ standards use the term "Undefined behavior"
close to 200 times, and an exaustive list is impossible to provide.
As you can see several of the common causes of undefined behaviour involve
pointers. So the moral is don't use pointers, prefer STL classes instead,
they are somewhat safer.
Overall, the C++ standard library does a better job than C's at trying to
prevent UB. What helps even more is if you are using an STL implementation
that
supports a 'debug' mode where all container iterators are checked at
runtime.
Some caveats I can think of include:
- initializing an std::string will a NULL char pointer.
- using [..] on standard containers (i.e. vector) does not verify range.
( vector::at() may be used instead, and will throw an exception ).
That's just adding my two cents, obviously my list is also partial and
incomplete...
Ivan