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

如何捕捉函数参数初始化中未定义的行为

孙朗
2023-03-14

下面的代码在clang中可以工作,但是在g中会崩溃

#include<vector>
#include<iostream>

template<class Iterator>
double abs_sum(double current_sum, Iterator it, Iterator it_end){
    if (it == it_end)
        return current_sum;
    return abs_sum(current_sum+std::abs(*it),++it,it_end);
}


int main(int argc, char** argv){
    std::vector<double> values {1.0, 2.0,-5};

    std::cout << abs_sum(0.0,values.begin(),values.end()) << std::endl;;
}

罪魁祸首原来是这句台词:

return abs_sum(current_sum+std::abs(*it),++it,it_end);

在clang中,*itit之前评估,在g中相反,导致迭代器在被取消引用之前被增加。事实证明,评估函数参数的顺序是由实现定义的。

我的问题是:我如何捕捉这种类型的错误?理想情况下,当我意外地依赖于特定的实现细节时,我希望有一个错误或至少一个警告。

即使使用-Wall,clang和gcc也不会产生任何警告。

共有3个答案

颛孙炜
2023-03-14

您最初拥有的不是未定义的行为,而是未指定的行为。编译器不需要对未指定的行为发出任何诊断。

几乎所有C运算符的操作数的求值顺序(包括函数调用表达式中函数参数的求值顺序以及任何表达式中子表达式的求值顺序)都是未指定的。编译器可以以任何顺序计算操作数,当再次计算同一表达式时,可以选择另一种顺序。

但在这种情况下,这种未指定行为的结果会导致对末端迭代器的解引用,从而导致未定义的行为。

GCC和Clang没有任何通用编译器选项来为未指定的行为发出诊断。

在通用条款中,有一个选项<;代码>;fstrong eval order<;代码>;>;它执行以下操作:

按从左到右的顺序评估成员访问、数组下标和 shift 表达式,并按从右到左的顺序评估赋值,如 C 17 所采用的那样。默认情况下启用 -std=c 17-fstrong-eval-order=some 仅启用成员访问和移位表达式的排序,并且是没有 -std=c 17 的默认值。

还有一个选项-Wreorder(仅限C和Objective-C)可以执行此操作:

当代码中给定的成员初始值设定项的顺序与它们必须执行的顺序不匹配时发出警告

但我认为这些选择对你的特殊情况没有帮助。

因此,在这种特定情况下,您可以按预期顺序执行操作。

宗政深
2023-03-14

不幸的是,即使使用了< code>-Wextra(记住,< code>-Wall更像是< code>-Wsome,因而也不够),对此也没有任何警告,这有点令人失望。

在一个更简单的例子中,使用一个原语,其中race*对编译器来说更明显:

void foo(int, int) {}

int main()
{
    int x = 42;
    foo(++x, x);
}

…警告您:

main.cpp: In function 'int main()':
main.cpp:6:9: warning: operation on 'x' may be undefined [-Wsequence-point]
     foo(++x, x);
         ^~~
main.cpp:6:9: warning: operation on 'x' may be undefined [-Wsequence-point]

(*不是真正的比赛,但你知道我的意思)

但是编译器很难“知道”你在迭代器上的操作分别是读和写。

最终,我担心你将不得不依靠测试、智慧和进取心

骆昊阳
2023-03-14

我的问题是:如何捕捉这种类型的错误?

你不知道。未定义的行为是未定义的。你抓不到它...

...但是一些工具可以帮助您:

    < li >您的编译器:启用所有警告(g/clang < code >-Wall-Wextra-pedantic 是一个好的开始); < li > cppcheck < li >铿锵分析器;

但他们并没有提供任何保证。这就是为什么C是硬的。你必须(你,编码员)知道得最好,不要写UB。祝你好运

 类似资料:
  • 问题内容: 在Java中,但是在其他OO语言中,初始化属性定义之间也有区别,例如 并使用构造函数对其进行初始化? 我想不出任何实际的区别,有没有?否则,即使结果相同,是否存在一种方法优于另一种方法的情况? 问题答案: 初始化顺序在这里很重要。 将字段设置为默认初始值(0,false,null) 调用对象的构造函数(但不要执行构造函数的主体) 调用超类的构造函数 使用初始化程序和初始化块初始化字段

  • 问题内容: 我正在使用Python3.4,并且尝试安装Fuzzy模块 由于提到了它仅适用于Python2,因此我尝试使用cython对其进行转换。这些是我遵循的步骤: cython Fuzzy.pyx gcc -g -02 -fpic -c Fuzzy.c -o Fuzzy.o 对double_metaphone.c做相同的操作 gcc -shared -o Fuzzy.so Fuzzy.o do

  • 问题内容: 我尝试编译,以便通过python导入它。对于建筑,我用过。 构建它之后,我尝试导入,但是发生以下错误。我怎么解决这个问题 ? 错误 fizzbuzz.c setup.py 问题答案: Python不支持,也不支持将任意C文件作为模块。您必须遵循某些约定才能让Python知道您的模块支持哪些功能。 为此,Python将寻找一个函数,模块名称在哪里。Python正在寻找但未能找到它,因此加

  • 本文向大家介绍Swift使用参数自定义初始化,包括了Swift使用参数自定义初始化的使用技巧和注意事项,需要的朋友参考一下 示例 请注意,您不能省略参数标签: 为了允许省略参数标签,请使用下划线_作为标签: 如果参数标签使用一个或多个属性共享名称,请使用self显式设置属性值:            

  • 上一节中我们给大家介绍了Fullpage的基本用法,可能很多用户有个性化的需求,没关系Fullpage提供了多个参数,我们可以配置这些参数,满足我们项目的需求。 controlArrows 默认值:true,决定是否使用控制箭头向左或向右移动幻灯片。 verticalCentered 默认值:true,决定是否初始化后,是否垂直居中网页的内容,如果你想自定义元素的位置,那么你可以设置为false,

  • 如下 ServletContext 接口方法允许 servlet 访问由应用开发人员在Web 应用中的部署描述符中指定的上下文初始化参数: getInitParameter getInitParameterNames 应用开发人员使用初始化参数来表达配置信息。代表性的例子是一个网络管理员的 e-mail 地址,或保存关键数据的系统名称。