打印
[菜农群课笔记]

20120106群课笔记

[复制链接]
2782|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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

相关帖子

沙发
xyz549040622|  楼主 | 2012-1-8 06:10 | 只看该作者
John Lee (1513562323)
2012-01-06 21:20:55

我们能从这 3 个 已知项中得到 baud_t& 吗?看似这些都是与 baud_t& 无关的数据啊。
CountryMan (176419557)
2012-01-06 21:21:31

强制转换
John Lee (1513562323)
2012-01-06 21:22:00

哦?依据是什么?
CountryMan (176419557)
2012-01-06 21:22:17

类似C语言的指针
CountryMan (176419557)
2012-01-06 21:22:22

通过this
CountryMan (176419557)
2012-01-06 21:22:46

因为已知的就这么多
John Lee (1513562323)
2012-01-06 21:22:48

this与 baud_t& 有关吗?
John Lee (1513562323)
2012-01-06 21:23:17

为什么可以强制转换?
murex (344582199)
2012-01-06 21:23:35

这个就是类似执行了三次函数
John Lee (1513562323)
2012-01-06 21:24:03
murex (344582199)
2012-01-06 21:25:09

类似p.f1().f2().f3()
CountryMan (176419557)
2012-01-06 21:25:10

只要有相同的数据类型就可以强制转换吧
John Lee (1513562323)
2012-01-06 21:25:25

其实很 easy 的,还记得 baud_t 的类型定义吗?
John Lee (1513562323)
2012-01-06 21:25:39

union!!!



John Lee (1513562323)
2012-01-06 21:27:06

我们知道,union 很重要的性质是:union 中所有成员的地址偏移(相对于 union 类型),都是 0。
CountryMan (176419557)
2012-01-06 21:27:14


可乐小子 (479357804)
2012-01-06 21:27:19


CountryMan (176419557)
2012-01-06 21:27:40

然后再加上CPP的函数重载
John Lee (1513562323)
2012-01-06 21:27:48

就是说,baud_t 临时对象中的各个“位对象”(BRD,DIV_X_ONE等)的地址,就等于 baud_t 临时对象的地址!!!
John Lee (1513562323)
2012-01-06 21:30:30

这样一来,我们就可以简单地把“位对象”的 this 指针“强制”转换为 baud_t 临时对象的类型的指针,因为两者本身是相等的(值相等,类型不同):

John Lee (1513562323)
2012-01-06 21:32:05

然后在 write() 返回时,返回指针指向的对象即可:

murex (344582199)
2012-01-06 21:32:48
这个就是write()返回的是临时对象内容
John Lee (1513562323)
2012-01-06 21:33:44

reinterpret_cast是C++的关键字,用于类型强制转换。
林木森森 (965243619)
2012-01-06 21:34:33

临时对象可以返回,这样做稳定吗
John Lee (1513562323)
2012-01-06 21:34:39

相当于C语法:
register baud_t* p = (baud_t*)this;
CountryMan (176419557)
2012-01-06 21:35:04


回头我把这些看看,没有学习过C++,先把这些记录下来

murex (344582199)
2012-01-06 21:35:36

http://baike.baidu.com/view/1263731.htm这个是reinterpret_cast的知识点
CountryMan (176419557)
2012-01-06 21:36:00


谢谢

John Lee (1513562323)
2012-01-06 21:36:12

murex<murex@126.com>  21:32:47
这个就是write()返回的是临时对象内容
林木森森(965243619)  21:34:34
临时对象可以返回,这样做稳定吗
----------------------
write()返回的是什么?临时对象吗?
可乐小子 (479357804)
2012-01-06 21:36:12

学习...
murex (344582199)
2012-01-06 21:37:06

临时对象析构后就是临时操作后的数据
林木森森 (965243619)
2012-01-06 21:37:28

如果是堆空间内的对象没问题可以返回,但函数内用的栈空间对象,随着函数返回,对象的内存也被回收,返回也就会不稳定。我的理解对吗
John Lee (1513562323)
2012-01-06 21:39:28
由于 write() 定义的返回值是引用,编译器会把指针 p 直接当做引用的值返回,而不是真的取出指针指向的对象作为返回值!
John Lee (1513562323)
2012-01-06 21:40:50

