【问题标题】:ACSL - Can't prove function [closed]ACSL - 无法证明功能[关闭]
【发布时间】:2026-02-16 19:25:01
【问题描述】:

我试图证明这个功能,但徒劳无功。我也在使用另一个函数,但我证明了这一点。

谁能帮帮我?

        I'm using Frama-C Sodium version with Alt-ergo 3 as prover.

  Given a not-empty string x.  An integer number p such that
    0 < p ≤|x| is meant to be "a period of x" if

      x[i] = x[i + p]

    for i = 0, 1, ... , |x| − p − 1.
    Note that, for each not-empty string, the length of the string
    is a period of itself.  In this way, every not-empty string
    has got at least one period.  So is well formed the concept of minimum
    period of string x, denoted by per(x):

      per(x) = min { p | p is period of x }.

    Write a C function

       unsigned per(const char x[], unsigned l)

    that, given a string x of length l, returns per(x). 

这是我目前编写的代码和规范:

/*@ 
    requires l > 0;
    requires p >= 0;

    behavior zero:
      assumes  p == l;
      ensures \result == 1;

    behavior one: 
      assumes l != p && (l%p) == 0;
      assumes \forall int i; 0 <= i < l-p-1 ==> x[i] == x[i+p];
      ensures \result == 1;

   behavior two:
      assumes l != p && (l%p) == 0;
      assumes !(\forall int i; 0 <= i < l-p-1 ==> x[i] == x[i+p]);
      ensures \result == 0;

   behavior three:
     assumes p != l && l%p != 0;
     ensures \result == 0;

   complete behaviors;
   disjoint behaviors;
*/

unsigned has_period(const char x[], unsigned int p, unsigned l) {
    if (p == l) return 1;
    if ((l % p) != 0) return 0;
        /*@
            loop assigns i;

            loop invariant \forall int j; 0 <= j < i ==> (x[j] == x[j+p]);
            loop invariant i <= l-p-1;
            loop invariant i >= 0;
        */

        for (int i = 0 ; i < l-p-1 ; ++i) {
            if (x[i] != x[i + p])
               return 0;
        }     

    return 1; 
}

/*
   predicate has_period(char* x, unsigned int p, unsigned l) =
      \forall int i; i < (l-p-1) ==>  x[i] == x[i+p]; 
*/

/*@
    requires l > 0;
    requires \valid(x+(0..l-1));

    ensures 1 <= \result <= l;
    ensures \forall unsigned int i; i < (l-\result-1) ==> x[i] == x[i+\result];
    ensures \forall unsigned int p; 1 <= p < \result ==> !(\forall int i; i < (l-p-1) ==> x[i] == x[i+p]);
*/

unsigned per(const char x[], unsigned l) {
     int p = 1;

    /*@
        loop assigns p;

        loop invariant 1 <= p <= l;
        loop invariant \forall unsigned j; 1 <= j < p ==> !(\forall int i; i < (l-j-1) ==> x[i] == x[i+j] || (l%p) == 0);
        loop invariant p >= 0;
    */

    while(p < l && !has_period(x,p,l)) 
        ++p;

    return p;
}

【问题讨论】:

  • 在此处发布您的代码。

标签: c frama-c acsl


【解决方案1】:

如果您告诉我们您的具体问题是什么,而不是笼统的“它不起作用”,那将会有所帮助,但这里有几点:

  • 您的合同缺少assigns 条款。在使用 WP 时,这些是可选的,尤其是对于在您的开发中调用的函数,例如 has_period。 WP 需要这些子句来了解在调用期间哪些位置可能发生了变化,并通过取补,哪些位置保持不变。
  • 您已经对什么是周期性字符串给出了一个很好的半正式定义。您应该使用它来定义 ACSL 谓词 periodic(与您的 C 函数 has_period 采用相同的参数),并根据此谓词编写规范。这将大大简化您的注释。特别是has_period不需要4个行为:如果periodic成立,它必须返回1,如果periodic不成立,它必须返回0
  • 使用-wp-rte 会导致一堆未经证实的义务。根据您的任务,您可能需要加强规范,尤其是关于x 所指位置的有效性。

【讨论】:

  • 首先,感谢 Virgile 的回答。我真的很感激。对于问题描述不佳,我深表歉意。
  • 无论如何,我还有一个问题。您对谓词“周期性”的想法非常好,但是如何使用谓词管理函数内部的两个“if”?
  • 好的维吉尔!谢谢你,我的项目终于完成了! :)