【问题标题】:C++ SegFault when dereferencing a pointer for cout取消引用 cout 指针时的 C++ SegFault
【发布时间】:2011-08-30 10:36:56
【问题描述】:

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

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

这个程序,如给定的,产生你所期望的:

c points to 27
b points to 27

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

【问题讨论】:

标签: c++ pointers segmentation-fault cout


【解决方案1】:

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

int c = 27;
int* b = &c;

cout << "c points to " << c << endl;
printf ("b points to %d\n", *b);
cout << "b points to " << (*b) << endl;

因为int* b 指的是您的程序可以访问的内存位置(因为内存实际上是您程序的一部分),所以一切正常。

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

int* b = new int();
*b = 27;
int c = *b;

//output

delete b;

【讨论】:

    【解决方案2】:

    更新 3

    我对@9​​87654321@ 的回答对为什么使用未初始化的指针是未定义的行为给出了更好的答案。基本逻辑来自C++ draft standard,部分24.2迭代器要求,具体部分24.2.1一般 部分5 10 分别表示(emphasis mine):

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

    更新 2

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

    b 尚未初始化,因此它的值为 indeterminate 但您在 b 上使用了间接寻址,即 undefined behavior

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

    int a ;
    int* b = &a;
    

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

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

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

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

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

    但是b 没有指向一个有效的对象

    原答案

    您没有为fb 分配任何内存,但您对两者都使用了间接寻址,即undefined behavior

    更新

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

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

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

    char a ;
    char *f = &a ;
    

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

    为了完整起见,如果我们查看C99 draft standard 附件J.2 未定义的行为 段落1 说:

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

    并包括以下项目符号:

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

    fb 的值都是自动变量,并且是不确定的,因为它们没有被初始化。

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

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

    C11 draft standard 中的段落是16

    【讨论】:

    • 为了迂腐,最后一句话是不相关的,因为指针根本没有被分配任何值。它们的值是不确定的。
    • @OliCharlesworth 他们的价值观是不确定的,让我暂时解决它,并在有机会的时候重新设计它。我认为这是无效的,但也许你是正确的。
    • 是的,这是吹毛求疵。这里的问题是“分配”这个词。
    • @ShafikYaghmour,你的意思是他们没有被分配一个变量吗?而是直接给定一个值?
    • @Link 我已经用更多细节更新了答案,但基本上指针没有已知值,所以通过执行*f,您将访问一些不确定的内存区域。您的进程最有可能无法访问的内存,在类似 unix 的系统中会产生段错误。
    【解决方案3】:

    指针只要有值就有效。但内存可能不是。是您的操作系统告诉您,您正在触摸不属于您的内存。

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


    原因如下:

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

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

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

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

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

    【讨论】:

    • 你只是告诉他什么是段错误,并没有真正解决问题。
    • @Dhaivat Pandya - 他刚刚回答了 OP 的答案。解决方案很清楚。
    • 好的。那么我需要使用 new 来使 int* 保持有效地址的解决方案是什么?
    • 等等,为什么倒数第二行不起作用? (对不起,我是个笨蛋)
    • 这可能是有充分理由的,但这并不重要。这是一种未定义的行为。
    【解决方案4】:

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

    【讨论】:

    • 请注意,该问题已与类似问题合并。您可能想更新您的答案以适应新问题。
    【解决方案5】:

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

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

    【讨论】:

    • 请注意,该问题已与类似问题合并。您可能想要更新您的答案以适应新问题。
    猜你喜欢
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2011-06-17
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    相关资源
    最近更新 更多