Por que os pointers não são inicializados com NULL por padrão?

Alguém pode explicar por que os pointers não são inicializados para NULL ?
Exemplo:

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

O programa não pisa dentro do if porque o buf não é nulo.

Eu gostaria de saber por que, em qual caso precisamos de uma variável com lixo, especialmente os pointers que lidam com o lixo na memory?

Todos nós percebemos que o ponteiro (e outros tipos de POD) devem ser inicializados.
A questão então se torna “quem deve inicializá-los”.

Bem, existem basicamente dois methods:

  • O compilador inicializa-os.
  • O desenvolvedor inicializa-os.

Vamos supor que o compilador tenha inicializado qualquer variável não inicializada explicitamente pelo desenvolvedor. Em seguida, nos deparamos com situações em que a boot da variável não foi trivial e o motivo pelo qual o desenvolvedor não fez isso no ponto de declaração foi que ele precisou executar alguma operação e, em seguida, atribuir.

Portanto, agora temos a situação em que o compilador adicionou uma instrução extra ao código que inicializa a variável para NULL e depois o código do desenvolvedor é adicionado para fazer a boot correta. Ou sob outras condições, a variável é potencialmente nunca usada. Muitos desenvolvedores de C ++ gritariam sob as duas condições ao custo daquela instrução extra.

Não é só o tempo. Mas também espaço. Há muitos ambientes em que ambos os resources são preciosos e os desenvolvedores também não querem desistir.

MAS : Você pode simular o efeito de forçar a boot. A maioria dos compiladores irá avisá-lo sobre variables ​​não inicializadas. Então, eu sempre transformo meu nível de alerta para o nível mais alto possível. Em seguida, diga ao compilador para tratar todos os avisos como erros. Sob essas condições, a maioria dos compiladores gerará um erro para as variables ​​não inicializadas, mas usadas e, portanto, evitará que o código seja gerado.

Citando Bjarne Stroustrup em TC ++ PL (Special Edition p.22):

A implementação de um recurso não deve impor sobrecargas significativas em programas que não exigem isso.

Porque a boot demora tempo. E em C ++, a primeira coisa que você deve fazer com qualquer variável é inicializá-la explicitamente:

 int * p = & some_int; 

ou:

 int * p = 0; 

ou:

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

Porque um dos pilares do C ++ é:


Você não paga pelo que não precisa


Por essa mesma razão, o operator[] da class de vector não verifica se o índice está fora dos limites, por exemplo.

Por razões históricas, principalmente porque é assim que é feito em C. Por que é feito assim em C, é outra questão, mas eu acho que o princípio de sobrecarga zero foi envolvido de alguma forma nesta decisão de design.

Além disso, temos um aviso para quando você explodi-lo: “é possivelmente usado antes de atribuir um valor” ou um verbage semelhante, dependendo do seu compilador.

Você compila com avisos, certo?

Há pouquíssimas situações em que faz sentido que uma variável não seja inicializada, e a boot padrão tem um custo pequeno, então por que fazê-lo?

C ++ não é C89. Inferno, mesmo C não é C89. Você pode misturar declarações e código, então você deve adiar a declaração até que você tenha um valor adequado para inicializar.

Um ponteiro é apenas outro tipo. Se você criar um int , char ou qualquer outro tipo de POD, ele não será inicializado como zero, então por que um ponteiro? Isso pode ser considerado uma sobrecarga desnecessária para alguém que escreve um programa como esse.

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

Se você sabe que vai inicializá-lo, por que o programa deve incorrer em um custo quando você cria o pBuf na parte superior do método? Este é o princípio de zero overhead.

Se você quiser um ponteiro que é sempre inicializado para NULL, você pode usar um modelo C ++ para emular essa funcionalidade:

 template 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(PObj pOther) const { return(m_pPointer>pOther); } inline bool operator< (const InitializedPointer& InPtr) const { return(m_pPointer(const InitializedPointer& InPtr) const { return(m_pPointer>InPtr.m_pPointer); } }; 

Observe que os dados estáticos são inicializados para 0 (a menos que você diga o contrário).

E sim, você deve sempre declarar suas variables ​​o mais tarde possível e com um valor inicial. Código como

 int j; char *foo; 

deve desencadear alarmes quando você lê-lo. Eu não sei se qualquer fiapo pode ser persuadido a carpa sobre isso, já que é 100% legal.

Bem, se o C ++ inicializou pointers, então o pessoal do C que reclama “C ++ é mais lento que o C” teria algo real em que se agarrar;)

Outra razão possível, é que em pointers link-time é dado um endereço, mas o endereçamento indireto / de-referenciamento de um ponteiro é de responsabilidade do programador. Muito comumente, o compilador não se importa menos, mas a carga é passada para o programador para gerenciar os pointers e para garantir que não ocorram vazamentos de memory.

Realmente, em poucas palavras, eles são inicializados no sentido de que no link-time a variável do ponteiro recebe um endereço. Em seu código de exemplo acima, é garantido que ele trave ou gere um SIGSEGV.

Por uma questão de sanidade, sempre inicializar pointers para NULL, dessa forma, se qualquer tentativa de desreferencia-lo sem malloc ou new irá indicar ao programador a razão pela qual o programa se comportou mal.

Espero que isso ajude e faça sentido, Atenciosamente, Tom.

O C ++ vem de um background em C – e há algumas razões que estão por trás disso:

C, ainda mais que C ++, é uma substituição de linguagem de assembly. Não faz nada que você não diga para fazer. Portanto: Se você quiser NULL, faça isso!

Além disso, se você nulo coisas em uma linguagem bare-metal como C, automaticamente surgem questões de consistência: se você malloc algo – ele deve ser zerado automaticamente? Que tal uma estrutura criada na pilha? todos os bytes devem ser zerados? E quanto às variables ​​globais? que tal uma declaração como “(* 0x18);” Isso não significa então que a posição de memory 0x18 deve ser zerada?

Quais são essas indicações de que você fala?

Para segurança de exceção, use sempre auto_ptr , shared_ptr , weak_ptr e suas outras variantes.
Uma característica do bom código é aquela que não inclui uma única chamada para delete .

Oh garoto. A resposta real é que é fácil zerar a memory, que é uma boot básica para um ponteiro. Que também não tem nada a ver com a boot do próprio object.

Considerando os avisos que a maioria dos compiladores fornece nos níveis mais altos, não consigo imaginar a programação no nível mais alto e tratá-los como erros. Desde que transformá-los nunca me salvou nem um bug em enormes quantidades de código produzido eu não posso recomendar isso.