在 for 循环的某些迭代中,字符串的长度为 0,而在其他迭代中则为 0

Posted

技术标签:

【中文标题】在 for 循环的某些迭代中,字符串的长度为 0,而在其他迭代中则为 0【英文标题】:Length of string is 0 on some iterations of for-loop and not others 【发布时间】:2019-11-12 21:14:52 【问题描述】:

我正在为 C++ 使用 nlohmann::json 库。我有以下代码。

#include <iostream>
#include <json.hpp>

using json = nlohmann::json;

int main()
    json m;
    m["aaaaaaaa"] = 0;
    m["bbbbbbbbbbbbb"] = 0;
    m["ccccccccccccccccccc"] = 0;
    m["dddddddddddddddddd"] = 0;
    m["eeeeeeeeeeeeeeeeee"] = 0;
    m["fffffffffffff"] = 0;
    m["gggggggggggg"] = 0;
    m["hhhhhhhhhhhh"] = 0;
    m["iiii"] = 0;
    m["jjjjjjjjjjjjjjj"] = 0;
    m["kkkkkkkkkkkkkk"] = 0;
    m["llllllllllllllll"] = 0;

    for (int i = 0; i < 100; i++) 
        const char* mstr = m.dump().c_str();
        std::cout << strlen(mstr) << std::endl;
    


我希望输出 strlen(mstr) 对于 for 循环的所有 100 次迭代都完全相同。

在某些运行中,我得到了预期的输出。

223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 程序以退出代码结束:0

但在其他运行中,我偶尔会看到字符串的长度为 0。

223 0 223 223 223 223 223 223 223 223 223 0 223 223 223 223 223 0 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 0 223 223 223 223 223 223 223 223 0 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 223 程序以退出代码结束:0

这怎么可能发生?

【问题讨论】:

m.dump().c_str() 有问题,您要从临时字符串的内脏中获取指针。 @Mat 谢谢,为什么这本身就很危险? 假设m.dump() 返回一个std::string,该字符串对象是一个临时对象。它将在语句末尾(;)被销毁,除非您明确地持有它(如在 Gearoid 的回答中,或通过将其绑定到 const 引用)。您的代码要求 .c_str() 返回临时拥有的指针。一旦临时对象被销毁,该指针就无效(它将被字符串的析构函数释放)。 【参考方案1】:

转储函数将一个对象返回到堆栈。通过使用来自该对象的指针,您可能偶尔会发现对象内存在打印值之前被重用。应该做的是直接存储转储的字符串:

std::string mstr = m.dump();
std::cout << mstr.size() << std::endl;

【讨论】:

谢谢。这是我的预感,但我不确定为什么分配给临时字符串的堆栈内存会被“重用”或超出范围(如果函数仍在运行)?我知道当函数返回时堆栈是清晰的,但是在函数执行期间它应该可以访问它的所有堆栈变量,对吧? @user11909399 查看我刚刚发布的答案【参考方案2】:

m.dump() 返回一个临时的string_t 对象。当到达整个 const char* mstr = m.dump().c_str(); 表达式末尾的 ; 时,该临时对象超出范围。这意味着当您在其上调用c_str() 时,该对象仍然存在,但是在您将char* 指针保存到mstr 之后,但在您可以将其打印到std::cout 之前,它就会被销毁。指针在该点悬空,因此输出的行为是 undefined

您需要保存临时对象,以便在使用指向它的指针完成之前保持数据活动,例如:

for (int i = 0; i < 100; i++) 
    string_t mstr = m.dump();
    std::cout << strlen(mstr.c_str()) << std::endl;

然后可以简化,因为在这种情况下您实际上根本不需要使用strlen(),C++ 字符串知道自己的长度:

for (int i = 0; i < 100; i++) 
    string_t mstr = m.dump();
    std::cout << mstr.size() << std::endl;

或者,您可以在临时对象仍在范围内时打印size()

for (int i = 0; i < 100; i++) 
    std::cout << m.dump().size() << std::endl;

【讨论】:

谢谢。关于你的最后一点,假设我调用了一个函数f(m.dump().c_str()) - 我猜这仍然很危险?我不能保证f 可以访问指向字符串的指针?出于某种原因,f(m.dump().c_str()) 实际上工作可靠 f(m.dump().c_str())中,dump()返回的临时对象在f()运行的整个过程中都在作用域内,所以f()使用@987654341返回的指针是完全安全的@。不安全的是 f() 将该指针保存在某处以便在 f() 退出后稍后访问。 谢谢。如果f 的签名是f(std::string&amp; s) 怎么办?由于std::string 的构造函数,这实际上是在重新分配c_str() 返回的指针,对吗? f(m.dump().c_str())中,如果const char*被声明为f(std::string&amp;),则不能将const char*传递给f(std::string&amp;),引用需要一个实际的std::string对象。 f(m.dump()) 可以工作,但前提是 f 被声明为 f(std::string)f(const std::string&amp;),而不是 f(std::string&amp;),因为临时不能绑定到非 const 引用。 在我的编译器中它可以工作,但具体来说,函数签名有const std::string&amp;

以上是关于在 for 循环的某些迭代中,字符串的长度为 0,而在其他迭代中则为 0的主要内容,如果未能解决你的问题,请参考以下文章

学习这个-js迭代条件i if else for for in循环for each长度

《Python核心编程》读书笔记

For循环迭代不断增长的向量

R中的for循环,增量

python系统学习08for循环知识点合集

For 循环可以“看到”当前迭代中即将出现的值吗?