在解析 STL 时引发未经处理的异常

fgets throwing unhandled exception while parsing stl

本文关键字:处理 异常 STL      更新时间:2023-10-16

我是 c++ 的新手,我正在尝试解析一个大约 64MB 的 stl 文件,其中有大约 ~18K 行。代码在前 100 行工作正常,但随后 fgets 抛出以下异常:

"Unhandled exception at 0x77B0BAC5 (ntdll.dll) in STLparser.exe: 0xC0000024: There is a mismatch between the type of object required by the requested operation and the type of object that is specified in the request." 我已经手动检查了fgets抛出异常的行,那里没有什么异常。我现在别无选择。任何解决此问题的帮助将不胜感激。

====

============CODE================================
#include<fstream>
#include<iostream>
#include"ParseString.h"
#include"Vectors.h"

using namespace std;
int main(void)
{
    //Define variables
    FILE *file;
    char *line = new char;
    parsestring oneline;
    int n_Vols = 0, n_Elms = 0, n_nods = -1, E = 0;
    Nod *nodes = new Nod();
    Nod dummy;
    Elm *elements = new Elm();
    int mycounter = 0;
    //Open file
    fopen_s(&file, "sample.stl", "r");
    while (fgets(line, 1024, file) != NULL) //**********Getting Error Here*************
    {
        // populate required data
    }
    fclose(file);
    printf("%d,%d,%d", n_Vols, n_Elms, n_nods);
    getchar();
    return 0;
}

====

===============

当损坏时,在此函数上恢复执行(不是我的函数,内部函数(

void __cdecl _unlock (
        int locknum
        )
{
        /*
         * leave the critical section.
         */
        LeaveCriticalSection( _locktable[locknum].lock );
}

而不是:

char* line = new char;

做:

char[1024] line;

您可能会收到损坏的数据,因为您没有分配任何内存。

char *line = new char;

这会在堆上分配一个字节,并指向它line

while (fgets(line, 1024, file) != NULL)

这将最多将 1024 个字节写入 line 指向的位置。 line只指向一个字节。 这就是你的代码爆炸的原因。

你对单个字符调用fgets,因为你做了:

char *line = new char;

您可能希望分配大小为 1024 的 char 数组

char line[1024]                // on the stack
char *line = new char[1024];   // or on the heap

一个问题是这样的:

char *line = new char;

你知道你想做多大的数组,所以需要调用new。 其次,即使您确实调用了new,上面的那行也只为 1 个字符分配了空间。

将上述内容更改为:

char line[1024];

因为同样,您确切地知道预先需要多少个字符,只需创建一个该大小的数组即可。

此外,您还可以在其他不需要的地方使用new

Nod *nodes = new Nod();
Elm *elements = new Elm();

通常这些错误是由以下人员造成的:

  1. Java(或类似的语言(程序员,他们错误地认为他们必须使用new创建所有对象,或者
  2. 初学者C++程序员认为在向具有指针参数的函数提供参数时必须声明指针变量并分配内存。 通常只需要传递现有变量的地址(使用 address-of 运算符 & (。

要创建对象,如果对象的生存期在声明它的块的范围内,则只需执行以下操作:

 Nod nodes;
 Elm elements;

完成此操作后,请使用 . 运算符而不是 -> 来访问 nodeselements 的成员。

另一个问题是,您没有检查对fopen_s的调用是否因 NULL 文件句柄而失败。 如果使用 NULL 文件句柄调用fgets,则行为未定义(可能是崩溃(。 在继续之前,请务必检查是否已成功打开文件。

您应该

为保存数据的行分配必要的内存空间。

char * fgets ( char * str, int num, FILE * stream );


str指向复制字符串读取的字符数组的指针。

数字要复制到 str 的最大字符数(包括终止空字符(。

在代码中,将 num 设置为 1024,因此您的行应指向分配 1024*char 大小内存空间的位置。因此,您需要将代码修改为:

char *line = new char[1024];

顺便说一句,当您不再需要堆资源时,您应该释放堆资源。

delete nodes;  
delete elements;

你有几个正确的答案。 但是,我认为您最好的方法是继续编写C++:

#include <cstdio>
#include <fstream>
#include <string>
#include <vector>
#include "ParseString.h"
#include "Vectors.h"
int main()
{
    //Open file
    std::ifstream file("sample.stl");
    if (file.fail()) {
        std::fprintf(stderr, "Unable to open sample.stln");
        return 1;
    }
    int n_Vols = 0, n_Elms = 0, n_nods = -1;
    std::string buffer;
    while (std::getline(file, buffer)) {
        // populate required data
        // you can use buffer.c_str() to get a const char*
        parsestring oneline;
        int E = 0, mycounter = 0;
        // based on the name, I suspect you actually want multiple Nod's
        std::vector<Nod> nodes;
        Nod dummy;
        // based on the name, I suspect you actually want multiple Elm's
        std::vector<Elm> elements;
        ...
    }
    printf("%d,%d,%d", n_Vols, n_Elms, n_nods);
    getchar();
    return 0;
}

就个人而言,我喜欢std::,但其他人喜欢您最初使用using namespace std方法。

我还应该提到,fgets(和fgets_s(不会抛出C++例外。 中止程序的方法不止一种,并且无法通过添加try { ... } catch块来修复您遇到的问题。

您可能会

遇到段错误,但您可能会触发断言。 您甚至可能会遇到 Windows 结构化异常处理,与名称相反,它与C++异常不同。 你的程序最终会死去,但它会因不同的原因而死机,你必须做不同的事情来处理异常、信号(段错误(或 SEH。

别担心,你最终会知道这些东西是什么;我只是想指出,"神秘死亡"可能是由未捕获的异常引起的,但它可能是由任何其他事情引起的。