使用memset初始化int**时,出现分段错误,而使用for循环则没有。

3

这是我的c++代码:

int** a;
try{
  a = new int*[m];
  for(int i = 0; i<m;i++)
    a[i] = new int[n];
}

现在我正在使用for循环来初始化上述内容,方法如下:
for(int i = 0; i<m; i++)
  for(int j = 0; i<n; j++)
      a[i][j] = 0;

我正试图提高性能,因此认为使用memset会是个好主意。所以修改了我的代码,使用memset而不是for循环,如下所示:

memset(a, 0, sizeof(a[0][0]) * m * n);

但是在执行这个命令时,我遇到了分段错误。有人可以帮我找出问题吗?


你的代码中有为“a”分配内存吗? - Tomer Arazy
抱歉,我忘记添加内存分配了。a = new int*[m]; for(int i =0; i<m ;i++) a[i] = new int[n]; - user2175966
好的,现在问题很清楚了 - 已添加答案。 - Tomer Arazy
5个回答

4
int** a;

这只是给你一个单一的对象。一个 int** 对象。它根本没有指向任何地方。没有 int 可以赋值。当你开始像存在一样分配 int 时,会导致未定义的行为。
此外,指向 "2D 数组" 的 int** 的内存布局如下: int** 指向 int* 数组中的第一个元素,而 int* 指向 int 数组中的第一个元素。这个内存不连续,因为它需要间接跳转到内存,即它不是单个内存块。你不能仅使用 memset 来写入它。
如果你只想要一个固定的编译时大小的二维数组 int,可以这样做:
int a[N][M];

其中NM是常量表达式。这个数据是连续存储的,但我仍然不建议使用memset

或者使用标准容器,例如:

std::array<std::array<int, M>, N> a;

如果您需要动态大小,请尝试以下方法:

std::vector<std::vector<int>> a(M, std::vector<int>(N));

或者,您可以继续使用您的int**,并确保您动态分配int*int

int** a = new int*[M];
for (i = 0; i < N; i++) {
  a[i] = new int[N];
}

但这很丑陋!


int a[N][M]; - 可变长数组是C99的一部分,也可以作为GCC的扩展使用,但不是C++标准的一部分。 - LihO
@LihO 其中 NM 是常量表达式。 - Joseph Mansfield
现在我正在使用你刚才描述的丑陋部分。所以我会改进它。 - user2175966

3
int** a;

这只是一个指向指向 int 的指针的声明。

"现在我正在使用 for 循环来初始化上述内容"

你没有在你的 for 循环中初始化它,你只是试图给不存在的元素赋值 0,这会产生未定义的行为。你需要动态分配这些元素的内存,或者更好的方法:使用 std::vector 代替:

std::vector< std::vector<int> > a(m, std::vector<int>(n, 0));

"我正在尝试提高性能"

除非必要,否则不要这样做。 不要过早优化。


编辑:在您提到已经面临性能问题后,您可以采取以下措施:使用此二维C风格数组的替代方法:

int** a = new int*[m];      // m = number of rows
for(int i = 0; i < m; i++)
    a[i] = new int[n];      // n = number of columns

您可以使用一维的 std::vector

std::vector<int> vec(rows * cols, 0);
...
vec[i * cols + j] = 7;   // equivalent of vec[i][j]
  • 您的二维数组将存储在连续的内存块中
  • 这个内存块一次性分配,而不是分成许多小块
  • 由于空间局部性,频繁访问元素将更快
    (靠近的元素将在缓存内存中可用,因此您的程序不必从主内存中加载它们)
  • 并且您不需要负责内存管理
    (一旦vector对象被销毁,内存将自动清理)

我尝试优化它的主要原因是使用for循环需要很长时间,我必须缩短它。 - user2175966
你确定 for 循环需要很长时间吗?你使用的编译器是最新的吗(例如来自 GCC 4.7 或 4.8 的 g++),并启用了优化选项(例如 -O2-O3)? - Basile Starynkevitch
我现在看到了区别。我使用g++编译并启用优化,速度更快。但我尝试优化的实际代码确实使用了这些选项。谢谢。 - user2175966
@user2175966:现在看我的回答,下划线下面的部分 :) - LihO

1
使用int **时,通常不会有一个单一的、连续的内存块。假设您使用它正确,您将拥有指针数组。然后,每个指针都将为其单独分配一个数组。

鉴于这种情况,您不能将循环转换为单个memset(并仍然获得定义行为)。


0

我认为问题出在没有为实际存储分配内存。变量 a 只是一个指针(而且还未初始化)。它指向哪个位置?


0

你说你是这样分配的:

a = new int*[m]; 
for(int i =0; i<m ;i++) a[i] = new int[n];

就像Jerry Conffin所说的-这不会给你一个单一的、连续的内存块。每个新数组(new int[n])都将在可能完全不同的位置分配,并且memset仅适用于连续块,因此您必须手动重置它们中的每一个。 顺便说一句-我相信使用memset而不是循环不会看到任何性能提升(memset本身使用循环实现,我想)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接