You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言解析Wavefront obj文件f索引行时其余输出数据异常问题

问题现象

注释掉Wavefront obj文件中f开头的面行解析逻辑时,程序读取其余行的运行结果完全符合预期;一旦取消该段代码的注释,除纹理坐标值外的所有读取数值都会被异常篡改。经初步排查,确认未使用跨所有行检查逻辑持久生效的变量,无法定位问题诱因。

相关代码

obj.h 实现代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<cglm/cglm.h>

int loadobj(const char* filename, float* vertices, float* texcoords, float* normals, int* indices){
   FILE* file = fopen(filename, "r");
    char lineheader[128];
    int res;
    int i = 0;
    int f = 0;
    int d = 0;
    int g = 0;
    float verticesout[24];
    float texcoordsout[28];
    float normalsout[18];
    unsigned int vertexindex[3], texindex[3], normalindex[3];
    vec3 vertex;
    vec2 texcoord;
    vec3 normal;
   vec3 vertexindices;
    vec3 texindices;
    vec3 normalindices;
    if(file == NULL){
      printf("Failed to open file!\n");
        return 1;
    }
    while(1){
        res = fscanf(file, "%s", lineheader);
        if(res == EOF){
         break;
        }
        if(strcmp(lineheader, "v") == 0){
           fscanf(file, "%f %f %f\n", &vertex[0], &vertex[1], &vertex[2]);
           verticesout[i] = vertex[0];
            verticesout[i + 1] = vertex[1];
            verticesout[i + 2] = vertex[2];
         i += 3;
        }else if(strcmp(lineheader, "vt") == 0){
         fscanf(file, "%f %f\n", &texcoord[0], &texcoord[1]);
         texcoordsout[f] = texcoord[0];
            texcoordsout[f + 1] = texcoord[1];
            f += 2;
        }else if(strcmp(lineheader, "vn") == 0){
         fscanf(file, "%f %f %f\n", &normal[0], &normal[1], &normal[2]);
         normalsout[d] = normal[0];
            normalsout[d + 1] = normal[1];
            normalsout[d + 2] = normal[2];
            d += 3;
        }else if(strcmp(lineheader, "f") == 0){
         fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexindex[0], &texindex[0], &normalindex[0], &vertexindex[1], &texindex[1], &normalindex[1], &vertexindex[2], &texindex[2], &normalindex[2]);
            vertexindices[g] = vertexindex[0];
            vertexindices[g + 1] = vertexindex[1];
            vertexindices[g + 2] = vertexindex[2];
            texindices[g] = texindex[0];
            texindices[g + 1] = texindex[1];
            texindices[g + 2] = texindex[2];
            normalindices[g] = normalindex[0];
            normalindices[g + 1] = normalindex[1];
            normalindices[g + 2] = normalindex[2];
            g += 3;
        }
        
    }
    memcpy(vertices, verticesout, sizeof(verticesout));
    memcpy(texcoords, texcoordsout, sizeof(texcoordsout));
    memcpy(normals, normalsout, sizeof(normalsout));
    memcpy(indices, vertexindices, sizeof(vertexindices));
    return 0;
}

main.c 调用代码

#include<stdio.h>
#include<stdlib.h>
#include<cglm/cglm.h>
#include"obj.h"

int main(){
   float vertices[24];
    float texcoords[24];
    float normals[24];
    int indices[36];
   if(loadobj("model.obj", vertices, texcoords, normals, indices) != 0){
      printf("Errors encountered!\n");
    }
    int i;
    int f;
    int d;
    int g;
    for(i = 0; i < sizeof(vertices)/sizeof(float); i++){
       if(i % 3 == 0 && i != 0){
         printf("\n");
        }
        if(vertices[i] >= 0){
         printf(" ");
      }
      printf("%f ", vertices[i]);
    }
    printf("\n\n");
    for(f = 0; f < sizeof(texcoords)/sizeof(float); f++){
      if(f % 2 == 0 && f != 0){
         printf("\n");
        }
        printf("%f ", texcoords[f]);
    }
    printf("\n\n");
    for(d = 0; d < sizeof(normals)/sizeof(float); d++){
      if(d % 3 == 0 && d != 0){
         printf("\n");
        }
        if(normals[d] >= 0){
         printf(" ");
        }
        printf("%f ", normals[d]);
    }
    printf("\n\n");
    for(g = 0; g < sizeof(indices)/sizeof(int); g++){
      if(g % 3 == 0 && g != 0){
         printf("\n");
        }
        printf("%d ", indices[g]);
    }
    printf("\n");
}
问题根因

数值篡改完全是栈内存越界写入导致的,核心问题出在面索引的存储逻辑:

  1. 你用cglm的vec3类型定义vertexindices、texindices、normalindices,这类类型本质是长度为3的float数组,仅能合法访问下标0、1、2三个位置。但解析面行时你用变量g做索引,每解析完一个三角面就给g加3,解析第二个面时g值已经是3,访问vertexindices[3]这类位置属于越界写,写入的数据会直接覆盖栈上相邻位置的verticesout、normalsout数组内容,直接篡改之前读好的顶点、法线数据。纹理坐标没被篡改只是因为栈上texcoordsout的位置离越界写的区域更远,刚好没被覆盖到。
  2. 代码里还存在其他会触发内存错误的隐患:
    • texcoordsout定义长度为28个float,但调用memcpy拷贝的目标texcoords仅分配了24个float的空间,拷贝长度sizeof(texcoordsout)超出目标缓冲区大小,会写坏栈内存。
    • 最后拷贝索引数据时,vertexindices是仅能存3个float的vec3,sizeof(vertexindices)仅12字节,而目标indices数组是用来存所有面索引的36个int长度,拷贝出来的索引数据完全不完整。
    • 索引本身是无符号整型值,你用float类型的vec3数组存储整型,再按int类型拷贝解析,本身就会出现数值错乱。
    • OBJ格式的索引是从1开始计数的,当前代码读取后没有减1,后续直接当作C数组下标使用会触发越界访问。
修复方案
  • 移除索引用的vec3类型定义,根据模型实际面数给顶点索引、纹理索引、法线索引分配足够长度的整型数组,不要用float类型数组存储整型索引值。
  • 统一所有缓冲区的长度,比如texcoordsout的长度要和传入的texcoords长度匹配,避免memcpy时源缓冲区比目标缓冲区大导致的越界。
  • 拷贝索引时按实际存储的索引总长度计算拷贝字节数,不要直接用vec3类型的长度做拷贝参数。
  • 读取到OBJ的索引值后统一减1,转换为C语言数组使用的0起始下标。

内容的提问来源于stack exchange,提问作者RadoslavL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:57:16