我正在使用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[] = {/*...*/};
检查编译器和链接器文档,看看是否可以为数据创建内存段,以及如何将数据库分配给该内存段。
编译器可能会对上述技术施加限制,例如必须仅使用Model
0,或者Vertex
不能有任何虚拟方法。最坏的情况是,你必须使用一个二维数组:
static const double Vertices[MAXIMUM_ROWS][3] = {/*...*/};
通过使用static const
,编译器可以将数据放入只读数据段中。这允许将数据放入只读存储设备,如闪存或ROM(是的,我知道闪存可以写入/编程,但大多数时候,它被视为ROM(。