我正在尝试为C函数编写一个规范,该函数在另一个字符串中搜索第一个字符串(实际上是string.h的strstr函数)。
我遇到的第一个问题是我无法证明循环不变量,我认为我使用strlen的方式有问题(在__fc_string_axiomatic.h中定义了公理)
#include<string.h>
/*@
requires valid_string(s1);
requires valid_string(s2);
*/
char *strfind (const char *s1, const char *s2) {
if (*s2 == 0)
return s1;
/*@
loop invariant 0 <= s1 - \at(s1,Pre) <= strlen(\at(s1,Pre));
*/
while (*s1) {
const char *rs1 = s1;
const char *rs2 = s2;
/*@
loop invariant 0 <= rs1 - s1 <= strlen(s1);
loop invariant 0 <= rs2 - s2 <= strlen(s2);
*/
while (*rs1 && *rs2 && (*rs1 == *rs2)) {
rs1++;
rs2++;
}
if (*rs2 == 0)
return s1;
s1++;
}
return 0;
}
答案 0 :(得分:2)
你的循环不变量是正确的(我成功地证明了它们),但它们太弱了,你应该强化它们。这是我用Jessie的继任者Frama-C的WP插件证明的函数的一个版本。
/*@
requires valid_string(s1);
requires valid_string(s2);
*/
char *strfind (const char *s1, const char *s2) {
if (*s2 == 0)
return s1;
/*@
loop invariant valid_string(s1);
loop invariant strlen(\at(s1,Pre)) == (s1 - \at(s1,Pre)) + strlen(s1);
loop invariant 0 <= s1 - \at(s1,Pre) <= strlen(\at(s1,Pre));
loop assigns s1;
*/
while (*s1) {
const char *rs1 = s1;
const char *rs2 = s2;
/*@
loop invariant valid_string(rs1);
loop invariant valid_string(rs2);
loop invariant strlen(\at(s1,Pre)) == (rs1 - \at(s1,Pre)) + strlen(rs1);
loop invariant strlen(s2) == (rs2 - s2) + strlen(rs2);
loop invariant 0 <= rs1 - s1 <= strlen(s1);
loop invariant 0 <= rs2 - s2 <= strlen(s2);
loop assigns rs1, rs2;
*/
while (*rs1 && *rs2 && (*rs1 == *rs2)) {
rs1++;
rs2++;
}
if (*rs2 == 0)
return s1;
s1++;
}
return 0;
}
首先,请注意valid_string
上添加的循环不变量。如果没有它们,那么strlen(s1/rs1/rs2)
仍然是正数的证明器是不明确的,因为指针可能在字符串结束后增加了。
接下来,我添加了相关的例如\at(s1,Pre)
,s1
及其各自的长度。这些比你的不等式的正确部分更强,并用来证明所说的不等式 - 使用假设valid_string(s1)
,确保strlen(s1)>=0
。
你的不等式的左边部分是正确的循环不变量,并且最初可以证明(没有任何额外的不变量)。
请记住,对于所有K,第一个K循环不变量必须是归纳。这意味着,根据它们在迭代i
处保持的假设,您应该能够证明它们在迭代i+1
处保持不变。因此,您可能需要编写比您想要证明的更强的不变量(因为那些可能不是归纳的)。