如果 write() 是这样定义的:
baud_t write( .... ) {
      .....
      return *p;
}
John Lee (1513562323)
2012-01-06 21:41:36

那么就真的要生成另一个临时对象了。
林木森森 (965243619)
2012-01-06 21:42:03

是的
John Lee (1513562323)
2012-01-06 21:42:39

我记得以前讨论过返回临时对象的问题。
John Lee (1513562323)
2012-01-06 21:42:59

大家都忘了?
Robin Lee (409655837)
2012-01-06 21:43:21


John Lee (1513562323)
2012-01-06 21:43:46

好,先继续,write() 返回的引用,再作为“()”操作符的返回。
John Lee (1513562323)
2012-01-06 21:44:35

而返回的 baud_t 引用,实际上就是那个“baud_t 临时对象”的引用,所以,我们就可以“连续”访问其中的“位对象”了。
John Lee (1513562323)
2012-01-06 21:45:54

总之,“连续”访问“位对象”的原理就是,临时对象的“位对象”的“()”操作符函数,又返回了那个“临时对象”的引用,以供访问下一个“位对象”所需。
murex (344582199)
2012-01-06 21:46:57

都是位操作后把当前值又继续操作下一位?
John Lee (1513562323)
2012-01-06 21:47:31

菜地说清楚一些。
John Lee (1513562323)
2012-01-06 21:48:36

大家参考一下上面讲的:p->f1()->f2(); 和 p.f1().f2();

murex (344582199)
2012-01-06 21:48:46

就是多位操作时,其实就是分开一步一步来的
John Lee (1513562323)
2012-01-06 21:49:02

肯定的
murex (344582199)
2012-01-06 21:49:24

只是没有把一步操作后直接赋值回去,而是把要操作的全部算出来后再回写
murex (344582199)
2012-01-06 21:49:45

这就是连续访问位对象了
John Lee (1513562323)
2012-01-06 21:49:50

这个还没有讲到
CountryMan (176419557)
2012-01-06 21:49:54

俺的理解是每次都返回首地址 类似数组的首地址
然后就可以通过这个访问其他位对象了
John Lee (1513562323)
2012-01-06 21:50:15

嗯,差不多可以这样理解
John Lee (1513562323)
2012-01-06 21:50:33

比较山寨吧。
John Lee (1513562323)
2012-01-06 21:51:07

我们再继续讨论 write() 函数的操作。
John Lee (1513562323)
2012-01-06 21:51:30

write() 的操作是把参数 v 写到“baud_t 临时对象”的 val 成员中去。
John Lee (1513562323)
2012-01-06 21:51:50

刚才说了,“baud_t 临时对象”由 this 指针强制而来,因为两者的地址是相等的。
John Lee (1513562323)
2012-01-06 21:53:28

这样,我们就能够访问 baud_t 临时对象的 val 成员了:

John Lee (1513562323)
2012-01-06 21:54:45

还记得 val 成员吧?这个是硬件寄存器的暂存值,由构造函数初始化而来。

使用特权

评论回复
板凳
xyz549040622|  楼主 | 2012-1-8 06:17 | 只看该作者
本帖最后由 xyz549040622 于 2012-1-8 06:20 编辑

John Lee (1513562323)
2012-01-06 21:55:20但我们的设计需求是访问 val 中规定的“位域”,如:BRD位对象,对应的位域是 bit0 - bit15。
John Lee (1513562323)
2012-01-06 21:56:53

下面课间提问:
write() 函数是如何知道这个“位域”的?
John Lee (1513562323)
2012-01-06 21:58:55

就是说,这个“位域”是一个已知项,它在什么地方给出的?
murex (344582199)
2012-01-06 21:59:26

寄存器的定义的结构体中出来的
MCS80982012-01-06 21:59:39

jiegouti
John Lee (1513562323)
2012-01-06 22:00:10

不准确
John Lee (1513562323)
2012-01-06 22:00:35

write() 函数是如何知道这个“位域”的????
MCS80982012-01-06 22:00:43

结构体函数 位于定义 的地址
MCS80982012-01-06 22:00:57

位域
CountryMan (176419557)
2012-01-06 22:01:21

MASK是啥东西
MCS8098
2012-01-06 22:03:24

老师 这个指针指向的地址是不是就是 write() 函数呢值呢
CountryMan (176419557)
2012-01-06 22:03:38

应该是模板中定义的那些东西
murex (344582199)
2012-01-06 22:03:43

通过参数v进来得知位域
CountryMan (176419557)
2012-01-06 22:03:44


潜龙思瑞 (373744463)
2012-01-06 22:04:06

sfb_t 模板类定义的
MCS80982012-01-06 22:04:07

有点晕
CountryMan (176419557)
2012-01-06 22:04:20

类似这样

John Lee (1513562323)
2012-01-06 22:08:50
晕,刚才蓝屏了。
CountryMan (176419557)
2012-01-06 22:08:58


murex (344582199)
2012-01-06 22:09:50

蓝屏了俺们也没把问题回答上来,唉
John Lee (1513562323)

2012-01-06 22:10:32

CountryMan(176419557) 和 潜龙思瑞(373744463)稍微靠点谱,但还是不准确。

MASK = (((1LL << W) - 1) << S)
最上面的“N”,是笔误,应该是“W”
murex (344582199)
2012-01-06 22:13:43

这个定义在哪个文件,一时没找到
murex (344582199)
2012-01-06 22:14:12

找到了
天天向上 (693917636)
2012-01-06 22:16:37

什么?我刚来~讲到哪了
John Lee (1513562323)
2012-01-06 22:17:13

准确的说法是:write() 函数所使用的“位域”定义,是通过 sfb_t 模板类在实例化时,给出的“模板参数”S 和 W 传入的。
John Lee (1513562323)
2012-01-06 22:18:21
其实,只要说出“模板参数”4 个字就行了。
MCS80982012-01-06 22:19:02


murex (344582199)
2012-01-06 22:19:08

嗯,是哟,原来就藏在这
CountryMan (176419557)
2012-01-06 22:19:18

这个语法太变态了
murex (344582199)
2012-01-06 22:19:39

看到什么下划线什么的一大堆就开始晕了
John Lee (1513562323)
2012-01-06 22:20:54

今天又讲不完了,还剩一点尾巴。
murex (344582199)
2012-01-06 22:21:13

改天再讲好了,哈哈
murex (344582199)
2012-01-06 22:23:15

老师,这个定义寄存器主要包含在哪几个文件中的
murex (344582199)
2012-01-06 22:23:42

numicro\sfr全在这里面的吗?
murex (344582199)
2012-01-06 22:25:05

嗯,有图才有真相,哈哈
John Lee (1513562323)
2012-01-06 22:25:27

<sfr>, <numicro/sfr/*>
murex (344582199)
2012-01-06 22:27:37

嗯,这样是差不多了,我刚才一直就找了<numicro/sfr/*>
murex (344582199)
2012-01-06 22:27:49

所以看着总感觉不够全,有部分内容没找着
John Lee (1513562323)
2012-01-06 22:28:08

下次讲“位对象”的其它操作符函数,临时对象的析构,临时对象的优化编译。

使用特权

评论回复
地板
Swallow_0322| | 2012-1-8 08:14 | 只看该作者
顶!很重要的课没听上,抽空认真学习笔记内容,多谢楼主!

使用特权

评论回复
5
xyz549040622|  楼主 | 2012-1-8 08:40 | 只看该作者
4# Swallow_0322 三心前辈客气了:lol

使用特权

评论回复
6
panchaoran| | 2012-1-8 09:09 | 只看该作者
做地板学习学习

使用特权

评论回复
7
hotpower| | 2012-1-8 10:29 | 只看该作者
很好,上传笔记让更多的人学习。谢谢。

使用特权

评论回复
8
西行侠客| | 2013-4-27 15:21 | 只看该作者
为什么这个帖米有做PDF?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群: 嵌入式系统arm初学者 224636155←← +→→点击-->小 i 精品课全集,21ic公开课~~←←→→点击-->小 i 精品课全集,给你全方位的技能策划~~←←

2782

主题

19267

帖子

104

粉丝