C:strerror(或 inet_ntoa) 返回值默认整型截断导致进程核心转储 core dumped
测试环境:
[test1280@localhost ~]$ uname -a
Linux localhost.localdomain 2.6.32-642.el6.x86_64 #1 SMP Tue May 10 17:27:01 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
错误示例:
#include <stdio.h>
#include <errno.h>
int main()
{
printf("sizeof(int) = %lu\n", sizeof(int));
printf("sizeof(char *)= %lu\n", sizeof(char *));
errno = EAGAIN;
printf("%s\n", strerror(errno));
}
编译、链接、运行:
[test1280@localhost ~]$ gcc -o main main.c -g
[test1280@localhost ~]$ ./main
sizeof(int) = 4
sizeof(char *)= 8
Segmentation fault (core dumped)
查看 core 文件定位问题:
可见,在执行 printf 【参数是 strerror 返回的字符串地址】段错误。
正确示例:
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
printf("sizeof(int) = %lu\n", sizeof(int));
printf("sizeof(char *)= %lu\n", sizeof(char *));
errno = EAGAIN;
printf("%s\n", strerror(errno));
}
编译、链接、执行:
[test1280@localhost ~]$ gcc -o main main.c -g
[test1280@localhost ~]$ ./main
sizeof(int) = 4
sizeof(char *)= 8
Resource temporarily unavailable
对比两份代码,在于是否 #include <string.h> 头文件。
man strerror:
使用 strerror 需要包含 string.h 头文件。
为何不包含会导致段错误?
对第一份错误的示例代码重新编译链接:
[test1280@localhost ~]$ gcc -o main main.c -g -Wall
main.c: In function ‘main’:
main.c:10: warning: implicit declaration of function ‘strerror’
main.c:10: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘int’
main.c:11: warning: control reaches end of non-void function
即在使用 gcc 编译链接生成可执行文件时打印警告信息 -Wall,可见 Row 10、Row 11 的 warnings。
核心转储(段错误)原因:
由于没有包含 string.s 头文件,因此 gcc 无法知道 strerror 的原型(重点是其返回值类型),于是假定其返回值类型是 int。
printf ("%s") 期待的参数是一个 char * 类型变量,现在传入的是 int 类型变量(strerror 被 gcc 默认 int 类型返回值)。
在当前 Linux 环境下,char * 大小8字节,int 大小4字节,也就是说 strerror 返回的 char * 的8字节数据会被截断成4字节 int 值。
strerror 返回的地址已经被截断,截断后的 int 值必然是一个非法地址,通过 printf 访问非法地址,于是核心转储。
如果通过 g++ 来编译链接有不同吗?
[test1280@localhost ~]$ g++ -o main main.c -g -Wall
main.c: In function ‘int main()’:
main.c:10: error: ‘strerror’ was not declared in this scope
直接报错无法生成可执行文件,而非警告。
猜测:
1.C中无函数重载,GCC只需依据函数名称进行函数地址映射,不查看函数返回值类型,因此能正常链接动态库的 strerror 实现并执行;
2.C++中有函数重载,G++做函数地址映射比较严格,相同的函数名称也需要依据参数类型以及顺序映射到不同的重载函数实现上;
总结:
1.执行系统调用以及库函数时要保证包含相关的头文件函数原型声明,不要缺漏,即使编译链接通过;
2.使用GCC、G++时要打开警告 -Wall 开关,解决警告,可以规避潜在的问题;
3.类似的问题也存在基于 64bit 操作系统的,返回值是 char * 的各类调用,且使用 GCC 编译器编译链接的,例如 inet_ntoa。
4.调用 strerror 要有 #include <string.h>,调用 inet_ntoa 要有 #include <arpa/inet.h>。
参考链接:
1.https://blog.csdn.net/yin138/article/details/50363248
2.https://www.cnblogs.com/acool/p/4708483.html