访问违规读取数组的元素

时间:2010-06-15 20:12:11

标签: c++ arrays multidimensional-array access-violation

我编写了自己的代码来解析.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 个答案:

答案 0 :(得分:2)

乍一看,我没有看到任何明显的错误。但如果你说它在int v = data.faceList[i].vertex[j];爆炸,那么问题似乎很可能是ij,或者两者都太大或太小。

除了使用调试器变得舒适并且将这个错误弄平的明显方法之外,解决这些问题的最佳方法可能是完全避免它们。程序员所做的某些事情比其他人更容易出错。列表很长,但你的代码中至少有两个,黑桃。

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

...然后返回并缩小原始循环: (psudocode,实际语法更复杂)

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

答案 1 :(得分:2)

我输入了一些愚蠢的回应,我意识到这是不对的......但我不得不继续思考它,我想出了另一个想法。

此对象在哪里分配?

如果data_data是同一个对象,您的代码不清楚,但我注意到您的方法似乎将_data作为对象返回。我被引导相信你可能正在某处使用像ObjData data = LoadObj("myfilename");这样的作业?

如果是这种情况,我相信您的问题可能来自缺少复制构造函数或您的ObjData类的重载赋值运算符。 (我不是C ++大师,所以我不记得究竟属于哪一个。希望其他人可以确认我们是否在正确的轨道上。)

如果在复制和赋值期间没有正确复制指针(从LoadObj返回,则调用复制构造函数iirc,然后明确赋值给data),那么即使你已经打算在该位置有一个int数组,实际上您可能正在访问未初始化的内存,从而导致您的访问冲突。

我不是复制构造函数或重载赋值运算符的专家,但快速解决此问题的方法是返回指向ObjData的指针,而不是返回对象本身。