Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to reference uninitialized memory without accessing it?

Following situation, I have a struct containing pointers to integer variables like this:

struct Structure[] = { 
    { &Var[0], &Var[1] },
    { &Var[2], &Var[3] }
};

Thing is: Var is uninitialized the first time this struct is filled. (As in: NULL) Shortly after (on first pass) the variable Var will be initialized and the references will be updated accordingly.

I see no reason for this to malfunction, but I'd like your expertise on it. Is it legal to put a reference to invalid memory (with array subscript) into an array like this? Or do I need a different kind of approach for this situation?

I'm not accessing the contents of these variables until after the first initialization.

Thank you very much.

Edit: For the benefit of future readers: Var is a global pointer variable, which is initialized to NULL at the beginning. The initialization turns it into an array by using new.

like image 663
ATaylor Avatar asked Aug 05 '13 15:08

ATaylor


People also ask

What does uninitialized memory mean?

Use of uninitialized memory means reading data from the buffer that was allocated but not filled with initial values. The program behavior in this case is considered an error which is quite difficult to detect sometimes.

Is reading uninitialized memory UB?

So yes, reading uninitialized memory is always UB.


1 Answers

I'm assuming that Var is a pointer object and that its current value is a null pointer. This is implied by your statement:

Var is uninitialized the first time this struct is filled. (As in: NULL)

I'm also assuming that Var is not defined at block scope. If it's defined at block scope, and you haven't initialized it or assigned a value to it, then its value is garbage, not necessarily a null pointer value, and any attempt to refer to its value has undefined behavior.

The behavior is undefined.

If Var == NULL, then &Var[N] has undefined behavior.

arr[index] is by definition equivalent to *(arr + index), so &Var[N] is equivalent to &(*(Var + N)). The behavior of pointer arithmetic is defined in terms of elements of the array object into which the pointer points (with a single object treated as a one-element array) and a null pointer doesn't point to anything.

A digression:

C explicitly says that &*x is evaluated as x, and &[x[i]) is evaluated as x+i; C++ doesn't say this, so the operand of & has to be valid. C++ has a special case for adding 0, which is well defined even for a null pointer (C has no such special case). But &Var[0] is still invalid in in both C and C++, but for different reasons. In C, it's equivalent to Var + 0, but adding 0 to a null pointer has undefined behavior. In C++, it's not equivalent to Var + 0; rather it's equivalent to &(*(Var + 0)); Var + 0 is a null pointer, and dereferencing it has undefined behavior.)

End of digression.

Yes, just computing an invalid address has undefined behavior, even if it's never dereferenced.

Here's the relevant text from the 2011 ISO C++ standard, 5.7 [expr.add] paragraph 5; note particularly the very end:

When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integral expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and i − n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.

like image 186
Keith Thompson Avatar answered Sep 28 '22 08:09

Keith Thompson