隐藏结构体成员的方法与问题

今天主要跟大家分享一种隐藏结构体成员的方法,很多地方也叫“不完全类型”,所以这里bug菌以更加通俗易懂的方式跟大家介绍下,并且谈一谈相关的一些问题。
1
引出话题
首先我们来看下面一个最简单的例子:
参考代码:
1/************filename:
app.h*************/ 2#ifndef __app_h__ 3#define __app_h__ 4 5 6typedef struct _tag_stobj stobj; 7struct _tag_stobj 8{ 9 int member1;
10 int member2;
11};
12 13//interface
14 15int sadd(stobj *pobj); 16 17#endif 18 19/************filename: app.c*************/ 20#include “app.h” 21 22/**********************************
23 * function : sadd 24 * note :加法函数,也是接口函数 25 * author: bug菌
26 **********************************/ 27int sadd(stobj *pobj) 28{ 29 return (pobj-》member1 + pobj-》member2); 30} 31 32/************filename: main.c*************/ 33#include 《stdio.h》 34#include
“。/app/app.h” 35 36int main(void) 37{ 38 stobj obj; 39 obj.member1 = 1;
40 obj.member2 = 2; 41 42 printf(“result = %d ”,sadd(&obj)); 43 44 return 0; 45}
以上是三个文件中的内容,程序编译通过,输出结果为3。在main函数中均可以通过结构体定义变量,并且直接访问其结构体内部的成员,而很多人觉得结构体作为一个对象不应该把其内部数据全部暴露出来供开放访问,非常不利于内部实现细节的封装和对象数据的安全性。那有什么办法不允许外部访问结构体成员呢?
2
不完全类型
“不完全类型”看起来很深奥的名字,主要还是翻译问题吧,从字面上来说就是不那么完整的类型,我们知道像常规的char,int,float类型,要作为一个类型,那么平台肯定为他们提供了所占据的内存大小和处理方式,而不完全类型几乎没有在定义的时候给出,比如没有指定长度的数组array[],他也是一种不完全类型,虽然表示的是数组,可是你不知道它到底有多大,这样编译器就不能够为其分配内存而定义报错。下面修改下之前的程序,把结构体定义放到对应的app.c文件,而app.h中留下一个啥也不含的同名结构体“空壳”。
1/************filename: app.h*************/ 2#ifndef __app_h__ 3#define __app_h__ 4 5 6typedef struct _tag_stobj stobj;
7/*struct _tag_stobj 8{ 9 int member1;
10 int member2; 11};*/ 12 13//interface 14 15int sadd(stobj *pobj);
16 17#endif 18 19/************filename: app.c*************/ 20#include “app.h” 21 22struct _tag_stobj 23{ 24 int member1; 25 int member2; 26};
27/********************************** 28 * function : sadd 29 * note :加法函数,也是接口函数 30 * author:
bug菌 31 **********************************/ 32int sadd(stobj *pobj) 33{ 34 return (pobj-》member1 + pobj-》member2);
35} 36 37/************filename: main.c*************/ 38#include 《stdio.h》 39#include “。/app/app.h” 40 41int main(void) 42{ 43 stobj obj;
44 obj.member1 = 1; 45 obj.member2 = 2;
46 47 printf(“result = %d ”,sadd(&obj)); 48 49 return 0; 50}
编译结果:
此时编译器会报一个error,表示不知道该结构体到底是多大,如果你要是问app.c文件里面不是定义了结构体成员吗?怎么还会报错?你需要看一下bug菌的往期精彩,c程序的编译都是以源文件为单元展开的。
3
求助指针
把前面的main.c改改看能不能编译通过:
1/************filename: main.c*************/ 2 3#include 《stdio.h》 4#include “。/app/app.h” 5 6int main(void) 7{ 8 stobj *obj; 9 //obj.member1 = 1;
10 //obj.member2 = 2; 11 12 printf(“result = %d ”,sadd(obj)); 13 14 return 0; 15}
然而此时编译通过:
当然上面程序语法没问题,运行却是有问题的,定义了一个野指针,一旦运行基本上都会奔溃。并且不能通过指针直接访问结构体成员,因为这是一个不知道成员的结构体“空壳”,同样sizeof也检测不了大小。
那问题来了,为什么用结构体定义变量不行,而定义成指针却可以呢?其实这个问题与bug菌之前谈到的可以定义成void*指针变量,却不能定义为void变量是相同的道理,因为指针的大小一般平台和编译器确定下来就基本确定下来了,它不依赖于所指向的对象类型,同样void也是一个不完全类型。
4
隐藏结构体成员
现在遵循两个原则:1、不能直接用不完全类型定义变量,可以定义指针:2、不能够访问其结构体内部成员,因为根本不知道。
参考代码:
1/************filename: app.h*************/ 2#ifndef __app_h__ 3#define __app_h__ 4 5 6typedef struct _tag_stobj stobj;
7 8//interface 9stobj * screate(int member1,int member2); 10int sadd(stobj *pobj);
11 12 13#endif 14 15/************filename: app.c*************/ 16#include “app.h” 17 18struct _tag_stobj 19{ 20 int member1; 21 int member2; 22}; 23 24/********************************** 25 * function : screate 26 * note :创建函数,也是接口函数 27 * author: bug菌 28 **********************************/ 29stobj * screate(int member1,int member2) 30{ 31 static stobj staticobj;
32 33 staticobj.member1 = member1; 34 staticobj.member2 = member2; 35 36 return &staticobj; 37} 38 39 40/********************************** 41 * function : sadd 42 * note :加法函数,也是接口函数 43 * author: bug菌 44 **********************************/ 45int sadd(stobj *pobj) 46{ 47 return (pobj-》member1 + pobj-》member2);
48} 49 50/************filename: main.c*************/ 51 52#include 《stdio.h》 53#include “。/app/app.h” 54 55int main(void) 56{ 57 stobj *obj; 58 59 obj = screate(3,5); //内部构造结构体
60 61 printf(“result = %d ”,sadd(obj)); //调用相应的接口 62 63 return 0; 64}
编译成功,运行ok,结果如下:
那么不完全类型隐藏结构体成员的目的基本上就达到了,以后外部也是无法通过结构体变量直接访问成员了,只能对象自身在相应的.c文件中定义对应的接口,然后声明在对应的interface中供外部使用。
5
but
那么我们回过头来想想这样的不完全类型究竟做了啥?1)不允许外部访问数据细节,因为这个类型不完整,编译器把握不住~2)全程通过指针传递,本质上和void*差别不大,但是他可以进行类型的检查,这样代码的数据意义更加的明确。
当我们使用不完全结构体类型,结构体所有的成员都变成了私有,即这一种封装私有数据的方式,且均需要通过相应的接口函数访问,确实一种好的面向对象的封装方式。但是完全私有的封装还是比较麻烦,还是要做到“公私分明”,函数调用也需要一定的开销,就看工程师们怎么去平衡“性价比”了。


