本帖最后由 cangyuan 于 2021-7-18 18:49 編輯
一、概述
1、結合8051介紹單片機C語言的優越性:
·無須懂得單片機的具體硬件,也能夠編出符合硬件實際的專業水平的程序;
·不懂得單片機的指令集,也能夠編寫完美的單片機程序;
·不同函數的數據實行覆蓋,有效利用片上有限的RAM空間;
·提供auto、static、const等存儲類型和專門針對8051單片機的data、idata、pdata、xdata、code等存儲類型,自動為變量合理地分配地址;
·C語言提供復雜的數據類型(數組、結構、聯合、枚舉、指針等),極大地增強了程序處理能力和靈活性;
·提供small、compact、large等編譯模式,以適應片上存儲器的大。
·中斷服務程序的現場保護和恢復,中斷向量表的填寫,是直接與單片機相關的,都由C編譯器代辦;
·程序具有堅固性:數據被破壞是導致程序運行異常的重要因素。C語言對數據進行了許多專業性的處理,避免了運行中間非異步的破壞
·提供常用的標準函數庫,以供用戶直接使用;
·有嚴格的句法檢查,錯誤很少,可容易地在高級語言的水平上迅速地被排掉;
·可方便地接受多種實用程序的服務:如片上資源的初始化有專門的實用程序自動生成;再如,有實時多任務操作系統可調度多道任務,簡化用戶編程,提高運行的安全性等等。
·頭文件中定義宏、說明復雜數據類型和函數原型,有利于程序的移植和支持單片機的系列化產品的開發;
2、HEX文件
建立了第一個單片機C語言項目,但為了讓編譯好的程序能通過編程器寫入51芯 片中,要先用編譯器生成HEX文件
3、C 編譯器所支持的注釋語句: 一種是以“//”符號開始的語句,符號之后 的語句都被視為注釋,直到有回車換行。另一種是在“/*”和“*/”符號之內的為注釋。注 釋不會被 C 編譯器所編譯。
4、main函數:
一個 C 應用程序中應有一個 main 主函數,main 函數能調用別的功能函數,但其它功能函數不允許調用 main 函數。不論 main 函數放在程序中的那個位置, 總是先被執行。
5、最小系統
其中加了一個電阻和一個 LED,用以顯示它的狀態,晶體震蕩器能根據自己的情況使用, 一般實驗板上是用 11.0592MHz 或 12MHz,使用前者的好外是能產生標準的串行口波特率,后 者則一個機器周期為 1 微秒,便于做精確定時。
二、C51常量
1、常量數據類型說明:
(1)整型常量能表示為十進制如 123,0,-89 等。十六進制則以 0x 開頭如 0x34,-0x3B 等。長整型就在數字后面加字母 L,如 104L,034L,0xF340 等。
(2)浮點型常量可分為 十進 制和指數表示形式。指數表 示形式為[±]數字[.數字]e[±]數字,[]中的內容為可選項,其中內容根據具體情 況可有可無,但其余部分必須有,如125e3,7e9,-3.0e-3。
(3)字符型常量是單引號內的字符,如‘a’,‘d’等,不能顯示的控制字符,能 在該字符前面加一個反斜杠“\”組成專用轉義字符。常用轉義字符表請看表
轉義字符 | 含義 | ASCII 碼(16/10 進制) | \o | 空字符(NULL) | 00H/0
| \n | 換行符(LF) | 0AH/10
| \r | 回車符(CR) | 0DH/13 | \t | 水平制表符(HT) | 09H/9 | \b | 退格符(BS) | 08H/8 | \f | 換頁符(FF) | 0CH/12 | \' | 單引號 | 27H/39 |
\" | 雙引號 | 22H/34 | \\ | 反斜杠 | 5CH/92 | (4)字符串型常量由雙引號內的字符組成,如“test”,“OK”等。當引號內的沒有字 符時,為空字符串。在使用特殊字符時同樣要使用轉義字符如雙引號。在 C 中字符 串常量是做為字符類型數組來處理的,在存儲字符串時系統會在字符串尾部加上\o 轉義字符以作為該字符串的結束符。字符串常量“A”和字符常量‘A’是不一樣的, 前者在存儲時多占用一個字節的字間。
(5)位標量,它的值是一個二進制。
2、應用
常量可用在不必改變值的場合,如固定的數據表,字庫等。常量的定義方式有幾種,下面來加以說明。
#difine False 0x0;//用預定義語句能定義常量
#difine True 0x1;//這里定義 False 為 0,True 為 1
//在程序中用到 False 編譯時自動用 0 替換,同理 True 替換為 1
unsigned int code a=100;//這一句用 code 把 a 定義在程序存儲器中并賦值
const unsigned int c=100;//用 const 定義 c 為無符號 int 常量并賦值 以上兩句它們的值都保存在程序存儲器中,而程序存儲器在運行中是不允許被修改的,所以如果在這兩句后面用了類似 a=110,a++這樣的賦值語句,編譯時將會出錯。
三、C51變量
1、變量格式
[存儲種類] 數據類型 [存儲器類型] 變量名表
在定義格式中除了數據類型和變量名表是必要的,其它都是可選項。
2、存儲種類
存儲種類有四種:自動(auto),外部(extern),靜態(static)和寄存器(register),缺省類型為自動(auto)。
(1)static(靜態局部)變量
在程序整個運行期間都不會釋放內存。如果定義局部變量的時候不賦值,則編譯的時候自動賦值為0。而對于自動變量而言,定義的時候不賦值,則是一個不確定的值。其他函數不能引用。
(2)用extern聲明外部變量
一個程序能由多個源程序文件組成。如果一個程序中需要引用另外一個文件中已經定義的外部變量,就需要使用extern來聲明。
例: 一個文件中: int abc;
另外一個文件中: extern abc;
3、數據類型
(1)數據類型
數據類型 | 長 度 | 值 域 | unsigned char | 單字節 | 0~255 | signed char | 單字節 | -128~+127 | unsigned int | 雙字節 | 0~65535 | signed int | 雙字節 | -32768~+32767 | unsigned long | 四字節 | 0~4294967295 | signed long | 四字節 | -2147483648~+2147483647 | float | 四字節 | ±1.175494E-38~±3.402823E+38 | * | 1~3 字節 | 對象的地址 | bit | 位 | 0 或 1 | sfr | 單字節 | 0~255 | sfr16 | 雙字節 | 0~65535 | sbit | 位 | 0 或 1 | 字節中最高位字節表示數據的符號,“0”表示正數,“1”表示負數, 負數用補碼表示。
(2)特殊
bit 位標量是 c51 編譯器的一種擴充數據類型,利用它可定義一個位標量,但不能定義 位指針,也不能定義位數組。它的值是一個二進制位,不是 0 就是 1,類似一些高級語 言中的 Boolean 類型中的 True 和 False。
sfr 也是一種擴充數據類型,點用一個內存單元,值域為 0~255。利用它能訪問 51 單片機內部的所有特殊功能寄存器。
sfr16 占用兩個內存單元,值域為 0~65535。sfr16 和 sfr 一樣用于操作特殊功能寄存 器,所不一樣的是它用于操作占兩個字節的寄存器,如定時器 T0 和 T1。sfr16 T2 = 0xCC; //這里定義8052定時器2,地址為T2L=CCH,T2H=CDH。用sfr16定義16位特殊功能寄存器時,等號后面是它的低位地址,高位地址一定要位于物理低位地址之上。注意的是不能用于定時器0和1的定義。
sbit 同樣是 單片機c語言 中的一種擴充數據類型,利用它能訪問芯片內部的 RAM 中的可尋址位或特殊功能寄存器中的可尋址位。如先前定義了sfr P1=0x90;//因 P1 端口的寄存器是可位尋址的,所以能定義sbit P1_1=P1^1;//P1_1 為 P1 中的 P1.1 引腳。同樣我們能用 P1.1 的地址去寫,如 sbit P1_1=0x91;這樣在以后的程序語句中就能用 P1_1 來對 P1.1 引腳進行讀寫操作了。
(3)重新定義數據類型的的語句typedef
typedef 的語法:typedef 已有的數據類型 新的數據類型名
樣寫:typedef int integer; integer a,b;
typedef 不能直接用來定義變量,它只是對已有的數據類型作一個名字上的置換,并不是產生一個新的數據類型。
4、存儲器類型
指定該變量在單片機c語言硬件系統中所使用的存儲區域,并在編譯時準確的定位。
存儲器類型 | 說明 | data | 直接訪問內部數據存儲器(128字節),訪問速度最快 | bdata | 可位尋址內部數據存儲器(16字節),允許位與字節混合訪問 | idata | 間接訪問內部數據存儲器(256字節),允許訪問全部內部地址 | pdata | 分頁訪問外部數據存儲器(256字節),用MOVX @Ri指令訪問 | xdata | 外部數據存儲器(64KB),用MOVX @DPTR指令訪問 | code | 程序存儲器(64KB),用MOVC @A+DPTR指令訪問 | 注意的是在AT89c51芯片中RAM只有低128位,位于80H到FFH的高128位則在52芯片中才有用,并和特殊寄存器地址重疊。如果省略存儲器類型,系統則會按編譯模式SMALL,COMPACT或LARGE所規定的默認存儲器類型去指定變量的存儲區域。無論什么存儲模式都能聲明變量在任何的8051存儲區范圍,然而把最常用的命令如循環計數器和隊列索引放在內部數據區能顯著的提高系統性能。還有要指出的就是變量的存儲種類與存儲器類型是完全無關的。
(1)特殊寄存器(SFR)的地址
AT89C51特殊功能寄存器列表(適用于同一架構的芯片)
符 號 | 地 址 | 注 釋 | *ACC | E0H | 累加器 | *B | F0H | 乘法寄存器 | *PSW | D0H | 程序狀態字 | SP | 81H | 堆棧指針 | DPL | 82H | 數據存儲器指針低8位 | DPH | 83H | 數據存儲器指針高8位 | *IE | A8H | 中斷允許控制器 | *IP | D8H | 中斷優先控制器 | *P0 | 80H | 端口0 | *P1 | 90H | 端口1 | *P2 | A0H | 端口2 | *P3 | B0H | 端口3 | PCON | 87H | 電源控制及波特率選擇 | *SCON | 98H | 串行口控制器 | SBUF | 99H | 串行數據緩沖器 | *TCON | 88H | 定時器控制 | TMOD | 89H | 定時器方式選擇 | TL0 | 8AH | 定時器0低8位 | TL1 | 8BH | 定時器1低8位 | TH0 | 8CH | 定時器0高8位 | TH1 | 8DH | 定時器1高8位 | 帶*號的特殊功能寄存器都是可以位尋址的寄存器
(2)數據存儲模式
①Small模式:所有缺省變量參數均裝入內部RAM,優點是訪問速度快,缺點是空間有限,只適用于小程序。
②Compact模式:所有缺省變量均位于外部RAM區的一頁(256Bytes),具體哪一頁可由P2口指定,在STARTUP.A51文件中說明,也可用pdata指定,優點是空間較Small為寬裕速度較Small慢,較large要快,是一種中間狀態。
③large模式:所有缺省變量可放在多達64KB的外部RAM區,優點是空間大,可存變量多,缺點是速度較慢。
5、Keil c51指針變量
單片機c語言支持一般指針(Generic Pointer)和存儲器指針(Memory_Specific Pointer)。
(1)一般指針
一般指針的聲明和使用均與標準C相同,不過同時還能說明指針的存儲類型,例如:char * xdata ptr;ptr為一個指向char數據的指針,而ptr本身放于外部RAM區。一般指針本身用3個字節存放,分別為存儲器類型,高位偏移,低位偏移量。
(2)存儲器指針
基于存儲器的指針說明時即指定了存貯類型,例如:char data * str;str指向data區中char型數據;這種指針存放時,只需一個字節或2個字節就夠了,因為只需存放偏移量。
(3)指針轉換
當基于存儲器的指針作為一個實參傳遞給需要一般指針的函數時,指針自動轉化。如果不說明外部函數原形,基于存儲器的指針自動轉化為一般指針,導致錯誤,因而請用“#include”說明所有函數原形。
6、單片機c語言中變量的空間分配幾個方法
(1)data區空間小,所以只有頻繁用到或對運算速度要求很高的變量才放到data區內,比如for循環中的計數值。
data區內最好放局部變量。局部變量空間在退出該函數是就釋放,當然靜態局部變量除外,其內存使用方式與全局變量相同;
(2)確保你的程序中沒有未調用的函數。在Keil C里遇到未調用函數,編譯器就將其認為可能是中斷函數。函數里用的局部變量的空間是不釋放,也就是同全局變量一樣處理。
(3)程序中遇到的邏輯標志變量能定義到bdata中,能大大降低內存占用空間。
(4)其他不頻繁用到和對運算速度要求不高的變量都放到xdata區。如果想節省data空間就必須用large模式,將未定義內存位置的變量全放到xdata區。當然最好對所有變量都要指定內存類型。
(5)當使用到指針時,要指定指針指向的內存類型。未定義指向內存類型的通用指針占用3個字節;而指定指向data區的指針只占1個字節;指定指向xdata區的指針占2個字節。如指針p是指向data區,則應定義為:char data *p;。還可指定指針本身的存放內存類型,如:char data *xdata p;
四、C51運算符和表達式
運算符按其表達式中與運算符的關系可分為單目運算符,雙目運算符和三目運算符。單目就是指需要有一個運算對象,雙目就要求有兩個運 算對象,三目則要三個運算對象。表達式則是由運算及運算對象所組成的具有特定含義的式子。表達式后面加“;”號就構成了一個表達式語句。 1、運算符和表達式
①賦值運算符:變量=表達式;
②算術運算符:+,/,-,*,%;除法運算符和一般的算術運算規則有所不一樣,如是兩浮點數相除,其結果為浮點數,如10.0/20.0 所得值為 0.5,而兩個整數相除時,所得值就是整數,如7/3,值為2。
③++ 增量運算符,-- 減量運算符。這兩個運算符是 C 語言中特有的一種運算符。在 VB,PASCAL 等都是沒有的。I++(或I--)是先使用I的值,再執行 I+1(或 I-1);++I(或--I)是先執行I+1(或 I-1),再使用I的值。
④關系運算符:>,<,>=,<=,==,。。“==”在 VB 或 PASCAL 等中是用“=”,“!=”則是用“not”。
⑤邏輯運算符:邏輯與:條件式1&&條件式2;邏輯或:條件式1||條件式2;邏輯非: !條件式2。注意的是用邏輯運算符的運算結果只有0和1兩種,也就是邏輯的真與假,換句話說也就是邏輯量。
⑥位運算符:~,<<,>>,&,^,|。位運算符的作用是按位對變量進行運算,但是并不改變參與運算的變量的值。如果要求按位改變變量的值,則要利用相應的賦值運算。位運算符是不能用來對浮點型數據進行操作的。
⑦復合賦值運算符:在賦值運算符“=”的前面加上其他運算符。+=,-=,*=,/=,>>=,&=,|=,^=,%=,!=,<<=。a+=56等價于a=a+56;y/=x+9 等價于y=y/(x+9)。
⑧逗號運算符
⑨條件運算符:邏輯表達式? 表達式1 : 表達式2。當邏輯表達式的值為真時(非0值)時,整個表達式的值為表達式1的值;當邏輯表達式的值為假(值為0)時,整個表達式的值為表達式2的值。
⑩指針和地址運算符:*: 取內容;&:取地址;取內容和地址的一般形式分別為:變量=*指針變量;指針變量=&目標變量。 ⑪sizeof 運算符:語法:sizeof(數據類型)。例句:printf("char 是多少個字節?½字節\n",sizeof(char)),結果是:char 是多少個字節?1字節。
⑫強制類型轉換運算符:語句:(類型) 表達式。
注:程序進行編譯時由編譯器自動去處理完成的轉換稱為隱式轉換。其規則如下:
變量賦值時發生的隱式轉換,“=”號右邊的表達式的數據類型轉換成左邊變量的數據類型。
所有 char 型的操作數轉換成 int 型。
兩個具有不一樣數據類型的操作數用運算符連接時,隱式轉換會按以下次序進行:如有一操作數是float類型,則另一個操作數也會轉換成float類型;如果一個操作數為long類型,另一個也轉換成long;如果一個操作數是 unsigned 類型,則另一個操作會被轉換成 unsigned 類型。
2、運算符優先級和結合性
級別 | 類 別 | 名 稱 | 運算符 | 結合性 | 1 | 強制轉換、數組、結構、聯合 | 強制類型轉換
下標
存取結構或聯合成員
| ( )
[ ]
->或.
| 右結合 | 2 | 邏 輯
字 位
增 量
減 量
指 針
算 術
長度計算
| 邏輯非
按位取反
加一
減一
取地址、取內容
單目減
長度計算
| !
~
++
--
&、*
-
sizeof
| 左結合 | 3 | 算 術 | 乘
除
取模
| *
/
%
|
右結合
| 4 | 算術和指針運算 | 加
減
| +
-
| 右結合
| 5 | 字 位 | 左移
右移
| <<
>>
| 右結合
| 6 | 關系 | 大于等于
大于
小于等于
小于
| >=
>
<=
<
|
右結合
| 7 | 關系 | 恒等于
不等于
| ==
!=
| 右結合
| 8 | 字 位 | 按位與 | & | 右結合 | 9 | 字 位 | 按位異或 | ^ | 右結合 | 10 | 字 位 | 按位或 | | | 右結合 | 11 | 邏 輯 | 邏輯與 | && | 左結合 | 12 | 邏 輯 | 邏輯或 | || | 左結合 | 13 | 條 件 | 條件運算 | ?: | 左結合 | 14 | 賦 值 | 賦值
復合賦值
| =
Op=
| 左結合 | 15 | 逗 號 | 逗號運算 | , | 右結合 |
五、C51表達式語句
1、不一樣的程序設計語言都會有不一樣的表達式語句,如VB就是在表達式后面加入回車就構成了VB 的表達式語句,而在51單片機的C語言中則是加入分號“;”構成表達式語句。
2、一個特殊的表達式語句,稱為空語句
通常用while,for 構成的循環語句后面加一個分號,形成一個不執行其它操作的空循環體。
示例:#include <AT89x51.h> void main(void) { unsigned int a; do { P1 = 0xFF; //關閉 P1 上的 LED while(P3_7); //空語句,等待 P3_7 按下為低電平,低電平時執行下面的語句 P1 = 0; //點亮 LED for(;a<60000;a++); //這也是空語句的使用方法,注意 a 的初值為當前值 } //這樣第一次按下時會有一延時點亮一段時間,以后按多久就亮多久 while(1); //點亮一段時間后關閉再次判斷 P3_7,如此循環 } 3、復合語句:將若干條語句組合在一起形成一種功能塊,這種由若干條語句組合 而成的語句就叫復合語句。它內部的各條語句還是需要以分號“;” 結束。復合語句是允許嵌套的。對于一個函數而言,函數體就是一個復合語句,也許大家會因 此知道復合語句中不單能用可執行語句組成,還能用變量定義語句組成。
4、條件語句:
C 語言供給了 3 種形式的條件語句: ①if(條件表達式) 語句。當條件表達式的結果為真時,就執行語句,不然就跳過。 如if(a==b) a++; 當a等于b時,a就加1 ②if(條件表達式) 語句1 else 語句2。當條件表達式成立時,就執行語句1,不然就執行語句2。如if(a==b) a++;else a--;當a等于b時,a加1,不然a-1。 ③if(條件表達式1) 語句1; else if(條件表達式2) 語句2; else if(條件表達式3) 語句3; else if(條件表達式m) 語句n; else 語句m; 5、開關語句: 語法:switch(表達式)
{case 常量表達式1: 語句1;break; case 常量表達式2: 語句2;break; case 常量表達式3: 語句3;break; case 常量表達式n: 語句n;break; default:語句}
6、循環語句:在C語言中構成循環控制的語句有while,do-while,for和goto語句。
(1)goto語句
一個無條件的轉向語句。語法:goto 語句標號; 其中的語句標號為一個帶冒號的標識符。示例如下:
void main(void) { unsigned char a; start: a++; if (a==10) goto end; goto start; end:; }
常見的 goto 語句使用方法是用它來跳出多重循環,不過它只能從內層循環 跳到外層循環,不能從外層循環跳到內層循環。(2)while語句
語法:while(條件表達式) 語句;
當條件表達式為真時,它才執行后面的語句,執行完后再次回到 while 執行條件判斷,為真時重復執行語句,為假時退出循環體。當條件一開始就為假時, 那么 while 后面的循環體(語句或復合語句)將一次都不執行就退出循環。在調試程序時要注意 while 的判斷條件不能為假而造成的死循環,調試時適當的在 while 處加入斷點,也許 會使你的調試工作更加順利。 (3)do while語句
語法:do 語句 while(條件表達式);
先執行循環體,再根據條件判斷是否要退出循環。
(4)for語句
for([初值設定表達式];[循環條件表達式];[條件更新表達式]) 語句;語句中括號中的表達式是可選的。
7、continue 語句(也叫中斷語句)
一個無條件跳轉語句。作用是結束本次循環,跳過循環體中沒有執行的語句,跳轉到下一次循環周期。和前面說到的break語句有所不一樣,continue執行后不是跳出循環,而是跳到循環的開始并執行下一次的循環。
8、return語句
語法:return (表達式);返回時先計算表達式,再返回表達式的值。不帶表達式則返回的值不確定。
六、C51函數
1、函數的定義
定義的模式如下: 函數類型 函數名稱(形式參數表) { 函數體 } 函數類型是說明所定義函數返回值的類型。返回值其實就是一個變量,只要按變量類型來定義函數類型就行了。如函數不需要返回值函數類型能寫作“void”表示該函數沒 有返回值。注意的是函數體返回值的類型一定要和函數類型一致。形式參數是指調用函數時要傳入到函數體內參與運算的變量,它能有一個、幾個或沒有,當不需要形式參數也就是無參函數,括號內能為空或寫入“void”表示,但括號不能少。函數體中能包含有局部變量的定義和程序語句,如函數要返回運算值則要使用return語句進行返回。在函數的{}號中也能什么也不寫,這就成了空函數,在一個程序項目中能寫一些空函數,在以后的修改和升級中能方便的在這些空函數中進行功能擴充。 2、函數調用
(1)在調用函數前,必須對函數的類型進行說明,就算是標準庫函數也不例外。標準庫函數的說明會被按功能分別寫在不一樣的頭文件中,使用時只要在文件最前面用#include< .h> 預處理語句引入相應的頭文件。
(2)函數語句。如printf("Hello World!\n"); 這是在我們的第一個程序中出現的,它以"Hello World!\n"為參數調用printf這個庫函數。在這里函數調用被看作了一條語句。
(3)函數參數:如 temp=StrToInt(CharB(16));CharB 的返回值作為 StrToInt 函數的實際參數傳遞。
(4)函數表達式:temp=Count();這樣一句,這個時候函數的調用作為一個運算對象出現在表達式中,能稱為函數表達式。
(5)調用的是自定義的函數則要用如下形式編寫函數類型說明:類型標識符 函數的名稱(形式參數表); 這樣的說明方式是用在被調函數定義和主調函數是在同一文件中。
(6)寫到 文件名.h 的文件中用#include "文件名.h"引入。
如果被調函數的定義和主調函數不是在同 一文件中的,則要用如下的方式進行說明,說明被調函數的定義在同一項目的不一樣文件之上
extern 類型標識符 函數的名稱(形式參數表);
3、中斷服務函數
擴展的關鍵字是 interrupt,它是函數定義時的一個選項。
形式:函數類型 函數名(形式參數) interrupt n [using n]
AT89C51芯片中斷號和中斷向量
中斷號 | 中斷源 | 中斷向量 | 0 | 外部中斷 0 | 0003H | 1 | 定時器/計數器 0 | 000BH | 2 | 外部中斷 1 | 0013H | 3 | 定時器/計數器 1 | 001BH | 4 | 串行口 | 0023H | 例:
- #include <at89x51.h>
- unsigned char P3State(void); //函數的說明,中斷函數不用說明
- void main(void)
- {
- IT0 = 0; //設外部中斷 0 為低電平觸發
- EX0 = 1; //允許響應外部中斷 0
- EA = 1; //總中斷開關
復制代碼七、C51數組的使用
例:unsigned int xcount [10]; //定義無符號整形數組,有 10 個數據單元 char inputstring [5]; //定義字符形數組,有 5 個數據單元 float outnum [10],[10];//定義浮點型數組,有 100 個數據單元 unsigned char LEDNUM[2]={12,35}; //一維數組賦初值 int Key[2][3]={{1,2,4},{2,2,1}}; //二維數組賦初值 unsigned char IOStr[]={3,5,2,5,3}; //沒有指定數組長度,編譯器自動設置 unsigned char code skydata[]={0x02,0x34,0x22,0x32,0x21,0x12}; //數據保存在 code 區
八、C51指針的使用
指針變量的內容是另一個變量的地址,地址所屬的變量稱為指針變量所指向的變量。
方法是先用&STR 取變量地址并賦于 STRIP 指針變量,然后就能用*STRIP 來對 STR 進行訪問了。‘*’是指針運算符,用它能取得指針變量所指向的地址的值。
形式:數據類型 [存儲器類型] *變量名;
例: unsigned char xdata *pi;//指針會占用二字節,指針自身存放在編譯器默認存儲區,指向xdata存儲區的char類型
unsigned char xdata * data pi; //除指針自身指定在 data 區,其它同上 int * pi; //定義為一般指針,指針自身存放在編譯器默認存儲區,占三個字節 指針變量最大的值為 0xFFFF,這樣就決定了一般指針在內存會占用3個字節,第一字節存放該指針存儲器類型編碼,后兩個則存放該指針的高低位址。而基于存儲器的指針因為不用識別存儲器類型所以會占一或二個字節,idata,data,pdata存儲器指針占一個字節,code,xdata則會占二個字節。
九、C51結構、聯合和枚舉的使用
1、結構:一種數據的集合體
(1)結構類型一般定義格式:struct 結構名 {結構元素表};
例:truct FileInfo{unsigned char FileName[4]; unsigned long Date; unsigned int Size;}
(2)定義結構變量格式:struct 結構名 結構變量名1,結構變量名2,……結構變量N;
例:struct FileInfo NewFileInfo, OleFileInfo;
只有結構變量才能參與程序的執行,結構類型只是用于說明結構變量是屬于那一種結構。通過上面的定義 NewFileInfo 和 OleFileInfo 都是 FileInfo 結構,都具有一個字符型數組一個長整型和一個整形數據。定義結構類型只是給出了這個結構的組織形式,它不會占用存儲空間,也就說結構名是不能進行賦值和運算等操作的。結構變量則是結構中的具體成員, 會占用空間,能對每個成員進行操作。結構是允許嵌套的,也就是說在定義結構類型時,結構的元素能由另一個結構構成。
例: struct clock{unsigned char sec, min, hour;}
struct date{ unsigned int year; unsigned char month,day; struct clock Time; //這是結構嵌套 } struct date NowDate; //定義 data 結構變量名為 NowDate (3)引用、賦值
格式:結構變量名.結構元素。
要存取上例結構變量中的月份時,就要寫成 NowDate.year=2021。
NowDate.Time.min++; //分針加 1,嵌套時只能引用最低一級元素一個結構變量中元素的名字能和程序中其他地方使用的變量同名,因為元素是屬于它所在的結構中,使用時要用成員運算符指定。
(4)其他定義方式
struct{結構元素表} 結構變量名1,結構變量名2……結構變量名N; 例:struct{unsigned char FileName[4]; unsigned long Date; unsigned int Size;} NewFileInfo, OleFileInfo; 這一種定義方式定義沒有使用結構名,稱為無名結構。通常會用于程序中只有幾個確定 的結構變量的場合,不能在其它結構中嵌套。 struct 結構名{結構元素表} 結構變量名1,結構變量名2……結構變量名N; 例:struct FileInfo{unsigned char FileName[4]; unsigned long Date; unsigned int Size;} NewFileInfo, OleFileInfo; 使用結構名能便于閱讀程序和便于以后要在定義其它結構中使用。 2、枚舉:把某些整型常量的集合用一個名字表示,其中的整型常量就是這種枚舉類型變量的可取的合法值。
(1)定義格式
enum 枚舉名 {枚舉值列表} 變量列表;例:enum TFFlag {False, True} TFF;
enum 枚舉名 {枚舉值列表}; emum 枚舉名 變量列表;例:enum Week {Sun,Mon,Tue,Wed,Thu,Fri,Sat};enum Week OldWeek,NewWeek;
(2)在枚舉列表中,每一項名稱代表一個整數值,在默認的情況下,編譯器會自動為每一項賦值,第一項賦值為0,第二項為1……如Week中的Sun為0,Fri為5。C語言也允許對各項值做初始化賦值,要注意的是在對某項值初始化后,它的后續的各項值也隨之遞增。
例: enum Week {Mon=1, Tue, Wed, Thu, Fri, Sat, Sun};
3、聯合
(1)它和其他結構類型一樣能包含不一樣類型的數據元素。所不一樣的是其他類型的數據元素都是從同一個數據地址開始存放,結構變量占用的內 存大小是該結構中數據元素所占內存數的總和,而聯合變量所占用內存大小只是該聯合中最長的元素所占用的內存大小。
(2)程序先為聯合中的 int 賦值 1000,后來又為 char 賦值 10,那么這個時候就不能引用int了,不然程序會出錯,起作用的是最后一次賦值的元素,而上一次賦值的元素就失效了。使用中還要注意定義聯合變量時不能對它的值初始化、能使用指向聯合變量的指針對其操作、聯合變量不能作為函數的參數進行傳遞,數組和結構能出現在聯合中。
(3)定義形式:union 結構名{結構元素表};
union 結構名 結構變量名1,結構變量名2,……結構變量N;
union {結構元素表} 結構變量名1,結構變量名2……結構變量名N;
union 結構名{結構元素表} 結構變量名1,結構變量名2……結構變量名N;
|