1

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

一个相关的问题:CPP Reference 既不是临时的,ostringstream也不ostringstream::str是临时的。这么多人怎么知道的?或者是否有我应该查阅的不同文件?


我在使用 GCC 4.7.2 的 Debian 7.3 (x64)-std=c++11std::ostringstream. 它导致了像https://stackoverflow.com/questions/21260815/which-r-in-this-create-table-error-message这样的结果。

完整的程序使用 Sqlite。从命令行与 Valgrind 运行简化案例会打印 2 个不同的错误。整个简化案例程序可在Code Viewer上找到(我认为在这里发布整个示例有点长)。它所做的只是初始化 Sqlite,打开数据库,创建表,关闭数据库,以及初始化数据库。如果发生错误,它会报告错误。没有其他事情发生。

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

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
}

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

sqlite3_exec failed, error 1
Sqlite error: table 

在 Valgrind 下,消息是:

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

该程序大量使用ostringstream.Valgrind 围绕它们产生了近 13 个问题,其中 9 个涉及operator delete(void*)底层basic_string. 例如,下面显示了一个(第 76 行来自t.cppis 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 的端口?

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

4

2 回答 2

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

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

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

于 2014-01-22T12:39:28.960 回答
5

仔细看看这条线: const char* stmt = qs.str().c_str();

qs.str()为您提供一个临时std::string对象,您可以从中获取指向内部char*数组的指针。一旦这一行完成执行,您的指针将不再有效,并且可能(并且可能)存储其他内容。

尝试这样做:

std::string strstmt(qs.str());
const char* stmt = strstmt.c_str();
于 2014-01-22T12:39:21.913 回答