代码之家  ›  专栏  ›  技术社区  ›  Language Lawyer

如果函数调用表达式的参数数量不正确,如果不计算该表达式,则该表达式是否具有UB?[副本]

  •  0
  • Language Lawyer  · 技术社区  · 6 年前

    考虑一下

    void swap(int* a, int* b)
    {
        if (a != b){
            *a = *a ^ *b;
            *b = *a ^ *b;
            *a = *a ^ *b;
        }   
    }
    
    int main()
    {
        int a = 0;
        int b = 1;
        swap(&a, &b); // after this b is 0 and a is 1
        return a > b ? 0 : a / b;
    }
    

    swap 是试图欺骗编译器不优化程序。

    该程序的行为是否已定义? a / b 永远无法到达,但如果是,你会得到一个除以零的结果。

    0 回复  |  直到 8 年前
        1
  •  26
  •   John Bollinger    8 年前

    无论C++的定义有多相似,都没有必要根据任何给定的代码构造或实践的有用性,也没有必要根据关于C++的任何内容来确定立场,无论是在标准中还是在另一个SO答案中。要考虑的关键是 C's definition of undefined behavior :

    行为, 使用时 指不可移植或错误的程序构造,或 对于本国际标准未规定的错误数据 要求

    (C2011,3.4.3/1;强调部分已添加)

    因此,未定义的行为是暂时触发的(“在使用”构造或数据时),而不仅仅是由存在触发的。 * 方便的是,这对于数据和程序构造产生的未定义行为是一致的;标准不必在那里保持一致。正如另一个答案所描述的那样,这种“使用时”的定义是一个很好的设计选择,因为它允许程序避免执行与错误数据相关的未定义行为。

    另一方面,如果一个程序 做 执行未定义的行为,那么根据标准的定义,程序的整个行为都是未定义的。这种随之而来的不确定性是一种更普遍的不确定性,源于这样一个事实,即与错误数据或构造直接相关的UB原则上可能包括改变程序其他部分的行为,甚至是追溯性的(或显然如此)。当然,对于可能发生的事情有额外的语言限制——所以不,鼻恶魔实际上不会出现——但这些并不一定像人们想象的那么强大。


    * 警告:一些程序构造是 习惯于 翻译时。这些会在程序翻译中产生UB,导致程序的每次执行都有完全未定义的行为。举个有点愚蠢的例子,如果你的程序源代码没有以未转义的换行符结尾,那么程序的行为是完全未定义的(参见 C2011, 5.1.1.2/1 第2点)。

        2
  •  22
  •   Eric Postpischil    8 年前

    未求值的表达式的行为与程序的行为无关。如果表达式被求值,则未定义的行为与程序的行为无关。

    如果是这样,那么这段代码将毫无用处:

    if (p != NULL)
        …; // Use pointer p.
    

    (你的XOR可能有未定义的行为,因为它们可能会产生陷阱表示。你可以通过声明一个对象是volatile来击败像这样的学术示例的优化。如果一个对象不稳定,C实现无法知道它的值是否会因外部手段而改变,因此每次使用该对象都需要实现读取它的值。)

        3
  •  0
  •   supercat    8 年前

    一般来说,如果执行将调用未定义行为的代码,如果不执行,则该代码不得有任何效果。然而,在某些情况下,现实世界的实现可能会以相反的方式运行,并拒绝生成代码,这些代码虽然没有违反约束,但不可能以定义的行为执行。

    extern struct foo z;
    
    int main(int argc, char **argv)
    {
        if (argc > 2) z;
        return 0;
    }
    

    根据我对标准的阅读,它明确地将不完整类型上的左值转换描述为调用未定义行为(除其他事项外,尚不清楚实现可以为这样的事情生成代码),因此如果 argc 为3或更多。然而,我无法确定上述代码会违反标准中的任何约束,也无法确定在以下情况下不应完全定义行为的任何原因 argc 为2或更小。尽管如此,包括gcc和clang在内的许多编译器完全拒绝上述代码。