动态二维数组。为啥是分段错误?

Posted

技术标签:

【中文标题】动态二维数组。为啥是分段错误?【英文标题】:Dynamic 2D array. Why is segmentation fault?动态二维数组。为什么是分段错误? 【发布时间】:2019-11-23 23:16:56 【问题描述】:

我的问题很简单。看看这段代码。

#include <iostream>
using namespace std;

int main() 
    int **arr = new int*[3];
    cout << "arr : " << arr << endl;   // It will display address, may be 0xffc1e...
    cout << "*arr : " << *arr << endl;    // will be just 0(zero), may be default value
    cout << "**arr : " << **arr << endl;    // it will display nothing,  and after that program terminates
    cout << "Program completed." << endl;    // This will not display because program terminated
 

由于检索 **arr,此程序终止。如果我们摆脱线是 **arr。程序将正常完成,并会写成“程序完成”。 gdb 说这是分段错误。这样这段代码就可以工作了。

#include <iostream>
using namespace std;

int main() 
    int **arr = new int*[3];
    cout << "arr : " << arr << endl;   // It will display address, may be 0xffc1e...
    cout << "*arr : " << *arr << endl;    // will be just 0(zero), may be default value
    cout << "Program completed." << endl;    // This at last will be displayed
 

所以没有 **arr 所在的行,代码可以正常工作。为什么???

【问题讨论】:

'new' 分配了一个包含 3 个指向 int 的指针的数组,但它们没有设置为任何值。在它们出现之前,您可以阅读其中一个,但您无法阅读它所指向的内容。 @TedLyngmo 谢谢你,我有正确的感谢你。 您期望**arr 的值是多少?你能说服a rubber duck 你在某个时候设置了这个值吗? 同时打印*arr**arr 的语句具有未定义的行为。 arr 是三个未初始化指针的动态分配数组,因此访问 *arr 具有未定义的行为。评估**arr 过程的一部分涉及评估*arr,因此它也具有未定义的行为。当行为未定义时,您可能会很幸运(或不幸,取决于您如何看待它)并且代码似乎可以工作。您的第二个代码示例(删除了打印**arr)似乎工作的事实只是偶然 - 打印*arr STILL 的语句具有未定义的行为。 @Makari21 - new int *[3] 动态分配三个指针的数组,但不初始化这些指针。因此,访问*arr(或该数组的任何元素的值,例如arr[0]arr[1]arr[2])会给出未定义的行为。你很幸运——使用你的编译器/库,当你运行你的代码时——数组元素被初始化为空指针。标准没有要求。 【参考方案1】:

您已默认初始化动态数组。它是一个指针数组。默认初始化的指针有一个不确定的值。读取不确定值的行为是未定义的。

为什么???

因为程序的行为是未定义的。

未定义的行为意味着程序的行为没有任何保证。就语言而言,该程序可能:

产生您期望的输出。 产生出乎意料的输出。 生成您想要生成的输出。 产生一些您不想要的输出。 根本不产生输出。 崩溃 不会崩溃 在另一个系统上表现不同。 在同一系统上表现不同。 调试时表现不同。 只有在度假时才能表现得与众不同。 出于任何可能的原因采取不同的行为方式。 似乎完全没有理由表现出不同的行为。 始终保持一致 不要那样做。 有任何行为。

应避免未定义的行为。

【讨论】:

谢谢你,你的回答这么快,太好了。你能告诉我这种行为的线索,我可以在其中读到吗?我的意思是一些文章或手册。 @Makari21:您可以阅读有关未定义行为的更多信息here。简而言之,这意味着如果您做了不允许做的事情,那么任何事情都可能发生(出错),例如您的程序可能由于分段错误而崩溃。但是,在我看来,您需要先阅读有关指针如何工作以及如何使用它们的正确 C/C++ 教程。 @Makari21 我添加了对未定义行为含义的解释。 @AndreasWenzel 谢谢。我是新手,我认为这些答案会对其他人有所帮助,因为我们描述了特定的实例。【参考方案2】:

你已经为 3 个int* 分配了空间,但是你还没有初始化它们中的任何一个,所以像*arr 那样读取它们会使你的程序有未定义的行为。

*arr(如果有)返回的地址中读取,就像**arr 所做的那样,也会使其具有未定义的行为 - 并且经常导致崩溃(如果你幸运的话)。

你需要让int*s 指向at。初始化指针。另请注意,对于每个new,您都需要一个delete,对于每个new[],您都需要一个delete[]

一些例子:

int main() 
    int** arr = new int*[3];

    arr[0] = new int[5];  // an array of 5 int
    arr[1] = new int[10]; // 10...
    arr[2] = new int[20]; // 20...

    // use arr

    // cleanup
    delete[] arr[2];
    delete[] arr[1];
    delete[] arr[0];

    delete[] arr;
 

请注意,分配的内存仍未初始化,因此在将数据存储在其中之前,您不应该从中读取。

如果发生任何事情(如异常),清理工作很容易被遗忘或跳过。为此,有智能指针。起初它们可能看起来很尴尬,但会为您省去很多麻烦。

#include <iostream>
#include <memory>
int main() 
    std::unique_ptr<std::unique_ptr<int[]>[]> arr = 
                                         std::make_unique<std::unique_ptr<int[]>[]>(3);

    arr[0] = std::make_unique<int[]>(5);
    arr[1] = std::make_unique<int[]>(10);
    arr[2] = std::make_unique<int[]>(20);

    // using arr
    std::cout << arr[0][4] << "\n";  // Using make_unique also default initialized the 
    std::cout << arr[1][9] << "\n";  // elements in the arrays, so reading from them is 
    std::cout << arr[2][19] << "\n"; // fine.

 // all arrays held by smart pointers are automatically deleted when arr
  // goes out of scope

【讨论】:

非常感谢。我的掌握变得更加完整。

以上是关于动态二维数组。为啥是分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

处理二维数组时出现分段错误[关闭]

大型二维数组给出分段错误

C在函数中传递二维数组会给我分段错误

C - 将结构写入二维数组会导致分段错误

尝试连接二维数组的元素时出现分段错误

为啥二维动态数组有初始值?