来自 `strsep` 的字符串标记未打印(段错误)

String token from `strsep` not printing (seg fault)

我正在使用一小段代码来测试较大(初学者)程序的功能,但我在显示从字符串中提取的令牌时遇到问题。

我发现并使用了:

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

int main()
{

char *string, *found;

string = strdup ("1/2/3");
printf("Original string: '%s'\n",string);

while ((found = strsep(&string,"/")) != NULL )
  printf ("%s\n",found);

return (0);
}

这很好用,一次将一个标记打印为字符串。

然后当我尝试移动到用户输入的字符串时:

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

int main()
{
  char string[13];
  char *found, *cp = string;

  fprintf(stderr, "\nEnter string: ");
  scanf("%12s",string);
  printf("Original string: '%s'\n",string);

  while((found =  strsep(&cp,"/,-")) != NULL )
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);

  return(0);
}

我在 printf("%s\n",found); 行上遇到段错误。我掌握了指针、数组和字符串的基础知识,但显然我遗漏了一些东西,希望有人告诉我它是什么!

此外 - 如果我更改 printf("%s\n",found); 的参数,例如到 printf("%i\n",found); 我得到了一些随机性返回,但总是正确的数量,例如如果我输入 1/2/3 我得到三行垃圾,输入 1111/2222 得到两行。我尝试了 %c、%i、%d、%p,它们都做同样的事情,但是 %s 段错误。

我完全被难住了。

段错误是因为您缺少 while 周围的大括号。您将继续打印 "Test 1" 直到 strsep returns NULL,然后您尝试打印该结果(和段错误)。

有几个警告标志(可能 -Wall),gcc 在这里提供帮助:

sep.c:13:3: warning: this ‘while’ clause does not guard... [-Wmisleading-indentation]
   while((found =  strsep(&cp,"/,-")) != NULL )
   ^~~~~
sep.c:15:5: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the ‘while’
     printf("%s\n",found);
     ^~~~~~

while 周围添加大括号后,程序按预期运行:

./sep 

Enter string: abc/def
Original string: 'abc/def'
Test 1abc
Test 1def

这是问题所在:

while((found =  strsep(&cp,"/,-")) != NULL )
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);

并且您认为您在循环中同时执行了两个 printf,但实际上这 代码相当于

while((found =  strsep(&cp,"/,-")) != NULL )
{
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
}
    printf("%s\n",found);

也就是说,printf("%s\n",found); 基本上是在做 printf("%s\n",NULL); 这是未定义的行为,可能会导致段错误。

请注意,在 C 语言中缩进对编译器无关紧要。所以你需要 在代码周围使用 {}

while((found =  strsep(&cp,"/,-")) != NULL )
{
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);
}

这样做我得到

$ ./a 

Enter string: aa/bb/cc/dd
Original string: 'aa/bb/cc/dd'
Test 1aa
Test 1bb
Test 1cc
Test 1dd

另请注意,您的第一个代码正在泄漏内存,您没有释放 strdup 返回的已分配内存。您必须保存指向该指针的指针:

#include <stdio.h>
#include <stdlib.h> // for the free function
#include <string.h>

int main()
{

    char *orig = *string, *found;

    orig = string = strdup ("1/2/3");
    printf("Original string: '%s'\n",string);

    while ((found = strsep(&string,"/")) != NULL )
      printf ("%s\n",found);

    free(orig);

    return 0;
}

编辑

和我似乎都没有同样的问题 代码的更正版本。 OP 提供了 link 到 onlinegdb.com 显示更正后的版本以段错误结尾。

我在 ideone.com 上尝试了相同的代码,但我也遇到了段错误。那似乎 对我来说很奇怪,所以我打开了 strsep 的手册页,发现了这个:

man strsep

SYNOPSIS

   #include <string.h>

   char *strsep(char **stringp, const char *delim);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

strsep():

Since glibc 2.19:
    _DEFAULT_SOURCE
Glibc 2.19 and earlier:
    _BSD_SOURCE

这里是重要的部分:从 glibc 2.19 开始:_DEFAULT_SOURCE

所以如果你添加

#define _DEFAULT_SOURCE

在包含任何标准 C 头文件之前,它可以在 onlinegdb.com 上运行 和 ideone.com.

所以代码应该是:

#define _DEFAULT_SOURCE   // <-- important
#include <stdio.h>
#include <string.h>

int main()
{
  char string[13];
  char *found, *cp = string;

  fprintf(stderr, "\nEnter string: ");
  scanf("%12s",string);
  printf("Original string: '%s'\n",string);

  while((found =  strsep(&cp,"/,-")) != NULL )
    {
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);
    }

  return(0);
}

参见: