I have a header file defined as
#pragma once
#include <iostream>
template<int size>
struct B
{
double arr[size * size];
constexpr B() : arr()
{
arr[0] = 1.;
}
};
template<int size>
struct A
{
const double* arr = B<size>().arr;
void print()
{
// including this statement also causes undefined behaviour on subsequent lines
//printf("%i\n", arr);
printf("%f\n", arr[0]);
printf("%f\n", arr[0]); // ???
// prevent optimisation
for (int i = 0; i < size * size; i )
printf("%f ", arr[i]);
}
};
and call it with
auto a = A<8>();
a.print();
Now this code only runs expectedly when compiled with msvc release mode (all compiled with c 17).
expected output:
1.000000
1.000000
msvc debug:
1.000000
-92559631349317830736831783200707727132248687965119994463780864.000000
gcc via mingw (with and without -g
):
1.000000
0.000000
However, this behaviour is inconsistent. The expected output is given if I replace double arr[size * size]
with double arr[size]
instead. No more problems if I allocate arr
on the heap of course.
I looked at the assembly of the msvc debug build but I don't see anything out of the ordinary. Why does this undefined behaviour only occur sometimes?
CodePudding user response:
In this declaration
const double* arr = B<size>().arr;
there is declared a pointer to (the first element of ) a temporary array that will not be alive after the declaration
So dereferencing the pointer results in undefined behavior.
CodePudding user response:
When you wrote:
const double* arr = B<size>().arr;
The above statement initializes a pointer to const double
i.e., const double*
named arr
with a temporary array object. Since this temporary array object will be destroyed at the end of the full-expression, using arr
will lead to undefined behavior.
Why does this undefined behaviour only occur sometimes?
Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.
So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as i said don't rely on the output of a program that has UB. The program may just crash.
So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.
CodePudding user response:
It seems that it was completely coincidental that smaller allocations were always addressed in a spot that would not get erased by the rep stosd
instruction present in printf. Not caused by strange compiler optimisations as I first thought it was.
What does the "rep stos" x86 assembly instruction sequence do?
I also have no idea why I decided to do it this way. Not exactly the question I asked but I ultimately wanted a compile time lookup table so the real solution was static inline constexpr auto arr = B<size>()
on c 20. Which is why the code looks strange.