【问题标题】:How do I get an error from `pop_back()` if `size()` is 0?如果 `size()` 为 0,我如何从`pop_back()` 得到错误?
【发布时间】:2022-01-04 02:31:45
【问题描述】:

我正在向一位同事解释为什么我们要对他们进行消毒剂小测试。他问关于弹出一个向量的次数太多,如果它是一个异常,断言,UB 以及哪个 sanitizer 捕获它

似乎没有人抓住他们。如果您在弹出太多次后调用back(),地址和内存将会出现,但如果您弹出并执行size(),您会因为包装而得到一个很大的无效值

当我弹出太多次时,有没有办法获得断言或异常或运行时终止?我真的认为没有消毒剂的调试版本会发现这一点(带有断言或异常)

我使用 clang sanitizer,但使用 gcc 构建选项也会有所帮助

【问题讨论】:

  • size() 不返回无符号类型吗?它应该不可能小于零。
  • @MarkRansom:我不是字面意思。我用了printf。 -1 和最大值都是相同的位模式。我的意思是包装是不正确的,我想要某种警告
  • if (v.size() == 0) throw "curses, foiled again."; v.pop_back();
  • size() 确实返回了一个未签名的size_type。如果它被强制转换为有符号类型,尽管它最终可能会导致缩小。大多数时候编译器应该警告你隐式缩小。
  • @floomby -- 如果它被转换为有符号类型,则没有隐式缩小;强制转换始终是显式的——它是您在源代码中编写的内容,用于告诉编译器进行转换。

标签: c++ vector


【解决方案1】:

libstdc++ 和 libc++ 都有一个带有断言的“调试模式”,可以使用以下命令启用:

  • -D_GLIBCXX_DEBUG 用于 libstdc++
  • -D_LIBCPP_DEBUG 用于 libc++

-fsanitize=undefined 也似乎捕捉到了它,但错误消息更加神秘。

【讨论】:

  • -fsanitize=undefined 在我的系统(clang 12.0.1,x86_64-pc-linux-gnu)上没有这样做,但是 -D_GLIBCXX_DEBUG 工作得很好(在我的一次测试中使用和不使用消毒剂)跨度>
  • @EricStotch 它工作了here
  • 有趣。我没想过在没有推动的情况下做一个流行音乐。我这样做是为了消除该错误gcc.godbolt.org/z/ffqexhs6j
  • 似乎消毒剂只处理向量未初始化并因此为空指针的情况;但当 pop_back 在非空向量上被调用太多时,情况并非如此。我猜后者在实践中也是一个相当常见的错误。
【解决方案2】:

消毒剂可能需要一些注释才能很好地处理库类型。 g++ -fsanitize=address -D_GLIBCXX_SANITIZE_VECTOR (documentation)、clang++ -stdlib=libc++ -fsanitize=addressclang++ -stdlib=libstdc++ -fsanitize=address -D_GLIBCXX_SANITIZE_VECTOR -D__SANITIZE_ADDRESS__(需要最后一个宏是 bug)都检测到问题并打印消息

=================================================================
==12312==ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container:
      beg     : 0x602000000010
      end     : 0x602000000014
      old_mid : 0x602000000010
      new_mid : 0x60200000000c
    #0 0x7f6a9db3b707 in __sanitizer_annotate_contiguous_container ../../../../src/libsanitizer/asan/asan_poisoning.cpp:362
    #1 0x55b9dcd1fc41 in std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Asan<std::allocator<int> >::_S_adjust(std::_Vector_base<int, std::allocator<int> >::_Vector_impl&, int*, int*) (/tmp/a.out+0x1c41)
    #2 0x55b9dcd1fb66 in std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Asan<std::allocator<int> >::_S_shrink(std::_Vector_base<int, std::allocator<int> >::_Vector_impl&, unsigned long) (/tmp/a.out+0x1b66)
    #3 0x55b9dcd1f6c4 in std::vector<int, std::allocator<int> >::pop_back() (/tmp/a.out+0x16c4)
    #4 0x55b9dcd1f36d in main (/tmp/a.out+0x136d)
    #5 0x7f6a9d57fe49 in __libc_start_main ../csu/libc-start.c:314
    #6 0x55b9dcd1f199 in _start (/tmp/a.out+0x1199)

SUMMARY: AddressSanitizer: bad-__sanitizer_annotate_contiguous_container ../../../../src/libsanitizer/asan/asan_poisoning.cpp:362 in __sanitizer_annotate_contiguous_container
==12312==ABORTING

请注意,对于这种情况,不需要像消毒剂那样复杂的东西,正如另一个答案中提到的那样,库通常具有调试模式。例如使用 libstdc++,定义 _GLIBCXX_DEBUG 启用完全调试模式(ABI-与正常模式不兼容)

/usr/include/c++/11/debug/vector:523:
In function:
    void std::__debug::vector<_Tp, _Allocator>::pop_back() [with _Tp = int; 
    _Allocator = std::allocator<int>]

Error: attempt to access an element in an empty container.

Objects involved in the operation:
    sequence "this" @ 0x0x7ffe20bf5f80 {
      type = std::__debug::vector<int, std::allocator<int> >;
    }

在定义 _GLIBCXX_ASSERTIONS 时,可以启用更轻松的断言并保留 ABI,但提供的有关错误的信息较少

/usr/include/c++/11/bits/stl_vector.h:1227: void std::vector<_Tp, _Alloc>::pop_back() [with _Tp = int; _Alloc = std::allocator<int>]: Assertion '!this->empty()' failed.

并使用 libc++ 打印定义 _LIBCPP_DEBUG

/usr/lib/llvm-11/bin/../include/c++/v1/vector:1703: _LIBCPP_ASSERT '!empty()' failed. vector::pop_back called for empty vector

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 2015-01-28
    • 1970-01-01
    • 2020-11-18
    • 1970-01-01
    • 2022-06-13
    • 1970-01-01
    • 2021-12-22
    • 2013-04-27
    相关资源
    最近更新 更多