Неинициализированная память в C ++ Builder / Delphi - PullRequest
2 голосов
/ 06 июня 2009

Неинициализированные переменные в Delphi гарантированно имеют какое-либо конкретное значение

  • в стеке?
  • в куче?

Поскольку C ++ Builder, как правило, следует дизайну Delphi, неинициализированные переменные в C ++ Builder гарантированно будут иметь любое конкретное значение

  • в стеке?
  • в куче, для переменных-членов классов, производных от TObject?
  • в куче, для переменных-членов POCOs ?

Я унаследовал некоторый код C ++ Builder, который местами сильно зависит от переменных-членов, инициализируемых нулями, и пытаюсь выяснить, гарантирует ли язык это.

Гарантирует ли Windows, что память инициализируется нулями, когда она впервые передается в стек или кучу программы? ( Редактировать: Я понимаю, что программа будет перезаписывать память при выполнении, и поэтому она не может продолжать зависеть от этого; я просто пытаюсь выяснить поведение, которое я наблюдал.)

Ответы [ 5 ]

7 голосов
/ 06 июня 2009

Поскольку ответ Каски касается только c ++, мой ответ для delphi:

В delphi см. этот ответ от Джакомо Дель Эспости

  • Поля объекта всегда инициализируются равными 0, 0.0, '', False, nil или любым другим.
  • Глобальные переменные всегда инициализируются. (до 0)
  • Локальные переменные унифицированы, поэтому вам необходимо присвоить значение, прежде чем вы сможете их использовать.

мс-помощь: //borland.bds4/bds4ref/html/Variables.htm

Все кредиты Джакомо Дельи Эспости

Редактировать : " Гарантирует ли Windows, что память инициализируется нулями, когда она впервые передается в стек или кучу программы? "

Windows гарантирует, что память инициализируется нулями, когда она впервые передается новому процессу (в противном случае у вас возникнет серьезная проблема безопасности с программами, способными считывать другие процессы, отбрасывавшие память независимо от разрешений). Однако, используя c ++, эта гарантия вам мало поможет, так как c-runtime может перезаписать память по своему усмотрению, прежде чем ваш код получит какую-либо возможность ее использовать.

Edit2 : переменные построителя c ++ явно инициализируются для " классов в стиле VCL " (что бы это ни значило, все, что наследуется от TObject?), См. http://docs.embarcadero.com/products/rad_studio/cbuilder6/EN/CB6_DevelopersGuide_EN.pdf

Цитирую:

"Поскольку члены данных могут использоваться в виртуальных функциях, важно понять, когда и как они инициализируются. В Object Pascal все неинициализированные данные инициализируется нулями. Это относится, например, к базовым классам, чьи конструкторы не вызывается с наследственным. В стандарте C ++ нет гарантии значения неинициализированные члены данных. Следующие типы членов класса должны быть инициализируется в списке инициализации конструктора класса: • Рекомендации • Элементы данных без конструктора по умолчанию

Тем не менее, значение этих элементов данных или инициализированных в теле конструктор, не определен при вызове конструкторов базового класса. В C ++ Builder, память для классов в стиле VCL инициализируется нулями.

Технически, это память класса VCL или CLX, равная нулю, то есть биты ноль, значения на самом деле не определены. Например, ссылка равна нулю.

Виртуальная функция, основанная на значении переменных-членов, инициализированных в Тело конструктора или в списке инициализации может вести себя так, как будто переменные инициализируется до нуля. Это потому, что конструктор базового класса вызывается перед обработан список инициализации или введено тело конструктора.

#include <sysutils.hpp>
class Base : public TObject {
public:
    __fastcall Base() { init(); }
        virtual void __fastcall init() { }
    };
class Derived : public Base {
    public:
        Derived(int nz) : not_zero(nz) { }
        virtual void __fastcall init()
        {
        if (not_zero == 0)
        throw Exception("not_zero is zero!");
        }
    private:
        int not_zero;
};
int main(void)
{
    Derived *d42 = new Derived(42);
    return 0;
}

Этот пример вызывает исключение в конструкторе Base. Потому что база созданный до Derived, not_zero, еще не был инициализирован со значением 42 перешел к конструктору. Имейте в виду, что вы не можете инициализировать данные членов вашего Класс в стиле VCL перед вызовом его конструкторов базового класса. "

2 голосов
/ 06 июня 2009

Краткий ответ: в C ++ нужно все инициализировать

Если C ++ Builder похож на C ++, то нет никаких гарантий относительно содержимого памяти, если вы явно не инициализируете его.

Хотя окна могут очищать страницы до того, как они станут доступны для стека или кучи программы, если вы не запрашиваете собственную память у ОС, вы, вероятно, используете конструкторы или библиотеку для выделения памяти. Гораздо чаще вы получаете страницу или область памяти, которую вы уже использовали сами. В этом случае это в значительной степени гарантированно будет грязным. Вдвойне для стековых страниц, которые почти никогда не бывают новыми.

memset () будет таким образом, как вы стираете память в C, но в C ++ вам нужно предоставить значения по умолчанию для каждого поля или явно инициализировать их в конструкторе.

1 голос
/ 06 июня 2009

В Delphi конструктор TObject инициализирует нулями выделенную кучу (всегда в куче, потому что TObjects не может быть создан в стеке в Delphi) память объекта, тем самым очищая все переменные-члены.

1 голос
/ 06 июня 2009

В C ++ память гарантированно инициализируется при следующих обстоятельствах:

  • для статически распределенных переменных
  • для объекта с конструктором, который инициализирует его члены

В первом из этих случаев типы данных POD, такие как int, указатели и т. Д., Будут инициализироваться нулями.

Это единственные гарантии, которые дает стандарт C ++. Windows не дает никаких гарантий в этой области.

0 голосов
/ 12 июня 2009

На Delphi, насколько я знаю: * vcl классы автоматически инициализируют свои поля. * Глобалы тоже

Локальные переменные не инициализированы. Их начальное содержание полностью не определено. Таким образом, Assigned (переменная) типа TObject будет возвращать false все время, если переменная является локальной.

...