为什么我的字符串分配会导致分段错误?

发布于 2024-12-02 06:04:15 字数 1798 浏览 2 评论 0原文

我试图消除段错误的常见原因,例如取消引用空指针,但我被难住了。此错误不会出现在调试模式下的测试机器上,但会出现在发布编译的生产机器上。两者都设置为 /O0 以消除这种可能性。当我注释掉字符串分配时,段错误就消失了,现在我只需要了解发生了什么,这样我就可以修复它。

可能有一些更明显的事情,但一个额外的复杂性是,该代码是由内核模式应用程序调用的,而我从供应商那里得到的关于为系统编写安全代码的唯一说明是“每个函数都需要来自操作系统的任何东西”。系统或必须等待操作系统中的任何内容都将无法工作”(逐字记录)。

union {
    REAL_T real[8];
    char   byte[64];
} fileName;

void transferFilenames () {
    string tempname;

    // This is from an api I must use, it retrieves values from NVRAM on
    // an accessory board.  Specifically it will return 8 REAL_T values
    // and store them starting at &filename.real[0], inp/outpArray are 
    // globals defined elsewhere.
    inpArray.I7_ARRAY.INDEX = 2903;
    inpArray.I7_ARRAY.LEN = 8;
    inpArray.I7_ARRAY.Z_PAR_PTR = &fileName.real[0];
    b_array ( READ_CYCLE_PARAMS, &status, &inpArray, &outpArray );

    // if status != 0, there was an error
    if ( status == 0 ) {
        // there is no guarantee of being null terminated
        fileName.byte[63] = 0;

        /* This test code didn't fix the problem
        int i;
        char myStr[64];
        for ( i = 0; i < 64; i++ )
            myStr[i] = fileName.byte[i];
        if ( myStr[0] != '\0' )
            string mystring( myStr ); // seg fault
        */
        tempname.assign( fileName.byte ); // Throws seg fault
        // tempname.assign( &fileName.byte[0] ); // try to be more explicit

        // controlBlock is a global class defined elsewhere
        controlBlock->setFileName ( tempname, ISINPUT);
    } else {
        controlBlock->setFileName( "BAD", ISINPUT );
    }
    return;
}

当我重载控制块以直接获取 char* controlBlock->setFileName( &fileName.byte[0] ) 并完全删除字符串分配时,分段错误消失了。重载所做的只是将 char* 分配给本地字符串并调用常规方法。

我在幕后错过了什么?

I've tried to eliminate the common causes of seg faults such as dereferencing a null pointer, but I'm stumped. This error does not appear on my test machine in debug mode, but does appear on my production machine the release compile. Both are set to /O0 to eliminate that as a possibility. When I comment out the string assignment, the seg fault goes away, now I just need to understand what's happening so I can fix it.

There may be something more obvious, but an additional complication is that this code is being called by a kernel mode application, and the only instructions I have from the vendor about writing safe code for the system is "every function which needs anything from the operating system or has to wait for anything from the OS will not work" (verbatim).

union {
    REAL_T real[8];
    char   byte[64];
} fileName;

void transferFilenames () {
    string tempname;

    // This is from an api I must use, it retrieves values from NVRAM on
    // an accessory board.  Specifically it will return 8 REAL_T values
    // and store them starting at &filename.real[0], inp/outpArray are 
    // globals defined elsewhere.
    inpArray.I7_ARRAY.INDEX = 2903;
    inpArray.I7_ARRAY.LEN = 8;
    inpArray.I7_ARRAY.Z_PAR_PTR = &fileName.real[0];
    b_array ( READ_CYCLE_PARAMS, &status, &inpArray, &outpArray );

    // if status != 0, there was an error
    if ( status == 0 ) {
        // there is no guarantee of being null terminated
        fileName.byte[63] = 0;

        /* This test code didn't fix the problem
        int i;
        char myStr[64];
        for ( i = 0; i < 64; i++ )
            myStr[i] = fileName.byte[i];
        if ( myStr[0] != '\0' )
            string mystring( myStr ); // seg fault
        */
        tempname.assign( fileName.byte ); // Throws seg fault
        // tempname.assign( &fileName.byte[0] ); // try to be more explicit

        // controlBlock is a global class defined elsewhere
        controlBlock->setFileName ( tempname, ISINPUT);
    } else {
        controlBlock->setFileName( "BAD", ISINPUT );
    }
    return;
}

