Почему указатели не инициализируются с NULL по умолчанию? - PullRequest
108 голосов
/ 16 декабря 2009

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

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

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

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

Ответы [ 15 ]

152 голосов
/ 16 декабря 2009

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

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

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

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

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

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

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

38 голосов
/ 16 декабря 2009

Цитирование Бьярна Страуструпа в TC ++ PL (Специальный выпуск, стр. 22):

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

23 голосов
/ 16 декабря 2009

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

int * p = & some_int;

или

int * p = 0;

или

class A {
   public:
     A() : p( 0 ) {}  // initialise via constructor
   private:
     int * p;
};
19 голосов
/ 16 декабря 2009

Потому что один из столпов C ++:


Вы не платите за то, что вам не нужно


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

11 голосов
/ 16 декабря 2009

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

7 голосов
/ 16 декабря 2009

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

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

5 голосов
/ 16 декабря 2009

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

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

2 голосов
/ 16 декабря 2009

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

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

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

1 голос
/ 16 декабря 2009

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

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); }
};
1 голос
/ 16 декабря 2009

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

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

int j;
char *foo;

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

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