Нарушение доступа к элементам чтения массива - PullRequest
0 голосов
/ 16 июня 2010

Я написал свой собственный код для анализа файла модели .obj - по сути, просто текст ASCII.Файл анализируется и сохраняется в классе правильно в соответствии с моими тестами.Я прекрасно могу прочитать значения (из членов данных) в функции загрузки.

Проблема возникает, когда я пытаюсь прочитать значения в моем основном цикле рендеринга.В начале строки «int v» есть ошибка нарушения доступа:

 for(int i = 0; i<data.numFaces; i++){
  for(int j = 0; j<3; j++){ //Assuming triangles for now.

   int v = data.faceList[i].vertex[j]; // Access violation here.
   double vX = data.vertexList[v].x;
   double vY = data.vertexList[v].y;
   double vZ = data.vertexList[v].z;
   glVertex3d(vX, vY, vZ);
  }
 }

Я не совсем уверен, почему это происходит, и я проверил все, что мог придумать.Я не очень опытный в C ++.Большая часть моего опыта программирования связана с Java, Python и PHP, хотя ранее я писал проекты среднего размера на C ++.

Я уверен, что проблема связана с распределением памяти или указателями, используемыми для динамических массивов..

Вот соответствующие части кода в классе загрузки obj:

ObjData ObjLoader::LoadObj(std::string filename){

    //... Initalization ...

 // 1st pass: find number of elements so array sizes can be defined.
 while(!file.eof()){
  //...
 }

 //...close file...

 _data.faceList = new ObjFace[_data.numFaces];
 _data.vertexList = new ObjVert[_data.numVertices];
 _data.uvList = new ObjUV[_data.numUVcoords];
 _data.normalList = new ObjNormal[_data.numNormals];

    //TODO: Make size dynamic according to each face. Just use the first 3 points for now.
 for (int i = 0; i < _data.numFaces; i++){
  _data.faceList[i].vertex = new int[3];
  _data.faceList[i].normal = new int[3];
  _data.faceList[i].uv = new int[3];
 }

 //... file stuff ...

 // 2nd pass: read values into arrays.
 while(!file.eof()){
  //...

  if(type=="v"){
   _data.vertexList[currentVertex].x = atof(param1.c_str());
   _data.vertexList[currentVertex].y = atof(param2.c_str());
   _data.vertexList[currentVertex].z = atof(param3.c_str());
   currentVertex++;
  }else if(type=="vt"){
   _data.uvList[currentUV].u = atof(param1.c_str());
   _data.uvList[currentUV].v = atof(param2.c_str());
   currentUV++;
  }else if(type=="vn"){
   _data.normalList[currentNormal].x = atof(param1.c_str());
   _data.normalList[currentNormal].y = atof(param2.c_str());
   _data.normalList[currentNormal].z = atof(param3.c_str());
   currentNormal++;
  }else if(type=="f"){
  //...Within loop over each vertex in a single face ...

        if(endPos != string::npos){
        // Value before 1st "/" (Vertex index).
        // ...find value in string...
        _data.faceList[currentFace].vertex[i] = atoi(token.c_str()) -1; // File format begins indices from 1.

        // Value between slashes (UV index).
        // ...find value in string...
        _data.faceList[currentFace].uv[i] = atoi(token.c_str()) -1;

        // Value after 2nd "/" (Normal index).
        // ...find value in string...
        _data.faceList[currentFace].normal[i] = atoi(token.c_str()) -1;
   }
//...End of loop over every vertex in a single face...
currentFace++;
}

}

 return _data;

    }

И структуры ObjFace, ObjVert, ObjUV и ObjNormal определены как:

    struct ObjVert{
       float x, y, z;
    };

    struct ObjUV{
      float u, v;
    };

    struct ObjNormal{
       float x, y, z;
    };

    // Contains indexes.
       struct ObjFace{
       int* vertex;
       int* uv;
       int* normal;
    };

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

Ответы [ 2 ]

