为什么指针在默认情况下不使用 NULL 初始化?

有人能解释一下为什么指针没有初始化为 NULL吗?
例如:

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

程序不会进入 if,因为 buf不是 null。

我想知道为什么,在什么情况下我们需要一个带有回收站的变量,特别是指向内存中回收站的指针?

86949 次浏览

出于历史原因,主要是因为 C 语言就是这么做的。为什么在 C 语言中这样做,是另一个问题,但是我认为 零开销原理零开销原理在某种程度上参与了这个设计决策。

因为初始化需要时间。在 C + + 中,对任何变量首先要做的就是显式地初始化它:

int * p = & some_int;

或:

int * p = 0;

或:

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

在 TC + + PL 中引用比雅尼·斯特劳斯特鲁普(特别版第22页) :

一个特性的实现不应该给不需要它的程序带来重大的开销。

在极少数情况下,变量没有被初始化是有意义的,而默认初始化的成本很小,那么为什么要这样做呢?

C + + 不是 C89。连 C 都不是 C89。可以混合使用声明和代码,因此应该将声明推迟到有合适的值进行初始化之后。

此外,我们有一个警告,当你吹它: “可能使用之前分配一个值”或类似的语句取决于你的编译器。

你是用警告编译的,对吧?

指针只是另一种类型。如果你创建一个 intchar或任何其他 POD 类型,它没有初始化为零,那么为什么应该一个指针?对于编写这样的程序的人来说,这可能被认为是不必要的开销。

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

如果你知道你将要初始化它,为什么当你第一次在方法的顶部创建 pBuf时,程序会产生成本呢?这就是零开销原理。

好吧,如果 C + + 确实初始化了指针,那么抱怨“ C + + 比 C 慢”的 C 族人就会有一些实实在在的东西可以抓住;)

另一个可能的原因是,在链接时指针被赋予一个地址,但间接寻址/解引用指针是程序员的责任。通常情况下,编译器不会太在意,但是负担会转移到程序员身上,让他们管理指针,并确保不会发生内存泄漏。

实际上,简而言之,它们被初始化的意义在于在链接时指针变量被赋予了一个地址。在上面的示例代码中,这肯定会崩溃或生成 SIGSEGV。

为了安全起见,始终初始化指向 NULL 的指针,这样,如果尝试在没有 mallocnew的情况下取消引用它,程序员就会知道程序出错的原因。

希望这能帮到你,

请注意,静态数据被初始化为0(除非您另有说明)。

是的,你应该尽可能晚的声明你的变量并且使用一个初始值

int j;
char *foo;

当你读它的时候应该会触发警报。我不知道是否可以说服任何 棉绒吹毛求疵,虽然因为它是100% 合法的。

C + + 来自于 C 背景——这里有几个原因:

C,甚至比 C + + 更是一种汇编语言的替代品。它不会做任何你没有告诉它去做的事情。因此: 如果你想让它为空,那就去做吧!

此外,如果在像 C 这样的裸金属语言中为空,那么自动出现一致性问题: 如果 malloc 某些内容——它是否应该自动为零?在堆栈上创建的结构如何?所有字节都应该归零吗?那全局变量呢?像“(* 0x18) ;”这样的语句是否意味着内存位置0x18应该为零?

因为 C + + 的座右铭之一是:


不用的东西不用付钱


正是由于这个原因,例如,vector类的 operator[]不检查索引是否超出了界限。

我们都意识到指针(和其他 POD 类型)应该被初始化。
然后问题变成了“谁应该初始化它们”。

基本上有两种方法:

  • 编译器初始化它们。
  • 开发人员初始化它们。

让我们假设编译器初始化了开发人员没有显式初始化的任何变量。然后我们会遇到这样的情况: 初始化变量是非常重要的,开发人员没有在声明点进行初始化的原因是他/她需要执行一些操作,然后分配。

所以现在我们有这样一种情况,编译器向代码中添加了一条额外的指令,将变量初始化为 NULL,然后添加开发人员代码以执行正确的初始化。或者在其他条件下,该变量可能永远不会被使用。许多 C + + 开发人员在这两种情况下都会大喊大叫,以牺牲额外的指令为代价。

这不仅仅是时间的问题。还有太空。在许多环境中,这两种资源都很宝贵,开发人员也不想放弃。

但是 : 您可以模拟强制初始化的效果。大多数编译器都会警告您未初始化的变量。所以我总是把我的警告级别调到最高级别。然后告诉编译器将所有警告视为错误。在这些情况下,大多数编译器会为未初始化但使用的变量生成错误,从而阻止生成代码。

你说的这些指标是什么?

为了异常安全,始终使用 auto_ptrshared_ptrweak_ptr和它们的其他变体。
好代码的一个标志是不包括对 delete的单个调用。

如果您想要一个始终初始化为 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); }
};

哦,天啊。真正的答案是很容易清零内存,这是指针的基本初始化。这也与初始化对象本身无关。

考虑到大多数编译器在最高级别提供的警告,我无法想象在最高级别编程并将其视为错误。由于启动它们从来没有为我节省过大量代码中的一个 bug,所以我不推荐这样做。