返回内部静态成员 熟练C/C++(一)

2024-01-17 13:32

本文主要是介绍返回内部静态成员 熟练C/C++(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2007年10月12日 01:25:00

看完陈皓的C/C++返回内部静态成员的陷阱,认识到自己确实对C/C++本身语法研究的不够清楚,所以这些时间就在对基本知识进行回顾,真的还蛮有意思的。

我在用C/C++函数时,从没有全面考虑过该函数功能,只是知道它能做,基本对函数细节没有了解,就拿下面这个函数做个例子:

char *inet_ntoa(struct in_addr in);

struct in_addr { unsigned long int s_addr; }

看到这个我就能想到该函数是把一个unsigned long type的数转换成一个字符串。其它什么都不想。现在让我们来仔细品读里面的东西。

我传入一个unsigned long type的数据,它给我传出一个char *,那这个char * 在函数里怎么分配空间的。首先不可能是堆分配,因为如果是那样的话,你用完这个函数后还要释放资源。其次不可能是栈分配,因为那样函数返回后栈也会跟着释放。那还有可能是全局变量,如果这样的话,C/C++中已经有好多全局了。那还有一种是static的可能,static不会随着函数的返回而释放,也就是说,它是一块长期被分配的内存空间,现在在假若我在程序中这样写:

printf(“%s, %s”, inet_ntoa(a), inet_ntoa(b)); //a, b 是两个不相等的值

输出会让我大吃一惊,输出结果一样。原因很简单,就是printf先求b,把值给了那个static,然后再求a, 把值又给了static,static的那块内存最终被写入了a的值,这个时候输出,那当然就输出的同一个值了。还有一种错误写法,如下:

Char *tmp1 = inet_ntoa(a);

Char *tmp2 = inet_ntoa(b);

这样也是有问题的,因为 tmp1 和 tmp2 都指向了一块内存,当前的 static 的值就是 b 的值了。所以总结如下,使用这种函数一定要 copy 函数返回的值,而不能去保存其内存地址!
附inet_ntoa()源码:
#include >stdio.h<
#include >stdlib.h<
#include >arpa/inet.h<
#include >bits/libc-lock.h<

/* The interface of this function is completely stupid, it requires a
static buffer. We relax this a bit in that we allow at least one
buffer for each thread. */


/* This is the key for the thread specific memory. */
static __libc_key_t key;

/* If nonzero the key allocation failed and we should better use a
static buffer than fail. */

static char local_buf[18];
static char *static_buf; //静态

/* Destructor for the thread-specific data. */
static void init (void);
static void free_key_mem (void *mem);


char *
inet_ntoa (struct in_addr in)
{
__libc_once_define (static, once);
char *buffer;
unsigned char *bytes;

/* If we have not yet initialized the buffer do it now. */
__libc_once (once, init);

if (static_buf != NULL)
buffer = static_buf;
else
{
/* We don't use the static buffer and so we have a key. Use it
to get the thread-specific buffer. */

buffer = __libc_getspecific (key);
if (buffer == NULL)
{
/* No buffer allocated so far. */
buffer = malloc (18);
if (buffer == NULL)
/* No more memory available. We use the static buffer. */
buffer = local_buf;
else
__libc_setspecific (key, buffer);
}
}

bytes = (unsigned char *) ∈
__snprintf (buffer, 18, "%d.%d.%d.%d",
bytes[0], bytes[1], bytes[2], bytes[3]);

return buffer;
}


/* Initialize buffer. */
static void
init (void)
{
if (__libc_key_create (&key, free_key_mem))
/* Creating the key failed. This means something really went
wrong. In any case use a static buffer which is better than
nothing. */

static_buf = local_buf;
}

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1821037


这篇关于返回内部静态成员 熟练C/C++(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/616209

相关文章

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性: