2013-10-17 24 views
2

比方说,我开始与功能,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()是在其他地方定义的。

在这种情况下是否可以使用某些工具组合来证明功能对等?

+11

检查它是否符合相同的asm? – user2802841

+2

@ user2802841这确实是一个选项。我们必须牢记,它很容易产生假阴性。 – Angew

+2

为了真正证明这些变体是功能上相同的(即做同样的事情),使用回归测试。 –

回答

5

你必须有点PROBL的他们在这里。假设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的定义来排除它)。

您可以使用允许未指定行为更改的“等效”定义 - 例如“as-if”规则。在优化器中,根据“as-if”规则了解代码是否等价,但通常它们通过应用一系列已知正确的转换来工作,而不是通过获取两位代码并对其进行测试。

+0

比较指向同一数组(或数组末尾)的指针会调用未定义的行为。 –

+1

@Anonymous:如果使用'std :: less ()'来比较它们,则不会。 –

+0

哦!有趣! (和upvoted) –

1

测试类的好工具是单元测试,像boost::testcppunit。测试相同的类是否以相同的方式通过测试,然后它们在功能上是等同的。显然,你必须选择适当的测试。

1

我想通过比较翻译你的2个函数后编译器产生的AST得到的最好结果。可能最简单的方法是使用clang,因为它可以轻松访问AST并根据它制作工具。

+0

就像生成的asm的比较一样,这可能会导致很多错误的否定:在这个例子中,我想这个clang会显示当地人'a'和'b'是以相反的顺序声明的。 – Virgile

+4

@ Virgile:*所有*解决方案要么产生假阴性,要么有时不能停止。否则,我可以通过编写两个简单函数并测试它们的等价性来确定哥德巴赫猜想的真实性或虚假性;-)在这种情况下,困难在于确定AST上的哪些转换在功能上等同并且与“重新格式化“过程。 –

0

如果您想要检查生成的装配方法,请按照以下步骤操作。

我假设你的两个功能被放入两个文件,a.cb.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以上,但在第二个函数中,它是相反的。

所以,如果我换ab我现在看到生成的组件实际上是相同的:

$ gcc -O0 -S b1.c 
$ diff a.s b1.s 
1c1 
< .file "a.c" 
--- 
> .file "b1.c" 
$ 

另外,如果你只关心生成的程序集是否是不同的,而不是什么生成的程序集实际上是,那么你就可以做到这一切在一个花式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 
$ 
+0

如果您对差异不感兴趣,您可以使用'cmp'而不是'diff'。 – Virgile