本帖最后由 xyz549040622 于 2012-1-8 06:23 编辑
PS:趁着早上2M的网速没人和俺抢联通,终于把聊天记录拉出来了,嘎嘎!!!珍惜菜地来之不易的学习机会!!!
John Lee (1513562323)
2012-01-06 20:27:25
我们继续上次的讨论,表达式“UART0.BAUD().BRD(20).DIV_X_ONE(1);”,我们已经讨论了“UART0.BAUD()”部分。
John Lee (1513562323)
2012-01-06 20:27:53
这个部分返回了一个类型为 uart_t 的临时对象,我们继续讨论表达式的剩余部分:“.BRD(20).DIV_X_ONE(1);”。
John Lee (1513562323)
2012-01-06 20:28:27
表达式中的 BRD 和 DIV_X_ONE 都是 union baud_t 的成员,很多人看了这种连续访问成员的表达式,都比较迷惑。
John Lee (1513562323)
2012-01-06 20:28:59
我们来详细分析一下,“.BRD(20).DIV_X_ONE(1);”,可以分为 3 个部分:
John Lee (1513562323)
2012-01-06 20:29:20
1、“.BRD(20)”
2、“.DIV_X_ONE(1)”
3、“;”
John Lee (1513562323)
2012-01-06 20:31:12
看看 uart_t 类型中,BRD 和 DIV_X_ONE 成员的定义:
sfb_t<baud_t, uint32_t, 0, 16> BRD;
sfb_t<baud_t, uint32_t, 28, 1> DIV_X_ONE;
John Lee (1513562323)
2012-01-06 20:31:46
可以发现,它们都是 sfb_t 模板类的对象,sfb_t 是专门用“special function bit”相关操作的模板类定义
John Lee (1513562323)
2012-01-06 20:32:42
在这里,我们把 sfb_t 模板类的对象,如:BRD,DIV_X_ONE等,称为“位对象”。
John Lee (1513562323)
2012-01-06 20:33:29
使用“.”操作符访问“位对象” BRD,DIV_X_ONE等,这要求“.”操作符的左边必须是 union baud_t 的“对象”或者“对象的引用”。
John Lee (1513562323)
2012-01-06 20:34:03
大家对 C++ “引用”的概念是否熟悉?
John Lee (1513562323)
2012-01-06 20:35:05
我们使用 C 的指针来举例模拟一下,先定义一个结构 s 和其变量 t:
struct s {
struct s *(*f1)();
struct s *(*f2)();
} t;
murex (344582199)
2012-01-06 20:35:38
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名;
John Lee (1513562323)
2012-01-06 20:36:00
struct s 的 f1 和 f2 是函数指针,函数类型为 struct s*,然后我们可以这样操作:
John Lee (1513562323)
2012-01-06 20:36:35
struct s *p = &t;
p = p->f1();
p->f2();
能看懂吗?
murex (344582199)
2012-01-06 20:37:35
p->f2();不是太懂
murex (344582199)
2012-01-06 20:37:43
第一个还能看懂
John Lee (1513562323)
2012-01-06 20:38:19
p->f1() 和 p->f2() 是一回事啊,怎么不懂?
John Lee (1513562323)
2012-01-06 20:39:51
然后,我们稍微变形一下:
struct s *p = &t;
p->f1()->f2();
John Lee (1513562323)
2012-01-06 20:40:43
以上的两种写法,逻辑上是完全相同的。
潜龙思瑞 (373744463)
2012-01-06 20:42:07
老师这是分别执行两个函数吗?
☆潜心钻研☆ (583199723)
2012-01-06 20:42:18
指针,链表
John Lee (1513562323)
2012-01-06 20:42:25
是的
John Lee (1513562323)
2012-01-06 20:42:32
不是链表。
潜龙思瑞 (373744463)
2012-01-06 20:43:21
它和刚才的p = p->f1();
p->f2();
一样吗?也是执行两个函数么?
☆潜心钻研☆ (583199723)
2012-01-06 20:43:27
C的精华就在指针
Robin Lee (409655837)
2012-01-06 20:43:48
怕乱指啊
John Lee (1513562323)
2012-01-06 20:43:48
是的,都是执行了两个函数调用。
潜龙思瑞 (373744463)
2012-01-06 20:43:56
明白了
Robin Lee (409655837)
2012-01-06 20:44:20
我还是默默的听
John Lee (1513562323)
2012-01-06 20:44:30
只不过第2中,省略了中间的 p 赋值。
John Lee (1513562323)
2012-01-06 20:44:54
直接用 f1()的返回值,调用了f2()。
murex (344582199)
2012-01-06 20:44:57
相当于函数中把返回值丢掉了
☆潜心钻研☆ (583199723)
2012-01-06 20:45:06
指针使用不当,或是用错了,造成蓝屏是常有的事!
得好好注意!
Robin Lee (409655837)
2012-01-06 20:45:26
哦
murex (344582199)
2012-01-06 20:45:48
没写过上位机,没碰上蓝屏,哈哈
John Lee (1513562323)
2012-01-06 20:46:46
下面,我们用C++的引用来写一遍:
struct s {
struct s &(*f1)();
struct s &(*f2)();
} t;
struct s &p = t;
p.f1().f2();
murex (344582199)
2012-01-06 20:48:07
这样的话->变成了.
是否是为了打印可以少打字啊
John Lee (1513562323)
2012-01-06 20:48:10
注意,这里没有刚才第1种对应的写法,如:
struct &p = t;
p = p.f1();
p.f2();
John Lee (1513562323)
2012-01-06 20:50:05
p = p.f1(); 并不表示把 p 本身赋值为 f1()的返回值,而是把 变量 t 修改了。
murex (344582199)
2012-01-06 20:51:09
那这样执行后,p为什么内容了呢
John Lee (1513562323)
2012-01-06 20:51:19
C++中,除了引用定义时的初始化外,任何对引用的赋值,都是修改引用对应的变量。
☆潜心钻研☆ (583199723)
2012-01-06 20:52:43
结构体
John Lee (1513562323)
2012-01-06 20:53:49
例如:
int i = 0;
int &r = i; // r 引用初始化为 i。
int j = 1;
r = j;
Robin Lee (409655837)
2012-01-06 20:54:37
这个我听懂了 呵呵
John Lee (1513562323)
2012-01-06 20:55:11
r = j时,并不表示“重新”定义 r 为 j 的引用,而表示把 j 赋值到 r 所引用的变量“i”中。
John Lee (1513562323)
2012-01-06 20:56:58
引用,只能(必须)在定义时初始化为某个对象的引用,一旦定义后,就不能在修改为另外的对象的引用。
John Lee (1513562323)
2012-01-06 20:57:32
这有点像“常量指针”。
murex (344582199)
2012-01-06 20:58:11
那这个i还是可以再赋值更改吧
John Lee (1513562323)
2012-01-06 20:58:59
可以啊,相当于现在 i 有了两个名字:i,r。
Robin Lee (409655837)
2012-01-06 20:59:18
恩
John Lee (1513562323)
2012-01-06 20:59:41
对其中任何一个修改,都会修改到 i 本身。
murex (344582199)
2012-01-06 20:59:43
嗯,明白了,就是说r只能用i了,不能再引用别的变量了
John Lee (1513562323)
2012-01-06 20:59:51
对
John Lee (1513562323)
2012-01-06 21:00:26
编译器实现引用,实际上和指针是相似的。
John Lee (1513562323)
2012-01-06 21:00:55
r实际上就是一个指向 i 的常量指针。
Robin Lee (409655837)
2012-01-06 21:00:56
不懂
murex (344582199)
2012-01-06 21:01:06
理解
John Lee (1513562323)
2012-01-06 21:01:44
常量指针嘛,就是不能再修改指针本身了,赋值会报错。
murex (344582199)
2012-01-06 21:01:51
嗯,是的
Robin Lee (409655837)
2012-01-06 21:01:55
哦
John Lee (1513562323)
2012-01-06 21:02:51
int i;
int *const r = &i;
*r = 0; // 合法
int j;
r = &j; // 报错
John Lee (1513562323)
2012-01-06 21:03:49
而引用与此类似,只是写法上隐藏了指针,更像变量本身了。
John Lee (1513562323)
2012-01-06 21:04:25
int i;
int &r = i;
r = 0; // 合法,修改了 i
murex (344582199)
2012-01-06 21:04:29
嗯,的确看着跟变量一样了
John Lee (1513562323)
2012-01-06 21:05:35
由于隐藏了指针的写法,所以,你没有办法写出 r = &j; 那样的错误语法了。
John Lee (1513562323)
2012-01-06 21:06:19
C++ 的引用,其实是将“->”操作符,转为了“.”操作符,隐含了指针的操作,使之更像对象本身。
John Lee (1513562323)
2012-01-06 21:06:34
我们继续表达式“.BRD(20).DIV_X_ONE(1);”的讨论。
murex (344582199)
2012-01-06 21:07:00
欧耶,强悍,本来是->变成了.
John Lee (1513562323)
2012-01-06 21:07:17
对第一个被访问的“位对象” BRD 来说,“.”操作符的左边“UART0.BAUD()”是“union baud_t”临时对象,所以,这是没问题的。
John Lee (1513562323)
2012-01-06 21:07:59
但后续的“位对象”DIV_X_ONE 的访问呢?同样要求“.”操作符的左边“.BRD(20)”是 union baud_t 的“对象”或者“对象的引用”。
John Lee (1513562323)
2012-01-06 21:08:49
那么,关键就是 BRD 之后的“()”操作符的类型(返回值)了。
murex (344582199)
2012-01-06 21:09:32
嗯,跟之前的结构体里调用两个函数一致了
John Lee (1513562323)
2012-01-06 21:09:51
不错,菜地懂了。
John Lee (1513562323)
2012-01-06 21:10:10
在前面群课中,我们知道,如果一个标识符后出现一个函数调用操作符“()”,那么,这个标识符可能是:
1、成员函数。
2、函数指针。
3、类的对象,此类中定义了“()”操作符。
murex (344582199)
2012-01-06 21:10:10
小懂一点了
John Lee (1513562323)
2012-01-06 21:10:50
很明显,BRD 是第 3 种(sfb_t 模板类的对象)。
murex (344582199)
2012-01-06 21:11:04
嗯,是的
MCS8098 (285216815)
2012-01-06 21:11:23
嗯
Robin Lee (409655837)
2012-01-06 21:11:31
嗯
爱在人间 (412240302)
2012-01-06 21:11:57
John Lee (1513562323)
2012-01-06 21:12:47
我们在 sfb_t 模板类中找到了“()”操作符的定义:
__INLINE baud_t& operator ()(uint32_t v, bool safe = true) { return write(v, safe); }
John Lee (1513562323)
2012-01-06 21:13:17
果然,它的类型是 union baud_t 类型的引用(baud_t&)。
John Lee (1513562323)
2012-01-06 21:13:31
黄色部分。
John Lee (1513562323)
2012-01-06 21:14:14
这个函数要求一个 uint32_t 的形参 v,和一个 bool 的形参 safe,
John Lee (1513562323)
2012-01-06 21:15:06
bool safe = true,意思是这个形参 safe 有默认值 true,当调用此函数时,允许省略 safe 对应的实参,那么编译器将以默认值 true 代替省略的实参。
John Lee (1513562323)
2012-01-06 21:15:59
此函数的实现仅仅是调用了 write() 函数,并返回了 write() 函数的返回值。
John Lee (1513562323)
2012-01-06 21:17:30
我们来看看 write() 函数:
John Lee (1513562323)
2012-01-06 21:17:54
很显然,write() 函数的类型也是 baud_t&(黄色部分),那么,问题来了,write() 函数的返回值(baud_t&)是如何得到的?
John Lee (1513562323)
2012-01-06 21:19:03
有谁知道?
John Lee (1513562323)
2012-01-06 21:19:48
我们先看看 write() 函数的已知项:
1、参数 this 指针,类型是“位对象”(sfb_t<baud_t, uint32_t, 0, 16>)。
2、参数 uint32_t v
3、参数 bool safe |