2 голосов
/ 16 июня 2010

Я напечатал какой-то глупый ответ, который, как я понял, был неправильным ... но я должен был продолжать думать об этом, и у меня возникла другая идея.

Где этот объект назначается?

Ваш код неясен, если data и _data - это один и тот же объект или нет, но я заметил, что ваш метод там, похоже, возвращает _data как объект.Я склонен полагать, что, возможно, вы используете какое-то назначение, например ObjData data = LoadObj("myfilename"); где-то?

Если это так, я полагаю, что ваша проблема может быть связана с отсутствием конструктора копирования или перегруженного оператора присваивания для вашегоObjData класс.(Я не гуру C ++, поэтому я не помню точно, к какому из них это относится. Надеюсь, кто-то еще может подтвердить, что мы на правильном пути).

Если ваши указатели не используютсяправильно копируется во время копирования и назначения (возврат из LoadObj вызывает конструктор копирования iirc, а затем очевидное присваивание data), тогда даже если вы намеревались уже иметь массив int в этом месте, вы можетена самом деле доступ к неинициализированной памяти, что приводит к нарушению прав доступа.

Я не эксперт ни по конструкторам копирования, ни по перегруженным операторам присваивания, но быстрый способ обойти это - вернуть указатель на ObjData а не возвращать сам объект.

2 голосов
/ 16 июня 2010

На первый взгляд я не увидел ничего явно неправильного. Но если, как вы говорите, он взрывается на int v = data.faceList[i].vertex[j];, то вполне вероятно, что проблема либо в i, либо j, либо в том, что оба они слишком большие или слишком маленькие.

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

1) Вы используете динамически распределяемые массивы 2) Вы используете петли ручной работы

Старайтесь избегать подобных вещей, используя инструменты, предоставляемые C ++. Начните с # 1 и избавьтесь от динамически размещаемых массивов.

У вас есть структура:

struct ObjFace{
  int* vertex;
  int* uv;
  int* normal;
};

... с 3 указателями на int. Вместо этого используйте vector:

struct ObjFace{
  vector<int> vertex;
  vector<int> uv;
  vector<int> normal;
};

... и тогда весь код, который вам приходилось писать раньше, теперь становится намного проще и гораздо менее подвержен ошибкам:

// all this goes away
//_data.faceList = new ObjFace[_data.numFaces];
//_data.vertexList = new ObjVert[_data.numVertices];
//_data.uvList = new ObjUV[_data.numUVcoords];
//_data.normalList = new ObjNormal[_data.numNormals];

... и:

// now you ask the vector how many elements it really has
for(int i = 0; i<data.faceList.size(); i++){
  for(int j = 0; j<data.faceList.size(); j++){ //Ask the vector instead of assuming triangles

   int v = data.faceList[i].vertex[j]; // Access violation here.
   double vX = data.vertexList[v].x;
   double vY = data.vertexList[v].y;
   double vZ = data.vertexList[v].z;
   glVertex3d(vX, vY, vZ);
  }
 }

Теперь посмотрите на этот цикл. Циклы являются очень распространенным источником ошибок. Лучший цикл - это цикл, который вам никогда не придется писать. Так что используйте вместо этого алгоритмы STL. Добавьте функцию в ObjFace для выполнения glVertex3d для каждого из ее элементов:

struct ObjFace{
//... 
  void do_vertex() const 
  {
    typedef vector<int> ints;
    for( ints::iterator it = vertex.begin(); it != vertex.end(); ++it )
      glVertex3d(it->x, it->y, it->z);
  }
};

... затем вернитесь назад и уменьшите исходный цикл: (псевдокод, фактический синтаксис более сложный)

typedef vector<ObjFace> ObjFaces;
for( ObjFaces::iterator it = data.faceList.begin(); it != data.faceList.end(); ++it )
  it->do_vertex();

... или, чуть больше усилий:

  for_each( data.faceList.begin(), data.faceList.end(), &ObjFace::do_vertex );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...