这是我程序的摘录
#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手册中的代码段似乎是
在feature_test_macros中(非常宽松地)对glibc的功能测试宏要求(请参阅 feature_test_macros(7)):
strtok_r(): _POSIX_C_SOURCE || /* Glibc versions <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE
进行了解释。
||表示为了从中获取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,这最终就是这里发生的事情。
答案 0 :(得分:3)
如果未使用正确的启用宏(根据the manual page,则为_POSIX_C_SOURCE
),则不会自动声明该功能。这意味着编译器必须推断出参数类型,更重要的是,返回类型将自动为int
(如第二条警告消息中所述)。
如果参数类型或返回类型错误,则该调用将导致undefined behavior,并且很可能崩溃。
通过添加正确的宏,该函数将在头文件中正确声明,并将使用正确的参数类型和返回值。
这里的问题可能是返回类型不匹配。在64位系统上,指针(例如char *
)的宽度为64位,而int
的宽度通常仅为32位。大小上的这种不匹配将导致许多指针出错,而尝试使用它们是分割错误的重要来源。