怪异的inet_ntoa函数---C/C++返回内部静态成员的陷阱_inet_ntoa 释放-程序员宅基地

技术标签: struct  leak  buffer  api  windows  crash  

在我们用C/C++开 发的过程中,总是有一个问题会给我们带来苦恼。这个问题就是函数内和函数外代码需要通过一块内存来交互(比如,函数返回字符串),这个问题困扰和很多开发 人员。如果你的内存是在函数内栈上分配的,那么这个内存会随着函数的返回而被弹栈释放,所以,你一定要返回一块函数外部还有效的内存。

这 是一个让无数人困扰的问题。如果你一不小心,你就很有可能在这个上面犯错误。当然目前有很多解决方法,如果你熟悉一些标准库的话,你可以看到许多各式各样 的解决方法。大体来说有下面几种:

1)在函数内部通过malloc或new在堆上分配内存,然后把这块内存返回(因为在堆上分配的内存是 全局可见的)。这样带来的问题就是潜在的内存问题。因为,如果返回出去的内存不释放,那么就是memory Leak。或者是被多次释放,从而造成程序的crash。这两个问题都相当的严重,所以这种设计方法并不推荐。(在一些Windows API中,当你调用了一些API后,你必需也要调用他的某些API来释放这块内存)

2)让用户传入一块他自己的内存地址,而在函数中把要 返回的内存放到这块内存中。这是一个目前普遍使用的方式。很多Windows API函数或是标准C函数都需要你传入一个buffer和这个buffer的长度。这种方式对我们来说应该是屡见不鲜了。这种方式的好处就是由函数外部的 程序来维护这块内存,比较简显直观。但问题就是在使用上稍许有些麻烦。不过这种方式把犯错误的机率减到了最低。

3)第三种方式显得比较另 类,他利用了static的特性,static的栈内存一旦分配,那这块内存不会随着函数的返回而释放,而且,它是全局可见的(只要你有这块内存的地 址)。所以,有一些函数使用了static的这个特性,即不用使用堆上的内存,也不需要用户传入一个buffer和其长度。从而,使用得自己的函数长得很 漂亮,也很容易使用。

这里,我想对第三个方法进行一些讨论。使用static内存这个方法看似不错,但是它有让你想象不到的陷阱。让我们 来用一个实际发生的案例来举一个例子吧。



示例

有过socket编程经验的人一定知道一个函数 叫:inet_ntoa,这个函数主要的功能是把一个数字型的IP地址转成字符串,这个函数的定义是这样的(注意它的返回值):

char *inet_ntoa(struct in_addr in);

显然,这个函数不会分配堆上的内存,而他又没有让你 传一下字符串的buffer进入,那么他一定使用“返回static char[]”这种方法。在我们继续我们的讨论之前,让我们先了解一下IP地址相关的知识,下面是inet_ntoa这个函数需要传入的参数:(也许你会 很奇怪,只有一个member的struct还要放在struct中干什么?这应该是为了程序日后的扩展性的考虑)

        struct in_addr {
                unsigned long int s_addr;
        }

对于IPV4来说,一个IP地址由 四个8位的bit组成,其放在s_addr中,高位在后,这是为了方便网络传输。如果你得到的一个s_addr的整型值是:3776385196。那么, 打开你的Windows计算器吧,看看它的二进制是什么?让我们从右到左,8位为一组(如下所示)。

11100001   00010111    00010000    10101100

再把每一组转成十进制,于是我们就得到:225   23   16   172, 于是IP地址就是 172.16.23.225。

好了,言归正传。我们有这样一个程序,想记录网络包的 源地址和目地地址,于是,我们有如下的代码:

    struct in_addr src, des;
    ........
    ........
    fprintf(fp, "源IP地址<%s>\t  目的IP地址<%s>\n", inet_ntoa(src),   inet_ntoa(des));

会发生什么样的结果呢?你会发现记录到文件中的源IP地址和目的IP地址完全一样。这是什么问题呢?于 是你开始调试你的程序,你发现src.s_addr和des.s_addr根本不一样(如下所示)。可为什么输出到文件的源和目的都是一样的?难道说是 inet_ntoa的bug?

    src.s_addr = 3776385196;    //对应于172.16.23.225
    des.s_addr = 1678184620;  //对应于172.16.7.100

