Почему указатели не инициализируются с помощью NULL по умолчанию?

Может кто-нибудь объяснить, почему указатели не инициализируются на NULL?
Пример:

  void test(){
     char *buf;
     if (!buf)
        // whatever
  }

Программа не будет входить внутри if, потому что buf не является нулевым.

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

Ответ 1

Мы все понимаем, что указатель (и другие типы POD) должен быть инициализирован.
Затем возникает вопрос: "кто должен их инициализировать".

Ну есть в основном два метода:

  • Компилятор инициализирует их.
  • Разработчик инициализирует их.

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

Итак, теперь у нас есть ситуация, когда компилятор добавил дополнительную инструкцию к коду, который инициализирует переменную до NULL, а затем код разработчика добавляется для правильной инициализации. Или в других условиях переменная потенциально никогда не используется. Многие разработчики на С++ кричали фол в обоих условиях за счет этой дополнительной инструкции.

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

НО: вы можете имитировать эффект принудительной инициализации. Большинство компиляторов предупредит вас о неинициализированных переменных. Поэтому я всегда ставил свой уровень предупреждения на самый высокий уровень. Затем скажите компилятору обработать все предупреждения как ошибки. В этих условиях большинство компиляторов затем генерируют ошибку для переменных, которые не инициализированы, но используются и, таким образом, предотвратят создание кода.

Ответ 2

Цитата Bjarne Stroustrup в TС++ PL (Special Edition с .22):

Реализация функции не должна налагать значительные накладные расходы на программы, которые этого не требуют.

Ответ 3

Потому что для инициализации требуется время. И в С++ первое, что вам нужно сделать с любой переменной, это явно инициализировать ее:

int * p = & some_int;

или

int * p = 0;

или

class A {
   public:
     A() : p( 0 ) {}  // initialise via constructor
   private:
     int * p;
};

Ответ 4

Потому что один из девизов C++:


Вы не платите за то, что не используете


По этой же причине operator[] vector класса не проверяет, например, находится ли индекс вне границ.

Ответ 5

По историческим причинам, главным образом потому, что это так, как это делается на C. Почему это делается так, как в C, это еще один вопрос, но я думаю, что нулевой принцип служебных данных каким-то образом был задействован в этом дизайнерском решении.

Ответ 6

Кроме того, у нас есть предупреждение, когда вы его взорвали: "возможно, используется до присвоения значения" или аналогичного слова в зависимости от вашего компилятора.

Вы компилируете с предупреждениями, правильно?

Ответ 7

Существует довольно мало ситуаций, когда имеет смысл для неинициализации переменной, а инициализация по умолчанию имеет небольшую стоимость, поэтому зачем это делать?

С++ не является C89. Черт, даже C не C89. Вы можете смешивать объявления и код, поэтому вы должны отложить объявление до тех пор, пока у вас есть подходящее значение для инициализации.

Ответ 8

Указатель - это еще один тип. Если вы создаете int, char или любой другой тип POD, он не инициализируется равным нулю, так почему же должен указывать указатель? Это может считаться ненужным накладным для тех, кто пишет такую ​​программу.

char* pBuf;
if (condition)
{
    pBuf = new char[50];
}
else
{
    pBuf = m_myMember->buf();
}

Если вы знаете, что собираетесь его инициализировать, почему программа должна стоить, когда вы сначала создаете pBuf в верхней части метода? Это принцип нулевой надбавки.

Ответ 9

Обратите внимание, что статические данные инициализируются до 0 (если не указано иное).

И да, вы должны всегда объявлять свои переменные как можно позже и с начальным значением. Код, например

int j;
char *foo;

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

Ответ 10

Если вы хотите, чтобы указатель всегда инициализировался с помощью NULL, вы можете использовать шаблон С++ для эмуляции этой функции:

