Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it undefined behaviour to access two objects of type T declared next to each other using T[]?

I recently watched a CppCon talk by Miro Kenjp on "Non-conforming C++: the Secrets the Committee Is Hiding From You".

At 28:30 (https://youtu.be/IAdLwUXRUvg?t=1710) He states that accessing doubles next to each other was UB and I don't quite understand why. Can someone explain why is this the case?

And if this is the case then surely its UB to access other things this way for example:

int* twoInts = malloc(sizeof(int) * 2);
int secondInt = twoInts[1]; //Undefined behaviour?
like image 693
name Avatar asked Aug 19 '21 14:08

name


People also ask

What is undefined behavior in programming?

In computer programming, undefined behaviour is defined as 'the result of compiling computer code which is not prescribed by the specs of the programming language in which it is written'.

What type of behavior C is undefined?

In C the use of any automatic variable before it has been initialized yields undefined behavior, as does integer division by zero, signed integer overflow, indexing an array outside of its defined bounds (see buffer overflow), or null pointer dereferencing.

What causes undefined Behaviour in C?

So, in C/C++ programming, undefined behavior means when the program fails to compile, or it may execute incorrectly, either crashes or generates incorrect results, or when it may fortuitously do exactly what the programmer intended.

Why does C++ allow undefined behavior?

Undefined behavior exists mainly to give the compiler freedom to optimize. One thing it allows the compiler to do, for example, is to operate under the assumption that certain things can't happen (without having to first prove that they can't happen, which would often be very difficult or impossible).

What is undefined behavior in C++?

undefined behavior - there are no restrictions on the behavior of the program. Examples of undefined behavior are memory accesses outside of array bounds, signed integer overflow, null pointer dereference, modification of the same scalar more than once in an expression without sequence points, access to an object through a pointer...

What causes undefined behavior due to incorrect pointer alignment?

The following might have undefined behavior due to incorrect pointer alignment: The undefined behavior happens as the pointer is converted. According to C11, if a conversion between two pointer types produces a result that is incorrectly aligned (6.3.2.3), the behavior is undefined.

What does it mean when a pointer is undefined?

The undefined behavior happens as the pointer is converted. According to C11, if a conversion between two pointer types produces a result that is incorrectly aligned (6.3.2.3), the behavior is undefined. Here an uint32_t could require alignment of 2 or 4 for example.

Can we modify an object defined with a non-const-qualified type?

Quoting ISO/IEC 9899:201x, section 6.7.3 §2: If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. [...]


2 Answers

I want to start with a quote from the presentation:

You are not programming against the CPU, you are programming against the abstract machine

You say:

He states that accessing doubles next to each other was UB

But your quote is incomplete. He specifies this very crucial fact:

... unless the objects are part of an array

malloc is a red herring (and a bag of another set of problems). His code uses new[] so malloc is just poisoning the well here.

The specific problem he mentions on his slides is that the double objects created on the buffer are created by std::uninitialized_default_construct_n and this method doesn't create an array of doubles, but instead creates multiple objects that are consecutive in memory. He asserts that in the C++ standard (the abstract machine you are programming against) you can't treat objects as part of an array unless you actually created the array of objects.

The point the author tries to make is that the C++ standard is flawed and there is no strictly conforming way to create a flexible array (pre C++20).


For reference here is the code (reproduced after image):

struct header
{
    int size;
    byte* buffer;
    thing some;
};
constexpr size_t x = ...;

byte* buffer = new byte[x + n * sizeof(double)];
header* p = new (buffer) header{n, buffer};
uninitialized_default_construct_n(
    reinterpret_cast<double*>(buffer + x), n);
double* data = reinterpret_cast<double*>(p->buffer + x);

data[0] = data[1] + data[2]; // <-- problem here
                             // because we never created an array of doubles
like image 121
bolov Avatar answered Oct 17 '22 11:10

bolov


There is no guarantee that the variables will be allocated next to each other.

  1. The memory area used by malloc may be a different region than the memory area for temporary, local, variables.

  2. Local variables may not be stored in memory. They could be stored in registers.

  3. Accessing array elements outside the declared range is undefined behavior.
    The array could be allocated at the end of memory, so accessing outside the array has no memory assigned to it.

  4. Local variables may be defined in another memory segment, e.g. stack., that is not the same as memory for dynamic allocation.

Here's an example.
An embedded system has On-Chip Memory and memory outside the "System on the Chip" (SOC). The On-Chip memory is faster, but there is less of it. The architects assign this memory to the stack. The memory outside the SOC is slower, but there is more of it, so it is assigned to the dynamic memory.

Another example:
The operating system supports virtual memory. Memory is paged onto the hard drive as needed. The operating system assigns a small amount of memory to your program. The small amount of memory will be assigned to the stack and the virtual memory will be assigned to dynamic memory.

Not all platforms are made of contiguous memory. Memory locations may also be assigned to hardware devices.

like image 2
Thomas Matthews Avatar answered Oct 17 '22 11:10

Thomas Matthews