假设我从一个函数foo()
开始,这个函数是正确的,但风格很差:
int foo(void)
{
// Some comment
int b;
int a;
getAandB(&a, &b);
return a+b;
}
我想重新格式化此代码块而不进行任何功能更改:
int foo(void)
{
// A more descriptive comment
int a, b;
getAandB(&a, &b);
return a + b;
}
我可以用一个工具来证明这两个块在功能上是等价的吗?让我们假设重新构建整个代码库不是一个选项,我想单独测试这个代码块。我将无法链接它,因为getAandB()
在其他地方定义。
在这种情况下,是否可以使用某些工具组合来证明功能等同?
答案 0 :(得分:5)
这里有点问题。假设getAandB
定义如下:
void getAandB(int *a, int *b) {
if std::less<int*>()(a, b) {
*a = 1;
*b = 2;
} else {
*a = 2;
*b = 3;
}
}
然后,您对函数foo
所做的更改很可能会对行为产生影响(因为更改变量声明的顺序可能会在堆栈上切换它们的位置)。
现在可以肯定的是,它改变的行为是未指定的,很可能你可以对foo
进行许多无害的改变,这些改变会改变局部变量在堆栈上的布局,从而改变在这种特殊的getAandB
的情况下的行为。但是你可能用来测试等价的任何工具都不会知道你是否关心这种可能性(尽管有getAandB
的定义它可能会把它排除在外。)
您可以使用&#34;等效&#34;的定义允许更改未指定的行为 - 例如,&#34; as-if&#34;规则。需要付出很多努力来了解代码是否等同于&#34; as-if&#34;规则,在优化器中,但通常它们通过应用一系列已知正常的转换来工作,而不是通过采用两位代码并对其进行测试。
答案 1 :(得分:1)
Uncrustify,一个开源项目可能对C语言很有用。
另一个经常使用的是AStyle
答案 2 :(得分:1)
测试类的好工具是单元测试,例如boost::test或cppunit。测试相同的类是否以相同的方式通过测试,然后它们在功能上是等效的。显然,你必须选择适当的测试。
答案 3 :(得分:1)
答案 4 :(得分:0)
如果您想检查生成的装配方法,请按照以下步骤进行操作。
我假设您的两个函数分为两个文件a.c
和b.c
:
$ gcc -O0 -S a.c
$ gcc -O0 -S b.c
$ diff a.s b.s
1c1
< .file "a.c"
---
> .file "b.c"
13,14c13,14
< leaq -4(%rbp), %rsi
< leaq -8(%rbp), %rdi
---
> leaq -8(%rbp), %rsi
> leaq -4(%rbp), %rdi
17,18c17,18
< movl -8(%rbp), %edx
< movl -4(%rbp), %eax
---
> movl -4(%rbp), %edx
> movl -8(%rbp), %eax
$
我们转向任何优化(-O0)以防止任何工件优化。
令人惊讶的是,这两个函数的gcc(4.1.2)输出略有不同。但仔细观察一下我们可以看到原因 - 在第一个函数中,b
的声明在a
之前,因此b
在堆栈上方a
以上,但在第二个功能中,它是相反的。
所以,如果我交换a
和b
,我现在看到生成的程序集实际上是相同的:
$ gcc -O0 -S b1.c
$ diff a.s b1.s
1c1
< .file "a.c"
---
> .file "b1.c"
$
或者,如果您只关心生成的程序集是否不同,而不是生成的程序集实际上是什么,那么您可以在一个fancy-schmancy bash命令中执行此操作:
$ diff --brief <(cat a.c | gcc -O0 -S -xc - -o-) <(cat b.c | gcc -O0 -S -xc - -o-)
Files /dev/fd/63 and /dev/fd/62 differ
$ echo $?
1
$ diff --brief <(cat a.c | gcc -O0 -S -xc - -o-) <(cat b1.c | gcc -O0 -S -xc - -o-)
$ echo $?
0
$