字符串标记符在C中的奇怪行为

时间:2012-04-22 08:21:07

标签: c string pointers strtok

我编写了以下程序来解析多个目录名的路径

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *
tokenizer(char *path, char **name){
  char s[300];
  char *buffer;
  memcpy(s, path, strlen(path)+1);
  printf("%s\n",s);    // PROBLEM
  int i=0;
  while(s[i] == '/'){
    i++;
  }
  if (i == strlen(path)){
    return NULL;
  }
  *name = strtok_r(s, "/", &buffer);
  return buffer;
}

int main(void){
  char str[300];
  char *token, *p;
  scanf("%s",str);
  p = tokenizer(str, &token);
  if (p != NULL)
    printf("%s\n",token);
  else
    printf("Nothing left\n");
  while((p=tokenizer(p, &token)) != NULL){
    printf("%s\n",token);
  }
}

上述程序的输出

Input: a/b/c
Output: a/b/c
a/b/c
a
b/c
b
c
c

如果我评论标记为问题的行

Input: a/b/c
Output: Some garbage value

有人可以解释一下这种奇怪行为的原因吗?

注意: 我已经意识到s是一个堆栈分配变量,它在函数main()中不再存在,但为什么程序在我使用printf()时有效?

5 个答案:

答案 0 :(得分:3)

您正在将指针返回到堆栈分配的字符串(buffer指向s); <{1}}返回后,s的记忆力不再有意义。

答案 1 :(得分:3)

除了geekasaur所说的内容:

strtok_r的第三个参数使用不正确,有两种方式:
1.在第一次调用之前应将其初始化为NULL 2.不应以任何方式使用它(将其返回给调用者)。它应该只传递给另一个strtok_r电话。

答案 2 :(得分:1)

你不能这样做

char s[300];
char *buffer;
...
*name = strtok_r(s, "/", &buffer);
return buffer;

此处buffer是指向s[300]位置的指针。 s[300]是在调用函数时在堆栈上分配的函数局部变量,并在函数返回时销毁。 所以你没有返回有效的指针,你不能将该指针用在函数之外。

答案 3 :(得分:0)

除了观察到您返回指向本地变量的指针之外,我认为值得注意的是您的tokenizer几乎100%没有意义。

tokenizer所做的大部分内容都会在调用/之前跳过任何前导strtok_r个字符 - 但您已经过了&#39; /&#39;作为strtok_r的分隔符,它将自动跳过任何前导分隔符字符。

相当简单的代码足以打印出没有分隔符的路径组件:

char path[] = "a/b/c";
char *pos = NULL;

char *component = strtok_r(path, "/", &pos);
while (NULL != component) { 
    printf("%s\n", component);
    component = strtok_r(NULL, "/", &pos);
}

答案 4 :(得分:0)

试试这个:

char*
token(char * path, char ** name){

    static char * obuffer = NULL;
    char * buffer = NULL, * p, * q;

    if(path == NULL) {
        buffer = realloc(buffer, strlen(obuffer) + 1);
        p = obuffer;
    } else {
        buffer = malloc(257);
        p = path;
    }

    if(!buffer) return NULL;
    q = buffer; 

    if(!p || !*p) return NULL;

    while(*p != '\0') {
          if(*p == '/') { 
            p++; /* remove the / from string. */
            break;
          }
          *q ++ = *p++;
    }

    *q ++ = '\0';
    obuffer = p;
    *name = buffer;

    return buffer;
}

int main(void)
{

    char * s = "foo/baa/hehehe/";
    char * name = NULL;
    char * t = token(s, &name);
    while(t) {
        printf("%s\n", name);
        t = token(NULL, &name);
    }

    return 0;
}

输出:

foo
baa
hehehe

但你基本上是“重新发明”strtok()功能......