使用frama-c

作为作业,我决定尝试使用带有wp和rte插件的frama-c来验证quicksort的实现(取自here)。请注意,第一个最左端为0,最右端等于size-1。这是我的证明。

     /*@
    requires \valid(a);
    requires \valid(b);
    ensures *a == \old(*b);
    ensures *b == \old(*a);
    assigns *a,*b;
    */
    void swap(int *a,int *b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }

    /*@
    requires \valid(t +(leftmost..rightmost));
    requires 0 <= leftmost;
    requires 0 <= rightmost;
    decreases (rightmost - leftmost);
    assigns *(t+(leftmost..rightmost));
    */
    void quickSort(int * t,int leftmost,int rightmost)
    {
        // Base case: No need to sort arrays of length <= 1
        if (leftmost >= rightmost)
        {
            return;
        }  // Index indicating the "split" between elements smaller than pivot and 
        // elements greater than pivot
        int pivot = t[rightmost];
        int counter = leftmost;
        /*@
            loop assigns i,counter,*(t+(leftmost..rightmost));
            loop invariant 0 <= leftmost <= i <= rightmost + 1 <= INT_MAX ;
            loop invariant 0 <= leftmost <= counter <= rightmost;
            loop invariant \forall int i; leftmost <= i < counter ==> t[i] <= pivot;
            loop variant rightmost - i;
        */
        for (int i = leftmost; i <= rightmost; i++)
        {
            if (t[i] <= pivot)
            {
                /*@assert \valid(&t[counter]);*/
                /*@assert \valid(&t[i]);*/
                swap(&t[counter],&t[i]);
                counter++;
            }
        }

        // NOTE: counter is currently at one plus the pivot's index 
        // (Hence,the counter-2 when recursively sorting the left side of pivot)
        quickSort(t,leftmost,counter-2); // Recursively sort the left side of pivot
        quickSort(t,rightmost);   // Recursively sort the right side of pivot
    }

作为旁注,我知道wp不支持递归,因此在运行decreases时忽略了Frama-c -wp -wp-rte语句。

这是gui中的结果:

使用frama-c

您可以看到,即使对我来说有意义,我的循环不变式也没有得到验证。

Frama-c在不支持递归的情况下能够在假设下验证第二个递归调用。据我了解,该呼叫quickSort(t,counter-2)未得到验证,因为它可能违反先决条件requires 0 <= rightmost。不过,我不太确定那种情况下的Frama-c行为以及如何解决它。

我想要一些有关正在发生的事情的信息。我认为不验证不变量与递归无关,即使通过删除递归调用也不会对其进行验证。最后,您能否向我解释在递归调用的情况下Frama-c的行为是什么?是将它们视为其他任何函数调用,还是我不了解的行为?

谢谢

xin5232823 回答:使用frama-c

首先,与Eva不同,除了证明终止,WP没有递归函数的实际问题,后者完全正交以证明每次条件返回时,后置条件均保持(这意味着我们(对于非终止情况,无需证明任何内容):在文献中,当您还可以证明以下内容时,这称为部分正确性总正确性该功能始终终止。 decreases子句仅用于证明终止,因此,如果您希望完全正确,则不支持该事实只是一个问题。为了部分正确,一切都很好。

为了部分正确,递归调用与其他调用一样被对待:您获得了被调用方的合同,证明前提在此刻保持不变,并尝试证明调用方的后置条件,前提是通话后,被呼叫者的后置条件成立。递归调用实际上对开发人员来说更容易:由于调用者和被调用者是相同的,因此您仅需编写一份合同。

现在证明失败的证明义务:当循环不变式的“已建立”部分失败时,开始进行调查通常是一个好主意。与保留相比,这通常是一个更简单的证明义务:对于已建立的部分,您想证明注释是在您第一次遇到循环时保持的(即,这是基本情况),而对于保留,您必须证明如果在任意循环步骤的开始时假设不变为真,则在所述步骤的结尾保持不变(即,归纳情况)。特别是,您无法从前提right_most+1 <= INT_MAX中得出。即,如果您有rightmost == INT_MAX,则会遇到问题,尤其是最后一个i++会溢出。为了避免这种算术上的微妙之处,对size_t使用leftmost并将rightmost视为要考虑的最大偏移量之一可能更简单。但是,如果您要求leftmostrightmost都严格小于INT_MAX,则可以继续进行操作。

但是,这还不是全部。首先,您的边界计数器不变性太弱。您需要的是counter<=i,而不仅仅是counter<=rightmost。最后,有必要保护递归调用,以免违反leftmostrightmost的前提条件,以防万一枢轴选择不当且您的原始索引接近极限(即{ 1}}最终是counter0,因为枢轴太小,或者1因为枢轴太大,在任何情况下,只有在对应的一侧为空时才会发生)。

最后,WP(Frama-C 20.0钙,使用INT_MAX)完全证明了以下代码:

-wp -wp-rte
本文链接:https://www.f2er.com/2913370.html

大家都在问