指针结构体Word文件下载.docx
- 文档编号:15884547
- 上传时间:2022-11-16
- 格式:DOCX
- 页数:7
- 大小:19.01KB
指针结构体Word文件下载.docx
《指针结构体Word文件下载.docx》由会员分享,可在线阅读,更多相关《指针结构体Word文件下载.docx(7页珍藏版)》请在冰豆网上搜索。
规则11.4(推荐):
指向不同数据类型的指针之间不能相互转换。
思考如下程序:
uint8_t*pl;
uint32)_t*p2;
p2=(uint32_t*)pl;
/*注:
uint8_t表示8位无符号整型,uint3_t表示32位无符号整型。
*/
程序员希望将从p1单元开始的4个字节组成一个32付的整型来参与运算。
如果CPU允许各种数据对象存放在任意的存储单元,则以上转换没有问题。
但某些CPU对某种(些)数据类型加强了对齐限制,要求这些数据对象占用一定的地址空间,比如某些字节寻址的CPU会要求32位(4字节)整型存放在4的整倍数地址上。
在这个前提下.思考程序中的指针转换:
假设pl一开始指向的是0x00O3单元(对uint8_t型的整型没有对齐要求),则执行最后一行强制转换后,p2到底指向哪个单元就无法预料了。
规则11.5:
指针转换过程中不允许丢失指针的const、volatile属性。
按如下定义指针:
uIntl6一tx;
uint16_t*constcpi=&
x;
/*const指针*/
uintl6_t*const*pcpi;
/*指向const指针的指针*/
constuintl6_t**ppci;
/*指向const整型指针的指针*/
uIntl6_t**ppi;
constuint16_t*pci;
/*指向const整型的指针*/
volatikuint16_t*pvi;
/*指向volatile整型的指针*/
uintl6_t*pi;
则以下指针转换是允许的:
pl=cpi;
以下指针转换是不允许的:
pi=(umtl6_t*)pci;
pi=(uintl6_t*)pvil
ppi=(uintl6_t**)pcpi;
ppi=(uintl6_I**)ppci+
以上非法指针类型转换将会丢失const或者volatile类型。
丢失const属性,将有可能导致在对只读内容进行写操作时,编译器不会发出警告,编译器将不对具有volatile属性的变量作优化;
丢失volatile属性,编译器的优化可能导致程序员预先设计的硬件时序操作失效,这样的错误很难发现。
关于const和volatile关键字的详细作用,读者可参考ISOC获取更多信息。
1.2指针的运算
ISOC标准中,对指向数组成员的指针运算(包括算术运算、比较等)做了规范定义,除此以外的指针运算属于未定义(undefined)范围,具体实现有赖于具体编译器,其安全性无法得到保障,MISRA—C中对指针运算的合法范围做了如下限定。
规则17.1:
只有指向数组的指针才允许进行算术运算①。
规则172:
只有指向同一个数组的两个指针才允许相减②。
规则173:
只有指向同一个数组的两个指针才允许用>
,>
=,<
,<
=等关系运算符进行比较。
为了尽最大可能减少直接进行指针运算带来的隐患,尤其是程序动态运行时可能发生的数组越界等问题,MISRA—C对指针运算作了更为严格的规定。
规则174:
只允许用数组索引做指针运算。
按如下方式定义数组和指针:
uint8_ta[10];
uint8_t*p;
则*(p+5)=O是不允许的.而p[5]=O则是允许的,尽管就这段程序而言,二者等价。
以下给出一段程序,读者可参照相应程序行的注释,细细品味上述规则的含义。
voidmy_fn(uInt*_t*p1.uint8_tp2[]){
①其实此处的算术运算仅限定于指针加减某个整数.比如ppoint=point一5.ppoint++等。
0两个指针可指向不同的散组成员。
uint8_tindex=0;
uint8_t*p3
uint8_t*p4;
*pl=O;
p1++;
/*不允许,pl不是指向数组的指针*/
p1=p1+5;
/*不允许,pl不是指向数组的指针*/
pl[5]=O;
/*不允许,p1不是指向数组的指针*/
p3=&
p1[5];
p2[0]=O;
index++;
index=index+5:
p2[index]=0;
/*允许*/
*(p2+index)=O;
/*不允许*/
p4=&
p2[5];
}
1.3指针的有效性
下面介绍《MISRA—C:
2004》中关于指针有效性的规则。
规则176:
动态分配对象的地址不允许在本对象消亡后传给另外一个对象。
这条规则的实际意义是不允许将栈对象的地址传给外部作用域的对象。
请看以下这段程序:
#include″stdi0.h″
char*getm(void){
charp[]=″helloworld″;
returnp;
intmain(){
char*str=NULL;
str=getm();
printf(str);
程序员希望最后的输出结果是″helloworld″这个字符串,然而实际运行时,却出现乱码(具体内容依赖于编译环境)。
简单分析一下,由于chatp[]=″hell0world″这条语句是在栈中分配空间存储″hell0world″这个字符串,当函数getm()返回的时候,已分配的空间将会被释放(但内容并不会被销毁),而priM(str)涉及系统调用,有数据压栈,会修改从前分配给数组p[]存储空间的内容,导致程序无法得到预期的效果。
倘若将getm()函数体中的charp[]=″hell0world″程序行改成char*q=″helloworld″,则执行main()的时候可以正确输出″helloworld″,这是由于q指向的是静态数据区,而非栈中的某个单元。
所以,数组名是指针不假,但在实现细节上还是有很大的差异,程序员在使用指针的时候必须慎之又慎。
2结构体、联合体的安全规范
规则184:
不允许使用联合体。
这是一个不太近情理的规定,在具体阐述为何《MIS—RA—C:
2004》如此“痛恨”联合体之前,首先需要明确与联合体相关的细节:
①联合体的末尾有多少个填充单元?
②联合体中的各个成员如何对齐?
③多字节的数据类型高低字节如何排放顺序?
④如果包含位字段(bit—field),各位如何排放顺序?
针对细节3举个例子。
程序段2.1
typedefunion{
uilat32_tword;
uint8_tbytes[4];
}word_msg_t;
unit32_tread_nasg(void){
word_rnsg_ttmp;
tmnbvte[O]对府干tmp.word的高8位,tmpbyter[l]对应于
tmp.WOfO的次高8位,依次类推。
tmp.bytes[O]=read_byte();
tmp.bytes[1]=read_byte();
tmp.bytes[2]=read_byte();
tmp.bytes[3]=read_byte();
retlarn(trap.word);
以上代码格式在各种通信协议中使用的频率很高,接收端接收到的数据一般都以字节为单位存放,主控程序需要根据相应的协议将接收到的多个字节进行组合。
为了实现相同的功能,《MISRA-C:
2004》推荐了read_msg()函数的另外一种写法。
程序段2.2
uint32_trcad_msg(void){
uint32_tword;
Word=((unit32_t)read_byte())<
<
24;
word=word│(((unit32_t)read_byte())<
16);
word=word│(((unit32_t)read_byted_byte<
8);
word=word│(((unit32_t)read_byte());
return(word):
无论从程序的清晰程度还是执行效率来讲,程序段2.1都优于程序段2.2。
然而,程序段2.1在Intel80x86/Pentlurn体系(1ittle—endian,存储多字节整数的时候低字节存放在低地址,高字节存放在高地址)CPU中和在Mo—torola68K体系(big—endian,存储多字节整数的时候高字节存放在低地址,低字节存放在高地址)cPu中的执行结果完全不一样。
假设read_byte()函数返回的数据依次是0x01、0x02、0x03和0x04,则在Intel体系中,程序段2.1
中read_msg()函数的返回值是0x432l;
在Motorola体系中,read_msg()的返回值是0x1234。
无论在Intel体系还是Motorola体系中,程序段2.2中read_msg()的返回值都是0x1234。
以上是联合体中多字节整型字节排放顺序不定导致漏洞的一个例子。
倘若不明确联合体末尾填充的细节,或者不清楚联合体成员的对齐方式,或者不注意联合体中位字段成员的位排列次序,都有可能导致错误。
作为将安全性放在第一位的C标准,MlSRA—C禁止使用联合体并非不可理喻。
然而,联合体毕竟是C语言的一个重要元素,所以MISRA—C主张禁止使用联合体的同时,也为效率和资源要求比较苛刻的情况开了一扇门,程序员在明确联合体各个实现细节的前提下,在万不得已的时候,仍可谨慎使用联合体,在不同体系的CPU间移植程序的时候要注意做相应的修改。
此外,《MISRA—C:
2004》中也对结构体和联合体的编程风格作了限定。
规则181:
所有结构体和联合体的定义必须保证完整性。
由于涉及ISOC中类型定义完整性等概念,碍于篇幅的原因,此处就不再赘述,读者可以参阅《MISRA-C:
2004》一书和ISOC标准以了解更多信息,完善自己的编程风格。
3小结
总而言之,对于C程序中最为灵活的指针、结构体和联合体,程序员不仅仅要关注其定义和操作的一般方法,更要注重实现的细节。
由于指针、联合体等的功能性错误一般都可以逃过编译器的检查,所以稍有疏忽,就可能导致程序在运行的时候出现严重错误,程序员必须以严谨甚至苛刻的态度对待指针、结构体和联合体。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 指针 结构