我是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)。
f
和 b
的值都是自动变量,并且由于它们未初始化而不确定。
从阅读引用的部分来看不清楚哪个语句使其未定义,但作为规范文本一部分的第 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
必须在使用前初始化。