由较大的操作数引起的C中的分段故障11

时间:2017-11-23 16:10:33

标签: c macos memory segmentation-fault fft

我知道当遇到segmentation fault 11时,表示程序试图访问不允许访问的内存区域。

这里我尝试使用以下代码计算傅里叶变换。

nPoints = 2^15(或当然点数较少)时效果很好,但是当我进一步将点数增加到2^16时,它会破坏。我想知道,是因占用太多内存造成的吗?但是在操作过程中我没有注意到太多的内存占用。虽然它使用递归,但它会就地转换。我以为它不会占用太多记忆。然后,问题在哪里?

提前致谢

PS:有一件事我忘了说,上面的结果是在Max OS(8G内存)上。

当我在Windows(16G内存)上运行代码时,它会在nPoints = 2^14时损坏。所以它让我很困惑它是否由内存分配引起,因为Windows PC有更大的内存(但它很难说,因为这两个操作系统使用不同的内存策略)。

#include <stdio.h>
#include <tgmath.h>
#include <string.h>

// in place FFT with O(n) memory usage

long double PI;
typedef long double complex cplx;

void _fft(cplx buf[], cplx out[], int n, int step)
{
  if (step < n) {
    _fft(out, buf, n, step * 2);
    _fft(out + step, buf + step, n, step * 2);

    for (int i = 0; i < n; i += 2 * step) {
      cplx t = exp(-I * PI * i / n) * out[i + step];
      buf[i / 2]     = out[i] + t;
      buf[(i + n)/2] = out[i] - t;
    }
  }
}

void fft(cplx buf[], int n)
{
  cplx out[n];
  for (int i = 0; i < n; i++) out[i] = buf[i];

  _fft(buf, out, n, 1);
}


int main()
{
  const int nPoints = pow(2, 15);
  PI = atan2(1.0l, 1) * 4;

  double tau = 0.1;
  double tSpan = 12.5;
  long double dt = tSpan / (nPoints-1);
  long double T[nPoints];
  cplx At[nPoints];

  for (int i = 0; i < nPoints; ++i)
  {
    T[i] = dt * (i - nPoints / 2);
    At[i] = exp( - T[i]*T[i] / (2*tau*tau));
  }

    fft(At, nPoints);

  return 0;
}

2 个答案:

答案 0 :(得分:2)

  1. 您无法在堆栈中分配非常大的数组。 macOS上的默认堆栈大小为8 MiB。 cplx类型的大小为32字节,因此2个 16 cplx元素的数组为2 MiB,您有两个({1}中的一个}和main中的一个,所以这是4 MiB。这适合堆栈,但是,在那个大小,程序在我尝试时运行完成。在2 17 时,它失败,这是有道理的,因为那时程序有两个阵列在堆栈上占用8 MiB。分配此类大型数组的正确方法是包含fft并使用<stdlib.h>后跟cmplx *At = malloc(nPoints * sizeof *At);。您应该为if (!At) { /* Print some error message about being unable to allocate memory and terminate the program. */ }AtT执行此操作。此外,当您完成每个数组后,您应该释放它,就像使用out一样。

  2. 要计算整数幂2,请使用整数运算free(At);,而不是浮点运算1 << power。我们已经在macOS上很好地设计了pow(2, 16),但是,在其他系统上,即使可以获得精确的结果,它也可以返回近似值。近似结果可能略小于精确整数值,因此将其转换为整数将截断为错误的结果。如果它可能是大于适合pow的2的幂,则使用int,其中(type) 1 << power是适当大的整数类型。

答案 1 :(得分:-1)

下面的检测代码清楚地表明,OP代码重复更新out[]数组中的相同位置,实际上并不更新该数组中的大多数位置。

#include <stdio.h>
#include <tgmath.h>
#include <assert.h>



// in place FFT with O(n) memory usage
#define N_POINTS  (1<<15)

double T[N_POINTS];
double At[N_POINTS];
double PI;


// prototypes
void _fft(double buf[], double out[], int step);
void fft( void );


int main( void )
{
    PI = 3.14159;

    double tau = 0.1;
    double tSpan = 12.5;
    double dt = tSpan / (N_POINTS-1);

    for (int i = 0; i < N_POINTS; ++i)
    {
        T[i]  = dt * (i - (N_POINTS / 2));
        At[i] = exp( - T[i]*T[i] / (2*tau*tau));
    }

    fft();

    return 0;
}


void fft()
{
    double out[ N_POINTS ];

    for (int i = 0; i < N_POINTS; i++)
        out[i] = At[i];

    _fft(At, out, 1);
}


void _fft(double buf[], double out[], int step)
{
    printf( "step: %d\n", step );

    if (step < N_POINTS)
    {
        _fft(out, buf, step * 2);
        _fft(out + step, buf + step, step * 2);

        for (int i = 0; i < N_POINTS; i += 2 * step)
        {
            double t = exp(-I * PI * i / N_POINTS) * out[i + step];
            buf[i / 2]     = out[i] + t;
            buf[(i + N_POINTS)/2] = out[i] - t;
            printf( "index: %d buf update: %d, %d\n", i, i/2, (i+N_POINTS)/2 );
        }
    }
}

建议运行via(其中untitled1是可执行文件的名称,并且在linux上)

./untitled1 > out.txt
less out.txt

out.txt文件是8630880字节

对该文件的检查显示缺少覆盖率并显示任何一个条目不是前两个条目的总和,因此我怀疑这不是有效的傅里叶变换,