Большой массив C ++ вызывает ошибку сегментации на iOS - PullRequest
0 голосов
/ 14 декабря 2018

Я создаю приложение для iOS с использованием C ++ и столкнулся с проблемой большого массива.Проблема заключается в том, что если массив достигает определенного размера, я получаю исключение типа EXC_BAD_ACCESS (SIGSEGV), подтип KERN_PROTECTION_FAILURE, с сигналом завершения ошибки сегментации (11).

Интересным аспектом является то, что I 'Я получаю это исключение независимо от того, помещаю ли я массив в стек или в кучу.

Код, помещающий массив в стек, выглядит следующим образом:

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;
};

Приведенный выше код вылетает на iOS 11.4, как только создается экземпляр экземпляра Model.

Теперь это происходит, даже если я изменяю строку

Vertex _vertices[VERTEX_COUNT] =

в (выделение памяти в куче)

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.

Кто-нибудь знает, что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Во встроенных системах практическим правилом является объявление констант и больших объемов данных как static:

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

Если ваши данные доступны только для чтения, используйте ключевое слово const:

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

Проверьте документацию компилятора и компоновщика, чтобы узнать, можете ли вы создать сегмент памяти для ваших данных и как назначить базу данных этому сегменту памяти.

Ваш компилятор может наложить ограничения на вышеперечисленные методы, такие как необходимость использовать только struct или что Vertex не может иметь никаких виртуальных методов.В худшем случае вам придется использовать 2d массив:

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

Используя static const, компилятор может поместить данные в сегмент данных только для чтения.Это позволяет размещать данные в запоминающих устройствах только для чтения, таких как Flash или ROM (да, я знаю, что Flash можно записывать в / программировать, но в большинстве случаев он рассматривается как ROM).

0 голосов
/ 14 декабря 2018

Вы должны создать массив 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 вершины отдельно.

...