2

我正在研究一些最终也需要调用操作系统级 C 代码的 C++ 代码,例如scandir

我想对大部分代码库使用 C++,这(对我而言)意味着我主要使用 std::string 而不是 char 指针。

我有一个接受 string_view 的方法,因此我可以传入 std::string 和 char*,具体取决于 C++ 或“C 互操作”代码是否需要调用它:

std::optional<std::vector<FileInfo>> scan_directory(const std::string_view directory)
{
    if (directory.empty()) {
        return {};
    }

    struct dirent **namelist;
    int num = scandir(directory.data(), &namelist, nullptr, nullptr);
    [...]
}

注意data()这里的调用,因为 scandir 需要一个const char *. 现在,我看到了这个注释

与 std::basic_string::data() 和字符串字面量不同,data() 可能返回指向非空终止缓冲区的指针。因此,将 data() 传递给仅采用 const CharT* 并期望以空字符结尾的字符串的例程通常是错误的。

这让我想到:有没有更好/更安全的方法?我知道在实践中,调用者将以空字符结尾的字符串,但是当我已经意识到这里存在潜在问题时,我不想在以后创建一个难以诊断的错误。虽然我猜已经不能保证 char* 是空终止的,所以我不会让情况变得更糟。

不过,好奇是否有更好的选择。

  • 我应该检查 string_view 是否有空终止符,如果不存在,创建一个char[directory.size() + 1]{0}并自己复制字符?
  • 或者创建两个重载,一个采用 std::string,另一个采用 const char*?

g++ (GCC) 10.2.1 20201016 (Red Hat 10.2.1-6)通过 CMake 的set(CMAKE_CXX_STANDARD 20).

4

1 回答 1

1

当您只有 astd::string_view时,您无法保证超出其范围的访问size()不是未定义的行为。这不是绝对保证它是未定义的行为,但你也不能保证它不是。如果string_view是使用指向 -\0终止字符串的指针构造的,但它不包含在构造string_view的大小中,那么您可以说是安全的(我注意到最近版本的 libstdc++ 可以选择对向量进行一些边界检查和字符串访问,如果在未来的某个时候为string_views 引入边界检查,它们会让你绊倒,你会不走运)。但是当然可以string_view用一个指向一个字符串的指针来构造一个不是\0终止,具有精确的字符数。那样的话,越过它size()就会产生鼻魔。而且您无法确定是否是这种情况。

这里最简单的解决方案是将参数声明为const char *,并创建一个采用const std::string &替代参数的重载,然后使用c_str() 调用此函数。

于 2020-11-30T02:35:39.133 回答