有没有办法对 C 代码进行 syntactic/symantic 比较?
Is there a way to do a syntactic/symantic diff of C code?
NEC2 was originally written in Fortran and there have been two different ports to C from the original Fortran (xnec2c and necpp).
变量名和函数名相似(通常完全相同)。然而,作者为全局值选择了不同的数据结构。
有没有办法区分这两个实现,看看是否有任何实际差异,而不仅仅是命名约定的差异?
似乎有可能,像 Coccinelle 这样的重构工具具有一定的结构意识并理解数据类型。
如果是这样,则可以通过这种静态分析来检测一个程序或另一个程序中由作者错误引起的错误。然后一个人可以将 C 代码实现与原始 Fortran 进行比较,看看哪个是正确的,或者语法上不同的表示在计算上是否等效。请注意,这是 static 分析,我们只想知道代码结构(分支和表达式以及数据类型)是否相同。
例如,这两个示例计算相同的东西,但变量 icon1
和 ind1
的存储不同:
if( -icon1[iprx] != jx )
ind1=2;
else
{
xi= fabsl( cabj* cab[iprx]+ sabj* sab[iprx]+ salpj* salp[iprx]);
if( (xi < 0.999999) || (fabsl(bi[iprx]/b-1.) > 1.e-6) )
ind1=2;
else
ind1=0;
}
对比
if( -data.icon1[iprx] != jx )
dataj.ind1=2;
else
{
xi= fabs( dataj.cabj* data.cab[iprx]+ dataj.sabj*
data.sab[iprx]+ dataj.salpj* data.salp[iprx]);
if( (xi < 0.999999) ||
(fabs(data.bi[iprx]/dataj.b-1.0) > 1.0e-6) )
dataj.ind1=2;
else
dataj.ind1=0;
} /* if( -data.icon1[iprx] != jx ) */
这个任务似乎很难自动化。即使是经验丰富的程序员也会遇到巨大的困难,并在试图找到这两个代码体之间有意义的语义差异时感到头疼。
最明显的区别是 fabs()
与 fabsl()
的使用,即 long double
版本。但在表面之下,需要跟踪所有使用的变量和函数的定义,最重要的是数据类型。这些变量具有相同的名称,但可能定义为略有不同的类型,计算出不同的结果,通常非常接近,但有时却非常不同。
编写一个 C 程序来执行 2 个源文件的语义比较,忽略空格和注释是可行的,尽管 non-trivial。处理局部变量名称、函数名称、结构成员名称和布局的差异要困难得多,但可能是可行的。处理重构,例如将 while
循环重写为 for
循环,将测试转换为三元表达式,需要更多工作但并非不可能。但是,如果类型发生变化,则需要对取值范围和精度进行全面分析以确保语义同一性,这似乎很困难。
NEC2 was originally written in Fortran and there have been two different ports to C from the original Fortran (xnec2c and necpp).
变量名和函数名相似(通常完全相同)。然而,作者为全局值选择了不同的数据结构。
有没有办法区分这两个实现,看看是否有任何实际差异,而不仅仅是命名约定的差异?
似乎有可能,像 Coccinelle 这样的重构工具具有一定的结构意识并理解数据类型。
如果是这样,则可以通过这种静态分析来检测一个程序或另一个程序中由作者错误引起的错误。然后一个人可以将 C 代码实现与原始 Fortran 进行比较,看看哪个是正确的,或者语法上不同的表示在计算上是否等效。请注意,这是 static 分析,我们只想知道代码结构(分支和表达式以及数据类型)是否相同。
例如,这两个示例计算相同的东西,但变量 icon1
和 ind1
的存储不同:
if( -icon1[iprx] != jx )
ind1=2;
else
{
xi= fabsl( cabj* cab[iprx]+ sabj* sab[iprx]+ salpj* salp[iprx]);
if( (xi < 0.999999) || (fabsl(bi[iprx]/b-1.) > 1.e-6) )
ind1=2;
else
ind1=0;
}
对比
if( -data.icon1[iprx] != jx )
dataj.ind1=2;
else
{
xi= fabs( dataj.cabj* data.cab[iprx]+ dataj.sabj*
data.sab[iprx]+ dataj.salpj* data.salp[iprx]);
if( (xi < 0.999999) ||
(fabs(data.bi[iprx]/dataj.b-1.0) > 1.0e-6) )
dataj.ind1=2;
else
dataj.ind1=0;
} /* if( -data.icon1[iprx] != jx ) */
这个任务似乎很难自动化。即使是经验丰富的程序员也会遇到巨大的困难,并在试图找到这两个代码体之间有意义的语义差异时感到头疼。
最明显的区别是 fabs()
与 fabsl()
的使用,即 long double
版本。但在表面之下,需要跟踪所有使用的变量和函数的定义,最重要的是数据类型。这些变量具有相同的名称,但可能定义为略有不同的类型,计算出不同的结果,通常非常接近,但有时却非常不同。
编写一个 C 程序来执行 2 个源文件的语义比较,忽略空格和注释是可行的,尽管 non-trivial。处理局部变量名称、函数名称、结构成员名称和布局的差异要困难得多,但可能是可行的。处理重构,例如将 while
循环重写为 for
循环,将测试转换为三元表达式,需要更多工作但并非不可能。但是,如果类型发生变化,则需要对取值范围和精度进行全面分析以确保语义同一性,这似乎很困难。