C++ SegFault 在取消引用 cout 的指针时



我是C++新手,只是想掌握它。总的来说,这似乎还不错,但我偶然发现了这种奇怪/病态的分段错误行为:

int main () {
  int* b;
  *b = 27;
  int c = *b;
  cout << "c points to " << c << endl; //OK                                                                                                                                      
  printf( "b points to %dn", *b); //OK                                                                                                                                          
  // cout << "b points to " << (*b) << endl; - Not OK: segfaults!                                                                                                               
  return 0;
}

正如给定的,该程序会产生您所期望的:

c points to 27
b points to 27

另一方面,如果您取消注释倒数第二行,则会得到一个在运行时崩溃(赛格错误)的程序。为什么?这是一个有效的指针。

int* b指向未知的内存地址,因为它未初始化。 如果将其初始化为编译器存在的任何空指针值(0 C++11,nullptr C++11及更高版本中),则肯定会更早地出现段错误。 问题在于您为指针分配了空间,但没有为其指向的数据分配空间。 如果您改为这样做:

int c = 27;
int* b = &c;
cout << "c points to " << c << endl;
printf ("b points to %dn", *b);
cout << "b points to " << (*b) << endl;

事情会起作用,因为int* b是指程序可以访问的内存位置(因为内存实际上是程序的一部分)。

如果使指针保持未初始化状态或为其分配 null 值,则在指针指向您知道可以访问的内存地址之前,您将无法使用它。 例如,将动态分配与 new 运算符结合使用将为数据保留内存:

int* b = new int();
*b = 27;
int c = *b;
//output
delete b;

更新 3

我的回答是C++标准究竟在哪里说取消引用未初始化的指针是未定义的行为?给出了一个更好的答案,为什么使用未初始化的指针是未定义的行为。C++草案标准24.2迭代器要求的基本逻辑,特别是第24.2.1在一般5段和第10段中,分别说(强调我的):

[...][ 示例:在声明未初始化的指针 x 之后(与 int* x; 一样),必须始终假定 x 具有指针的奇异值可取消引用的值始终是非奇异的。

更新 2

这最初是对具有几乎相同情况的C问题的回答,但我回答的原始问题与这个问题合并了。我正在更新我的答案,以包括针对新问题和C++标准草案的答案。

b尚未初始化,因此它的值不确定,但您在b使用了间接寻址,这是未定义的行为。

一种可能的简单解决方法是将b分配给现有变量的地址,例如:

int a ;
int* b = &a;

另一种选择是通过 new 使用动态分配。

为了完整起见,我们可以通过转到草案C++标准部分5.3.1一元运算符1 段看到这是未定义的行为,该段落说(强调我的):

一元 * 运算符执行间接寻址:应用它的表达式应是指向对象类型的指针,或指向函数类型的指针,结果是引用表达式指向的对象或函数的左值。[...]

如果我们然后转到第 3.10 节 左值和右值1 段说(强调我的):

左值

(历史上之所以这样称呼,是因为左值可能出现在赋值表达式的左侧)指定函数或对象。[...]

b不指向有效的对象

原始答案

您没有为f分配任何内存,也没有b,但您在两者上都使用了间接寻址,这是未定义的行为。

更新

值得注意的是,提高警告级别应该表明这是一个问题,例如使用 gcc -Wall 会为此代码提供以下警告:

warning: 'f' is used uninitialized in this function [-Wuninitialized]

最简单的解决方法是分配f指向有效对象,如下所示:

char a ;
char *f = &a ;

另一种选择是使用动态分配,如果您没有方便的参考,C FAQ 不是一个糟糕的起点。

为了完整起见,如果我们看一下C99草案标准附件J.2未定义的行为1段说:

在以下情况下未定义该行为:

并包括以下项目符号:

具有自动存储持续时间的对象的值在 不确定(6.2.4、6.7.8、6.8)。

fb 的值都是自动变量,并且由于它们未初始化而不确定。

从阅读引用的部分来看不清楚哪个语句使其未定义,但作为规范文本一部分的第 6.5.2.5 节复合字面第17 段有一个示例,其中包含以下文本,该文本使用相同的语言并说:

[...]下一次 p 将有一个不确定的值,这将导致未定义的行为。

在C11标准草案中,该段为第16段。

指针在它有一个值的情况下是有效的。但记忆可能不是。这是您的操作系统告诉您,您正在触摸不属于您的内存。

坦率地说,我很惊讶它不会比这更早崩溃。


原因如下:

int* b; // b is uninitialized.
*b = 27;

b指向哪里?它可能在某个地方有效,也可能在某个完全禁止的地方。您通常可以押注后者。

这是做你想做的事的更好方法。

int b1 = 27;
int *b = &b1;

现在,b指向堆栈上存储b1值的位置。

这是因为

f是一个指针,需要为其分配一些内存。

一般规则:使用前初始化变量

char* f;是一个变量。 *f是此变量的用法。与任何变量一样,f必须在使用前初始化。

最新更新