为什么默认情况下不使用NULL初始化指针?
有人可以解释为什么指针没有初始化为NULL
?
例:
void test(){ char *buf; if (!buf) // whatever }
如果因为buf
不为null,程序不会进入。
我想知道为什么,在什么情况下,我们需要一个带有垃圾的variables,特别是指向内存垃圾的指针?
我们都知道指针(和其他PODtypes)应该被初始化。
那么问题就变成了“谁来初始化它们”。
那么基本上有两种方法:
- 编译器初始化它们。
- 开发者初始化它们。
让我们假设编译器初始化任何未由开发人员明确初始化的variables。 然后我们遇到这样的情况,初始化variables不是微不足道的,开发者没有在声明点上做的原因是他/她需要执行一些操作然后分配。
所以,现在我们遇到这样的情况:编译器向代码中添加了一个额外的指令,将该variables初始化为NULL,然后添加开发者代码来执行正确的初始化。 或者在其他条件下,variables可能从未被使用过。 许多C ++开发人员会在这两种情况下都以这个额外的指令为代价来尖叫。
这不仅仅是时间。 但也是空间。 有很多的环境,两个资源都很重要,开发人员也不想放弃。
但是 :你可以模拟强制初始化的效果。 大多数编译器会警告你关于未初始化的variables。 所以我总是把我的警告级别提升到最高级别。 然后告诉编译器将所有警告视为错误。 在这些条件下,大多数编译器会为未初始化但使用的variables生成一个错误,从而阻止生成代码。
在TC ++ PL中引用Bjarne Stroustrup(特别版第22页):
一个function的实现不应该对不需要它的程序施加很大的开销。
因为初始化需要时间。 而在C ++中,你应该对任何variables做的第一件事是明确地初始化它:
int * p = & some_int;
要么:
int * p = 0;
要么:
class A { public: A() : p( 0 ) {} // initialise via constructor private: int * p; };
因为C ++的支柱之一是:
你不支付你不需要的东西
出于这个原因, vector
类的operator[]
不检查索引是否超出边界。
由于历史的原因,主要是因为这是怎么做的。为什么在C中这样做是另外一个问题,但我认为在这个devise决定中涉及零开销原理 。
另外,我们还有一个警告:你可能会用“在分配一个值之前使用”或类似的方式取决于你的编译器。
你用警告进行编译,对吧?
为什么variables被初始化是有意义的,而且默认初始化的代价很小,为什么呢?
C ++不是C89。 地狱,即使C不是C89。 你可以混合声明和代码,所以你应该推迟声明,直到你有一个合适的值来初始化。
指针只是另一种types。 如果你创build一个int
, char
或者任何其他的PODtypes,它不会被初始化为零,那么为什么要一个指针呢? 对于编写这样的程序的人来说,这可能被认为是不必要的开销。
char* pBuf; if (condition) { pBuf = new char[50]; } else { pBuf = m_myMember->buf(); }
如果您知道要初始化它,那么为什么在程序的顶部首次创buildpBuf
时程序会产生成本? 这是零开销原则。
如果你想要一个总是被初始化为NULL的指针,你可以使用一个C ++模板来模拟这个function:
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); } };
请注意,静态数据被初始化为0(除非另有说明)。
是的,你应该总是尽可能晚地宣布你的variables,并用初始值。 代码如
int j; char *foo;
当你读它时应该引起警钟。 我不知道是否可以说服任何棉花 ,因为它是100%合法的。
那么,如果C ++没有初始化指针,那么抱怨“C ++比C慢”的C们就会有一些真正需要的东西;)
另一个可能的原因是,在链接时间指针给了一个地址,但间接寻址/去引用指针是程序员的责任。 通常情况下,编译器并不在乎,但是负担被传递给程序员来pipe理指针并确保不会发生内存泄漏。
真的,简而言之,它们被初始化为在链接时指针variables被赋予一个地址。 在你上面的示例代码中,这是保证崩溃或生成一个SIGSEGV。
为了理智,总是将指针初始化为NULL,这样,如果试图在没有malloc
或new
情况下解引用它,将会导致程序员误解程序错误的原因。
希望这有助于和有道理,最好的问候,汤姆。
C ++来自C背景 – 这有几个原因:
C,甚至超过C ++是汇编语言的替代品。 它不会做任何你不告诉它做的事情。 因此:如果你想NULL – 做到这一点!
另外,如果你用C这样的裸机语言来清空它们,那么一致性问题就会出现:如果你使用malloc,它是否应该被自动清零? 怎么样在栈上创build一个结构? 应该将所有字节清零? 那么全局variables呢? 怎么样像“(* 0x18);” 那么这不意味着内存位置0x18应该被清零?
你说的这些指针是什么?
为了exception安全,请始终使用auto_ptr
, shared_ptr
, weak_ptr
及其他变体。
良好的代码标志是一个不包括一个单一的电话delete
。
好家伙。 真正的答案是很容易将内存清零,这就是指针的基本初始化。 这也与初始化对象本身无关。
考虑到大多数编译器在最高级别给出的警告,我无法想象在最高级别进行编程并将它们视为错误。 自从打开它以来,从来没有救过我,即使产生了大量代码中的一个错误,我也不能推荐这个。