Почему указатели не инициализируются с ПУСТЫМ УКАЗАТЕЛЕМ по умолчанию?

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

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

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

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

117
задан Archmede 29 June 2017 в 09:16
поделиться

15 ответов

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

Ну, в принципе, есть два метода:

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

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

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

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

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

.
158
ответ дан 24 November 2019 в 02:05
поделиться

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

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

-2
ответ дан 24 November 2019 в 02:05
поделиться

Если вам нужен указатель, который всегда инициализируется значением 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); }
};
2
ответ дан 24 November 2019 в 02:05
поделиться

О каких указателях вы говорите?

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

.
0
ответ дан 24 November 2019 в 02:05
поделиться

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

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

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

.
0
ответ дан 24 November 2019 в 02:05
поделиться

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

0
ответ дан 24 November 2019 в 02:05
поделиться

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

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

Надеюсь, это поможет и будет иметь смысл,

.
1
ответ дан 24 November 2019 в 02:05
поделиться

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

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

int j;
char *foo;

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

.
2
ответ дан 24 November 2019 в 02:05
поделиться

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

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

.
6
ответ дан 24 November 2019 в 02:05
поделиться

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

12
ответ дан 24 November 2019 в 02:05
поделиться

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

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

.
8
ответ дан 24 November 2019 в 02:05
поделиться

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

int * p = & some_int;

или:

int * p = 0;

или:

class A {
   public:
     A() : p( 0 ) {}  // initialise via constructor
   private:
     int * p;
};
23
ответ дан 24 November 2019 в 02:05
поделиться

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


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


Именно по этой причине оператор [] класса вектор не проверяет, не выходит ли, например, индекс за рамки.

20
ответ дан 24 November 2019 в 02:05
поделиться

Цитата из Bjarne Stroustrup в TC++PL (Special Edition p.22):

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

40
ответ дан 24 November 2019 в 02:05
поделиться

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

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

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

3
ответ дан 24 November 2019 в 02:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: