coreutils4.5.1 expr.c 源码分析2

今天又开始读代码。前段时间看算法分析相关的书,搜集了不少算法相关书籍,感觉自己功力太浅,还是读读源码吧。好在,读小说,养成了快速读书的好习惯,再加不求甚解,把快速+不求甚解利用到读代码上,感觉也很有意思。
今天重点翻了翻expr.c,这个源码,很有特色,首先启用debug功能。
文档中有注释,Define EVAL_TRACE to print an evaluation trace. 
我是通过读代码后,发现
#define EVAL_TRACE
可以启用注释功能,重新编译后,本地执行
sudo make
./expr 3 + 2
发现能打印出一堆东西,如图:
eval: 3 + 2
eval1: 3 + 2
eval2: 3 + 2
eval3: 3 + 2
eval4: 3 + 2
eval5: 3 + 2
eval6: 3 + 2
eval7: 3 + 2
eval4: 2
eval5: 2
eval6: 2
eval7: 2
5
------------------
这个程序很有意思,定义了一个结构体

/* The kinds of value we can have.  */
enum valtype
{
  integer,
  string
};
typedef enum valtype TYPE;

/* A value is.... */
struct valinfo
{
  TYPE type;            /* Which kind. */
  union
  {                /* The value itself. */
    intmax_t i;
    char *s;
  } u;
};
typedef struct valinfo VALUE;
你不知道,我上次读awk的源码,被那个结构体彻底弄晕了,所以这个结构体还是很清爽的。这个结构体很有意思。计算的返回值就是它。
main中调eval(),再printv,程序如下:
  args = argv + 1;

  v = eval ();
  if (!nomoreargs ())
    error (2, 0, _("syntax error"));
  printv (v);
因此,文章的重点就在eval函数上,而printv较简单,就是把结构打印出来。
我们来重点读eval
/* Handle |.  */

static VALUE *
eval (void)
{
  VALUE *l;
  VALUE *r;

#ifdef EVAL_TRACE
  trace ("eval");
#endif
  l = eval1 ();
  while (1)
    {
      if (nextarg ("|"))
    {
      r = eval1 ();
      if (null (l))
        {
          freev (l);
          l = r;
        }
      else
        freev (r);
    }
      else
    return l;
    }
}
开始我真不知道这个"|"是什么用处,因为expr命令也不熟悉。后来执行
./expr --help 
打印出来一段注释,也就是usage函数打印出来的,我一般忽略掉usage了,读了help后,才知道是
expr a \| b 
如果a=0就打印b,大概是这意思,于是,再读源码就理解了。
先调用eval1计算出左值,判断如果下一运算符是"|",再计算出右值,然后判断,如果左值是空,就取右值。最后返回值。
  while (1)
    {
      if (nextarg ("|"))
    {
      r = eval1 ();
      if (null (l))
        {
          freev (l);
          l = r;
        }
      else
        freev (r);
    }
      else
    return l;
    }
这段代码很有代表性,其中eval1,eval2,eval3,eval4....基本上都是这个套路。
不过,eval1-->eval2-->eval3--->eval4--->eval5--->eval6-->eval7--->eval1
其中--->表示函数调用的意思,出现了递归,看到没?因为eval7是处理外围的括号,于是用到了递归。如果出现
./expr \( 30 + 2 \)
结果是:
eval: (30 + 2 )
eval1: (30 + 2 )
eval2: (30 + 2 )
eval3: (30 + 2 )
eval4: (30 + 2 )
eval5: (30 + 2 )
eval6: (30 + 2 )
eval7: (30 + 2 )
eval4: 2 )
eval5: 2 )
eval6: 2 )
eval7: 2 )
./expr: non-numeric argument
开始没加转义符号,搞了一会没弄出来。
基本上把expr.c的代码过了一次。感觉很有收获。越看,越感觉这些人的写法很奇怪,
  while (1)
    {
      if (nextarg ("|"))
    {
      r = eval1 ();
      if (null (l))
        {
          freev (l);
          l = r;
        }
      else
        freev (r);
    }
      else
    return l;
    }
当时我在想,如何跳出循环呢?该死的是递归,另外,nextarg也很奇怪,上面说已经对args+1了,我想,岂不每次都往后面跑了吗?代码如下:
static int
nextarg (char *str)
{
  if (*args == NULL)
    return 0;
  else
    {
      int r = strcoll (*args, str) == 0;
      args += r;
      return r;
    }
}
你想args是字串指针数组,r为1,此时,args指下向一字串,也就是每比较一次,就向后跑一个,比如,本来应该是问同一个人吧,变成每问一次,就跳到下一位,有些烧脑子。不过,  我也不要求一次性全搞懂,知道大概,下次再看吧。


 

猜你喜欢

转载自blog.csdn.net/woshiyilitongdouzi/article/details/85074730