C:strerror(或 inet_ntoa) 返回值默認整型截斷導致進程核心轉儲 core dumped

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章