Chinaunix

标题: 这个函数里面的if() 是不是写的太罗嗦了? [打印本页]

作者: lylesong    时间: 2013-07-19 11:51
标题: 这个函数里面的if() 是不是写的太罗嗦了?
下面这个代码是在wget的源码中看到的,
1、定义了两个指针p, cp 指向buffer[]数组,一个是static char 一个是 char,这样的好处是啥子?
2、感觉定义的static char *p = buffer;在这里定义有点多余,感觉用不上;
3、if语句中,为啥写成了p + size >= buffer + sizeof (buffer),  直接用  size >= sizeof(buffer) 不是一样的么?
4、后面来了句 p += size;这个是搞啥子的啊?

发现完全不知道这个函数里面这么的意思了,请求解惑,谢谢。
  1. /* Return a string that contains S with "no-" prepended.  The string
  2.    is NUL-terminated and allocated off static storage at Wget
  3.    startup.  */

  4. static char *
  5. no_prefix (const char *s)
  6. {
  7.   static char buffer[1024];
  8.   static char *p = buffer;

  9.   char *cp = p;
  10.   int size = 3 + strlen (s) + 1;  /* "no-STRING\0" */
  11.   if (p + size >= buffer + sizeof (buffer))
  12.     abort ();

  13.   cp[0] = 'n', cp[1] = 'o', cp[2] = '-';
  14.   strcpy (cp + 3, s);
  15.   p += size;
  16.   return cp;
  17. }
复制代码

作者: sxcong    时间: 2013-07-19 13:21
p比较的是指针,你改了之后是比较长度。
你考虑一下,no_prefix 这个函数多次调用会怎么样。
作者: pmerofc    时间: 2013-07-19 13:34
提示: 作者被禁止或删除 内容自动屏蔽
作者: idi0t    时间: 2013-07-19 13:58
p是静态的局部变量,这样写才可可重入,至于pm说这样的写法有问题,就不懂了
作者: asdf2110    时间: 2013-07-19 15:07
1.  static 变量只初始化一次,所以第2,3,。。。,n次进入函数时,p的指向就是 buffer + n 了
2.  参考1
3. p + size >= buffer + sizeof (buffer) 的含义是 buffer中剩余的空间是不是能存下字符串s,size >= sizeof(buffer)  这里sizeof(buffer) 是常量 1024, 所以需要根据上次的位置算一下
4. p+=size表示下次存储的起始位置
作者: pmerofc    时间: 2013-07-19 15:07
提示: 作者被禁止或删除 内容自动屏蔽
作者: lin5161678    时间: 2013-07-19 15:14
回复 6# pmerofc

越界是未定义行为 但是
要改的话 不能直接 size >= sizeof(buffer)
size 这个数值应该是多次进入函数累计的

buffer + sizeof(buffer) - p > size
  
作者: pmerofc    时间: 2013-07-19 15:21
提示: 作者被禁止或删除 内容自动屏蔽
作者: lin5161678    时间: 2013-07-19 15:35
回复 8# pmerofc

我说得有点乱
代码第18行 p+=size;
还有就是5楼的回复

你都看看 应该能理解 size > sizeof(buffer) 如果要写这句 那么这个size 应该是累加前面进入这个函数的每一个size
   
作者: pmerofc    时间: 2013-07-19 16:07
提示: 作者被禁止或删除 内容自动屏蔽
作者: idi0t    时间: 2013-07-19 22:01
回复 6# pmerofc


    恩,学习了,那些东西在眼里只是内存地址,只要不去操作内存我都忽略了,但这可能会不小心造成潜在的许多问题,看来想问题还得深一些。
作者: yulihua49    时间: 2013-07-20 16:44
pmerofc 发表于 2013-07-19 13:34
p + size >= buffer + sizeof (buffer)

这个写法应该是有问题的

5楼有完美解释,认真看看吧。
作者: pmerofc    时间: 2013-07-20 17:08
提示: 作者被禁止或删除 内容自动屏蔽
作者: yulihua49    时间: 2013-07-20 20:11
本帖最后由 yulihua49 于 2013-07-20 20:22 编辑
pmerofc 发表于 2013-07-20 17:08
回复 12# yulihua49

看懂了,你完全是庸人自扰。
首先,p!=buffer,所以那些其它的比较式与原式不等价。
那些东西只是内存地址,只要不去操作内存都可以忽略。
因此原式无错。
buffer+sizeof(buffer),已经是原区域+1了,就是,已经刚刚越界。只要不操作它的内容就没有问题。
p+size,>=前者,就是越界。这样判定越界没有问题。
作者: pmerofc    时间: 2013-07-20 21:25
提示: 作者被禁止或删除 内容自动屏蔽
作者: buzs    时间: 2013-07-20 22:05
idi0t 发表于 2013-07-19 13:58
p是静态的局部变量,这样写才可可重入,至于pm说这样的写法有问题,就不懂了

no_prefix()函数是不可重入的,内部定义了静态变量。

