【问题标题】:Filename c_str() corruption when using directory_iterator使用 directory_iterator 时文件名 c_str() 损坏
【发布时间】:2019-07-20 23:40:42
【问题描述】:

在使用directory_iterator 存储一个文件的c_str() 名称之前遍历目录中的所有文件时,会导致无效读取(和垃圾输出)。

这对我来说似乎很奇怪。

代码示例:

工作:

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
 for (auto const &entry : fs::directory_iterator("./")) {
   std::cout << entry.path().filename().c_str() << '\n';
 }
}

valgrind 没有报告错误。

损坏的输出:

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main() {
 for (auto const &entry : fs::directory_iterator("./")) {
   auto filename = entry.path().filename().c_str();
   std::cout << filename << '\n';
 }
}

valgrind 报告 159 次无效读取(大小为 1)——确切数字取决于目录中有多少文件。

这两个 sn-ps 都是使用 gcc 9.1 编译的,使用以下命令: g++-9.1 test.cpp -std=c++17

【问题讨论】:

标签: filesystems c++17 gcc9


【解决方案1】:

临时对象的生命周期仅限于创建它的语句。通俗地说,语句是一行以分号结尾的代码。在整个语句结束之前,所有临时对象都处于活动状态。

From the C++ spec:

当实现引入具有非平凡构造函数([class.default.ctor],[class.copy.ctor])的类的临时对象时,它应确保为临时对象调用构造函数. 类似地,应为具有非平凡析构函数([class.dtor])的临时调用析构函数。 在评估完整表达式 ([intro.execution]) 的最后一步时销毁临时对象,该完整表达式(在词法上)包含它们的创建点。 即使评估以抛出异常结束也是如此。 销毁临时对象的值计算和副作用仅与完整表达式相关联,与任何特定子表达式无关。

剖析工作示例,我们看到operator&lt;&lt; 在销毁临时对象之前执行。

  • entry.path() = 临时 #1
  • .filename() = 临时 #2
  • .c_str() 从临时#2 中获取字符指针
  • .c_str() 传递到 operator&lt;&lt;std::cout 中,而以上所有内容仍然存在
  • 使用.c_str() 指针调用operator&lt;&lt; 被执行并返回。
  • '\n' 调用operator&lt;&lt; 被执行并返回。
  • 所有临时对象都被销毁了。

剖析损坏的示例,我们看到一个悬空指针:

  • entry.path() = 临时 #1
  • .filename() = 临时 #2
  • .c_str() 从临时 #2 中获取字符指针并存储在变量 filename
  • 语句结束:所有临时对象均已销毁。现在filename 指向已删除的内存——它是一个悬空指针。
  • operator&lt;&lt; 的调用会传递一个悬空指针,它会将其取消引用,就好像它是一个有效的字符串 = 未定义的行为一样。

您可以通过删除.c_str() 来提取不损坏的局部变量,这会使变量filename 成为std::filesystem::path 类型的对象。 std::filesystem::path 拥有它的内存(类似于std::string)。

for (auto const &entry : fs::directory_iterator("./")) {
    auto filename = entry.path().filename();
    std::cout << filename << '\n';
}

path also supports ostream output directly,不需要.c_str()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-25
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    • 2022-07-08
    • 2019-06-03
    • 2016-05-12
    相关资源
    最近更新 更多