未初始化的指针就像它被初始化一样工作

时间:2013-11-19 08:24:33

标签: c++ qt

这是一些Qt代码:(5.1.1)

QTreeWidgetItem *rootItem;

rootItem->setText(0, "Column 1");
rootItem->setText(1, "Column 2");
rootItem->setText(2, "Column 3");
rootItem->setText(3, "Column 4");

ui->treeWidget->addTopLevelItem(rootItem);

Messages::ShowMessage(rootItem->childCount());

上面的代码可以运行,人们会发现运行时错误,因为rootItem是未初始化但却有效。我在这里错过了什么?我正在使用Qt Creator。

他们做过某种自动初始化吗?如果是,我应该在哪里关闭它?我运行程序,我得到4列。这毫无意义。

但是当我尝试实际初始化指针时,我只得到1列。

QTreeWidgetItem *rootItem = new QTreeWidgetItem(ui->treeWidget);

rootItem->setText(0, "Column 1");
rootItem->setText(1, "Column 2");
rootItem->setText(2, "Column 3");
rootItem->setText(3, "Column 4");

ui->treeWidget->addTopLevelItem(rootItem);

我错过了什么?

4 个答案:

答案 0 :(得分:4)

它是未定义的行为,因此当您遇到它们时,您不能指望任何逻辑上合理或一致的行为。

但实际上,通过将指针设置为nullptrNULL,可以提高运行时系统检测到这类错误的几率。

QTreeWidgetItem *rootItem = nullptr;

同样在调试模式下运行程序也可能有所帮助,许多现代编译器在检测到它们时都会发出诊断和警告(你应该总是启用它们,将它们设置为最高,最偏执的级别)。

但请注意,上述内容并不保证您能做任何事情,并且您仍然需要制作正确且表现良好的代码。

这里有很多建议让你使用普通对象(堆栈分配,非指针指向的),或者如果你不能(如果你的可怜的库对它们过敏那么 [1] ),总是使用指针包装器,例如std::unique_ptrstd::shared_ptr,或者你的库提供的并且能够消化的东西。


[1] 我不喜欢那些类型的库。

答案 1 :(得分:3)

如果你添加

QMAKE_CXXFLAGS += -Wall

或更不严格:

QMAKE_CXXFLAGS += -Wuninitialized

到你的.pro文件,编译器会抱怨并且你会发现这个问题 - 你将被迫使用类似的东西:

QTreeWidgetItem *rootItem = NULL;

QTreeWidgetItem *rootItem = new QTreeWidgetItem();

答案 2 :(得分:2)

C ++不保证未定义的行为不会出现,并且你不幸遇到了这个问题。未初始化指针所指向的内存可能已被抢占,因此没有运行时错误,但你不能依赖它。

大多数编译器都会发出警告(你打开它们吗?)。此外,还有许多在调试版本中将指针初始化为某个虚拟值以表示错误 - 并且您在运行时会遇到访问冲突崩溃。

答案 3 :(得分:1)

  

人们会发现运行时错误。我在这里缺少什么?

使用无效指针会产生未定义的行为。这并不一定意味着运行时错误;这意味着任何事情都可能发生。

  

他们做过某种自动初始化吗?

据推测,这是班级内的自动变量。如果您没有指定初始化器,则它们根本不会初始化(无论是有效的还是“已知的无效”值);初始值是不确定的(无论是在那块内存中的垃圾),使用该值会给出未定义的行为。

指针完全有可能指向可寻址内存块,在这种情况下程序可能会起作用。该内存将不包含正确初始化的QTreeWidgetItem,并且可能包含您正在破坏的其他对象,因此这可能会在以后导致更多难以理解的错误。

  

如果是,我应该在哪里关闭它?

启用编译器警告(-Wall或特定-Wuninitialized,如果您正在使用GCC)应该捕获大多数未初始化变量的使用。避免指针和new除非你真的需要它们也会有所帮助,虽然我不知道在处理Qt时这是否是一个选项。

(我不太了解Qt回答最后一个问题,对不起)。