C++ self-referencing array? - c++

I accidentally created a bug in a program by self-referencing in an array. Here's a very simplified demo program similar in concept:
#include <iostream>
using namespace std;
int kTest[] = {
kTest[0]
};
int main() {
cout << kTest[0] << endl;
}
I was surprised that I received neither a compiler error or even a warning on this code! In my case it ended up producing unpredictable output. Is it accessing garbage memory?
I was curious about under what circumstances this would have well-defined output (if ever!).
Edit: Does it make a difference if kTest is static? What about const? Both?

int kTest[] = {
kTest[0]
};
is similar to, if not exactly same as
int x = x;
It will be undefined behavior, if declared locally in a function.
It seems to be well defined when kTest is a global variable. See the other answer for additional details.

I'm not so sure this is undefined. Quote from the current draft:
[basic.start.static]/3
If constant initialization is not performed, a variable with static
storage duration ([basic.stc.static]) or thread storage duration
([basic.stc.thread]) is zero-initialized ([dcl.init]). Together,
zero-initialization and constant initialization are called static
initialization; all other initialization is dynamic initialization.
Static initialization shall be performed before any dynamic initialization takes place.
To me it looks like kTest is already zero-initialized when the dynamic initialization starts, so it may be defined to initialize to 0.

Related

Pointers Default initialized Value is not NULL?

