【问题标题】:Undefined Behavior: A checklist for code reviews [duplicate]未定义的行为:代码审查清单 [重复]
【发布时间】:2011-06-07 03:58:14
【问题描述】:

可能重复:
What are all the common undefined behaviour that a C++ programmer should know about?

我正准备为 C++ 自我和同行代码审查准备一份清单或指南,因为有很多场景可能会导致可怕的 Undefined Behavior 领域,我正在考虑提出一种清单最常用的 C++ 语言结构中的未定义行为。

当然,不可能通过修改Sequence points 之间的变量来预测未定义的行为,但我认为可以列出从其他场景中产生的场景。

如果您进行代码审查,您会注意哪些常见的Undefined Behavior 生成场景?

【问题讨论】:

  • 在标准中搜索所有提及未定义行为并列出潜在原因需要半小时的工作...(我已将这样的列表包含在我的一个旧的SO答案)。如果你对此很认真,你会想要一份如此详尽的清单,而不是来自 S.O. 的临时即兴收藏。读者。鉴于您的编码环境、技术和健壮性需求,您可以比我们更好地说明您认为哪些值得检查......
  • @Tony:我找不到任何重复项,尽管我明确搜索过。您可以发布指向您之前答案的链接吗?
  • 我找不到我的(可能问题出在 S.O. housekeeping),但 Greg 的链接看起来很正确。
  • @Tony, @Greg Hwegill:谢谢!投票结束这个。

标签: c++ undefined-behavior


【解决方案1】:

(1) 错误delete

T* p = new T[N];
delete p; // ...1
delete (p+x); //...2

(2) 缺少return

int* foo ()
{
  // code
  return p;
  // code
}

(3) 双重delete

(4) 临时参考

(5)vector 元素添加到自身(定义大小时)

  vector<int> vi(1);
  vi.push_back(0);
  vi.push_back(vi[0]); // it can become a use case of pt(4) (sometimes)

【讨论】:

    【解决方案2】:

    多态基类中虚拟析构函数不可用

    简单的例子

    class Base
    {
       public:
        Base();
       // some virtual functions     
       // no virtual destructor
       ~Base();
    };
    
    class Derived : public Base
    {
       public:
       Derived();
       // override the functions here      
       ~Derived();
    };
    // definitions
    int main()
    {
       Base *p = new Derived();
       // function calls
       delete p; // UB
    }
    

    Here 是 C++ 中未定义行为场景的综合列表。

    【讨论】:

    • 这不一定是UB;它可能会造成不良影响。 www2.research.att.com/~bs/bs_faq2.html#virtual-dtor
    • @iammilind : 肯定 UB。 $5.3.5/3 在第一种选择(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,静态类型应具有虚拟析构函数或行为未定义
    • @iammilind:Stroustrup 教授的表达方式很好(显然令人困惑),但他的回答与 Prasoon 引用的标准一致。
    • 是的,标准应该是正确的选择。 Bjarne 确实低估了影响。
    • 另请注意,如果您使用 shared_ptr(您可能应该这样做),即使是多态使用也不需要虚拟析构函数。
    【解决方案3】:

    比其他答案不太明显的案例:

    • 不同翻译单元中非局部静态的初始化顺序未定义。那就是:

    a.cpp

    struct SomeStatic
    {
        SomeStatic()
        {
            // Some init code
        }
        void AMethodUsingTheInitdCode()
        {
            //Blah blah
        }
    } static;
    

    b.cpp

    struct SomeStatic;
    extern SomeStatic static;
    
    struct SomeOtherStatic
    {
        SomeOtherStatic()
        {
            static.AMethodUsingTheInitdCode(); //Undefined -- SomeStatic's init code may not have run yet.
        }
    } runMe;
    

    【讨论】:

      【解决方案4】:

      只删除新的指针。例如,您不能先调用p = new int [5];,然后再调用delete p+2;,这可能会导致未定义的行为。

      在尝试使用 dll 时也只使用原始类型,因为不同的编译器会创建不同的内存布局,如果您尝试交换类或结构可能会导致一些问题。

      我能想到的另一件事是注意已删除的内存,在某些情况下,您可以在这些地方进行读写操作而不会出现任何错误或访问冲突,但这始终是未定义的行为。

      【讨论】:

      • 在这种情况下你甚至不能delete p;。你必须delete [] p; 否则它是未定义的行为。
      • @fred larson 这件事取决于编译器,在某些编译器中,delete pdelete []p 之间没有差异
      • 未定义行为的一个惊人之处在于,在某些编译器/情况下,它可能看起来定义良好。
      • @dennis:同意,但遗憾的是,即使在同一个编译器上,如果您只是在与您的 行为不端代码!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-06
      • 1970-01-01
      • 2011-02-09
      相关资源
      最近更新 更多