在同一表达式内表达中使用副作用的结果是否安全?

时间:2016-03-01 17:55:24

标签: c++ c language-lawyer

请考虑以下代码:

int f(int a, int b, int *c) {
    *c = a + b;
    return *c > 0;
}

void check(int a, int b) {
    int c;
    if(f(a, b, &c) && c < 10) {
        puts("sum is in range [1, 9]");
    }
}

如果评估&&的第二部分,c是否保证保留函数调用f(a, b, &c)赋予它的值?此行为是否从C更改为C ++?

5 个答案:

答案 0 :(得分:7)

如果出现控制评估顺序的序列点,则是安全的。

// bad: evaluation order of f() and (c < 10) is not specified by C
if(f(a, b, &c) & c < 10) {
// OK,  left half of && must occur first.
if(f(a, b, &c) && c < 10) {

使用简单的&&表达式,没有C,C ++差异。使用C / C ++评估左侧,如果左侧不是假,则评估右侧。

在C ++中,这一点上的行为更复杂,&&运算符可以重载,然后&&的两边都会被评估。

答案 1 :(得分:4)

是的,表达式将从左到右进行评估,c的值将相应更新。执行程序将进入函数,函数中执行的任何操作都将在c < 10之前执行。

此行为不应从C更改为C++

答案 2 :(得分:3)

  

c保证保留函数调用f(a, b, &c)

分配给它的值

是。这在[expr.log.and]中非常清楚地表达出来:

  

&&运算符组从左到右。操作数都在上下文中转换为bool(第4条)。   如果两个操作数分别为truetrue,则结果为false。与&不同,&&保证从左到右   评估:如果第一个操作数为false ,则不评估第二个操作数。

     

结果是bool如果计算第二个表达式,则每个值计算和副作用都相关联   第一个表达式在每个值计算和与之相关的副作用之前进行排序   第二个表达。

答案 3 :(得分:0)

if(f(a, b, &c) && c < 10)运算符强制从左到右评估并引入一个序列点(函数调用本身也是如此);在决定是否需要对RHS进行评估之前,对LHS进行全面评估(并应用所有副作用)。

因此,c < 10将按预期工作;如果对c进行评估,则会使用函数f将值写入if ( f( a, b, &c ) ) { if ( c < 10 ) { // do stuff } }

话虽如此,这并不是很好的风格。仅仅因为C和C ++允许某些结构并不意味着使用它们是个好主意。任何人都需要维护您的代码,可能会感谢您将其编写为

COPY temp_table FROM loread(lo_open((SELECT csv_file_oid FROM files_table WHERE liberacao_base_id = 104), 262144), 1000000) WITH DELIMITER ; CSV HEADER;

代替。

是的,页面上需要更多空间,但逻辑更明显。

答案 4 :(得分:-1)

恕我直言,你展示的代码根本不行。让我稍微改变你的例子中的函数:

int f(int a, int b, int& c) {
    c = a + b;
    return c > 0;
}

现在假设您只获得以下代码:

void check(int a, int b) {
    int a = 3;
    int b = 10;
    int c = 7;
    std::cout << c << std::endl;
    if(f(a, b, c) || c < 10) {
        std::cout << "moo" << std::endl;
    }
}

期望它打印什么? 当然,一旦你知道f,就没有问题,而如果你写了

    /*...*/
    int result = f(a,b,c);
    std::cout << c << std::endl;
    if(result || c < 10) {
        std::cout << "moo" << std::endl;
    }

即使您不了解f,代码也更容易阅读(和调试)。