template<typename T> class InitializedPointer
{
public:
    typedef T       TObj;
    typedef TObj    *PObj;
protected:
    PObj        m_pPointer;

public:
    // Constructors / Destructor
    inline InitializedPointer() { m_pPointer=0; }
    inline InitializedPointer(PObj InPointer) { m_pPointer = InPointer; }
    inline InitializedPointer(const InitializedPointer& oCopy)
    { m_pPointer = oCopy.m_pPointer; }
    inline ~InitializedPointer() { m_pPointer=0; }

    inline PObj GetPointer() const  { return (m_pPointer); }
    inline void SetPointer(PObj InPtr)  { m_pPointer = InPtr; }

    // Operator Overloads
    inline InitializedPointer& operator = (PObj InPtr)
    { SetPointer(InPtr); return(*this); }
    inline InitializedPointer& operator = (const InitializedPointer& InPtr)
    { SetPointer(InPtr.m_pPointer); return(*this); }
    inline PObj operator ->() const { return (m_pPointer); }
    inline TObj &operator *() const { return (*m_pPointer); }

    inline bool operator!=(PObj pOther) const
    { return(m_pPointer!=pOther); }
    inline bool operator==(PObj pOther) const
    { return(m_pPointer==pOther); }
    inline bool operator!=(const InitializedPointer& InPtr) const
    { return(m_pPointer!=InPtr.m_pPointer); }
    inline bool operator==(const InitializedPointer& InPtr) const
    { return(m_pPointer==InPtr.m_pPointer); }

    inline bool operator<=(PObj pOther) const
    { return(m_pPointer<=pOther); }
    inline bool operator>=(PObj pOther) const
    { return(m_pPointer>=pOther); }
    inline bool operator<=(const InitializedPointer& InPtr) const
    { return(m_pPointer<=InPtr.m_pPointer); }
    inline bool operator>=(const InitializedPointer& InPtr) const
    { return(m_pPointer>=InPtr.m_pPointer); }

    inline bool operator<(PObj pOther) const
    { return(m_pPointer<pOther); }
    inline bool operator>(PObj pOther) const
    { return(m_pPointer>pOther); }
    inline bool operator<(const InitializedPointer& InPtr) const
    { return(m_pPointer<InPtr.m_pPointer); }
    inline bool operator>(const InitializedPointer& InPtr) const
    { return(m_pPointer>InPtr.m_pPointer); }
};

Ответ 11

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

В самом деле, в двух словах, они инициализируются в том смысле, что во время соединения переменной указателя присваивается адрес. В приведенном выше примере кода это гарантированно приведет к сбою или генерации SIGSEGV.

Ради здравого смысла всегда инициализируйте указатели на NULL, таким образом, если любая попытка разыменовать его без malloc или new укажет программисту причину неправильного поведения программы.

Надеюсь, что это помогает и имеет смысл,

Ответ 12

Ну, если С++ инициализировал указатели, тогда люди C, жалующиеся на "С++ медленнее, чем C", имели бы что-то реальное, чтобы зависать;)

Ответ 13

С++ исходит из фона C, и из этого вытекает несколько причин:

C, даже больше, чем С++, является заменой языка ассемблера. Он ничего не делает, что вы не говорите ему. Для этого: если вы хотите NULL это сделать - сделайте это!

Кроме того, если у вас есть нулевые вещи на языке с открытым текстом, например, возникают проблемы с автоматической последовательностью C: если вы malloc что-то - должно ли оно автоматически быть обнулено? Что относительно структуры, созданной в стеке? должны ли все байты быть обнулены? Что относительно глобальных переменных? как насчет утверждения типа "(* 0x18);" не означает ли это тогда, что ячейка памяти 0x18 должна быть обнулена?

Ответ 14

О чем вы говорите?

Для безопасности исключений всегда используйте auto_ptr, shared_ptr, weak_ptr и их другие варианты.
Ключ хорошего кода - это тот, который не включает один вызов delete.

Ответ 15

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

Учитывая предупреждения, которые большинство компиляторов дают на самом высоком уровне, я не могу представить себе программирование на самом высоком уровне и рассматривать их как ошибки. Поскольку их появление не спасло меня даже от одной ошибки в огромном количестве кода, я не могу это рекомендовать.