std:ostringstream 和 -std=c++11 的内存错误?

编辑:感谢所有指出问题并在 Stack Overflow 上讨论过的人.我自己投了最后一票.

EDIT: Thanks to everyone who pointed out the problem, and that it was discussed on Stack Overflow. I cast the last close vote myself.

一个相关的问题:ostringstreamostringstream::str 状态它是一个临时的.怎么这么多人知道?还是我应该查阅不同的文档?

A related question: neither CPP Reference on ostringstream or ostringstream::str state its a temporary. How did so many people know? Or is there different documentation I should have consulted?

我在带有 GCC 4.7.2、-std=c++11std::ostringstream 的 Debian 7.3 (x64) 下的内存错误方面遇到了很多麻烦代码>.它导致像 https://stackoverflow.com/questions/21260815/which-r-in-this-create-table-error-message.

I'm having a lot of trouble with memory errors under Debian 7.3 (x64) with GCC 4.7.2, -std=c++11 and std::ostringstream. Its leading to bizaare results like https://stackoverflow.com/questions/21260815/which-r-in-this-create-table-error-message.

完整的程序使用 Sqlite.从命令行运行简化案例与 Valgrind 打印 2 个不同的错误.整个简化案例程序可在 代码查看器 上找到(我认为发布时间有点长整个样本在这里).它所做的只是初始化 Sqlite、打开数据库、创建表、关闭数据库和单元化数据库.如果发生错误,它会报告错误.没有其他事情发生.

The full blown program uses Sqlite. Running a reduced case from the command line vs. Valgrind prints 2 different errors. The entire reduced case program is available at Code Viewer (I thought it was kind of long to post the entire sample here). All it does is initializes Sqlite, opens a database, creates a table, closes the database, and unitializes the database. And it reports errors if they occur. There's nothing else occurring.

这是简化案例程序的一部分,它只是尝试创建一个表.如果该表存在,它应该会产生一个错误(确实如此):

Here's part of the reduced case program that simply tries to create a table. If the table exists, it should produce an error (which it does):

ostringstream qs;
qs.str().reserve(96);

qs << "CREATE TABLE test";
qs << "(";
qs << "  userid INTEGER PRIMARY KEY AUTOINCREMENT,";
qs << "  username TEXT,";
qs << "  salt BLOB,";
qs << "  hmac BLOB";
qs << ");";

const char* stmt = qs.str().c_str();
AC_ASSERT(NULL != stmt);

rc = sqlite3_exec(db, stmt, NULL, NULL, &err);
AC_ASSERT(rc == SQLITE_OK);

if(rc != SQLITE_OK)
{
    ostringstream oss;
    oss.str().reserve(96);

    oss << "sqlite3_exec failed, error " << rc;
    LogError(oss.str().c_str());

    oss.clear(), oss.str("");
    oss << "Sqlite error: " << err;
    LogError(oss.str().c_str());

    // Break, handle error
}

但是,在命令行下,消息是:

However, under command line, the message is:

sqlite3_exec failed, error 1
Sqlite error: table 

在 Valgrind 下,消息是:

Under Valgrind, the message is:

sqlite3_exec failed, error 1
Sqlite error: table test already exists

程序大量使用ostringstream,Valgrind围绕它们产生了近13个问题,其中9个包括底层operator delete(void*)>basic_string.例如,如下所示(t.cpp 中的第 76 行是 const char* stmt = qs.str().c_str();):

The program makes heavy use of ostringstream, and Valgrind produces nearly 13 issues centered around them, 9 of which include operator delete(void*) on the underlying basic_string. For example, one is shown below (and line 76 from t.cpp is const char* stmt = qs.str().c_str();):

==14318== Invalid read of size 1
==14318==    at 0x45ACC8: sqlite3_exec (sqlite3.c:94542)
==14318==    by 0x405D07: main (t.cpp:79)
==14318==  Address 0x5d89728 is 24 bytes inside a block of size 127 free'd
==14318==    at 0x4C27870: operator delete(void*) (vg_replace_malloc.c:502)
==14318==    by 0x530EB1F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17)
==14318==    by 0x405CF1: main (t.cpp:76)

有没有人对这里发生的事情有任何想法?是 ostringstream 吗?或者也许是 GCC 4.7.2?或者也许是 Debian 的端口?

Does anyone have any ideas of what's going on here? Is it ostringstream? Or perhaps GCC 4.7.2? Or maybe Debian's port?

(对于开放式问题很抱歉.我已经无事可做).

(And sorry for the open ended question. I've run out of things to do).

推荐答案

const char* stmt = qs.str().c_str();

qs 中提取一个临时字符串,获取指向其内容的指针,然后销毁临时字符串,使指针悬空.在此之后使用指针将产生未定义的行为.

That extracts a temporary string from qs, takes a pointer to its content, then destroys the temporary leaving the pointer dangling. Using the pointer after this will give undefined behaviour.

要修复它,您可以将 str() 的结果分配给一个变量,使其不再是临时的,或者使用此表达式作为 sqlite3_exec 的参数>,以便临时文件在该函数调用之后继续存在.(在第二种情况下,您必须删除第一个断言;但无论如何该断言都毫无意义).

To fix it, you could either assign the result of str() to a variable, so that it's no longer temporary, or use this expression as the argument to sqlite3_exec, so that the temporary survives until after that function call. (In the second case, you'd have to remove the first assertion; but that assertion is rather pointless anyway).

相关文章