【问题标题】:String token from `strsep` not printing (seg fault)来自`strsep`的字符串标记不打印(段错误)
【发布时间】:2018-08-29 20:46:33
【问题描述】:

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

我找到并使用了:

#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 段错误。

我完全被难住了。

【问题讨论】:

  • 现在您知道为什么有很多很多 C 程序员总是ifwhilefor 语句之后的代码周围加上大括号。以实际上有助于防止错误的方式编写代码比在将它们放入代码后找到它们更容易 - 因为如果你知道你正在编写有错误的代码,你就不会这样做。

标签: c string pointers strsep


【解决方案1】:

段错误是因为您的while 周围缺少大括号。您将继续打印“测试 1”,直到 strsep 返回 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

【讨论】:

    【解决方案2】:

    这就是问题所在:

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

    并且您认为您在循环内同时执行printfs,但实际上这 代码相当于

    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;
    }
    

    编辑

    Stephen Newell 和我似乎都没有同样的问题 代码的更正版本。 OP 提供了指向onlinegdb.com 的链接 显示更正后的版本以段错误结束。

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

    man strsep

    概要

       #include <string.h>
    
       char *strsep(char **stringp, const char *delim);
    

    glibc 的功能测试宏要求(参见 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);
    }
    

    见:

    【讨论】:

    • 谢谢,这是一个非常好的答案!我很困惑,因为这只是我的问题中的一个错字,我的代码中有大括号,而且甚至在我放入测试打印之前它就出现了段错误。我尝试通过隧道进入 uni 但它仍然存在错误,并且我把它放到家里的在线编译器中,它又出现了故障。但我看到,既然两个人的工作输出与我遇到的错误相同,那么问题一定出在其他地方!
    • @William 请仔细检查您的代码并使用-Wall 编译它。如果您看到类似 Stephen Newell 回答中发布的警告,那么肯定缺少大括号。或者你在将文件传输到你的 uni 时犯了一些错误。无论如何,未定义行为的本质是段错误可能立即发生,但也可能在稍后执行某些其他(不相关)函数时发生,这就是您在printf("Test 1") 看到段错误的原因。
    • 谢谢,我看到了一个警告,关于从不进行强制转换的 int 创建指针,但 strdup 的工作代码具有相同的功能,我认为这不是问题。代码是相同的,我在 uni 研究了一段时间,然后在 SE 上发布,我尝试了几种变体。另外,%i 等不会出现故障,所以问题似乎出在 fprinting %s 上。奇怪的是它适用于你和 Stephen Newell,但不适用于我的 uni 编译器或 onlinegdb.com。最后,它必须在我的 uni 系统上工作,所以也许我必须尝试strtok
    • 把onlinegdb.com的链接贴出来,让我看看。 %i 不会出现段错误,因为%iNULL 指针解释为int,其中%s 取消引用NULL 指针,这是未定义的行为.
    • 干杯,伙计。 onlinegdb.com/fork/r1VBLGJ5G 是我一直在使用的。我已经让它与strtok 一起工作,但这并不是什么好事,因为我需要处理空字段。我会试着想想为什么我会收到NULL,而你没有。
    猜你喜欢
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    • 2015-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多