【问题标题】:Segmentation Fault 11 in C caused by larger operation numbers较大的操作数导致 C 中的分段错误 11
【发布时间】:2018-05-07 15:53:24
【问题描述】:

我知道当遇到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;
}

【问题讨论】:

  • 可能数组太大而无法在堆栈上分配。将它们设为全局或static
  • 不要将pow(2, 15);用于整数使用1&lt;&lt;15
  • out[i + step]: out 的大小为 ni 的大小最高可达 n。这是个问题。
  • 我很确定 PI 应该由您的数学库提供...
  • 如果你想做一些 FFT 的东西,但不知道 C 语言的可用运算符,你可能会遇到困难......查看你的 C 教科书以了解按位移位运算符。

标签: c macos memory segmentation-fault fft


【解决方案1】:
  1. 您不能在堆栈中分配非常大的数组。 macOS 上的默认堆栈大小为 8 MiB。 cplx 类型的大小是 32 字节,所以 216cplx 元素的数组是 2 MiB,你有两个(一个在 main 和一个在 @987654324 @),所以是 4 MiB。这适合堆栈,但是,在那个大小下,当我尝试它时,程序会运行到完成。在 217 时,它失败了,这是有道理的,因为该程序有两个数组占用了 8 MiB 的堆栈空间。分配如此大的数组的正确方法是包含&lt;stdlib.h&gt; 并使用cmplx *At = malloc(nPoints * sizeof *At); 后跟if (!At) { /* Print some error message about being unable to allocate memory and terminate the program. */ }。您应该为AtTout 这样做。此外,当您完成每个数组后,您应该释放它,就像 free(At); 一样。

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

【讨论】:

  • 我认为您在这里得到了完全正确的解决方案。非常感谢!这两个建议都很实用。维伦丹克
【解决方案2】:

以下经过检测的代码清楚地表明,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 );
        }
    }
}

建议运行方式(untitled1 是可执行文件的名称,在 linux 上)

./untitled1 > out.txt
less out.txt

out.txt 文件为 8630880 字节

对该文件的检查显示缺乏覆盖,并表明任何一个条目都不是前两个条目的总和,所以我怀疑这不是有效的傅立叶变换,

【讨论】:

  • 您已经打印了在每个步骤中传递给_fft 的数组中的索引。但是,在其中一个调用中,传递给_fft 的数组偏移了step,因此传递数组中的索引与原始数组中的索引有偏移。整个递归过程中使用的步骤组合导致数组的所有元素都按照设计使用。
  • @EricPostpischil,step 的值每次递归加倍。 IE。 1, 2, 4,8,16, 32, 64. 128, 256, 512,... 递归不处理step的增加大小,所以并不是所有的数组都更新了。
  • 更改malloc分配的数组后,我运行源代码。数组的所有元素都已更新。第一次调用_fft 将数组分成out+0out+1,步长为2,因此一个处理所有偶数元素,另一个处理所有奇数元素。这些数组中的第一个以递归方式传递给_fft,它将其分隔为out+0out+2,步长为4。第二个数组传递并分隔为out+1out+3,步长为4. 这会递归地继续,因此二进制中的一些元素是 1101 被处理在…
  • _fft的第二次调用(bit 1)第二次调用(bit 1)第二次调用(bit 0)第二次调用(bit 1)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-10
  • 1970-01-01
  • 2013-11-20
  • 2020-08-29
  • 2021-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多