从单片机基础到程序框架(全集 2019pdf版).pdf - 第296页
Gu32PinJ unZhi=PinJu nZhi(&Gu8B uffer[0]);/ /不用担心 G u8Buffer 数组 的数据被意外 更改。 View(Gu3 2PinJunZhi) ; //把第 1 个数 G u32PinJunZ hi 发送到电脑 端的串口助手 软件上 观察。 while(1) { } } /*---C 语言学习区 域的结束。 ----- ----------- ---------- --------…

第六十八节: 为函数接口指针“定向”的 const 关键词。
【68.1 为函数接口指针“定向”的 const 关键词。】
在函数接口处的指针,是一个双向口,既可以作为“输入”也可以作为“输出”,换句话说,既能“读”
也能“写”(被更改),这样一来,当你把一个数组(或者某变量)通过指针引入到函数内部的时候,当执行
完此函数,这个数组的数值可能已经悄悄发生了更改(“是否被更改”取决于函数内部的具体代码),进来时
是“摩托”出来后可能已变成“单车”,而实际项目上,很多时候我们只想传递数组(或者某变量)的数值,
并不想数组(或者某变量)本身发生变化,这个时候,本节的主角 const 关键词就派上用场了。
只要在函数接口的指针前面加上 const 关键词,原来双向的指针就立刻变成了单向,只能输入不能输出。
这个 const 有两个好处。第一个好处是方便阅读,通过 const 就知道此接口的“入口”和“出口”属性,如
果你是用别人已经封装好的函数,一旦发现接口指针带了 const 标签,就足以说明这个指针只能作为输入接
口,不用担心输入数据被意外修改。第二个好处是确保数据的安全,函数接口指针一旦加了 const 限定,万
一你不小心在函数内部对指针所关联的数据进行了更改(“更改”就意味着“出口”),C 编译器在编译的时候
就会报错让你编译失败,及时让你发现程序的 bug(程序的漏洞),这是编译器层面的一道防火墙。例子如下:
unsigned char ShuRu(const unsigned char *pu8Data)
{
unsigned char a;
a=*pu8Data; //这行代码是合法的,是指针所关联数据的“读”操作。
*pu8Data=a; //这行代码是非法的,是指针所关联数据的“写”操作,违背 const 的约束。
return a;
}
【68.2 例程练习和分析。】
在前面第 65 节讲函数入口的时候,用到一个求数组平均值的程序例子,这个数组是仅仅作为输入用的,
不需要被更改,因此,现在借本节讲 const 的机会,为此函数的接口指针补上一个 const 关键词,让该函数
更加科学规范,程序如下:
/*---C 语言学习区域的开始。-----------------------------------------------*/
unsigned long PinJunZhi(const unsigned char *pu8Buffer); //指针前增加一个 const 关键词
unsigned char Gu8Buffer[4]={2,6,8,4};
unsigned long Gu32PinJunZhi;
unsigned long PinJunZhi(const unsigned char *pu8Buffer) //指针前增加一个 const 关键词
{
unsigned long u32PinJunZhi;
u32PinJunZhi=(pu8Buffer[0]+pu8Buffer[1]+pu8Buffer[2]+pu8Buffer[3])/4; //求平均值
return u32PinJunZhi;
}
void main() //主函数
{

Gu32PinJunZhi=PinJunZhi(&Gu8Buffer[0]);//不用担心 Gu8Buffer 数组的数据被意外更改。
View(Gu32PinJunZhi); //把第 1 个数 Gu32PinJunZhi 发送到电脑端的串口助手软件上观察。
while(1)
{
}
}
/*---C 语言学习区域的结束。-----------------------------------------------*/
在电脑串口助手软件上观察到的程序执行现象如下:
开始...
第 1 个数
十进制:5
十六进制:5
二进制:101
分析:
平均值变量 Gu32PinJunZhi 为 5。
【68.3 如何在单片机上练习本章节 C 语言程序?】
直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C 语言学习区域”的代码就可以了,
其它部分的代码不要动。编译后,把程序下载进带串口的 51 学习板,通过电脑端的串口助手软件就可以观
察到不同的变量数值,详细方法请看第十一节内容。

第六十九节: 宏函数 sizeof()。
【69.1 宏函数 sizeof()的基础知识。】
宏函数 sizeof()是用来获取某个对象所占用的字节数。既然是“宏”,就说明它不是单片机执行的函数,
而是单片机之外的 C 编译器执行的函数(像#define 这类宏语句一样),也就是说,在单片机上电之前,C 编
译器在电脑端翻译我们的 C 语言程序的时候,一旦发现了这个宏函数 sizeof,它就会在电脑端根据 C 语言程
序的一些关键字符(比如“unsigned char,[,]”这类字符)来自动计算这个对象所占用的字节数,然后再
把我们 C 语言程序里所有的 sizeof 字符替换等效成一个“常量数字”,1 代表 1 个字节,5 代表 5 个字节,
1000 代表 1000 个字节。所谓在单片机之外执行的宏函数,就是说,在“计算”这些对象所占的字节数的时
候,这个“计算”的工作只占用电脑的内存(C 编译器是在电脑上运行的),并不占用单片机的 ROM 容量和内
存。而其它在单片机端执行的“非宏”函数,是占用单片机的 ROM 容量和内存。比如:
unsigned char a; //变量。占用 1 个字节
unsigned int b; //变量。占用 2 个字节
unsigned long c; //变量。占用 4 个字节
code unsigned char d[9]; //常量。占用 9 个字节
unsigned int Gu16GetBytes; //这个变量用来获取字节数
Gu16GetBytes=sizeof(a); //单片机上电后,在单片机程序里等效于 Gu16GetBytes=1;
Gu16GetBytes=sizeof(b); //单片机上电后,在单片机程序里等效于 Gu16GetBytes=2;
Gu16GetBytes=sizeof(c); //单片机上电后,在单片机程序里等效于 Gu16GetBytes=4;
Gu16GetBytes=sizeof(d); //单片机上电后,在单片机程序里等效于 Gu16GetBytes=9;
上述的“sizeof 字符”在进入到单片机的层面的时候,已经被编译器预先替换成对应的“常量数字”的,
这个“常量数字”就代表所占用的字节数。
【69.2 宏函数 sizeof()的作用。】
在项目中,通常用在两个方面:一方面是用在求一个数组的大小尺寸,另一方面是用在计算内存分配时
候的偏移量。当然,sizeof 并不是“刚需”,如果没有 sizeof 宏函数,我们也可以人工计算出一个对象所占
用的字节数,只是,人工计算,一方面容易出错,另一方面代码往往“动一发而牵全身”,改一个变量往往
就会涉及很多地方需要配合调整更改,没法做到“自由裁剪”的境界。下面举一个程序例子:要把 3 个不同
长度的数组“合并”成 1 个数组。
第一种情况:在没有使用 sizeof 宏函数时,人工计算字节数和偏移量:
unsigned char a[2]={1,2}; //占用 2 个字节
unsigned char b[3]={3,4,5}; //占用 3 个字节
unsigned char c[4]={6,7,8,9}; //占用 4 个字节
unsigned char HeBing[9];//合并 a,b,c 在一起的数组。这里的 9 是人工计算 a,b,c 容量累加所得。
unsigned char i; //循环变量 i
for(i=0;i<2;i++) //这里的 2,是人工计算出 a 占用 2 个字节