Frama-C预处理器将\ old插入到ensure子句中的每个指针引用

时间:2017-01-12 21:16:48

标签: frama-c

我使用的是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)的后置值(由于它没有被更改,它仍然是相同的指针)?这是一个错误吗?有没有快速的用户端修复?

1 个答案:

答案 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看作相关的,尽管不是完全相似的问题。