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