Reputation: 2052
I’m building an iOS app using C++ and encountered an issue with a large array. The problem is that if the array reaches a certain size I’m getting an exception of type EXC_BAD_ACCESS (SIGSEGV), subtype KERN_PROTECTION_FAILURE, with a segmentation fault (11) termination signal.
The interesting aspect is that I’m getting that exception regardless of whether I place the array on the stack or on the heap.
The code putting the array on the stack looks as follows:
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
}
The struct used to populate the array looks as follows:
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;
};
The above code crashes on iOS 11.4 as soon as an instance of Model gets instantiated.
Now, this happens even if I change the line
Vertex _vertices[VERTEX_COUNT] =
to (allocating memory on the heap)
Vertex* _vertices = new Vertex[VERTEX_COUNT]
or to
std::unique_ptr<Vertex[]> _vertices = std::unique_ptr<Vertex[]>(new Vertex[VERTEX_COUNT]
or move the entire array definition into the constructor of Model
.
The only way I could make it work so far is to change
Vertex _vertices[VERTEX_COUNT] =
to
static constexpr Vertex _vertices[VERTEX_COUNT] =
and add a corresponding constexpr constructor to the Vertex
struct. However, I need to be able to edit the array at runtime and hence cannot declare it to be static constexpr
.
Does anyone have some insight what might be going on here?
Upvotes: 3
Views: 319
Reputation: 57729
In embedded systems, the rule of thumb is to declare constants and large amounts of data as static
:
static Vertex database[] = {/*...*/};
If your data is read-only, use the const
keyword:
static const Vertex database[] = {/*...*/};
Check your compiler and linker documentation to see if you can create a memory segment for your data and how to assign the database to that memory segment.
Your compiler may impose restrictions on the above techniques, such as having to use only struct
or that Vertex
can't have any virtual methods. Worst case, you'll have to use a 2d array:
static const double Vertices[MAXIMUM_ROWS][3] = {/*...*/};
By using static const
, the compiler can place the data into a read-only data segment. This allows for placing the data into read only memory devices, such as Flash or ROM (Yes, I know that Flash can be written into/programmed, but most of the time, it is treated as ROM).
Upvotes: 1
Reputation: 20949
You should create array by new
without the initialization of its elements. When you create array onto heap with initialization, compiler needs to prepare enough room onto stack to call ctors for several objects of your array.
Look at the example below (it illustrates that the creation of dynamic array with initialization is dangerous):
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
the most important line from asm code is sub rsp,48
. Now we change the size of array to have 6 vertices:
vertex* v = new vertex[6] {
{1.43,2,3},
// 4 lines here
{3,4,5}
now compiler generates sub rsp, 80
, as you can see the value subtracted from stack pointer is increased.
The greater vertices array, the more space is taken from stack. Stack is limited. And probably this is why your app crashes even if you allocate array onto heap. All memory of stack was used to initialize vertices of your array.
I compiled this code on https://godbolt.org/ with clang 6.0 selected without any optimizations. (Enabled optimizations changed much in output asm code). Of course other compilers may generate different code, instead of sub rsp,BIG_VALUE
they can take the space of stack in portions for each ctor of vertex separately.
Upvotes: 2