从大型C阵列中的函数访问元素出错

时间:2016-01-28 22:50:25

标签: c arrays

我有一个奇怪的错误,我猜这个错误(像往常一样)在内存管理方面。这是我的代码:

#define LX 4
#define LY 4

int n = LX * LY;
void matrixInit( double *h );

int main(void)
{
  int dim;
  dim = (int) pow( 2.0, (double) n );
  int k;
  double *h;
  h = (double *)malloc( dim * dim * sizeof(double) );

  for(k = 0; k < dim * dim; k++)
    {
      h[k] = 0.0;
    }
  printf("dimension is: %d\n", dim);
  matrixInit( h );

  free( h );
  return 0;
}


void matrixInit( double *h)
{
  h[15379] = 0.0;
}

我有一个double数组2^16 2^16。我可以在main函数中为它赋值,没有问题。现在我创建了一个函数matrixInit来更改数组中的值。访问元素0-15379工作正常,但对于之后的任何元素,我得到&#34;分段错误(核心转储)&#34;。

1 个答案:

答案 0 :(得分:3)

你陷入了使用不合适类型的陷阱。通常,索引数组的正确类型是size_t。此类型显式是对平台上允许的所有索引有效的类型。

对于典型的32位和64位平台,int为32位宽。这允许最大。 2**31 - 1** =权力)。对于2**16 * 2**16 == "**32条目,这显然是不够的。有符号整数的任何溢出都是标准的未定义行为。搜索那个词,你不想要它!简单地说:一旦发生就会丢失 - 无需进一步研究。

说,让我们试试吧。

首先要确保您可以索引大型数组。这很可能只适用于64位平台。为确保使用

#include <stdint.h>

_Static_assert( (SIZE_MAX >= (uintmax_t)YOUR_MAX_DIM * YOUR_MAX_DIM), "Platform does not support arrays that large" )

如果您没有标准兼容(即C11)编译器,请使用#ifdefuintmax_t是您平台上最大的无符号整数类型。由于我们假设至少符合C99,因此至少为64位(unsigned long long所需的最小宽度)。

然后你必须确保所有的计算都被处理为size_t。所以:

#define LX ((size_t)4)
#define LY ((size_t)4)

这可以防止标准整数转换产生麻烦。

然后始终使用正确的类型:

size_t n = LX * LY;

int main(void)
{
    size_t dim;
    dim = (size_t)1 << n;  // don't use floating point for exact results!

等等。要非常小心,不要从常量中删除原始int

另一个问题是:你想要一个2D数组,也就是矩阵。但是你分配一个数组并有一个指向第一个条目的指针。指针不是数组!

对于2D数组和指向第一个条目的指针,使用:

double (*h)[dim];

这是指向一维数组的指针。这个1D数组是第二维或2D数组,因此指针仍然指向一维数组的第一个条目;只是这个条目本身就是一个数组。请注意,我们必须指定&#34; inner&#34;的维度。阵列。

然后,在C中你不应该投射void *malloc&amp;朋友:

h = malloc( dim * sizeof(*h) );

我还使用了一个简单的方法,只使用h点的大小。请记住,这是一维数组。但这种模式是普遍的。

然后,检查可能遇到与程序执行相关的错误的函数的结果:

if ( h == NULL )
    exit(1);

如果没有分配数组,这将优雅地终止程序 - 对于大小的数组也是如此。

循环必须在dim之上的内外循环中分割。使用正确的类型:

for ( size_t k = 0 ; k < dim ; k++)
    for ( size_t j = 0 ; ...
        h[k][j] = 0.0;

介意使用正常的2D索引和索引变量的本地定义。

对于输出,您还需要size_t参数的正确类型:

printf("dimension is: %zu\n", dim);

并且不要忘记释放已分配的内存块:

free(h);

最后必须更改功能签名:

void matrixInit(size_t dim, double h[dim][dim]);

这是有效的,因为array-argument总是转换为指向第一个条目的指针。请注意,对于第一个索引(h此处不再是数组)也是如此。这与

相同
void matrixInit(size_t dim, double (*h)[dim]);

(与hmain的声明进行比较!)。但阵列符号在尺寸上更清晰。

当然,调用必须通过dim,但数组仍然以h传递。

另请注意,我将数组的维度传递给函数。这是必要的,因为

  1. 指针不包含任何有关大小和
  2. 的信息
  3. 编译器需要计算内部数组的大小。
  4. 将所有必需信息传递给适当的函数通常是一种好的方式。否则你必须使用一个全局变量,这是一个明确的禁止。