满足下列条件的函数多数是不可重入的:
(1) 函数体内使用了静态的数据结构;
(2) 函数体内调用了malloc()或者free()函数;
(3) 函数体内调用了标准I/O函数;
作者: idi0t    时间: 2013-07-21 01:12
回复 16# buzs


    有局部静态变量的函数一定不可重入吗?如果你说那函数不能重入,那如果重入会造成什么问题? char *cp = p;这句应该不是多余的吧。
作者: edward_35    时间: 2013-07-22 16:11
5楼说的正确。
不过这种写法应该避免,不是线程安全的。
作者: yulihua49    时间: 2013-07-22 21:18
本帖最后由 yulihua49 于 2013-07-22 21:20 编辑
pmerofc 发表于 2013-07-20 21:25
回复 14# yulihua49

地址的内容不访问,就没有风险。
知也?不知也?
这种用法,用的多了去了,从没问题。
作者: pmerofc    时间: 2013-07-22 21:51
提示: 作者被禁止或删除 内容自动屏蔽
作者: 2005227042    时间: 2013-07-23 11:09
asdf2110 发表于 2013-07-19 15:07
1.  static 变量只初始化一次,所以第2,3,。。。,n次进入函数时,p的指向就是 buffer + n 了
2.  参考1 ...


5楼的说法正确,补充1:  static char buffer[1024];//是比malloc分配内存更理想的方式,申请释放由编译器和系统处理;也比全局变量分配方式更能限定作用域。这种应用场景在C语言环境下,还是比较理想。
              补充2: if (p + size >= buffer + sizeof (buffer))   abort ();//用于防止no_prefix(..)函数被超出次数调用,也防止strcpy内存访问出错。因为在函数注释中有"allocated off static storage at Wget startup."  ,调用函数上下文对函数调用环境做了限定。同时这个函数不是线程安全的, 当然注释中显示在wget startup使用,应该是单线程环境。
作者: djsxut    时间: 2013-07-23 11:25
按六楼说的“ 标准要求指针的加减法得到的结果必须仍指向数组元素或数组最后一个元素之后的第一个位置,否则是未定义行为”:
7 楼的用法蛮好:
buffer + sizeof(buffer) - p > size

作者: yulihua49    时间: 2013-07-23 13:39
本帖最后由 yulihua49 于 2013-07-23 17:03 编辑
pmerofc 发表于 2013-07-22 21:51
武断
你怎么知道“从没问题”?
本版就有人因为这个错误在产品升级时出现过问题

7楼那个很好,但是相对原式只是代数变换,并无实质不同。
你怎么证明原来错误?
如果原来错误,你又有什么高招避免该错误?
wget已经是实用的了,除了线程不安全,有别的错吗?


再重复一次,指针,只要不访问其内容,指向任何位置都没问题。
最简单的证明:
if(point == NULL) 。。。。最常用的了吧,一旦point==NULL,该语句有错吗?
更极端的用法:
point=(char *)-1L;
if(-1 == (long)point) {//千万别用*point
。。。。
没有问题。请放心使用。
它的潜在风险是,后边的代码如果只是进行这个判决:
if(point != NULL) {
        // 使用了*point
就出轨了。
但凡这种用法,一定会有一个使用说明:
返回值:
point==NULL,非致命错误;
point==-1,致命错误。

你说出的那个问题,一定是在某处用到那个内容才出的错。

关键是你不懂汇编,不知道C语句会怎样操作指令,哪条指令会引起异常。

最后,给你开开眼:
一段连接池代码:
  1.         if(rs->Conn.Socket<0 || rs->cli.Errno<0) {
  2.                 ret=sc_connect(pl,rs);
  3.                 if(ret) {
  4.                         ShowLog(1,"%s:scpool[%d].%d 连接%s/%s错:err=%d,%s",
  5.                                 __FUNCTION__,n,i,pl->log.HOST,pl->log.PORT,
  6.                                 rs->cli.Errno, rs->cli.ErrMsg);
  7.                         rs->TCBno=-1;
  8.                         rs->cli.Errno=-1;
  9.                         pthread_mutex_lock(&pl->mut);
  10.                         add_lnk(pl,i);
  11.                         pthread_mutex_unlock(&pl->mut);
  12.                         return (T_Connect *)-1;
  13.                 }
  14.         }

  15. 后边的:

  16. *connp=get_SC_connect(TCBno,poolno,1);
  17.         if(*connp == (T_Connect *)-1) {
  18.                 *connp=NULL;
  19.                 return -1;
  20.         }

复制代码
都是经过充分测试的代码,生产项目使用。

作者: pmerofc    时间: 2013-07-23 15:54
提示: 作者被禁止或删除 内容自动屏蔽
作者: yulihua49    时间: 2013-07-23 16:57
本帖最后由 yulihua49 于 2013-07-23 16:59 编辑
pmerofc 发表于 2013-07-23 15:54
回复 23# yulihua49

好吧,你对了。
7楼的程序没问题。
我赶紧检查了所有的程序,有类似需求,还真没有这种用法。
幸亏。
目前那些程序安全。





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2