- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我们有几个中等大小的 C 代码库,可以接收来自具有各种经验水平的开发人员的提交。一些缺乏纪律的程序员提交 assert()
具有副作用的语句会导致禁用断言的错误。例如。
assert(function_that_should_always_be_called());
assert()
实现,但使用
NDEBUG
评估表达式定义会导致 Not Acceptable 性能下降。是否有我们可以传递的 GCC 扩展或标志来触发这些的编译时警告/错误?通过足够简单的控制流程,GCC 应该可以确定您只是在调用纯函数。
最佳答案
尽管这个问题收到了许多无用的非答案,但我认为它在遗留代码库的上下文中具有很多优点。
想象一下,多年来积累了许多断言,但是由于没有使用 NDEBUG 构建/测试的习惯,一些副作用已经渗透到断言中,现在你不敢再禁用断言了。
您可以打开 NDEBUG 并在您的测试套件中检测到一些测试失败,但是将测试失败与“有效”断言联系起来并不简单,因为它可能离您检测到失败的点很远。即使是具有良好覆盖率的测试套件也不能被认为是完整的。
您可以对代码中的所有断言进行代码审查,但这可能需要大量工作并且容易出现人为错误。如果一些静态分析已经可以消除所有可以证明没有出现副作用的断言,并且您只需要调查那些不能保证它们不存在的情况,那就更好了。
以下是如何使用编译器的优化器来进行这样的静态分析。假设您组织替换 assert
的定义宏:
extern int not_supposed_to_survive;
#define assert(expr) ((void)(not_supposed_to_survive || (expr)))
如果
expr
有任何副作用,效果的执行取决于全局变量
not_supposed_to_survive
的值.但是如果
expr
没有任何副作用,全局变量的值无关紧要(注意
expr
结果被丢弃)。
一个好的优化器知道这一点,并将消除全局变量 not_supposed_to_survive
的负载。 ,因此变量的名称。
not_supposed_to_survive
的定义,当负载没有被消除时,我们会得到一个链接错误,我们可以用它来检测一个潜在有效的断言。
int g;
int foo() { return ++g; }
int main() {
assert(foo());
return 0;
}
gcc -O2 assert_effect.c
/tmp/ccunynya.o: In function `main':
assert_effect.c:(.text.startup+0x2): undefined reference to `not_supposed_to_survive'
collect2: error: ld returned 1 exit status
编译器帮助我找到了一个可疑的断言!另一方面,如果我替换
++g
通过
g+1
,链接错误消失,我不必调查。事实上,这种说法保证是无害的。
gcc -flto
) 跨编译单元进行分析。
error
在编译时(而不是链接时)获得人类可读的错误消息。功能属性。由于这个属性只对函数起作用,对变量不起作用,我们还需要告诉 GCC 它是一个纯函数,这意味着函数本身不会有副作用。这确保了如果返回值不相关,可以安全地删除对函数的调用。
extern int not_supposed_to_survive() __attribute__((pure)) __attribute__((error("assert() cannot be proven to have no side effects")));
#define assert(expr) do { (void)(not_supposed_to_survive() || (expr)); } while(0)
更新:我使用 gcc 5.3 在现实生活中的 C++ 代码库上应用了带有全局变量的简单变体。要使用链接时优化,您基本上使用
gcc -flto -g
作为编译器/链接器(编译器/链接器上的
-g
选项,用于获取链接错误的行引用)和
gcc-ar
和
gcc-ranlib
作为任何静态库的归档器/索引器。
关于捕获带有副作用的 assert(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10593492/
这2个有什么区别?一个使用 SideEffect,另一个不使用。 “每次成功重组都会调用 SideEffect”,但如果没有 SideEffect,它也会在每次重组时运行。 @Composable f
我在 DOM 元素引用方面遇到了一些问题,我想我已经追踪到它与更新 innerHTML 有关。 在这个例子中,在第一次警告时,两个变量引用同一个元素,正如预期的那样。奇怪的是,在更新父元素(body)
如果有人问过这个问题,请原谅我,但我似乎找不到它。 我正在尝试创建一个数组并反转它(不使用反转)这段代码完美运行: function reverseArrayInPlace(array) { fo
如果 reflector 是正确的(我倾向于相信它是正确的),这就是 Any() 的实现: public static bool Any(this IEnumerable source) {
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
是否可以用 LINQ 中的 lambda 表达式替换 foreach 循环 (.Select))? List l = {1, 2, 3, 4, 5}; foreach (int i in l)
我在一本书上读到以下说法: n = ((i++) > (j)?(i++):(j)); 书上说假设i>j,n有一个意想不到的值,i增加了两次。 我不明白为什么n在这句话之后有一个期望值。 我读了很多关于
我对更改 LD_LIBRARY_PATH 有奇怪的副作用。 当我附加一个包含库的路径时,例如: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_path/lib 然后,一切都
有人能告诉我下面一行中“副作用”的含义吗? If you're calling an EL function that doesn't return anything, then you're cal
是否有为包含副作用的 Java/JVM 语言方法编写 javadoc 的标准或最佳实践? 我定义了一个 void 方法,它修改了方法参数之一,但不知道如何记录实际返回值(因为没有实际返回)。 /**
我正在学习副作用和纯函数。我知道纯函数没有副作用,对于相同的参数,它们的返回值是相同的。我想知道 C 函数 strcmp() 是否是纯函数。我相信它是纯粹的,因为给定相同的两个字符串作为参数,结果将始
我正在尝试创建佛罗里达州的点密度图。虽然我知道 Highmaps 不支持带有 map 点的颜色轴。我扩展了它并且它有效,但它带来了副作用。当我单击图例中的某一类别时,不会发生隐藏。例如,如果我单击“>
我在 CS50 中研究 PSET 4,似乎遇到了 sprintf 更改不相关变量的问题。我只给出了没有揭示我的解决方案的代码...... #include #include #include t
我已经实现了这样的 UnaryOperation struct Converter { Converter( std::size_t value ): value_( valu
使用点符号调用自定义 getter 是否有副作用? 我一直在通过点符号在 Objective-C 中使用合成的 getter,即 tree.fruitnumber 返回树中果实的数量。我必须自定义 s
我无法理解页面 https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/void 中的这一段: This ope
我有一个在 IIS7 下运行的 Web 应用程序。我将全局变量存储在一个带有静态变量的类中。该类称为 SessionVariables 并且在其中例如我有以下内容: public class Sess
运行命令时 ng-packagr -p ng-package.json 我得到以下输出 Building Angular library - - - skipped 8 lines - - - Sid
我想模拟一个 OverflowError 因为我想在引发异常之后测试变量的值。但是,我不知道如何使用我正在使用的库复制 OverflowError。我在此特定测试中使用的库是 pysolar.sola
当我尝试在可变 Map 中插入一个元素时,我希望这个元素插入到我的 Map 而不是返回 Map(如 PF,不可变对象(immutable对象) ecc ...)出于这个原因,我使用了可变集合,但在我的
我是一名优秀的程序员,十分优秀!