繁体   English   中英

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

[英]Large C++ array causes segmentation fault on 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] =

to(在堆上分配内存)

Vertex* _vertices = new Vertex[VERTEX_COUNT]

或者

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

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

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

Vertex _vertices[VERTEX_COUNT] =

static constexpr Vertex _vertices[VERTEX_COUNT] =

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

有没有人对这里可能发生的事情有所了解?

您应该通过new创建数组,而无需初始化其元素。 当您使用初始化在堆上创建数组时,编译器需要在堆栈上准备足够的空间来为数组的多个对象调用ctors。

请看下面的示例(它说明了初始化动态数组的创建是危险的):

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代码中最重要的一行是sub rsp,48 现在我们将数组的大小更改为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[] = {/*...*/};

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

您的编译器可能会对上述技术施加限制,例如必须仅使用structVertex不能使用任何虚拟方法。 最坏的情况是,您将不得不使用二维数组:

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM