技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 403|回复: 4
收起左侧

C语言自定义函数如何返回数组(上)?

[复制链接]

1

主题

3

帖子

23

积分

用户组: 初·技术宅

UID
2682
精华
0
威望
0 点
宅币
20 个
贡献
0 次
宅之契约
0 份
在线时间
0 小时
注册时间
2017-7-12
发表于 2017-8-28 16:57:49 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
最近看到一些同学问题,有提到说:如何在一个函数中返回数组呢?

能否直接在自定义 函数中,写成char *类型返回值,直接返回呢?,代码如下:
a11.jpg

直接返回str数组名(注意不需要加&,还有好多同学犯这个错)

但事实上,运行结果并非正常,我们尝试在调用函数中输出,可以看到结果并非是原来内容(当然你的电脑输出可能还不是这个样子)

如下:
a22.jpg

原因大家可以从str的属性入手,str本身是一个自定义函数中的局部变量,是一个数组有一百个字节,它的生命周期当然也随着它所在的函数一起,正所谓“一招天子一朝臣”,随着fun函数调用的结束,其中的各种局部变量也将被系统收回,理所当然的str数组这一百个字节也将被收回,所以”Hello www.dotcpp.com”这串字符串也就灰飞烟灭了!自然你在main函数里再输出肯定已经不是原来的内容了!

怎么样,可以理解吧!

不过,还没有结束,依然有同学继续问,可为什么我换成下面这种写法就可以了呢?

如下图:

a33.jpg
答曰:这种写法情况下,str虽也属于是局部变量,但不是一个数组,而是一个指针,只有四个字节,存的是在常量区的字符串” Hello www.dotcpp.com”,但请注意,这个字符串在常量区,而不属于fun函数里的部分,全程序都可读,所以return之后依旧存在,返回的是str里的值,也就是字符串“Hello www.dotcpp.com”的首地址,是一个数,其实相当于把这个字符串的地址在str手里通过返回值转交到p里。

也可以打个比方:之前只有fun函数知道这个字符串,但现在已经马上不行了,临终前,交代:“我快不行了,赶紧把‘Hello www.dotcpp.com’的藏宝地址(字符串首地址)转交到main函数里!”

然后就return 快马加鞭的返回到main函数手里了!随后消失…

而后,main函数获得之后,你们也就知道了…

这样讲,大家能理解吗?

后期C语言逆向分析部分,也会有涉及到此处的原理,大家可以再深入学习理解。

同时,下篇我们将为大家讲解如何实现自定义函数的数组传递问题!

11

主题

44

帖子

340

积分

用户组: 中·技术宅

UID
2285
精华
0
威望
28 点
宅币
240 个
贡献
0 次
宅之契约
0 份
在线时间
26 小时
注册时间
2017-2-25
发表于 2017-8-29 11:07:23 | 显示全部楼层
原来是这样啊,,那没的编译器,结果都一样吗???

34

主题

133

帖子

6948

积分

用户组: 管理员

UID
77
精华
11
威望
112 点
宅币
6407 个
贡献
129 次
宅之契约
0 份
在线时间
91 小时
注册时间
2014-2-22
发表于 2017-8-29 21:58:02 | 显示全部楼层
我在工作中写的代码,经常需要返回结构体数组。我的套路是,弄一个OUT参数,返回一个申请的内存。
  1. //函数:
  2. long foo(struct XXXX **CallerFree)
  3. {
  4.     *CallerFree = malloc(1234);
  5.     return 1234;
  6. }
  7. //调用:
  8. struct XXXX *ps = NULL;
  9. long i,c = foo(&ps);
  10. if(c)
  11. {
  12.     for(i=0;i<c;i++){use((*ps)[i].XXXX);}
  13.     free(*ps);
  14. }
复制代码
DEMO代码比较凌乱,可能有误,但大概就是这么个意思。

1007

主题

2232

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
199
威望
263 点
宅币
16684 个
贡献
33348 次
宅之契约
0 份
在线时间
1595 小时
注册时间
2014-1-26
发表于 2017-8-30 05:19:46 | 显示全部楼层
所谓返回数组,其实是想生成一堆数据然后把它返回给调用者吧?这里有两种情况,一种是你静态分配内存存储你的数组(所谓静态分配内存,指的是你的数据由编译器在你的可执行文件的数据段里面分配。典型情况是使用全局数组变量,或者使用static修饰符定义局部的数组变量),另一种是动态分配内存存储你的数组(也就是用malloc等函数从内存里分配一块区域用于存储你的数组,然后你把数据写入到这片内存里)。

直接从栈上声明变量数组然后返回地址的方法是不行的。因为非静态变量的生命周期在你的函数返回后结束,你返回的地址实际上是一块不可用的内存区域。

想要把一定数量的数据返回给调用者方法很多,除了用return返回数组的地址以外,还可以在参数里提供一个变量用于指定接收数组地址的变量的地址

此外,一般情况下你也需要把数组的长度,或者数组的元素个数,也返回给调用者。除非你的数组是字符数组,也就是字符串。这种情况下你可以使用'\0'字符作为字符串结尾,让调用者自己去统计字符个数。

动态分配的内存返回给调用者之后,调用者还需要在用完后,将这块内存交还给内存管理器,通常用malloc或者realloc分配的内存,用free来释放。而静态分配的内存无需释放,或者说,你不能将其释放,因为它已经融入到你的可执行文件里面去了。此时调用free去释放一个静态分配的内存,你会把你的程序搞炸。而如果对于动态分配的内存,你不去释放的话,那你每次调用函数返回一个数组,它就要多占用一块内存区域,时间长了,你的程序就会吃光你的所有内存,这不仅会让你的程序崩溃,也会严重影响你的系统的运行。所以必须严格区分这两种情况。

如果你需要写一个组件给别的开发者用(或者给自己用),你需要提示使用者,这个需不需要释放,以及怎么去释放。此时建议将分配和释放的函数写成配套的,以便于用户翻阅你的文本的时候,能第一时间注意到某个函数有配套的销毁函数可用。典型例子,CreateWindowEx对应的函数是DestroyWindow

建议学习C语言的同时也要了解可执行文件的结构,包括代码段、数据段、PE头等。PE头、ELF头等结构你可以不用详细掌握,但你需要知道这个概念的存在。并且你需要知道你定义的变量在什么情况下会被分配到栈上,然后在什么情况下会被分配到数据段上。

0

主题

72

帖子

166

积分

用户组: 小·技术宅

UID
1291
精华
0
威望
2 点
宅币
89 个
贡献
1 次
宅之契约
0 份
在线时间
2 小时
注册时间
2015-11-25
发表于 2018-9-16 17:37:31 | 显示全部楼层
回复

使用道具 举报

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-11-20 05:54 , Processed in 0.092773 second(s), 16 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表