分享
 
 
 

从一个小程序详解codewarrior for MC68HC908GP32CP C语言指针

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

#include <hidef.h>

#include <MC68HC908GP32.h>

/* 在codewarrior中建立了C语言的程序之后程序自动引入头文件MC68HC908GP32.h在这个头文件中定义了

typedef unsigned char byte;

typedef unsigned int word;

typedef unsigned long dword;

typedef unsigned long dlong[2];

如果足够用,建议使用byte而不是int,因为它只占用一个字节,在单片机中内存是稀缺资源,非常宝贵*/

byte a @ 0x0050; // 定义unsigned char放在50单元,这种定义方式只能用于

// 全局变量。

byte *p @ 0x0051; // 定义一个指针放在0x0051单元,指向一个unsigned char,

// 究竟指向什么地址还没有定义。注意!这里非常重要的是,

// 虽然byte(unsigned char)只有一个字节,但是这个指针却

// 是两个字节的,也就是说在0x0051和0x0052分别存放了这

// 个指针的高、低字节。

byte b @ 0x0060; // 定义一个变量放在60单元

#define c (*(volatile byte*)0x0058) // 另一种为变量指定地址的方式:宏定义

// 不要为这种复杂的写法感到困惑或者烦躁,

// 它也可以分解成几个很简单的部分来看:

// (volatile byte*)是C语言中的牵制类型转换

// 它的作用是把0x0058这个纯粹的十六进制数

// 转换成为一个地址,其中volatile并不是必要

// 的,它只是告诉编译器,这个值与外环境有关,

// 不要对它优化。这里也没有必要对编译器的具

// 体机制讨论太多[1]。接下来在外面又加了一

// 个*号,就表示取0x0058内存单元中的内容了。

// 经过这个宏定义之后,c就被可以做为一个普通

// 的变量来操作,所有出现c的地方编译的时候

// 都被替换成(*(volatile byte*)0x0058),外

// 面一层括号是为了保证里面的操作不会因为运

// 算符优先级或者其它不可预测的原因被改变而

// 无法得到预期的结果。

void main(void)

{

byte i; // 定义byte类型变脸i,我也不知道编译之后它会被放在哪个内存单元

byte *p2; // 有一个指针,指向一个byte类型的数

a=0x70; // a赋值0x70,还记得吗,a在50单元也就是MOV #$70,$50

c=0x30; // MOV #$30,$58

p=(byte*)0x60; // p指向60单元,60单元存放的是b,于是P就很自然的成了指向b的指针(p=&b;)

// 这里的(byte*)是不能省略的,这也是一个强制类型转换,它告诉编译器,

// 0x60再也不是一个纯粹的十六进制数了,它已经代表了一个内存单元。

p2=(byte*)a; // a中存放的数是0x70,现在把它强制转换成地址类型,p2->0x0070

for(i=0;i<0x10;++i) // 循环16次

{

*p=i+a; // 把i+0x70的值放到p指向的地址中,循环开始时,p->0x60,

// 以后每次增加1,直到p->0x70,循环结束后,就可以看到,

// 60到70单元中存放了70,71,……,7F。

*p2++=(byte)p++; // 把p指向的地址值保存在p2指向的地址中(从0x60开始),然后

// p增1用于下一次循环;p2增1用于保存下一次循环中p指向的

// 地址。循环结束后,可以很清楚地看到,从60单元到6F单元

// 保存的16次循环中指针p所有的值:60,61,……,6F。

// 同前面一样,这里的(byte)是必不可少的,p原来是指针,保存

// 的是地址值,现在要把地址值当作纯粹的十六进制数来赋值

// 给p2指向的单元,就必须强制类型转换。

*((byte*)0x0080)=(byte)p; // [2]这种写法其实我们一开始的时候就用到了。对!就是

// 跟定义宏 #define c (*(volatile byte*)0x0058) 时

// 候语法完全一样。这种写法可以完全不用理会是不是要

// 定义全局变量(使用@关键字的时候必须定义全局变量),

// 也可以避免使用#define进行宏定义这种有可能成为一切

// 不安全因素根源的写法,而在程序运行过程中,把变量

// 放置在任意允许放置的内存单元。这一点非常有用,尤

// 其是在需要看到当前某个变量的值的场合。

//__RESET_WATCHDOG(); // 暂时不理会看门狗。因为程序小小的循环16次还不至于

// 让它溢出复位。但是我把它写出来,因为后面有更玄妙

// 的问题。

}

*((byte*)0x0081)=(byte)p; // [3]循环结束,把P指向的地址值放到81单元中。哈哈,

// 用膝盖都可以想到,81单元中存放的值一定是70。

*((int*)0x0082)=(int)&i; // 快要结束了,现在我们能做的事情已经那么多了,于是

*((int*)0x0084)=(int)&p2; // 谁还能容忍存在未知的事物呢?上面我说我也不知道编译

// 器在编译之后会把变量i(或者是指针P2)放到哪里去,可

// 是如果你现在还只满足于不知道,那可太悲哀了:(。我

// 可不是个求知欲容易满足的人,我一定要弄清楚。于是

// 这种强制类型转换的机制又一次帮助了我们。我不解释

// 这两句了,因为它们现在已经变得那样明了。唯一说明

// 一点,这里用int而没有用byte是因为我可不敢保证i和

// p2的地址一个字节装得下。

}