When I overloaded my control block to take a char* directly controlBlock->setFileName( &fileName.byte[0] ), and removed the string assignment altogether the segmentation fault disappeared. All the overload does is assign the char* to a local string and call the regular method.

What am I missing behind the scenes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

剑心龙吟 2024-12-09 06:04:15

问题可能不在于对 tempname.assign() 的调用。管道可能会导致对恶魔真正所在位置的报告有些错误。这句话让我觉得恶魔藏在别处:

当我重载控制块以直接获取 char* controlBlock->setFileName( &fileName.byte[0] ) 并完全删除字符串分配时,分段错误消失了。

在一种情况下,您将 std::string 传递给 setFileName,在另一种情况下,将 char* 传递给 setFileName。尝试

controlBlock->setFileName ( tempname, ISINPUT);

controlBlock->setFileName ( tempname.c_str(), ISINPUT);

附录
替换
问题很可能出在使用 std::string, period 上。如果保留大小太小,std::string::assign() 将调用 malloc。将保留大小设置得足够大也可能不起作用;这可能只是将 malloc 调用推送到构造函数中。使用动态分配内存的东西并不符合子句每个需要操作系统提供任何内容或必须等待操作系统提供任何内容的函数都将无法工作

事实上,这个条款很可能会阻止在这台机器上使用大量的 C++ 库。 C++ 库在分配和释放内存方面相当宽松,std::string 做了很多工作。

The problem may not be in the call to tempname.assign(). Pipelining can result in somewhat erroneous reports of where the demon really lies. This statement makes me think the demon lies elsewhere:

When I overloaded my control block to take a char* directly controlBlock->setFileName( &fileName.byte[0] ), and removed the string assignment altogether the segmentation fault disappeared.

You are passing a std::string to setFileName in one case, a char* in the other. Try replacing

controlBlock->setFileName ( tempname, ISINPUT);

with

controlBlock->setFileName ( tempname.c_str(), ISINPUT);

Addendum
The problem may well be with using std::string, period. std::string::assign() will call malloc if the reserved size is too small. Making the reserved size sufficiently large may not work either; this may just push the malloc call into the constructor. Using something that allocates memory dynamically doesn't jibe with the clause every function which needs anything from the operating system or has to wait for anything from the OS will not work.

In fact, this clause may well preclude the use of huge chunks of the C++ library on this machine. The C++ library is pretty loose with regard to allocating and deallocating memory, and std::string does lots of it.

命比纸薄 2024-12-09 06:04:15

该程序是否使用与您必须使用的采用 std::string 的 API 完全相同的编译器和 libstdc++ 标头进行编译?或者用调试程序调用发布库?

std::string 完全定义为标头中的模板。这意味着您将构造编译器关于 std::string 是什么的知识,将其传递给 API,并且库会将其解释为它认为的 std::string 是什么。当双方都有不二进制兼容的实现时,这有时会导致问题,并且不建议公开采用 std::string 作为库的 API。您确实应该将 char 数组传递给任何需要 std::string 的库以避免出现问题

Gcc5+ 的 _GLIBCXX_USE_CXX11_ABI 的使用与否也可能会影响部分代码中 std::string 的实现方式

我知道这是一个老问题,但有时它会出现在答案中,因此新用户应该了解这个可能的原因

Was the program compiled with the exact same compiler and libstdc++ headers as the API you have to use that takes std::string? Or calling a release library with a debug program?

std::string is fully defined as a template in headers. Meaning you will construct your compiler's knowledge of what a std::string is, pass this to the API, and the library will interpret it as what it thinks a std::string is. This ocasionally causes issues when both sides have implementations that are not binary compatible, and exposing APIs that take std::string for a library should not be recommended. You should indeed pass char arrays to any library that expects a std::string to avoid issues

The use or not of the _GLIBCXX_USE_CXX11_ABI for Gcc5+ might also affect how std::string is implemented in parts of the code

I know this is an old issue, but it sometimes comes first in answers, so new users should know about this possible cause

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文