5

我使用 Frama-C 工具来生成这个程序(main.c)的依赖图。

    #include<stdio.h>
    int main()
    {
        int n,i,m,j;
        while(scanf("%d",&n)!=EOF)
        { 
            m=n;
            for(i=n-1;i>=1;i--)
            {   
                m=m*i;
                while(m%10==0)
                {
                     m=m/10;
                }
                m=m%10000;
            }  
            m=m%10;
            printf("%5d -> %d\n",n,m);
        }
       return 0;
    }

命令是:

    frama-c -pdg -dot-pdg main main.c 
    dot -Tpdf main.main.dot -o main.pdf

结果是 在此处输入图像描述 我的问题是为什么语句“m=m*i;”,“m=m%10000”不映射到节点。结果似乎不对,因为代码中有三个循环。

4

1 回答 1

6

C 程序的切片器只有在其定义的目标是保留已定义的执行并且允许切片器更改未定义的执行时才能在实践中工作。

否则,切片器将无法删除语句,例如x = *p;一旦它无法确定那p是一个有效的指针,即使它知道它不需要x,只是因为如果语句被删除,执行p是 NULL 在这一点上被改变。

Frama-C 不处理复杂的库函数,例如scanf(). 因此,它认为在n没有初始化的情况下使用了局部变量。

类型frama-c -val main.c 您应该收到如下警告:

main.c:10:[kernel] warning: accessing uninitialized left-value:
               assert \initialized(&n);
...
[value] Values for function main:
      NON TERMINATING FUNCTION

该词assert表示 Frama-C 的选项-val无法确定所有执行都已定义,而“NON TERMINATING FUNCTION”表示无法找到程序的单个已定义执行以继续。

未初始化变量的未定义使用是 PDG 删除大多数语句的原因。PDG 算法认为它可以删除它们,因为它们是在它认为未定义的行为之后出现的,即对变量的第一次访问n

我稍微修改了你的程序,scanf()用更简单的语句替换了调用:

#define EOF (-1)
int unknown_int();

int scan_unknown_int(int *p)
{
  *p = unknown_int();
  return unknown_int();
}

int main()
{
  int n,i,m,j;
  while(scan_unknown_int(&n) != EOF)
    { 
      m=n;
      for(i=n-1;i>=1;i--)
      {   
        m=m*i;
        while(m%10==0)
        {
          m=m/10;
        }
        m=m%10000;
      }  
      m=m%10;
      printf("%5d -> %d\n",n,m);
    }
  return 0;
}

我得到了下面的PDG。据我所知,它看起来很完整。如果您知道比dot接受dot格式更好的布局程序,那么这是使用它们的好机会。

复杂的PDG 请注意,最外层的条件while变为tmp != -1。图的节点是程序内部规范化表示的语句。条件tmp != -1对语句的节点具有数据依赖性tmp = unknown_int();。您可以使用 显示内部表示frama-c -print main.c,它将显示最外层循环条件已分解为:

  while (1) {
    int tmp;
    tmp = scan_unknown_int(& n);
    if (! (tmp != -1)) { break; }

除其他外,这有助于切片以仅删除可以删除的复杂语句的部分,而不必保留整个复杂语句。

于 2012-03-25T14:17:03.667 回答