大型C++数组会导致iOS上的分段错误



我正在使用C++构建一个iOS应用程序,遇到了一个大数组问题。问题是,如果数组达到一定的大小,我会得到EXC_BAD_ACCESS(SIGSEGV(类型的异常,子类型KERN_PROTECTION_FAILURE,带有分段故障(11(终止信号。

有趣的是,无论我是将数组放在堆栈上还是堆上,我都会得到这个异常。

将数组放在堆栈上的代码如下所示:

class Model
{
public:
Model() { };
private:
static constexpr std::size_t VERTEX_COUNT =  25894;
Vertex _vertices[VERTEX_COUNT] =
{
{ { 46.629387f, 647.478271f,  58.987785f }, {  0.140482f, 0.716024f, 0.683795f }, false },
{ { 86.409439f, 639.203247f,  57.095085f }, {  0.273239f, 0.689217f, 0.671059f }, false },
{ { 94.825722f, 586.618164f,  91.772812f }, {  0.375726f, 0.404750f, 0.833671f }, false },
{ { 50.570183f, 586.068481f, 100.536209f }, { -0.003906f, 0.451161f, 0.892434f }, false },
// 25894 array entries in total
};
// all the rest
}

用于填充数组的结构如下所示:

struct Vertex
{
Vertex()
{
}
Vertex(glm::vec3 coords, glm::vec3 norm, bool selected) :
coordinates(coords),
normal(norm),
isSelected(selected)
{
}
glm::vec3 coordinates;
glm::vec3 normal;
bool      isSelected;
};

一旦Model的实例被实例化,上面的代码就会在iOS 11.4上崩溃。

现在,即使我更改线路,也会发生这种情况

Vertex _vertices[VERTEX_COUNT] =

到(在堆上分配内存(

Vertex* _vertices = new Vertex[VERTEX_COUNT]

std::unique_ptr<Vertex[]> _vertices = std::unique_ptr<Vertex[]>(new Vertex[VERTEX_COUNT]

或者将整个数组定义移动到CCD_ 1的构造函数中。

到目前为止,我唯一能让它发挥作用的方法就是改变

Vertex _vertices[VERTEX_COUNT] =

static constexpr Vertex _vertices[VERTEX_COUNT] =

并在CCD_ 2结构中添加相应的constexpr构造函数。但是,我需要能够在运行时编辑数组,因此不能将其声明为static constexpr

有人知道这里可能发生了什么吗?

您应该在不初始化其元素的情况下通过new创建数组。当您在初始化的情况下将数组创建到堆上时,编译器需要在堆栈上准备足够的空间来调用数组中几个对象的actor。

看看下面的例子(它说明了在初始化的情况下创建动态数组是危险的(:

struct vertex {
float x,y,z;
vertex() {}
vertex(double x,double y,double z){}
};
int main() {
vertex* v = new vertex[3] {
{1.43,2,3},
{3,4.34,5},
{3,4,5}
};
}
// main function in assembler code
push    rbp
mov     rbp, rsp
sub     rsp, 48   // <--- stack pointer is decresed
mov     eax, 36
mov     edi, eax
call    operator new[](unsigned long)
mov     rdi, rax
// call ctors for vertex

asm代码中最重要的一行是CCD_ 5。现在我们将数组的大小更改为具有6个顶点:

vertex* v = new vertex[6] {
{1.43,2,3},
// 4 lines here
{3,4,5}

现在编译器生成sub rsp, 80,您可以看到从堆栈指针中减去的值增加了。

顶点数组越大,从堆栈中占用的空间就越多。堆栈是有限的。这可能就是为什么即使将数组分配到堆中,应用程序也会崩溃的原因。堆栈的所有内存都用于初始化数组的顶点。


我在https://godbolt.org/在没有任何优化的情况下选择clang 6.0。(启用的优化在输出asm代码中发生了很大变化(。当然,其他编译器可能会生成不同的代码,而不是sub rsp,BIG_VALUE,它们可以分别为顶点的每个ctor分部分占用堆栈空间。

在嵌入式系统中,经验法则是将常量和大量数据声明为static:

static Vertex database[] = {/*...*/};

如果您的数据是只读的,请使用const关键字:

static const Vertex database[] = {/*...*/};

检查编译器和链接器文档,看看是否可以为数据创建内存段,以及如何将数据库分配给该内存段。

编译器可能会对上述技术施加限制,例如必须仅使用Model0,或者Vertex不能有任何虚拟方法。最坏的情况是,你必须使用一个二维数组:

static const double Vertices[MAXIMUM_ROWS][3] = {/*...*/};

通过使用static const,编译器可以将数据放入只读数据段中。这允许将数据放入只读存储设备,如闪存或ROM(是的,我知道闪存可以写入/编程,但大多数时候,它被视为ROM(。

相关内容

  • 没有找到相关文章

最新更新