Связь между неинициализированными переменными и безопасностью типов - PullRequest
0 голосов
/ 23 июня 2018

Я хотел бы спросить, почему использование переменных, которые не инициализированы, считается небезопасным?

Я читаю книгу для начинающих Бьярна Страуструпа (Принципы и практика программирования с использованием C ++) из руководства по C ++ на этом сайте.

В книге о безопасности типов есть часть, в которой говорится:

Программа - или часть программы - безопасна от типа, когда объекты используется только в соответствии с правилами для их типа. Например, используя переменная до ее инициализации не считается типобезопасной.

Затем в качестве примера в книге приводится следующий код:

 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
 }

Я понимаю, что локальная переменная, которая не инициализирована, будет иметь неопределенное значение и чтение этой переменной приведет к неопределенному поведению. Я не понимаю, как это связано с безопасностью типов. Мы все еще знаем типы из определения переменной.

Почему в комментарии в приведенном выше коде говорится, что значение + не определено, когда и 2.0, и x являются двойными, а + определено для double + double?

Ответы [ 3 ]

0 голосов
/ 23 июня 2018

Неопределенное поведение означает, что результат может быть тем, что вы ожидаете, или некоторым неопределенным значением, которое может быть вне допустимого диапазона типа .

Один яркий пример неопределенного поведения - переполнение целых чисел со знаком :

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

x может иметь значение вне допустимого диапазона int, если i содержит максимальное значение unsigned int.

Таким образом, безопасность типов не гарантируется для выражений, имеющих неопределенные значения.

0 голосов
/ 23 июня 2018

@ codekaizer и @Shankar правы: неопределенное поведение по определению не является безопасным поведением типа.Однако, как это относится к примитивным типам, немного сложнее обернуть голову.Представляется разумным, что любая подходящая длинная последовательность битов может быть действительной int.Как @BoPersson указал ниже, это не совсем верно , и реализации могут включать значения, которые вызывают прерывания в арифметике.Для целых чисел это практически применимо только к 0, когда используется для деления, но это не означает, что стандарт не допускает целочисленную версию чего-то вроде плавающей запятой NaN для достаточно необычной архитектуры.

Читательможет найти пример с виртуальными функциями, более наглядно показывающий, почему неинициализированные переменные не являются безопасными по типу.Обратите внимание:

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;
}

Тип abstractStructPtr означает, что вы можете вызвать foo() на нем.Выражение допустимо: abstractStructPtr имеет тип, поэтому вы можете вызвать foo().Однако реализация foo() живет в производных классах.

Поскольку abstractStructPtr не инициализирован, не гарантируется, что данные, на которые он указывает, структурированы таким образом, что он может выполнить вызовдо foo().Другими словами, хотя тип absractStructPtr равен Base*, нет никакой гарантии, что указанные данные на самом деле являются Base объектом любого вида.Вызов foo(), таким образом, является неопределенным поведением и небезопасен.Может произойти все, что угодно;практически он, вероятно, просто рухнет из-за нарушения доступа к памяти, но это не так!Kablooey.

0 голосов
/ 23 июня 2018

Несмотря на то, что x было объявлено как double, так как оно не было инициализировано, оно имеет случайный битовый шаблон в памяти, и этот шаблон может не представлять никакого действительного числа двойной точности. Следовательно, «значение z» не определено.

...