使用strtok_r进行段错误我在这里丢失了一些东西

时间:2018-10-08 08:34:15

标签: c

这是我程序的摘录

#include <stdio.h>
#include <string.h>
void  something_wrong_really(char *str)
{
        char *savedptr = NULL;
        char *delim    = " ";

        for ( char *p = str ; ; p = NULL) {
                char *token = strtok_r(p, delim, &savedptr);
                if (token == NULL)
                        break;
                printf(" %s\n", token);
        }
}

int main(void) {
    char str[] = "Okay so lets split this and see how it works";
    something_wrong_really(str);
    return 0;
}

基于strtok_r manual

strtok_r():_POSIX_C_SOURCE            || / Glibc版本<= 2.19: / _BSD_SOURCE || _SVID_SOURCE

所以如果我将程序编译为

cc t.c -std=c99

我最终收到警告

t.c: In function 'something_wrong_really':
t.c:10:3: warning: implicit declaration of function 'strtok_r' [-Wimplicit-function-declaration]
   char *token = strtok_r(p, delim, &savedptr);
   ^
t.c:10:17: warning: initialization makes pointer from integer without a cast [enabled by default]
   char *token = strtok_r(p, delim, &savedptr);
                 ^

执行segfaults

时有什么糟糕
./a.out
Segmentation fault

对应轨迹

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a5af19 in vfprintf () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64
(gdb) bt
#0  0x00007ffff7a5af19 in vfprintf () from /lib64/libc.so.6
#1  0x00007ffff7a61339 in printf () from /lib64/libc.so.6
#2  0x00000000004005e2 in something_wrong_really (str=0x7fffffffe0a0 "Okay") at t.c:13
#3  0x0000000000400653 in main () at t.c:19
(gdb)

另一方面,启用这些标志之一似乎一切正常

 _SVID_SOURCE || _BSD_SOURCE || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

对应的输出

# cc t.c -std=c99 -D_BSD_SOURCE
#
#
# ./a.out
 Okay
 so
 lets
 split
 this
 and
 see
 how
 it
 works
#

任何暗示为何会出现这种行为?


因此,strtok_r手册中的代码段似乎是

  

glibc的功能测试宏要求(请参阅   feature_test_macros(7)):

   strtok_r(): _POSIX_C_SOURCE
       || /* Glibc versions <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE
feature_test_macros

(非常宽松地)对

进行了解释。

  

||表示为了从中获取acct(2)的声明          ,则必须进行以下宏定义之一          在包含任何头文件之前:

       #define _BSD_SOURCE
       #define _XOPEN_SOURCE        /* or any value < 500 */

   Alternatively, equivalent definitions can be included in the compila‐
   tion command:

       cc -D_BSD_SOURCE
       cc -D_XOPEN_SOURCE           # Or any value < 500

正如@Some程序员dude指出的那样,必须包含这些MACRO定义,否则将导致UB,这最终就是这里发生的事情。

1 个答案:

答案 0 :(得分:3)

如果未使用正确的启用宏(根据the manual page,则为_POSIX_C_SOURCE),则不会自动声明该功能。这意味着编译器必须推断出参数类型,更重要的是,返回类型将自动为int(如第二条警告消息中所述)。

如果参数类型或返回类型错误,则该调用将导致undefined behavior,并且很可能崩溃。

通过添加正确的宏,该函数将在头文件中正确声明,并将使用正确的参数类型和返回值。


这里的问题可能是返回类型不匹配。在64位系统上,指针(例如char *)的宽度为64位,而int的宽度通常仅为32位。大小上的这种不匹配将导致许多指针出错,而尝试使用它们是分割错误的重要来源。