How do we know pointers are not initialized to NULL by default?
There is a similar questions directed at Why aren't pointers initialized with NULL by default?
Just for checking, here is a very simple code just to see if a pointer is set to NULL by default.
#include <iostream>
using namespace std;
int main()
{
int* x;
if(!x)
cout << "nullptr" << endl;
return 0;
}
and at the output, I received nullptr message. I appreciate if someone can clarify this.
How do we know pointers are not initialized to NULL by default?
Because we know that the standard says that default initialised pointer has an indeterminate value if it has automatic or dynamic storage. Quote from the standard (draft):
[dcl.init] If no initializer is specified for an object, the object is default-initialized. When storage for an object
with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if
no initialization is performed for the object, that object retains an indeterminate value until that value
is replaced. ...
And further:
[dcl.init] To default-initialize an object of type T means:
— If T is a (possibly cv-qualified) class type [pointer isn't a class, so we don't care]
— If T is an array type [pointer isn't an array, so we don't care]
— Otherwise, no initialization is performed.
I have declared a char (and also int) pointer without initializing it , and I got null pointers.
Reading an indeterminate value has undefined behaviour. Quote from the standard (draft):
[dcl.init] ... If an indeterminate value is produced by an evaluation, the behavior is undefined except in the
following cases: [cases which don't apply here]
The question you linked to handles variables with local storage duration exclusively, so I assume you refer to these as well.
Such variables are not initialised if you don't do so yourself, so they get the value of whatever was written in their memory location before (standard wording: their value is 'indeterminate') – nothing speaks against, though, that this memory already is zero – by pure accident!
You can try the following:
void test()
{
int* p; // uninitialized
std::cout << p << std::endl; // undefined behaviour!!!
// (that's what you most likely did already...)
// now something new: change the memory...
p = reinterpret_cast<int*>(static_cast<uintptr_t(0xaddadaad));
}
int main()
{
test();
// again something new: call it a SECOND time:
test();
}
As this is undefined behaviour there are no guarantees at all that you will get any meaningful output – chances are, though that the memory of first function call is reused in second one and you might get output ressembling to the following:
00000000
addadaad
So even if there just happened to be all zero memory at programme start, it might differ from that at some later point while your programme is running...

int x; int y; int *ptr; is not initialization, right?

I'm reading 'C++ All-in-One for Dummies' by J. P. Mueller and J. Cogswell and stumbled onto this:
#include <iostream>
using namespace std;
int main()
{
int ExpensiveComputer;
int CheapComputer;
int *ptrToComp;
...
This code starts out by initializing all the goodies involved — two integers
and a pointer to an integer.
Just to confirm, this is a mistake and should read '... by declaring', right? It's just strange to me that such basic mistakes still make their way to books.
From the point of view of the language, this is default initialization. The problem is, they are initialized to indeterminate values.
otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.
Default initialization of non-class variables with automatic and dynamic storage duration produces objects with indeterminate values (static and thread-local objects get zero initialized)
Note that any attempt to read these indeterminate values leads to UB.
From the standard, [dcl.init]/7
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type ([class]), constructors are considered. The applicable constructors are enumerated
([over.match.ctor]), and the best one for the initializer () is chosen
through overload resolution ([over.match]). The constructor thus
selected is called, with an empty argument list, to initialize the
object.
If T is an array type, each element is default-initialized.
Otherwise, no initialization is performed.
Yes you are correct.
You declared and defined these variables, you did not initialize them!
PS: What is the difference between a definition and a declaration?
This code both declares and defines three variables but does not initialize them (their values are said to be indeterminate).
A variable declaration only must include keyword extern.
Right. Hence, "dummies". :)
We can't even blame this on legacy; historically C programmers would declare* a variable and then "initialize" it later with its first assignment.
But it was never the case that simply declaring a variable, without an initializer, were deemed to be "initializing" it.**
So the wording is just wrong.
* Technically we're talking about definitions, but when we say "declare a variable" we almost always mean defining declarations.
** Though objects with static storage duration do undergo their own zero-initialisation phase before anything else happens, so forgoing initialisation yourself is not a catastrophe in that case. Still, we cannot claim that we have initialised that object.

Default value of enum declared in class

I have a class whose member is an enum declared inside this class:
#include<iostream>
class test
{
public:
enum TYPE{MAN, WOMAN};
TYPE type;
};
int main()
{
test x;
if(x.type == test::MAN) std::cout<<"MAN"<<std::endl;
if(x.type == test::WOMAN) std::cout<<"WOMAN"<<std::endl;
std::cout<<"ok"<<std::endl;
return 0;
}
I know that if an enum is declared at namespace scope, it has a default value 0 and when it's declared locally, it doesn't have any default values, which leads to undefined behavior.
My question is: what if I have an enum which belongs to a class? Is it undefined behavior as well?
I tested the above code and x.type is neither MAN nor WOMAN. However, I've done it for only one compiler and one operating system. I'm interested in a more general answer. I haven't found any information regarding this issue anywhere else.
Edit1: Can referring to this indeterminate value cause segmentation fault?
Edit2: I know this is not a well designed class- it's not mine and I'm trying to debug it. So telling me that I can default-initialize object doesn't solve my problem. Please, treat it as a theoretical question.
The default value of the first name in an enum is 0, regardless of the scope of the enum.
There is no guaranteed default value of an automatic local variable like test x; in main here. It has an indeterminate value. And it's Undefined Behavior to use that value.
You can ¹default-initialize it like this:
test x{};
¹ A subtle point is that at top level this gives a “value-initialization”.
If your object don't have any constructors, then it depends on where you create your object. If it's created globally, then all variables are zero-initialized. If not, they are not initialized properly and reading from them results in UB.
You can force zero-initialization of a non-global variable with test x{}; syntax.
First: "Testing" for undefined behavior is almost never going to give you the right answer.
This is undefined behavior because you are reading from an uninitialized variable with automatic storage duration. Such a variable has an indeterminate value and must not be read from. Every non-static function scope variable has automatic storage duration.
I think you are confusing the definition of the enum type (which happens inside the class definition) with the declaration of a variable of this type (at function scope). In your example x is a variable with automatic storage duration no matter where the type TYPE has been defined.

How to comprehend that an implementation is permitted to treat dynamic initialization of non-local variable as static initialization in some cases?

In fact, the problem comes from the words in the standard draft N4582:
[basic.start.static/3] An implementation is permitted to perform the initialization of a variable with static or thread storage duration as a static initialization even if such initialization is not required to be done statically, provided that
— the dynamic version of the initialization does not change the value of any other object of static or thread storage duration prior to its initialization, and
— the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.
Do these words mean that if the two conditions are satisfied, a non-local variable of class type may be fully initialized statically (zero-initialized) so that its constructor is not called (since the dynamic version, initializing by calling a constructor, may be replaced by a static version)?
Static initialization is performed during compilation/linking. The compiler/linker assigns a location to the variable in the static memory and fills it with the correct bytes (the bytes don't need to be all zeros). When the program starts, those regions of the static memory are loaded from the program's binary file and no further initialization is required.
Examples:
namespace A {
// statically zero-initialized
int a;
char buf1[10];
// non-zero initialized
int b = 1;
char date_format[] = "YYYY-MM-DD";
}
Unlike static initialization, dynamic initialization requires running some code after program start-up to set thus initialized variables to their initial state. The code that needs to be run doesn't need to be a constructor call.
Examples:
namespace B {
int a = strlen(A::date_format); (1)
int b = ++a; (2)
time_t t = time(); (3)
struct C {
int i;
C() : i(123) {}
};
C c; (4)
double s = std::sqrt(2); (5)
}
Now, the C++ standard allows the compiler to perform the computations that would be carried out during dynamic initialization, provided that those computations do not have side effects. Besides, those computations must not depend on external environment. In the above example:
(1) can be performed statically since strlen() doesn't have any side-effects.
(2) must stay dynamic since it mutates a.
(3) must stay dynamic since it depends on external environment/makes system calls.
(4) can be performed statically.
(5) is a little tricky, since floating point computation depends on the state of the FPU (namely, rounding mode). If the compiler is told not to treat floating point arithmetic that seriously, then it can be performed statically.

Will function pointers always initialize to NULL?

I'm using MSVC and it seems like the code below does not crash and the function pointer is initialized to NULL by the compiler.
int (*operate)(int a, int b);
int add(int a, int b)
{
return a + b;
}
int subtract(int a, int b)
{
return a - b;
}
int main()
{
if(operate) //would crash here if not NULL
{
cout << operate(5,5);
}
operate = add;
if(operate)
{
cout << operate(5,5);
}
operate = subtract;
if(operate)
{
cout << operate(5,5);
}
return 0;
}
So it seems MSVC initializes function pointers to NULL, but if I build this on gcc in Linux would it also be NULL? Is it conventional or MSVC specific, can I rely on it being NULL wherever I go?
Thanks
operate is initialised to NULL because it is a global variable, not because it is a function pointer. All objects with static storage duration (which includes global variables, file-level static variables and static variables in functions) are initialised to 0 or NULL if no initialiser is given.
[EDIT in response to Jim Buck's comment:]
In C++, this is guaranteed by clause 3.6.2/1 of the language standard, which begins:
Objects with static storage duration
(3.7.1) shall be zero-initialized
(8.5) before any other initialization
takes place. Zero-initialization and
initialization with a constant
expression are collectively called
static initialization; all other
initialization is dynamic
initialization.
I expect the same behaviour is true of C, since C++ is designed to be compatible with it on most things, although I don't have the standard for it.
[EDIT #2] As Jeff M points out in a comment, it's important to realise that variables of automatic storage duration (that is, "ordinary" local variables) are not automatically zero-initialised: unless an initialiser is given, or they are assigned values by a constructor, they will initially contain random garbage (whatever was already sitting in memory at that location). So it's a good habit to initialise all variables -- it can't hurt but can help.