C語言隱式函數聲明帶來的錯誤實例(當隱式聲明遇到printf)

關於C語言隱式函數聲明的基本問題,請參見我的博文萬惡之源:C語言中的隱式函數聲明。 下面是最近遇到的一個實例之一。

    client_sock = accept(server_sock, (struct sockaddr*)&client_name, &client_name_len);
    printf("from %s:%d\n", inet_ntoa(client_name.sin_addr), client_name.sin_port);

1
2
上述代碼段摘自一個網絡偵聽程序,功能就是打印出客戶端的IP地址和端口號。出現的症狀是一運行就報段錯誤(segment Fault)。

網上有很多文章提到了解決方式,但是卻沒有分析其問題產生的原因。這裏我們將徹底分析其原因,並給出解決此類問題的終極方法。

1 原因分析
inet_ntoa()這個函數的調用出了問題,是萬惡之源。在萬惡之源:C語言中的隱式函數聲明中我們說過,C語言編譯器對於沒有聲明原型的函數,通通作爲返回類型爲整數的函數來處理,參數的類型則由調用的實參自動提升後確定。例如:

non_exist_function(12, ‘c’);
1
在編譯時,這個沒有事先聲明的函數將被當作如下形式:

int non_exist_function(int,int);
1
注意,’c’(char)被提升爲了int。

現在回到我們的代碼上來。

inet_ntoa(client_name.sin_addr), client_name.sin_port)
1
將會被當作:

int inet_ntoa(addr_in, unsigned short);
1
然而實際上真正的inet_ntoa的原型定義在了

extern char *inet_ntoa (struct in_addr __in);
1
這樣返回值本來是指針類型,卻被截斷成了int類型。對於32位系統來說,由於指針類型和int類型的大小都是32位,恰好不會出現截斷的情況。這也是爲什麼上述代碼在32位系統下編譯運行不會出現問題的原因。

然而到了64位系統下,char*是64位,int仍然是32位,就出現了截斷問題。

然而由於printf(char*, …)是個變參函數,所以調用它時,編譯器不會檢查可變參數的數據類型,而是按照實參類型進行準備參數入棧。相當於

printf(“from %s:%d\n”, 123, 8080);
1
這樣指示符%s對應的第一個參數的類型卻是int,從而導致printf()內部在通過va_arg()提取參數時產生錯誤,最終導致了段錯誤。

如果把上述代碼改寫爲:

client_sock = accept(server_sock, (struct sockaddr*)&client_name, &client_name_len);
char* s = inet_ntoa(client_name.sin_addr);
printf(“from %s:%d\n”, s, client_name.sin_port);
1
2
3
編譯器就會給出警告信息:

warning: initialization makes pointer from integer without a cast [enabled by default]
1
這樣程序員就容易發現存在的隱式函數聲明。
然而我們的實際代碼確實非常簡潔的一行代碼,導致編譯器不會給出警告。

  • 隱式函數聲明+printf()將會導致非常隱蔽的錯誤!*

2 終極解決方案
GCC有個開關名爲: -Wimplicit-function-declaration。只要把這個開關打開就會對所有的隱式聲明函數的調用發出警告。

[smstong@cf-19 ~]$ gcc -Wimplicit-function-declaration 1.c
1.c: In function ‘main’:
1.c:61:3: warning: implicit declaration of function ‘inet_ntoa’ [-Wimplicit-function-declaration]
printf(“from %s:%d\n”, inet_ntoa(client_name.sin_addr), client_name.sin_port);
1
2
3
4
這種警告比錯誤還嚴重!代碼一定要徹底清除這種警告。

知道了原因,解決方法異常簡單,只要把包含函數原型聲明的頭文件包含進來就可以了。

#include <arpa/inet.h>
————————————————
版權聲明:本文爲CSDN博主「smstong」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/smstong/article/details/50767380

分割線----------------------------------------------------------------------------------------------------

以上是我轉載的大佬的,下面就說一說我自己遇到的情況:

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