原因就是inet_ntoa()“自作聪 明”地把内部的static char[]返回了,而我们的程序正是踩中了这个陷阱。让我们来分析一下fprintf代码。在我们fprintf时,编译器先计算 inet_ntoa(des),于是其返回一个字符串的地址,然后程序再去求inet_ntoa(src)表达式,又得到一个字符串的地址。这两个字符串 的地址都是inet_ntoa()中那个static char[],显然是同一个地址,而第二次求src的IP时,这个值的des的IP地址内容必将被src的IP覆盖。所以,这两个表达式的字符串内存都是 一样的了,此时,程序会调用fprintf把这两个字符串(其实是一个)输出到文件。所以,得到相同的结果也就不奇怪。

仔细看一下 inet_ntoa的man,我们可以看到这句话:The string is returned in a statically allocated buffer,  which  subsequent calls will overwrite. 证实了我们的分析。




小结

让我们大家都扪心自 问一下,我们在写程序的过程当中是否使用了这种方法?这是一个比较危险,容易出错的方法。这种陷阱让人防不胜防。想想,如果你有这样的程序:

    if ( strcmp( inet_ntoa(ip1), inet_ntoa(ip2) )==0 ) {
        .... ....
   }

本想判断一下两个IP地址是否一样,却不料掉入了那个陷阱——让这个条件表达式永真。

这个事情 告诉我们下面几个道理:

1)慎用这种方式的设计。返回函数内部的static内存有很大的陷阱。
2)如果一定要使用这种方式的 话。你就必须严肃地告诉所有使用这个函数的人,千万不要在一个表达式中多次使用这个函数。而且,还要告诉他们,不copy函数返回的内存的内容,而只是保 存返回的内存地址或是引用是没用的。不然的话,后果概不负责。
3)C/C++是很危险的世界,如果你不清楚他的话。还是回火星去吧。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/whhit_436/article/details/6934870

智能推荐

react-native 仿网易云音乐旋转唱片动画_react-native 音乐旋转唱片效果-程序员宅基地

