当C语言隐式函数声明遇到printf

最近在编程时遇到一个bug,通过gdb定位到printf函数,却怎么也找不到原因。从网上这篇博客中发现了问题所在:http://blog.csdn.net/smstong/article/details/50523120

还有一篇,是作者给出的实例,和我遇到的问题一模一样:http://blog.csdn.net/smstong/article/details/50767380

0x00 遇到的问题

在C语言网络编程时,功能是打印IP地址和端口号,如下,乍一看并没有什么错误。

printf("SRC: %s:%d\n", inet_ntoa(ipheader->ip_src), ntohs(tcpheader->th_sport));
printf("DST: %s:%d\n", inet_ntoa(ipheader->ip_dst, ntohs(tcpheader->th_dport));

可是,程序一运行,就报segment fault,通过gdb调试发现ipheader也并没有是NULL或者不正确的值,这是为什么呢?

0x01 原因分析

通过上面那篇博客链接,发现是inet_ntoa()这个函数调用出了问题。那篇博客中讲到,C语言编译器对于没有声明原型的函数,通通作为返回类型为整数的函数来处理,参数的类型则由调用的实参自动提升后确定。例如:

non_exit_function(12, 'c');

在编译时,这个没有事先声明的函数将被当作如下形式:

int non_exist_function(int, int);

这里,'c'被提升为了int类型。

所以,在我的代码中,

inet_ntoa(ipheader->ip_src);

被当作这样的函数原型处理了:

int inet_ntoa(addr_in);

然而,实际上这个函数原型应该是:

extern char* inet_ntoa(struct in_addr __in);

这样一来,返回值本来是指针类型,却被截断成了int类型。对于32位系统来说,由于指针类型和int类型长度相同,都是32位,所以恰好不会出现截断的情况,编译运行也是正常的。

然而在64位系统下,char *是64位,int仍然是32位,所以就出现了截断问题。

由于printf(char *, ...)是个变参函数,所以调用它时,编译器不会检查可变参数的数据类型,而是按照实参类型进行准备参数入栈。相当于:

printf("from %s:%d", 123, 80);

指示符%s对应的第一个参数类型却是int,从而导致printf()函数内部在通过va_arg()提取参数时产生错误,最终导致段错误。

这种段错误非常非常隐蔽,编译时又不会报错。所以出现之后并不能很容易的找出来。如果我们把代码改成这样:

char* sip = inet_ntoa(ipheader->ip_src);
printf("SRC: %s:%d\n", inet_ntoa(sip), ntohs(tcpheader->th_sport));

编译器就会给出警告信息:

warning: initialization makes pointer from integer without a cast [enabled by default]

所以说,虽然printf函数内加上函数调用,代码看起来只有一行,却埋下了编译器不会发生警告的种子。所以,将这样的语句分开写,该函数调用就函数调用,该传参就传参,是种良好的编程习惯。

0x02 解决方法

GCC有个开关选项:-Wimplicit-function-declaration。只要把这个选项加上,编译器就会对所有的隐式声明函数的调用发出警告,非常方便。

$ gcc -Wimplicit-function-declaration -o tcpsyndos tcpsyndos.c -lpcap
tcpsyndos.c: In function ‘main’:
tcpsyndos.c:96:9: warning: implicit declaration of function ‘inet_ntoa’ [-Wimplicit-function-declaration]
         printf("DST IP: %s\n", inet_ntoa(iphdr->ip_dst.s_addr));
         ^
tcpsyndos.c: In function ‘TCP_RST_send’:
tcpsyndos.c:216:5: warning: implicit declaration of function ‘close’ [-Wimplicit-function-declaration]
     close(rawsocket);
     ^

如何解决这种警告呢?将含有函数原型声明的头文件包含进来就可以了。从编译器发出的警告看来,我缺少了两种头文件(当然,本人忘记了加这两个头文件非常愚蠢0_0)。

#include <arpa/inet.h>
#include <unistd.h>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容