Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connection between uninitialized variables and type safety

I would like to ask why using variables that are not initialized is considered non type-safe?

I'm reading Bjarne Stroustrup's beginner book(Programming Principles and Practice Using C++) from the C++ book guide on this site.

There is a part in the book about type-safety that states :

A program - or a part of a program - is type-safe when objects are used only according to the rules for their type. For example, using a variable before it has been initialized is not considered type-safe.

Then the book provides the following code as an example:

 int main() {
        double x; // we "forgot" to initialize
                  // the value of x is undefined

        double y = x; // the value of y is undefined
        double z = 2.0+x; // the meaning of + and the value of z are undefined
 }

I understand that a local variable that is not initialized will have an indeterminate value and reading this variable will cause undefined behavior. What I do not understand is how is it connected to type-safety. We still know the types from the variable's definition.

Why does the comment in the above code states that the meaning of + is undefined when both 2.0 and x are double, and the + is defined for double + double?

like image 587
CyberMarmot Avatar asked Jun 23 '18 14:06

CyberMarmot


People also ask

Do uninitialized variables pose any danger in a program?

An uninitialized variable has an undefined value, often corresponding to the data that was already in the particular memory location that the variable is using. This can lead to errors that are very hard to detect since the variable's value is effectively random, different values cause different errors or none at all.

What happens when you try to use an uninitialized variable?

So using an uninitialized variable will result in undefined behavior. 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.

What type of error is uninitialized variable?

In computing, an uninitialized variable is a variable that is declared but is not set to a definite known value before it is used. It will have some value, but not a predictable one. As such, it is a programming error and a common source of bugs in software.

What happens if we use an uninitialized variable where it is declared but no initial value is given?

An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.


2 Answers

Undefined behavior means the output could be what you expect or some indeterminate value that may be outside the valid range of a type.

One clear example of undefined behavior is signed integer overflow:

unsigned int i;   // uninitialized
int x = i + 2;    // indeterminate value
if (x + 1 > x) {} // undefined behavior due to signed overflow

x can have a value outside int valid range if i holds a max value of unsigned int.

Thus, type safety is not guaranteed for expressions having indeterminate values.

like image 59
Joseph D. Avatar answered Oct 16 '22 11:10

Joseph D.


@codekaizer and @Shankar are right: undefined behavior is, by definition, not type safe behavior. How that applies to primitive types is a little harder to wrap your head around, though. It seems reasonable that any appropriately long sequence of bits could be a valid int. As @BoPersson pointed out below, this is not strictly true and implementations are free to include values which cause interrupts under arithmetic. For integers this practically only applies to 0 when used to divide, but that does not mean the standard doesn't allow for an integer version of something like floating point NaN on a suitably unusual architecture.

The reader may find an example with virtual functions more intuitively illustrative of why uninitialized variables aren't type safe. Consider:

struct Base {
    virtual int foo() const =0;
};

struct DerivedA : public Base {
    int foo() const override { return 10; }
};

struct DerivedB : public Base {
    int foo() const override { return -10; }
};

int main() {
    Base* abstractStructPtr;
    std::cout << abstractStructPtr->foo() << std::endl;
    return 0;
}

The type of abstractStructPtr means you can call foo() on it. The expression is valid: abstractStructPtr has a type, that is why you can call foo(). However, the implementation of foo() lives in derived classes.

Since abstractStructPtr isn't initialized, the data it points to isn't guaranteed to be structured in such a way that it can fulfill the call to foo(). In other words, while the type of absractStructPtr is Base*, there is no guarantee that the data that is pointed to is actually a Base object of any kind. Calling foo() is thus undefined behavior and not type safe. Anything could happen; practically it will probably just crash via a memory access violation, but it might not! Kablooey.

like image 2
MikeTheCoder Avatar answered Oct 16 '22 10:10

MikeTheCoder