当前位置: 首页 > 知识库问答 >
问题:

c 标准::bad_alloc 在标准::文件系统::p附加

方砚
2023-03-14

我正在经历一个非常奇怪的行为,我把它总结成一个非常基本的测试:

#include <string>
#include <filesystem>

int main(void)
{
  const std::string name = "foo";
  const std::filesystem::path lock_dir = "/tmp";
  std::filesystem::path lockfile = lock_dir / name;

  return 0;
}

我用g-std=c17-Wall-Wextra-Werror-gfoo编译它。cpp-o foo。当我运行它时,在附加两个路径的行上得到一个std::bad_alloc异常。这是我看到的gdb

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff742c801 in __GI_abort () at abort.c:79
#2  0x00007ffff7a8e1f2 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7a99e36 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7a99e81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7a9a0b5 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++++.so.6
#6  0x00007ffff7a907a7 in std::__throw_bad_alloc() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x0000555555558cfe in __gnu_cxx::new_allocator<std::filesystem::__cxx11::path::_Cmpt>::allocate (this=0x7fffffffe080, __n=12297828079348111650) at /usr/include/c++/8/ext/new_allocator.h:102
#8  0x00005555555587d0 in std::allocator_traits<std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::allocate (__a=..., __n=12297828079348111650) at /usr/include/c++/8/bits/alloc_traits.h:436
#9  0x0000555555557f76 in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_M_allocate (this=0x7fffffffe080, __n=12297828079348111650)
    at /usr/include/c++/8/bits/stl_vector.h:296
#10 0x0000555555558387 in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_M_create_storage (this=0x7fffffffe080, __n=12297828079348111650)
    at /usr/include/c++/8/bits/stl_vector.h:311
#11 0x00005555555579cf in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_Vector_base (this=0x7fffffffe080, __n=12297828079348111650, __a=...)
    at /usr/include/c++/8/bits/stl_vector.h:260
#12 0x0000555555556d39 in std::vector<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::vector (this=0x7fffffffe080, 
    __x=std::vector of length -1303124922760, capacity -1303124922760 = {...}) at /usr/include/c++/8/bits/stl_vector.h:460
#13 0x000055555555635f in std::filesystem::__cxx11::path::path (this=0x7fffffffe060, Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__p=...) at /usr/include/c++/8/bits/fs_path.h:166
#14 0x00005555555563c8 in std::filesystem:: (Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__lhs=..., Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__rhs=...) at /usr/include/c++/8/bits/fs_path.h:554
#15 0x0000555555555fbe in main () at foo.cpp:8

这带来了几个问题:

  1. 我的测试代码有什么问题
  2. 为什么GDB在调用堆栈中使用python显示任何内容

预料到问题,我的g是< code>gcc版本8 . 3 . 0(Ubuntu 8 . 3 . 0-6 Ubuntu 1 ~ 18 . 04 . 1)我的gdb是< code > GNU gdb(Ubuntu 8.2-0 Ubuntu 1 ~ 18.04)8.2

这里的更新是成功编译的可执行文件的ldd输出

linux-vdso.so.1 (0x00007ffc697b2000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5c35444000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5c3522c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c34e3b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5c34a9d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5c35a2d000)

共有2个答案

白昊乾
2023-03-14

我将总结一下我自己的发现和其他人在评论中的发现。这还不是一个实际的答案,因为此时我无法解释失败的原因。

我能够通过在常规的 ubuntu Docker 映像中安装 g -8 和 g -9 来重现此行为,因此我同时拥有 /usr/bin/g -8/usr/lib/x86_64-linux-gnu/libstdc .so.6.0.26

根据 gdb 堆栈跟踪,错误发生在 std::矢量构造函数中的某个地方。似乎当 std::文件系统::p ath 的默认复制构造函数在其运算符/

/usr/include/c/8/bits/fs_path.h

  /// Append one path to another
  inline path operator/(const path& __lhs, const path& __rhs)
  {
    path __result(__lhs);  // <-- fails here
    __result /= __rhs;
    return __result;
  }

这一发现使得进一步简化测试用例成为可能:

#include <filesystem>

int main(void)
{
  const std::filesystem::path first = "/tmp";
  const std::filesystem::path second(first);

  return 0;
}

这清楚地表明问题出在调用复制构造函数的某个地方。

std::filesystem::path中唯一的矢量=是这个矢量(大概是路径组件的矢量):

/usr/include/c/8/bits/fs_path.h

    struct _Cmpt;
    using _List = _GLIBCXX_STD_C::vector<_Cmpt>;
    _List _M_cmpts; // empty unless _M_type == _Type::_Multi

