Chinaunix首页 | 论坛 | 博客
  • 博客访问: 571929
  • 博文数量: 50
  • 博客积分: 571
  • 博客等级: 中士
  • 技术积分: 1162
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-20 14:01
个人简介

希望成为一个有思想,有信仰的程序设计师。

文章分类

全部博文(50)

文章存档

2016年(2)

2015年(2)

2014年(13)

2013年(10)

2012年(23)

分类: C/C++

2013-08-07 23:00:12

strtok()的源码:
  1. #include <string.h>


  2. static char *olds;

  3. #undef strtok

  4. /* Parse S into tokens separated by characters in DELIM.
  5.    If S is NULL, the last string strtok() was called with is
  6.    used. For example:
  7.     char s[] = "-abc-=-def";
  8.     x = strtok(s, "-");        // x = "abc"
  9.     x = strtok(NULL, "-=");        // x = "def"
  10.     x = strtok(NULL, "=");        // x = NULL
  11.         // s = "abc\0=-def\0"
  12. */
  13. char *
  14. strtok (s, delim)
  15.      char *s;
  16.      const char *delim;
  17. {
  18.   char *token;

  19.   if (s == NULL)
  20.     s = olds;

  21.   /* Scan leading delimiters. */
  22.   s += strspn (s, delim); //将指针移到第一个非delim中的字符的位置
  23.   if (*s == '\0')
  24.     {
  25.       olds = s;
  26.       return NULL;
  27.     }

  28.   /* Find the end of the token. */
  29.   token = s;
  30.   s = strpbrk (token, delim);// 获取到delimz中字符在字符串s中第一次出现的位置
  31.   if (s == NULL)
  32.     /* This token finishes the string. */
  33.     olds = __rawmemchr (token, '\0');
  34.   else
  35.     {
  36.       /* Terminate the token and make OLDS point past it. */
  37.       *s = '\0';
  38.       olds = s + 1;
  39.     }
  40.   return token;
  41. }
从上面的源码可以看出,strtok是用一个静态指针变量来保存下一次字符串分割的起始位置。当有多个线程同时调用这个函数的时候就会出现问题,这个静态的指针变量就会变的混乱。同时在同一个程序中同时有两个字符串要解析,并且同时进行解析也是会出错的。
代码很简单也就没有什么要详细解释的了,主要是要注意这是线程不安全函数,用的时候要注意。
下面介绍一下strtok_r()

点击(此处)折叠或打开

  1. #include <string.h>

  2. #undef strtok_r
  3. #undef __strtok_r

  4. #ifndef _LIBC
  5. /* Get specification. */
  6. # include "strtok_r.h"
  7. # define __strtok_r strtok_r
  8. # define __rawmemchr strchr
  9. #endif

  10. /* Parse S into tokens separated by characters in DELIM.
  11.    If S is NULL, the saved pointer in SAVE_PTR is used as
  12.    the next starting point. For example:
  13.     char s[] = "-abc-=-def";
  14.     char *sp;
  15.     x = strtok_r(s, "-", &sp);    // x = "abc", sp = "=-def"
  16.     x = strtok_r(NULL, "-=", &sp);    // x = "def", sp = NULL
  17.     x = strtok_r(NULL, "=", &sp);    // x = NULL
  18.         // s = "abc\0-def\0"
  19. */
  20. char *
  21. __strtok_r (char *s, const char *delim, char **save_ptr)
  22. {
  23.   char *token;

  24.   if (s == NULL)
  25.     s = *save_ptr;

  26.   /* Scan leading delimiters. */
  27.   s += strspn (s, delim);
  28.   if (*s == '\0')
  29.     {
  30.       *save_ptr = s;
  31.       return NULL;
  32.     }

  33.   /* Find the end of the token. */
  34.   token = s;
  35.   s = strpbrk (token, delim);
  36.   if (s == NULL)
  37.     /* This token finishes the string. */
  38.     *save_ptr = __rawmemchr (token, '\0');
  39.   else
  40.     {
  41.       /* Terminate the token and make *SAVE_PTR point past it. */
  42.       *s = '\0';
  43.       *save_ptr = s + 1;
  44.     }
  45.   return token;
  46. }
  47. #ifdef weak_alias
  48. libc_hidden_def (__strtok_r)
  49. weak_alias (__strtok_r, strtok_r)
  50. #endif

从上面的代码可以这个函数是通过save_ptr这个指针来保存下一次分割的起始地址的。这样就消除了上面的线程不安全的影响。
这两个函数主要的区别就这里。这个函数在使用的时候,一定要注意,你传入的这个save_ptr的指针,一定要是下次分割的起始地址。不可以每分割一次就申请一个指针变量。

说明:
1.这两个函数都会改变原来字符串的值。
2.这两个函数都不会造成内存泄露,因为这两个函数只是,将原来是分割符的地方用‘\0’替代了,不影响内存的释放。
3.这两个函数的参数都不可以是const char *类型,一般char *p="hello world",会被编译成const char *.


参考资料:

【1】线程安全——strtok VS strtok_r
【2】glibc 的源码 strtok.c和strtok_r.c


 

阅读(9329) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~