1.1 什么是JNIJava Native Interface(JNI),它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。
1.12 为什么用JNI![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/768029743AA040EFB4F1144EC6510302)
1 JNI扩展了Java虚拟机的能力,因为Java不能直接和硬件交互, 不能开发驱动
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C3605D0AB9B2406888F41000CF224FD6)
2 Java代码效率一般要低于C代码,而Native code效率高,因此在数学运算,实时渲染的游戏上以及音视频处理上都需要用Java调用C语言
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/0B098FBB4326438BB1631F3E072A2538)
3 复用C/C++代码,C语言经过几十年的发展,已经形成了强大的类库(比如文件压缩,人脸识别opencv,7zip,ffmpeg等),这些类库我们没必要用java语言重新实现一遍,通过JNI直接调用这些类库即可
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/32D76F2A77BE41D9A70AEFFF0D0218BC)
4 特殊的业务场景,比如电视、车载系统、微波炉等跟硬件直接相关的开发
2. C语言入门(★★★)2.1 C语言开发工具
C语言的开发工具比较多,最常用是微软的Visual Studio系列。我们教学用的是一款轻量级开发工具Dev-Cpp.exe,其gcc编译器是C99标准。该软件的安装比较简单,直接下一步,下一步即可。
安装好的图标如图,双击打开该软件,然后创建一个新源文件(默认是CPP 文件,在保存的时候文件名称改为hello.c)。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C657A41263BF410DA3132B592147B45E)
编写hello world程序
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/735D5FD5ED904C669023C01809DC5AFA)
编译源程序,源文件要想运行必须先编译成hello.exe二进制文件,然后才能运行。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/4FF55FEF07464494AD08EFC39D7DC15C)
运行程序
2.2 C语言的基本数据类型java语言的8大基本类型:
boolean 1byte 8位
byte 1byte 8位
short 2byte 16位
char 2byte 16位
int 4byte 32位
float 4byte 32位
long 8byte 64位
double 8byte 64位
C语言的基本数据类型:
在C语言里面没有boolean类型,0假 非0真
在C语言里面没有byte类型 可以用char表示byte类型。
char 1byte 8位 和java不同
short 2byte 16位 还可以表示java里面的char
int 4byte 32位 和java一致
float 4byte 32位 和java一致
long 4byte 32位和java不同
double 8byte 64位和java一致
: int、long等整型可以用signed和unsigned关键字修饰,而float、double等浮点类型则不可以。signed有符号的,是默认的,本身不会修改类型的长度unsigned无符号,第一位不是符号位,所有的数都是正数。
2.2.1 案例:通过C代码查看C语言的常用数据类型长度在C语言中查看数据类型主要靠sizeof(type)函数实现的。
执行上面代码,运行结果如下:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/A4340B94CA7643D7A8F6AAF778D68011)
:在上述代码中\n代表着换行,启动一个控制台程序后,必须将这个程序关闭才能再次运行另外一个控制台程序。long long类型是长长整型,64位,这个类型一般很少用。
2.3 C语言的输出/输入2.3.1 C语言的输出C语言格式化输出都需要用到占位符,常用的占位符如下表格所示。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/471BC64EF990448389CA260B8F9627CC)
%d - int 整数
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/CDCFBAB5EE9946C4B1F05583ED34CC11)
%ld - long int 长正数
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/E4673167790D4B1B80423235C88E793E)
%c - char 表示字符
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/F623623AD73A4EBE8A72CA6E84ED44B8)
char 类型如果以%d输出会打印当前字符的ASII值
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/6AB91689EEC74E129900A796A28FEBB1)
%f - float 浮点类型
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/E2382FFF33624709BA2373D95D1F13EE)
%u - 无符号数
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/46C49F3DB2374E5DAB05477355D8FA4D)
%hd - 短整型 short
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/EB46026A91C7498D9D70EF9BAC1FFF84)
%lf - double 双精度浮点类型
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C5BFC39DAB5943B0B1A07D78BE659E92)
%x - 十六进制输出 int 或者long int 或者short int
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/760CFD6D00CD40B899513A933675BB6C)
%o - 八进制输出(是字母o而不是数字0)
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/ADF93FDFFB9F433A956CE98D0364EC99)
%s - 字符串
2.3.2案例-练习C语言的常用输出语句运行结果如下:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/F117F09B975D41B09D052C37BE18595B)
:如果在输出的语句中包含中占位符(或者说是特殊字符),那么需要在占位符本身前面再加个%。比如 printf("%%d");输出的结果就是%d。
2.3.3 C语言的输入C语言的键盘输入主要是通过scanf(“%c”,&c)函数实现的。
需求:从键盘中分别输入整数,字符串,模拟用户信息的录入并打印在控制台。
程序运行结果如下:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/2A199C0E6E9F405FB3D2C3F9B080EC0F)
:scanf("%d",&num);中%d是要输入的数据类型,&num是取变量num的地址,其实输入的原理就是将num在内存的地址处存放输入的数据,这样num变量的值就有了。在C语言中没有String类型,因此只能用char* 指针代替。
2.4 C语言的指针指针是C语言的难点,也是精华所在!
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/F34268FCC3EA4A83814CD36CD853067D)
指针的类型:把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型,比如int* p;语句中int* 就是指针的类型。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/3761972220AF47DF964E81661EAC10B1)
指针所指向的类型:把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型(在指针的算术运算中,指针所指向的类型有很大的作用)比如int* p;语句中,int就是指针指向的类型。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/67935780A15940C29AF486F0AAAA6E15)
指针所指向的内存区:从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。(一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址)
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/06EBD51554DA4CD89316F623AA4C7EF4)
指针本身所占据的内存区:用函数sizeof(指针的类型)可以测出指针本身所占据的内存区(在 32位平台里,指针本身占据了 4个字节的长度)
2.4.1 指针用法入门2.4.2 认识多种指针![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/829F39C9C69D42E0ACB691513B61CDF4)
int *p; (普通指针) //首先从p处开始,先与*结合,所以说明p是一个指针,然后再与int结合,说明指针所指向的内容的类型为int 型。所以p是一个返回整型数据的指针。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/1C0587D2DC18434AB4F0108BC8E5FE0F)
int p[3]; (数组不是指针) //首先从P处开始,先与[]结合,说明p是一个数组,然后与int结合,说明数组里的元素是整型的,所以p是一个由整型数据组成的数组。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/A4BAE8CA4E5841A497DF344163871153)
int *p[3];(多个指针组成的数组) //首先从P处开始,先与[]结合,因为其优先级比*高,所以P是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与 int结合,说明指针所指向的内容的类型是整型的,所以是一个由返回整型数据的指针所组成的数组
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/69B17E6101AB4CE38DF9E2F36C53213A)
int (*p)[3]; (指向数组的指针) //首先从p处开始,先与*结合,说明p是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的。所以p是一个指向由整型数据组成的数组的指针
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C7C3162C29344A558AE3AC79E29EC778)
int **p; (二级指针,指向指针的指针) //首先从p开始,先与*结合,说明p是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与 int结合,说明该指针所指向的元素是整型数据。所以p是一个返回指向整型数据的指针的指针。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/EDD7810082D54599A0F8A01E018AB210)
int p(int); (返回值为int的函数,不是指针) //从p处起,先与()结合,说明p是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数然后再与外面的int 结合,说明函数的返回值是一个整型数据。所以p是一个有整型参数且返回类型为整型的函数
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/61FC57A44F6C49D098BB516A91A43D88)
int (*p)(int);(指向函数的指针) //从p处开始,先与指针结合,说明p是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以p是一个指向有一个整型参数且返回类型为整型的函数的指针
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C7D0272BB69A40EB9CBA902AA07A0310)
int *(*p(int))[3]; (大脑分析不出来了,有点儿变态了,看说明吧) //从 p开始,先与()结合,说明p是一个函数,然后进入()里面,与int结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据。所以p是一个参数为一个整数且返回一个指向由整型指针变量组成的数组的指针变量的函数
2.4.3 外挂的原理(拓展趣味知识)很多游戏的外挂,其实就是通过找到变量(比如记录游戏剩余时间的变量)的地址,然后修改该地址的值来实现的。CheatEngine561.exe就是一款可以查找其他应用地址,并能修改地址值的应用。
CheatEngine561.exe软件的安装很简单,一直点击下一步即可。安装好以后打开程序主界面,如下图所示:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/2D4777B18CD547C8A94D6C9522398D63)
在菜单项打开Process选项,选择扫雷进程
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/01A01FE295E347378197BCAF49133E89)
在Value输入框中输入一个跟当前计时相近的值,如果是大于真实计时值则在scan type下来框中选择Bigger than,然后点击First Scan按钮。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/0496629E70B04717A3607F76DE2D4D04)
扫描以后会在左侧列表框中列出所有符合条件的变量,但是太多还无法确定扫雷记录时间的变量时哪个,因此需要在Value框中继续反复多次输入数字,然后选择Scan type。第二次扫描要点击 Next Scan按钮,这样才会对左侧的值进行筛选。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/FE38339A0D2B42A7A9E7A99AAEC7026B)
重复第3步骤,指导找到扫雷记录时间的变量,双击变量值,这是该变量值就会在软件的最下侧显示
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/00A49F142AE5426FBFAC80C2E6BC4068)
找到扫雷记录时间的地址以后,我们可以慢慢的把扫雷完成,然后修改时间为3秒,大功告成。
2.4.4 案例-使用多级指针运行结果如图:
显然我们通过四级指针成功修改了变量i的值。
2.4.5 指针常见的错误![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/E3DEA182E9C843F9BAAD5B71DD154B8C)
野指针错误
使用指针一定要先赋值,再使用。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/524626E1528E4535845618E34915FC08)
指针类型不正确异常
上面代码会有警告: [Warning] initialization from incompatible pointer type 而且运行结果值为负数,因为已经溢出了。
上面代码运行时,程序异常终止。
2.4.6 案例-通过函数交换两个变量的值运行的结果为:
显然a、b的值成功交换了。
2.5 C语言的数组![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/64FDC613D26648C181F9E9F21EB9A515)
1 数组中的所有元素存在一块连续的内存空间中
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/C92434D7CC45425981B97071A818BF66)
2 数组名就是第一个元素的地址
2.5.1 案例-数组的基本使用运行结果如下:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/6056C9B5D6304BACA71B08F6704DD5C8)
:通过上面的运行结果发现:
1)C语言对脚标越界不进行检查,当脚标越界时照样输出内存中的一个地址值
- 数组各个成员在内存中连续的,肯定的int类型的元素占用了4个字节
2.5.2 案例-遍历数组运行结果如下:
2.6 内存结构2.6.1 C程序结构一个C程序本质上都是由 BSS(Block Started by Symbol) 段、Data段、Text段三个组成的。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/B6E8FD9F19D244C5878BBF27F24EA96A)
BSS段:在采用段式内存管理的架构中,BSS段(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS 段属于静态内存分配,即程序一开始就将其清零了。比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/8CA4E29A462D450D8DCE9AA2C747B504)
数据段:在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/A1A37D76AAC3414E92B2E2CA6C683BE6)
代码段:在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/3EDABFBB053740A7B55AB8FD1EB4F0F7)
:text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而BSS段不在可执行文件中,由系统初始化。
程序编译后生成的目标文件至少含有这三个段,这三个段的大致结构图如下所示:
其中data段包含三个部分:heap(堆)、stack(栈)和静态数据区。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/7AE900D90CCA48F58DB778BBF1184007)
堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/ED548C190FEF4510ACCD987CE367F1FA)
栈 (stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。
stack段存放函数内部的变量、参数和返回地址,其在函数被调用时自动分配,访问方式就是标准栈中的LIFO方式。(因为函数的局部变量存放在此,因此其访问方式应该是栈指针加偏移的方式)
2.6.2案例-不同变量在内存中的区域2.6.3案例-动态内存的申请与回收![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/1AED26A952674C91A85173ED31D840AC)
静态内存是由系统分配的,是栈内存中的连续内存空间,其运行效率非常高,且可以被系统自动回收。但是在某些情况下我们需要动态的申请一些内存空间,比如,在创建数组的时候我们不知道数组的长度是多少,那么我们就需要创建动态数组
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/2695B92C519943BE91BF1A15C36056EF)
动态内存是程序员手动申请的在堆内存中开辟的空间不一定是连续,,运行效率略慢,容易产生碎片需要手动回收
运行结果如图:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/59B38C016B354497BBB392B45882569E)
:通过运行结果可以看到在申请内存前p指针是一个野指针指向一个不确定值,当调用free(p)方法,指针又指向一个不确定值。
2.6.4案例-动态分配数组长度![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/3B5A514CFF5F43B087CAD93CDC330B24)
:上面的代码用到了两个函数,malloc(int)和realloc(int*,int),第一个函数是申请一个内存空间,第二个函数式在已有空间基础上在增加部分内存空间。printArrary函数用于打印数组,在之前的代码中写过就不重复给出。
2.7 typedef&宏定义2.7.1 typedef的使用typedef作为类型定义关键字,用于在原有数据类型(包括基本类型、构造类型和指针等)的基础上,由用户自定义新的类型名称。
用法演示如下:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/A9DD37ACA6DB4D92AFE80EC20B0F99B7)
:
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/D9942363514F472AACCA2E8B37623A53)
(1) typedef可以声明各种类型名,但不能用来定义变量。用typedef可以声明数组类型、字符串类型,使用比较方便。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/364C6B414979403B966F84B2C59D35D9)
(2) 用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/1D151FABA8D64A4DB9FAA98178E292DA)
(3) 当在不同源文件中用到同一类型数据(尤其是像数组、指针、结构体、共用体等类型数据)时,常用typedef声明一些数据类型,把它们单独放在一个头文件中,然后在需要用到它们的文件中用#include命令把它们包含进来,以提高编程效率。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/9DE93834BA214247B890D30338D040BC)
(4) 使用typedef有利于程序的通用与移植。有时程序会依赖于硬件特性,用typedef便于移植。
2.7.2 宏定义#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。
(1) 简单的宏定义:
#define <宏名> <字符串> 例: #define PI 3.1415926
(2) 带参数的宏定义
#define <宏名> (<参数表>) <宏体> 例: #define MUL(A,B) ((A)*(B))
一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。
2.7.3案例-宏定义的简单使用运行结果如下:
2.8 函数指针函数指针是指向函数的指针变量。 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。
2.9 结构体结构体是由基本数据类型构成的、并用一个标识符来命名的各种变量的组合。
定义结构变量的一般格式为:
struct 结构名
{
类型 变量名;
类型 变量名;
...
} 结构变量;
2.10 枚举C语言的枚举跟Java的枚举功能是相似的。
方法一:先声明变量,再对变量赋值
方法二:声明变量的同时赋初值
方法三:定义类型的同时声明变量,然后对变量赋值
方法四:类型定义,变量声明,赋初值同时进行
2.11 联合体当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/7F8E33BCE2AB43608440FB93997F057A)
1)联合体是一个结构;
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/6233E6AF4F06479ABB313FC010C731C2)
2)它的所有成员相对于基地址的偏移量都为0;
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/7EAC2AB5368D44C1BF0708E54D569AC4)
3)此结构空间要大到足够容纳最"宽"的成员;
![](http://note.youdao.com/yws/public/resource/0060baa53a761e0969975c474b61c454/BE7B883E60D64A16942027E642FAA4C0)
4)其对齐方式要适合其中所有的成员;
联合体分析:
在上面代码中:s占9字节,n占4字节,d占8字节,因此其至少需9字节的空间。然而其实际大小并不是9,用运算符sizeof测试其大小为16。这是因为这里存在字节对齐的问题,9既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。
测试程序:
运行上面代码结果如下:
通过上面代码测试发现:1)联合体U1的内存占用长度是16个字节,因为U1的s占9个字节,double占8个字节,按理说整个联合体应该是9才对,但是9不是int(4个字节)的整数倍,因此占用了两个double字节的长度。U2内存占用长度是8个字节。2)联合体的地址跟联合体内部成员的地址相同
至此,本文档完!
2015年1月26日 星期一 15:32:32
北京市海淀区中关村软件园国际软件大厦