定华雷达知识讲堂:雷达物位计的天线到底是干什么的?
电竞手机抢占市场 华硕携手腾讯求翻身
日本氢能战略的考虑
三星Note 10的面世使得触控笔成为亮点
Zigbee无线通信模块的特点和应用场景
隐藏结构体成员的方法与问题
(TOSHIBA)东芝光耦:SOP16封装
回收宝宣布iPhone 12补贴价为4567元!
MicroLED离我们还有多远?问问三星和苹果
瑞萨电子推出业内首款25 Gbps直调激光二极管RV2X6376A系列,支持4.9G和5G LTE基站
中国家用博览会:电视打出人工智能牌,家电瞄准年轻用户
3-30V/3A直流电源电路图
芯片AI性能跑分榜单:华为海思麒麟810以23944获探花之名紧随其后
美国掀起半导体人才大战,迫使ASML拒招中国籍员工
英特尔傲腾(Optane)内存即将上市:评价电脑性能新指标出现了!
09年科技领域10大热点回顾:智能手机掀热潮
工业自动化程度不断提升!工业读码器让智能制造加速升级
博盛尚科技向市场推出一种新型的RAC高精度卫星定位接收机
分布式协作通信网络中的CoopMAC层协议研究
集成电路产业是厦门坚定不移的优先发展方向