/* 好了,循环结束,程序运行完了,下面要提出那个玄妙的问题了。有没有发现[2]和[3]两处做了同一件事情,都是把p指向的地址保存起来。为了方便比较,我选择两个相邻的地址80和81。还有一个地方也保存了p指向的地址,就是51单元(还记得byte *p @ 0x0051;吗?)。我强调过,byte是一个字节,可是byte*默认为两个字节,而我在[2]和[3]两处把两个字节的p用(byte)强制类型转换成了一个字节的十六进制数,会产生错误吗?这里不会。很简单,因为这里的p本身就只有一个字节,强制类型转换之后,80和81单元都保存的是p的低字节,这已经足够了。0x51,0x80,0x81都保存了p指向的地址的值,有什么不一样呢?0x51是p定义时候的位置,可以算是它的hometown了,0x81存放的是循环结束后p指向的地址,也就是0x70,而0x80呢?它存放每次循环末尾的p的值,与51单元和81单元不同的是,每次循环的时候,他都是会变的,变化过程就是循环结束后0x60到0x6F地址中存放的那些值。由此可以推断,在程序最后一次循环结束后,0x80与0x81单元的值应该是一致的,看看你的内存,一致吗?0x51和0x80是一致的,而和0x81呢?呵呵,如果我没猜错的话,51单元和80单元的值都是64,而81单元跟我们之前所确定的一样,是千真万确的70。为什么呢?发现问题本身比解决问题有更大的意义。为了解决这个问题,我们只有单步运行了。在main()函数的入口处设置一个端点,然后点连续运行,让程序在初始化的那些部分执行完后停下来,在main()入口处开始单步运行。一步,两步,三步,...,一直到十六次循环全都执行完,惊奇地发现51单元和80单元确实是70,这下更困惑了,为什么单步运行就能得到预期的结果,连续运行就不行呢?别急,故事还没有结束,继续单步运行,咦,程序跳到了什么地方?呵呵,欢迎来到天涯海角^_^。这是程序其实的地方,也就是刚才我们设置断点之后连续运行跳过的地方,程序回到了这里,还能说明什么——这个总程序是在永远循环执行的,他不会停止,一遍完成,再来一遍。哎,探索真理的路程真是艰辛而又充满趣味:)。好了,现在总算可以解答那个玄妙的问题了:为什么51单元、80单元中的指针地址会和81单元中不一样。81单元中的值是for循环结束后指针p的最后地址(6F)加1(70),这一点毋庸置疑,而51单元和80单元在总程序第二遍、第三遍、……不断的重复执行过程中又被改变了,在这期间,81单元虽然也是每次都被重写,可是都是被同一个值70重写,所以不会有变化。总程序不会这样一遍再一遍无穷无尽的执行下去的,记得我们在 __RESET_WATCHDOG(); 这一句话的注释里买了个关子,现在是揭示谜底的时候了,因为没有喂看门狗,在程序这样一遍遍反复执行的时候,总会在某一处使得看门狗计数器溢出复位,这样整个程序就停了下来,可是停的时候for循环并不一定执行完成了16次,它可能是在执行任何一次循环的时候看门狗复位了,这个时候51单元和80单元的值就成了当前指针p指向的地址,这里就是0x64,而此时81单元还是上一次for循环结束后的值70,当然不会一样。问题解决了,但是并不完整,如果我们禁止掉看门狗会怎么样呢?去掉__RESET_WATCHDOG(); 前面的引用,在执行程序,这个时候看到CPU Cycles不断的增加,程序一直在运行,打开内存窗口,会开到51单元和80单元的值在不断的变化(这里有个小技巧,全屏内存窗口以后,点击右边的滚动条,窗口里面的值就会不断刷新了),当然,你也可以人为让程序停在某个地方,而这个时候51和80单元的值是不定的,两者可能相等,也可能相差1,而81单元则一定是70。到这里问题总算得到了圆满的解决。*/

/* 总结一下要点:

1. byte默认为一个字节,但是byte*默认为两个字节。

2. codewarrior中的c语言程序是不断的循环运行的,即使程序本身没有循环,在一遍执行完成后它也会自动从头再开始,直到看门狗计数器溢出复位(如果看门狗没被有关掉的话)。

3. 三种方法把一个值放置到指定内存单元:

1)宏定义。例如:

#define value1 (*(volatile unsigned char*)0x0040) //定义变量value1在40单元[4]。

2)使用@关键字,这时变量只能是全局的。例如:

volatile unsigned char value2 @ 0x0041;

3)使用强制类型转换把一个十六进制数换成地址值用来存放一个值,例如:

*((unsigned char*)0x0042)=value3; //当然,value3应该是之前定义好的、并且已经被赋值的。

发挥一下想象力,我们甚至可以这么写:

for(i=0;i<0x10;++i)

*((unsigned char*)0x0043+i)=value4[i]; // 看出来了吗?我们把一个数组中的每一个值分别放到

// 43开始的每一个内存单元;这里数组value4[]也应该

// 是之前就有了的。

*/

//----------------------------------------------------------------------------------------------------------

/* [1]其实这里(volatile byte*)的作用类似C++中的reinterpret_cast,也可具体写作

byte* pointer=new (reinterpret_cast<void*>(0x0058)) byte

不同的是这里的pointer是指针,而程序中定义的c仅仅是一个byte类型的变量。可以参考The C++ programming language(special edition

10.4.11)

[4]宏定义并没有真正的定义变量,它只是一个编译的命令。它告诉编译器,在编译的时候把后面程序中所有的value都替换成(*(volatile unsigned char*)0x0040)。

*/

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有