我使用的是Frama-C版Silicon-20161101。每次在ensure子句中引用指针值*x
时,预处理器都会不必要地插入*\old(x)
。例如
// File swap.c:
/*@ requires \valid(a) && \valid(b);
@ ensures A: *a == \old(*b) ;
@ ensures B: *\at(\old(b),Post) == \old(*a) ;
@ assigns *a,*b ;
@*/
void swap(int *a,int *b)
{
int tmp = *a ;
*a = *b ;
*b = tmp ;
return ;
}
使用frama-c swap.c -print
输出
/* Generated by Frama-C */
/*@ requires \valid(a) ∧ \valid(b);
ensures A: *\old(a) ≡ \old(*b);
ensures B: *\old(b) ≡ \old(*a);
assigns *a, *b;
*/
void swap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
return;
}
有趣的是,WP插件仍然可以验证这是正确的!我假设这是因为*\old(a)
是\old(a)
的后置值(由于它没有被更改,它仍然是相同的指针)?这是一个错误吗?有没有快速的用户端修复?
答案 0 :(得分:2)
你的问题有两点。首先,*\old(a)
不应与\old(*a)
混淆:在这两种情况下,我们都会评估a
状态中的指针 Old
,但在{ {1}},我们取消引用在当前状态,而在*\old(a)
中,取消引用也在\old(*a)
状态下完成。 / p>
其次,形式参数有点特殊:如ACSL手册中所述,在函数的后置状态下,它们具有与前置状态相同的值,即使它们已被函数修改,如下所示:
Old
这里的基本原理是,由于C具有按值调用的语义,因此外部世界不能看到对形式的任何内部修改(为此目的充当局部变量)。换句话说,只要涉及到契约,无论在函数的实现中发生什么,上面示例中与/*@
requires \valid(a + 0 .. 1);
ensures *a == \old(*a) + 1;
ensures a[1] == \old(a[1]) + 1;
*/
void f(int *a) {
*a = *a + 1;
a++;
*a = *a + 1;
}
绑定的值都是传递给它的实际参数之一。请注意,在指针的情况下,这当然仅适用于指针本身的值,而不适用于它指向的内存块中存储的值。
为了使这个语义显式化,type-checker会在a
内的ensures
子句中包装所有出现的形式参数。
您可能还希望将此answer看作相关的,尽管不是完全相似的问题。