由于阵列大小而导致分段错误?

Segmentation fault because of the array size?

本文关键字:分段 错误 于阵列 阵列      更新时间:2023-10-16

我一直在做一个非常简单的有限差分代码来解决一维对流方程。 它似乎工作得很好,但是如果我增加正在使用的数组的大小,就会出现分段错误。当我减少时间步长或增加时间间隔时,就会发生这种情况。 代码是

#include <math.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <cmath>
using namespace std;

int main(){
double xi = 0.0;
double xf = 10.0;
double ti = 0.0;
double tf = 1.0;

时间间隔,如果等于 1,则代码工作正常。

double x,t;
double dt = 0.1;
double dx = 0.1;
int nstep_x = (xf - xi)/dx;
int nstep_t = (tf - ti)/dt;
double f[nstep_x][nstep_t];
double ex[nstep_x][nstep_t];
// Parameters
const double v = 0.05; 
const double D = 0.0001;
const double pi = 3.141592654;
ofstream salida;
salida.open("out");
for (int i = 0 ; i <= nstep_x; i++){
x = xi + dx*i;
f[i][0] = 0.5*sin(pi*x); //Initial conditions
salida << x << " " << 0 << " " << f[i][0] << endl;
}
salida << endl;
for (int n = 0; n <= nstep_t ; n++){
t = ti + n*dt;
for (int i = 1; i <= nstep_x; i++){
x = xi + dx*i;
f[i][n+1] = f[i][n]  - ((v*dt)/(2*dx))*(f[i+1][n] - f[i-1][n]); //CONV|SOC
ex[i][n] =  0.5*sin(pi*x - v*t);
salida << x << " " << t << " " << ex[i][n] << " " << f[i][n] << endl;
}
salida << endl;
salida << endl;
}
}

我认为这不是在循环中超出数组边界的问题,因为代码适用于"小"数组。 我想我一定是数组处理出了问题,但我找不到错误。

您的代码有几个问题。 一个是你正在使用不是标准C++的可变长度数组(VLA)。

double f[nstep_x][nstep_t];
double ex[nstep_x][nstep_t];

这C++无效,因为数组的大小必须在编译时而不是运行时知道。

快速的解决方案是使用std::vector<std::vector<double>>

#include <vector>
//...
std::vector<std::vector<double>> f(nstep_x, std::vector<double>(nstep_t));
std::vector<std::vector<double>> ex = f; // use copy constructor to easily create a copy

上面的代码基本上做了你最初做的事情,但有几个优点:

1) 代码现在是标准C++,因为它使用标准C++容器类std::vector.

2)如果nstep_x和/或nstep_t是大值,您不会遇到堆栈空间问题,因为std::vector会获得内存来存储堆中的项目。

3) 如果怀疑您越界访问矢量,您可以使用std::vector::at()检查边界条件。 如果您使用的是VLA(或一般的数组),则没有此测试。

在尝试查找错误时,第 3 项变得很重要。


如果我们采用您的代码,将其更改为 使用std::vector,我们看到"小数组"存在问题,与您认为不是问题的情况背道而驰。 如果我们看一下这段代码:

for (int i = 0 ; i <= nstep_x; i++)
{
x = xi + dx*i;
f.at(i).at(0) = 0.5*sin(pi*x); // exception is thrown here
}

我们看到存在越界条件。 这是通过使用vector::at()而不是[ ]来访问矢量中的元素来检测的。 在分配f[i][0]的行处引发std::out_of_range异常。

下面是显示此错误的实时示例。

你如何解决这个问题? 只是不要通过更改循环来越界:

for (int i = 0 ; i < nstep_x; i++)

在其他循环中也存在边界条件问题:

for (int n = 0; n <= nstep_t ; n++)
{
t = ti + n*dt;
for (int i = 1; i <= nstep_x; i++)
{
x = xi + dx*i;
f.at(i).at(n+1) = f[i][n] - ((v*dt)/(2*dx))*(f.at(i+1).at(n) - f[i-1][n]); 
ex.at(i)(n) =  0.5*sin(pi*x - v*t);
}
}    

您将看到使用at()您正在越界访问fex向量,因此可以正确诊断问题(就像其他答案所做的那样)。

gdb 将有助于查看它实际崩溃的位置,但是:

f[i+1][n]i增长到nstep_x,但f被分配为f[nstep_x][nstep_t], 所以看起来你会访问f[nstep_x+1][n],但你能做的最多是f[nstep_x-1][n]

如注释中所述,原因不是数组大小,而是 for 循环

for (int i = 0 ; i <= nstep_x; i++) {
// ...
f[i][0] = 0.5*sin(pi*x);
}

这是一个典型的一次性错误,超出了数组的末尾。正确的方法是

for (int i = 0 ; i < nstep_x; i++) {
// ...
}

注意<vs<=.


for (int n = 0; n <= nstep_t ; n++) {
for (int i = 1; i <= nstep_x; i++) {
// ...
f[i][n+1] = f[i][n]  - ((v*dt)/(2*dx))*(f[i+1][n] - f[i-1][n]);
}
}

在这里,您也有<=而不是<.此外,您分别在i + 1n + 1处访问索引,这意味着您不是在数组末尾的一步,而是两步。

在计算第二个时间步长时,您指的是在x=xift=ti+dt在第一个时间步长中没有计算,因为i从 1 开始运行。在另一个边界也存在类似的问题。

您需要为所有t指定x=xix=xf的空间边界条件,并修复其他答案中指出的逐一错误。

为了澄清,对流方程需要为f(x=xi,t)f(x=xf, t)指定边界条件。在"绝缘"边界的情况下,这通常是恒定或规定的流速,但存在其他类型。