文章浏览阅读1.4k次。一直觉得网易云音乐的播放界面很酷,现在利用react-native 动画实现这个界面.要点有两个,1图片要变园使用borderRadius=1/2height.2动画处理改变图片的旋转角度Untitled.gif使用方法,直接react-native init 一个项目,然后把这段代码复制到入口文件中下面看代码/** * Sample React Native_react-native 音乐旋转唱片效果

插电式混动汽车引非议 未来命运是沉是浮?-程序员宅基地

文章浏览阅读153次。OFweek新能源汽车网讯 在环保理念越发受到人们重视,低碳生活逐渐成为一种时尚的大背景下,新能源汽车因具备节能环保的优势开始走进万千百姓家。今年上半年,我国新能源汽车总销量较去年同期增长了135%,其中纯电动车销量增长了166%,插电混动车增长91%。据预测,到2020年全球插电式混动车销量将达135万辆,到2025年销量将达270万辆。从市场份额上..._插电混动未来怎么样

视频教程-Linux小白光速入门完整视频(CentOS7)-Linux-程序员宅基地

文章浏览阅读792次。Linux小白光速入门完整视频(CentOS7) 京东商城运维架构师,京峰L...

axios库读不到cookie_Vue axios 跨域请求无法带上cookie的解决-程序员宅基地

文章浏览阅读285次。在main.js设置// 携带cookieaxios.defaults.withCredentials = true补充知识:VUE axios请求跨域时没有带上cookie或者每次cookie都改变这两天用VUE写管理后端时,碰到一个奇葩问题:我本地使用dev配置开发的时候 请求可以带上cookie信息 打包出来部署在服务器上 请求就没带上cookie信息。然后自己慢慢排查,联合后端同事,排查..._axios获取co0kies

2. PKI - 语法[ASN.1]、规范[PKCS、X.509]、编码[DER、PEM]_pkcs 和x509 asn.1-程序员宅基地

文章浏览阅读1.4k次,点赞3次,收藏5次。PKI - RSA密钥文件的数据结构及编码ASN.1 - Abstract Syntax Notation One密钥数据结构(文件格式)PKCSPKCS#1X.509密钥编码ASN.1相关编码PEM编码密钥文件后缀参考上节我们介绍了RSA的诞生和密钥对的生成,其中生成的密钥均是以PEM编码展示和存储的,那么这些密钥文件究竟都包含了哪些内容、都是以什么格式进行存储的呢。本节将介绍ASN.1,PKCS, X.509, DER, PEM等概念以及他们之间的关系。然后对密钥文件或证书文件的后缀做一些简单的介绍。_pkcs 和x509 asn.1

matlab 木板最优切割方案,数学建模——木材分割 matlab代码实现-程序员宅基地

文章浏览阅读5.5k次,点赞4次,收藏29次。数学建模——木材分割具体方法参考: 木材最优切割数据模块function [L,W,A]=initFunc()L=2196;%木板长W=1500;%木板宽A=[477 282];%产品长与宽%A=[373 201;406 229];%A=[373 201;477 282;406 229];%A=[373 201;477 282;406 229];具体数据参考2019年5-1建模B题代码模块clea..._木板最优切割方案数学建模

随便推点

【Scala教程】Scala 简介与配置_scala 存配置信息的写法-程序员宅基地

文章浏览阅读272次。Scala 简介与配置文章目录Scala 简介与配置Scala 简介Scala 特性面向对象特性函数式编程静态类型扩展性并发性Scala 安装Java 开发环境配置Scala 下载与安装系统环境配置参考Scala 简介Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行在Java虚拟机上,并兼容现有的Ja..._scala 存配置信息的写法

酒店预订订房小程序源码系统 平台版 带完整的搭建教程_微酒店预订 源码-程序员宅基地

文章浏览阅读422次,点赞9次,收藏9次。传统的酒店预订系统已无法满足消费者的需求,因此,开发一款简单、高效、功能齐全的酒店预订小程序成为行业迫切的需求。同时,系统还支持积分兑换、会员优惠券等功能。2.完整的酒店信息展示:酒店详情页、房型展示页、价格展示页等,用户可以直观地了解酒店的各种信息。4.优惠活动推送:系统会根据用户的浏览记录和预订习惯,推送相应的优惠活动信息,提高用户的预订率。3.多种预订方式:支持在线预订、电话预订、微信预订等多种预订方式,方便用户选择。7.完整的支付体系:支持支付宝、微信支付等多种支付方式,保障用户的支付安全。_微酒店预订 源码

FS4052】12.6v2A三节锂电池充电芯片方案_fs4052f-程序员宅基地

文章浏览阅读802次。FS4052】是我公司热销的一款锂电池充电芯片。芯片输入电压DC 4-23v,待机功耗小于0.05W,可编程充电电压和充电电流,芯片的灵活应用范围被广大客户给予了极高的应用好评。我公司AH2155锂电池充电ic常用参数有:4.2v1A单节锂电充电,8.4v1.5A双节锂电充电,12.6v2A三节锂电池充电,8.4v2A两串两并四节锂电池充电等应用参数。其应用的产品有12v适配器锂电池充电方案,车载锂电池充电方案,太阳能板充电方案等其他太阳能充电应用方案。FS4052】是一个4V-23V输入电压,输出_fs4052f

Verilog十大基本功---testbench的设计 文件读取和写入操作_vivado如何写testben-程序员宅基地

文章浏览阅读2.3w次,点赞18次,收藏152次。转自:https://blog.csdn.net/times_poem/article/details/52036592需求说明:Verilog设计基础内容 :testbench的设计 读取文件 写入文件来自 :时间的诗十大基本功之 testbench1. 激励的产生对于 testbench 而言,端口应当和被测试的 module 一一对应。端口分为 input,outpu..._vivado如何写testben

Spring Boot项目系列(3)项目中统一异常处理,统一标准返回格式,统一错误返回码_fastadmin 如何将错误码统一放到一个文件中-程序员宅基地

文章浏览阅读874次。spring boot 项目 (3)----controller层,统一异常处理,统一标准返回格式,统一错误返回码 前言spring boot 项目中的MVC分层,在controller层我们需要做的是什么,统一项目中的返回格式,统一异常的统一处理,统一错误的返回码。项目中的三个统一息息相关,互相联系。项目架构 1.统一标准返回格式     项目中在controller层出现的各种返回,例:List ,Map<String,Object&_fastadmin 如何将错误码统一放到一个文件中

数理方程——达朗贝尔公式-程序员宅基地

文章浏览阅读1.3w次。数理方程——达朗贝尔公式1 行波法2 达朗贝尔公式3 求解问题1 行波法行波法:研究行进波的方法,求解无界空间的波动问题。  对于一个定解问题中偏微分方程的求解,我们可以仿照常微分方程,先求通解,然后用初始条件来求特解。2 达朗贝尔公式  对于一个定解问题:{utt=a2uxx−∞<x<∞u∣t=0=φ(x)−∞<x<∞ut∣t=0=ψ(x)−∞<x<∞\begin{cases}u_{tt}=a^2u_{xx}&-\infty < x <\_达朗贝尔公式

推荐文章

热门文章

相关标签