根据堆栈跟踪,复制此向量时,我们立即进入stl_vector.h

< code >/usr/include/c/8/bits/STL _ vector . h

      vector(const vector& __x)
      : _Base(__x.size(),
        _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
      {

但是,如果我们在这里_Vector_base的构造函数中打印__n的值:

      _Vector_base(size_t __n, const allocator_type& __a)
      : _M_impl(__a)
      { _M_create_storage(__n); }

我们将得到一些疯狂的大数字,这让我认为一个不正确的向量__x以某种方式传递给了复制构造函数。

现在,当你把G8和g9的库结合起来时,为什么会发生这种情况,我不知道(现在),我猜如果他们需要理解真正的原因,他们应该更深入一层。

但我想,你主要问题的答案是“问题是由编译器和库版本之间的不兼容性引起的”:)

鱼宜
2023-03-14

这是由Ubuntu的一个“特性”造成的,它提供了一个更晚的< code>libstdc。所以比系统附带的< code>g 更好。有关更多详细信息,请参见https://bugs.launchpad.net/ubuntu/来源/gcc-8/ bug/1824721。

通常在用GCC 8编译时,< code>std::filesystem符号不会出现在< code>libstdc中。所以如果您无法链接到< code>-lstdc fs,那么您将会得到一个链接器错误。但是因为较新的< code>libstdc。所以GCC 9中的包含了< code>std::filesystem的符号,所以不会发生链接器错误。不幸的是,GCC 9版本的文件系统符号与GCC 8头文件不兼容(因为GCC 8中的文件系统库是实验性的且不稳定,而GCC 9中的< code>filesystem::path的布局发生了变化)。这意味着您的程序链接了,但是在运行时它使用了错误的符号作为< code>filesystem::path,于是不好的事情发生了。

我没有预料到这个问题,因为我不知道Ubuntu将旧的libstdc头文件与新的libstdtc共享库混合在一起。这样做通常是安全的,除非使用“实验性”、不完整的特性,例如GCC 8中的C17特性。

我对Ubuntu的建议是让g自动将-lstdc fs添加到编译命令的末尾。如果您使用任何std::filessystem特性,那么这些符号的正确定义应该在GCC 8的libstdc fs. a中找到(而不是在GCC 9的libstdc. so中),并且在大多数情况下,一切都应该正常工作。如果Ubuntu还没有使用该解决方案更新他们的GCC包,您也可以通过确保手动链接-lstdc fs来使其工作(无论如何,这是GCC 8所必需的)。

 类似资料:
  • 附录 C 描述了 Erlang 标准库模块的一些函数。 io Io 模块提供了基本的输入输出。这儿的所有函数都有可选参数 Dev ,它是一个用于输入输出的文件描述符。默认值是标准输入输出。 format([Dev],F,Args) 按格式 F 输出参数 Args 。 get_chars([Dev],P,N) 输出提示 P 并读出 Dev 的前 N 个字符。 get_line([Dev], P) 输

  • C++ 标准库可以分为两部分: 标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。 面向对象类库: 这个库是类及其相关函数的集合。 C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。 标准函数库 标准函数库分为以下几类: 输入/输出 I/O 字符串和字符处理 数学 时间、日期和本地化 动态分配 其他 宽字符函数 面向对象类库 标

  • 现代C编译器支持一些或全部的ANSI提议的标准C。无论何时可能的话,尽量用标准C编写和运行程序,并且使用诸如函数原型,常量存储以及volatile(易失性)存储等特性。标准C通过给优化器提供有有效的信息以提升程序的性能。标准C通过保证所有编译器接受同样的输入语言以及提供相关机制隐藏机器相关内容或对于那些机器相关代码提供警告的方式提升代码的可移植性。 17.1 兼容性 编写很容易移植到老编译器上的代

  • std::io::fs 模块包含几个处理文件系统的函数。 use std::fs; use std::fs::{File, OpenOptions}; use std::io; use std::io::prelude::*; use std::os::unix; use std::path::Path; // `% cat path` 的简单实现 fn cat(path: &Path) -> i

  • 本文向大家介绍系统测试标准有哪些?相关面试题,主要包含被问及系统测试标准有哪些?时的应答技巧和注意事项,需要的朋友参考一下 不存在致命或严重级别的BUG 不存在优先级为P1的BUG 遗留问题不能大于总BUG数的8% 遗留问题不能明显影响用户使用