51單片機基礎剖析(基于C語言)
在編寫應用程序時,定義一個變量,一個數組,或是說一個固定表格,到底存儲在什么地方;當定義變量大小超過MCU的內存范圍時怎么辦;如何控制變量定義不超過存儲范圍;
本文引用地址:http://www.104case.com/article/201611/318636.htm以及如何定義變量才能使得變量訪問速度最快,寫出的程序運行效率最高。以下將一一解答。
1.六類存儲類型 code data idata xdata pdata bdata
code:程序存儲器,也即只讀存儲器,用來保存常量或是程序,采用16位地址線編碼,可以是在片內,或是片外,大小被限制在64KB。
作用:定義常量,如八段數碼表或是編程使用的常,在定義時加上code或明確指明定義的常量保存到code memory(只讀。)比如:
char code table[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
此關鍵字的使用方法等同于const。
data:數據存儲區,只能用于聲明變量,不能用來聲明函數,該區域位于片內,采用8位地址線編碼,具有最快的存儲速度,但是數量被限制在128byte或更少。
使用方法:unsigned char data fast_variable=0;
Idata:數據存儲區,只能用于聲明變量,不能用來聲明函數。該區域位于片內,采用8位地址線編碼,內存大小被限制在256byte或更少。該區域的低地址區與data區地址一致,高地址區域是52系列在51系列基礎上擴展的并與特殊功能寄存器具有相同地址編碼的區域。即:data memory是idata memory的一個子集。
xdata:只能用于聲明變量,不能用來聲明函數,該區域位于MCU外部,采用16位地址線進行編碼,存儲大小被限制在64KB以內。如:unsigned char xdata count=0;
pdata:只能用于聲明變量,不能用來聲明函數,該區域位于MCU外部,采用8位地址線進行編碼。存儲大小限制在256byte,是xdata memory的低256byte。為其子集。如:unsigned char pdata count=0;
bdata:只能用于聲明變量,不能用來聲明函數。該區域位于8051內部位數據地址。定義的量保存在內部位地址空間,可用位指令直接讀寫。使用方法:unsigned char bdata varab=0。
注:一般情況下,定義字符型變量時,在缺省unsigned的情況下,默認為無符號。但是本人在Keil uV3中遇到并非如此的案例。在缺省的情況下默認為有符號。要注意一下,或許不同的編譯器規則不同。所以我們在寫程序的時候,還是最好把unsigned signed加上。
2.函數的參數和局部變量的存儲模式
C51 編譯器允許采用三種存儲器模式:SMALL,COMPACT 和LARGE。一個函數的存儲器模式確定了函數的參數的局部變量在內存中的地址空間。處于SMALL模式下的函數參數和局部變量位于8051單片機內部RAM中,處于COMPACT和LARGE模式下的函數參數和局部變量則使用單片機外部RAM。在定義一個函數時可以明確指定該函數的存儲器模式。方法是在形參表列的后面加上一存儲模式。
示例如下:
#pragma large //此預編譯必須放在所有頭文前面
int func0(char x,y) small;
char func1(int x) large;
int func2(char x);
注:上面例子在第一行用了一個預編譯命令#pragma,它的意思是告訴c51編譯器在對程序進行編譯時,按該預編譯命令后面給出的編譯控制指令LARGE進行編譯,即本例程序編譯時的默認存儲模式為LARGE。隨后定義了三個函數,第一個定義為SMALL存儲模式,第二個函數定義為LARGE第三個函數未指定,在用C51進行編譯時,只有最后一個函數按LARGE存儲器模式處理,其它則分別按它們各自指定的存儲器模式處理。
本例說明,C51編譯器允許采用所謂的存儲器混合模式,即允許在一個程序中將一些函數使用一種存儲模式,而其它一些則按另一種存儲器模式,采用存儲器混合模式編程,可以充分利用8051系列單片機中有限的存儲器空間,同時還可以加快程序的執行速度。
3.絕對地址訪問(頭文件為:absacc.h(相當重要))
#define CBYTE ((unsigned char volatile code *) 0)
#define DBYTE ((unsigned char volatile data *) 0)
#define PBYTE ((unsigned char volatile pdata *) 0)
#define XBYTE ((unsigned char volatile xdata *) 0)
功能:CBYTE尋址CODE區
DBYTE尋址DATA區
PBYTE尋址XDATA(低256)區
XBYTE尋址XDATA區
例:如下指令在對外部存儲器區域訪問地址0x1000
xvar=XBYTE[0x1000];
XBYTE[0x1000]=20;
#define CWORD ((unsigned int volatile code *) 0)
#define DWORD ((unsigned int volatile data *) 0)
#define PWORD ((unsigned int volatile pdata *) 0)
#define XWORD ((unsigned int volatile xdata *) 0)
功能:與前面的一個宏相似,只是它們指定的數據類型為unsigned int。
通過靈活運用不同的數據類型,所有的8051地址空間都是可以進行訪問。例如:
DWORD[0x0004]=0x12F8;// 即內部數據存儲器中(0x08)=0x12; (0x09)=0xF8
注:用以上八個函數,可以完成對單片機內部任意ROM和RAM進行訪問,非常方便。還有一種方法,那就是用指鐘,后面會對C51的指針有詳細的介紹。
4.寄存器變量(register)
為了提高程序的執行效率,C語言允許將一些頻率最高的那些變量,定義為能夠直接使用硬件寄存器的所謂的寄存器變量。定義一個變量時,在變量類型名前冠以“register” 即將該變量定義成為了寄存器變量。寄存器變量可以認為是一自動變量的一種。有效作用范圍也自動變量相同。由于計算機寄存器中寄存器是有限的。不能將所有變量都定義成為寄存器變量,通常在程序中定義寄存器變量時,只是給編譯器一個建議,該變量是否真正成為寄存器變量,要由編譯器根據實際情況來確定。另一方面,C51編譯器能夠識別程序中使用頻率最高的變量,在可能的情況下,即使程序中并未將該變量定義為寄存器變量,編譯器也會自動將其作為寄存器變量處理。被定義的變量是否真正能成為寄存器變量,最終是由編譯器決定的。
5.內存訪問的實現
(1)指鐘
指鐘本身是一個變量,其中存放的內容是變量的地址,也即特定的數據。8051的地址是16位的,所以指針變量本身占用兩個存儲單元。指針的說明與變量的說明類似,僅在指針名前加上“*”即可。
如: int *int_point; //聲明一個整型指針
char *char_point; //聲明一個字符型指針
利用指針可以間接存取變量。實現這一點要用到兩個特殊運算符
& 取變量地址
* 取指針指向單元的數據
示例一:
int a=15,b;
int *int_point; //定義一個指向整型變量的指針
int_point=&a; //int_point指向 a
*int_point=5; //給int_point指向的變量a 賦值5 等同于a=5;
示例二:
char i,table[6],*char_point;
char_point=table;
for(i=0;i<6;i++)
{
char_point=i;
char_point++;
}
注:指針可以進行運算,它可以與整數進行加減運算(移動指針)。但要注意,移動指針后,其地址的增減量是隨指針類型而異的,如,浮點指針進行自增后,其內部將在原有的基礎上加4,而字符指針當進生自增的時候,其內容將加1。原因是浮點數,占4個內存單元,而字符占一個字節。
宏晶科技最新一代STC12C5A360S2系列,每一個單片機出廠時都有全球唯一身份證號碼(ID號),用戶可以在單片機上電后讀取內部RAM單元F1H~F7H的數值,來獲取此單片機的唯一身份證號碼。使用MOV @Ri指令來讀取。下面介紹C51獲取方法:
char id[7]={0};
char i;
char idata *point;
for(i=0;i<7;i++)
{
id[i]=*point;
point++;
}
(此處只是對指針做一個小的介紹,達到訪問內部任何空間的方式,后述有對指針使用的詳細介紹)
(2)對SFR,RAM ,ROM的直接存取
C51提供了一組可以直接對其操作的擴展函數
若源程序中,用#include包含頭文件,io51.h 后,就可以在擴展函數中使用特殊功能寄存器的地址名,以增強程序的可讀性:
注 此方法對SFR,RAM,ROM的直接存取不建議使用.因為,淡io51.h這個頭文件在KEIL中無法打開,可用指針,或是采用absacc.h頭文件,
(3) PWM與PCA
STC12系列有兩路PWM/PCA
PWM:(Pulse Width Modulation)脈寬調制,是一種使用程序來控制波形占空比,周期,相位波形的技術。
PCA:(Programmable Counter Array)可編程計數陣列,它比通常的定時/計數器的定時能力強,需要CPU的干預少。其優勢一是軟件簡單,二是精度大有提高。
*6.動態內存分配的實現
在單片機的實際開發中,很多情況下我么需要開辟一塊內存,但是具體開辟多大,也就是內存的字節數我們還無法確定,比如可能要等到上位機的指令發送下來才能確定,這個時候我們就得動態分配內存。注意,單片機內部存儲資源是極其有限的,不允許開發人員開辟出一塊很大的存儲區來備用。在VC 6.0環境下很容易用malloc()來得到一塊RAM,但是由于單片機內部沒有操作系統(如何在51上跑uC/OS-II我以后會寫出來),所以在51上實現動態內存分配就是個難點也是一個重點問題。下面給出代碼,詳細分析大家可以參考求是科技編的《8051系列單片機C程序設計完全手冊》這本書。
#include
#include
……
void main (void)
{
char *ptr1;
init_mempool (0x1000,0x500); //內存池初始化,0x1000為起始地址,0x500為內存大小
ptr1=malloc(30); /*動態為指針變量分配長度為30字節的存儲空間*/
……
//此處為你的代碼
……
free(ptr1) ; //注意,動態內存用完之后務必要釋放,否則程序將會出錯
while (1);
}
二、變量類型及其作用域剖析
變量可分為 1.局部變量;2.全局變量(按變量的有效作用范圍劃分)
1.局部變量
是指函數內部(包括main函數)定義的變量,僅在定義它的那個函數范圍內有效,不同函數可使用相同的局部變量名,函數的形式參數也屬于局部變量,在一個函數的內部復合語句中也可以定義局部變量,該局部變量只在該復合語合中有效。
2.全局變量
是指函數外部定義的變量,以稱外部變量。可為多個函數共同使用,其有效作用范圍是從它定義開始到整個程序文件結束。如果全局變量,定義在一個程序文件的開始處,則在整個程序文件范圍都可以使用它,如果一個全局變量不是在程序文件的開始處定義,但又希望在它定義之前的函數中引用該變量,這時應在引用該變量的函數中用關鍵字extern將其聲明為“外部變量”。另個,如果在一個程序模塊文件中引用另一個程序模塊文件中定義的變量時,也必須用extern進行說明。
外部變量的說明與外部變量的定義是不同的,外部變量定義只能有一次,定義的位置在所有函數之外,而同一個程序文件中(不是指模塊文件)的外部變量聲明可以有多次,聲明的置在需要引用該變量的函數之內,外部變量的聲明的作用只是聲明該變量是一個已經在外部定義過了的變量而已。
如在同一個程序文件中,全局變量與局部變量同名,則在局部變量的有效作用范圍之內,全局變量不起作用,也就是說,局部變量的優先級比全局變量高。
在編寫C語言程序時,不是特別必要的地方一般不要使用全局變量,而應當盡可能的使用局部變量。因為局部變量只在使用它的時候,才為其分配內存單元,而全局變量在整個程序的執行過程中都要占用內存單元,且當全局變量使用過多時,會降低程序的可讀性。
變量的存儲種類
(1).自動變量(auto)
定義變量時,在變量類型名前加上 “auto” ,自動變量是C語言中使用最為廣泛的一類變量,在函數體內部或是復合語句內部定義的變量,如果省略了存儲種類說明,則該變量默認為自動變量。
例如:
{ 等價于 {
char x; auto char x;
int y; auto int y;
…… ……
} }
注:自動變量的作用范圍在定義它的函數體或是復合語句內部,只有在定義它的函數內被調用,或是定義它的復合語句被執行時,編譯器才會為其分配內存空間,開始其生存期。當函數調用結束返回,或復合語句執行結束,自動變量所占用的內存空間就被釋放,變量的值當然也就不復存在,其生存期結束。當函數再次調用,或是復合語句被再次執行時,編譯器又會為其內部的自動變量重新分配內存空間。但不會保留上一次運行的值。而必須被重新分配。因此自動變量始終是相對于函數或復合語句的局部變量。
(2).外部變量(extern)
用說明符“extern”定義的變量稱為外部變量。按缺省規則,凡是在所有函數之前,在函數外部定義的變量都是外部變量,定義時可以不寫extern說明符,但是一個函數體內說明一個已在該函數體外或別的程序模塊文件中定義過的外部變量時,剛必須要使用extern說明符。外部變量定義后,它就被分配了固定的內存空間。外部變量的生存期為程序的整個執行時間。 外部變量的存儲不會隨函數或復合語句執行完畢而釋放,因此外部變量屬于全局變量。
C語言允許將大型程序分解為若干個獨立的程序模塊文件,各個模塊可分別進行編譯,然后再將它們連接在一起,如果某個變量需要在所有程序模塊文件中使用,只要在一個程序模塊文件中將該變量定義成全局變量,而在其它程序模塊文件中用extern聲明該變量是已被定義過的外部變量就可以了。
函數是可以相互調用的,定義函數時,如果冠以關鍵字extern 即將其明確定義為一個外部函數。例如 extern int func2(char a,b) 。如果在定義函數時省略關鍵字extern,則隱含為外部函數。如果在調用一個在本程序模塊文件以外的其它模塊文件所定義的函數,則必須要用關鍵字extern說明被調用的函數是一個外部函數。對于具有外部函數相互調用的多模塊程序,可用C51編譯器分別對各個模塊文件進行編譯,最后再用L51連接定位器將它們連接成為一個完整的程序。如下為一個多模塊程序:
程序模塊1,文件名為file1.c
#include
int x=5;
void main()
{
extern void fun1( );
extern viod fun2(int y);
fun1( );
fun1( );
fun1( );
printf( “n%d %dn”,x,fun2(x));
}
程序模塊2,文件名為file2.c
#include
extern int x;
void fun1( )
{
static int a=5; //靜態變量只在第一次調用函數時賦值,退出函數時
//會保留上次的值,下次調用不再重新賦值。
int b=5;
printf(“%d %d %d |”,a,b,x);
a-=2;
b-=2
x-=2;
printf(“%d %d %d |”,a,b,x);
}
int fun2(int y)
{
return(35*x*y);
}
程序執行如果如下:
5 5 5 | 3 3 3
3 5 3 | 1 3 1
1 5 1 | -1 3 1
-1 35
注:C語言不允許在一個函數內嵌套定義另一個函數。為了能夠訪問不同文件中各個函數的變量,除了可以采用參數傳遞的方法外,還可以采用外部變量的方法,上面的例子就說了這一點。不過,盡管使用外部變量在不同函數之間傳遞數據有時比使用函數參數傳遞更為方便,不過當外部變量過多時,會增加程序的調試排錯的困難。使得程序不便于維護。別外不通過參數傳遞直接在函數中改變全局變量的值,有時還會發生一些意想不到的副作用。因些最好還是使用函數參數來傳遞數據。
(3).寄存器變量(register)
為了提高程序的執行效率,C語言允許將一些頻率最高的那些變量,定義為能夠直接使用硬件寄存器的所謂的寄存器變量。定義一個變量時,在變量類型名前冠以“register” 即將該變量定義成為了寄存器變量。寄存器變量可以認為是一自動變量的一種。有效作用范圍也自動變量相同。由于計算機寄存器中寄存器是有限的。不能將所有變量都定義成為寄存器變量,通常在程序中定義寄存器變量時,只是給編譯器一個建議,該變量是否真正成為寄存器變量,要由編譯器根據實際情況來確定。另一方面,C51編譯器能夠識別程序中使用頻率最高的變量,在可能的情況下,即使程序中并未將該變量定義為寄存器變量,編譯器也會自動將其作為寄存器變量處理。被定義的變量是否真正能成為寄存器變量,最終是由編譯器決定的。
(4).靜態變量(static)
使用存儲種類說明符“static”定義的變量為靜態變量,在上面模塊2程序文件中使用了一個靜態變量:static int a =5 ;由于這個變量是在函數fun1( )內部定義,因此稱為內部靜態變量或局部靜態變量。局部靜態變量始終都是存在的,但只有在定義它的函數內部進行訪問,退出函數之后,變量的值仍然保持,但不能進行訪問。
還有一種全局靜態變量,它是在函數外部被定義的。作用范圍從它的定義點開始,一直到程序結束,當一個C語言程序由若干個模塊文件所組成時,全局靜態變量始終存在,但它只能在被定義的模塊文件中訪問,其數據值可為該模塊文件內的所有函數共享,退出該文件后,雖然變量的值仍然保持著,但不能被其它模塊文件訪問。在一個較大的程序中,這就方便了多人設計時,各自寫的程序模塊不會被別的模塊文件所引用。全局靜態變量和單純的全局變量,在編譯時就已經為期分配了固定的內存空間,只是他們的作用范圍不同而已。局部靜態變量是一種在兩次函數調用之間仍能保持其值的局部變量。如下,局部變量的使用——計算度輸出1~5的階乘值。
#include
int fac( int n)
{
static int f=1;
f=f*n;
return(f);
}
main( )
{
int i;
for(i=1;i<=5;i++)
printf(“%d!=%dn”,i,fac(i));
}
程序執行結果
1!=1
2!=2
3!=6
4!=24
5!=120
注:在這個程序中一共調用了5次計算階乘的函數fac(i),每次調用后輸出一個階乘值i!,同時保留了這個i!值,以便下次再乘(i+1).由此可見,如果要保留函數上一次調用結束時的值,或是在初始化之后變量只被引用而不改變其值,則這時使用局部靜態變量;較為方便,以免在每調用時都要重新進行賦值,但是,使用局部靜態變量需要占用較多的內存空間,而且降低了程序的可讀性,因此并不建議多用局部靜態變量。
靜態函數:
對于函數也可以定義成為具為靜態存儲種類的屬性,定義函數時在函數名前冠以關鍵字static即將其定義為一個靜態函數。例如static int func1(char x, y)函數是外部型的,使用靜態函數可以使該函數只局限于當前定義它的模塊文件中。其它模塊文件是不能調用它的。換名話說,就是在其它模塊文件中可以定義與靜態函數完全同名的另一個函數。不會因為程序中存在相同的函數名而發生函數調用時的混亂。 這一點對于進行模塊化程序設計是很有用的。
三、中斷淺談
0 | 外中斷0 |
1 | 定時器0 |
2 | 外中斷1 |
3 | 定時器1 |
4 | 串行口 |
定義中斷函數如下
void timer1() interrupt 3
{
……
……
}
強烈建議:如上所述,定義中斷函數時不要加using n選項。除非你對你的程序以及單片機的工作過程非常熟悉,否則會帶來不必要的麻煩。具體原因由于篇幅的限制暫不討論。
C51中斷程序編寫要求:
1.中斷函數不能進行參數傳遞,否則,將導致編譯出錯
2.中斷中,不能包含任何參數聲明,否則,將導致編譯出錯。
3.中斷函數沒有返回值,如果企圖定義一個返回值將得到不正確的結果,因些建議在定義中斷函數的時將其定義為void 類型,明確說明沒有返回值。
4.任何情況下都不能直接調用中斷函數,否則會主生編譯出錯。
5.如果中斷函數中用到了浮點運算,必須保存浮點寄存器的狀態。當沒有其它的程序執行浮點運算時(即只有中斷中用到浮點運算),可以不用保存。
6.如果中斷函數中調用了其它函數,則被調用的函數所使用的寄存器組必須與中斷函數相同,用戶必須保證按要求使用相同的寄存器組,否則會產生不正確的結果,這一點必須引起足夠的注意,如果定義中斷函數時沒有使用using選項,則由編譯器選擇一個寄存器組作絕對寄存器訪問。另外,不斷的產生不可預測,中斷函數對其它函數的調用可能形成遞規調用,需要時,可將被中斷調用的其它函數定義為再入函數。
淺析函數的遞規調用與再入函數:
函數的遞規調用: 在調用一個函數的過程中雙直接或間接的調用該函數本身;
再入函數:一種可以在函數體內直接或間接調用其自身的一種函數。
C51編譯器采用一個擴展關鍵字reentrant 作為定義函數時的選項,需要將一個函數定義為再入函數時,只要在函數名后加上關鍵字reentrant即可。空不空格以及空幾格都無所謂。
再入函數剖析:
再入函數可被遞歸調用,無論何時,包括中斷服務函數在內的任何函數都可調用再入函數。與非再入函數的參數傳遞和局部就是的存儲分配方法不同,C51編譯器為每個再入函數都生成一個模擬棧。模擬棧所在的存儲器空間根據再入函數的存儲模式的不同,可以分配到DATA,PDATA 或XDATA。
對再入函數有如下規定:
1.再入函數不能傳送bit類型的參數。也不能定義一個局部位變量,再入函數不能包括位操作以及8051系列單片機的可位尋址區。
2.與PL/ M51兼容的函數,不能具有reentrant屬性,也不能調用再入函數。
3.編譯時,在存儲器模式的基礎上,為再入函數在內部或外部存儲中建立一個模擬堆棧區,稱為再入棧,再入函數的局部變量及參數被放在再入棧中,從而使得再入函數可以進行遞規調用。再非再入函數的局部變量被放在再入棧之外的暫存區內,如果對非再入函數進行遞規調用,則上次調用時使用的局部變量數據將被覆蓋。
4.在同一個程序中可以定義和使用不同存儲器模式的再入函數,任意模式的再入函數不能調用不同模式的再入函數,但可以任意調用非再入函數。
5.在參數的傳遞上,實際參數,可以傳遞給間接調用的再入函數,無再入屬性的間接調用函數不能包含調用參數。但是可以使用定義的全局變量來進行參數傳遞。
四、C51指針深度剖析(非常重要,嵌入式系統開發人員必須要掌握的內容)
注意:由于篇幅所限,本人暫時不打算討論抽象指針的內容。但是你必須上網或去圖書館找找關于抽象指針的資料好好看看,抽象指針很有用的。
指針是C語言中的一個重要概念,使用也十分普遍,正確使用指針類型數據可以有效的表示復雜的數據結構,直接處理內存地址,而且可以更為有效的使用數組。
在C語言中,為了能夠實現直接對內存單元的操作,引入了指針類型的數據,指針類型數據是專門用來確定其它數據類型的地址的,因此一個變量的地址就被稱為該變量的指針如: 一個整形變量i 存放在內存單元40H中,則該內存單元地址40H就是變量i 的指針。如果有一個變量專門用來存放另一個變量的地址,則稱之為“指針變量”
變量指針與指針變量
變量的指針: 是指某個變量的地址,而一個指針變量里面存放的是另一個變量在內存中的地址。擁有這個地址的變量則稱為該指針變量所指向的變量。 所以每個變量都有它自己的指針(地址),而每一個指針變量都是指向另一個變量的。C語言中用符號“*”來表示“指向”,如下:
i=50;
*ip=50;
如果指針ip這個指針變量指向i那么,兩個賦值表達或同義,第二個表達式可以解釋為“給指針變量ip所指向的變量賦值50”。
(1).指針變量的定義
指針變量的定義與一般變量的定義類似,其一般形式如下:
數據類型 [存儲器類型] * 標識符;
標識符, 是所定義的指針變量名
數據類型, 說明了該指針變量所指向的變量類型
存儲器類型,是可選的,它是C51編譯器的一種擴展,如果帶有此選項,指針被定義為基于存儲器的指針,無此選項時,被定義為一般指針,這兩種指針的區別在于它們的存儲字節不同。
一般指針:占用三個字節,第一個字節存放該指針存儲器類型的編碼,第二和第三個字節分別存放該指針的高位和低位地址的偏移量
存儲器類型 | IDATA | XDATA | PDATA | DATA | CODE |
編碼值 | 1 | 2 | 3 | 4 | 5 |
基于存儲器指針:則該指針長度可為一個字節,也可為兩字節
一個字節: (存儲器類型 idata data pdata)
兩個字節: (存儲器類型為code xdata)
注:在定義指針變量時最好指定其為基于存儲器的指針,這個生成的匯編代碼長精 練一些,而且也節省空間(讀者可自行到C51中寫一個程序,查看其反匯編程序)但在一些函數調用的參數中指針需要采用一般指針,為此C51編譯器允許這兩種指針相互轉換,轉換規則如下:
一般指針轉換成基于存儲器指針,采取截斷,基于存儲器類型指針轉換成一般指針采用擴展的。
(2).指針變量的引用
指針變量是含有一個數據對象地址的特殊變量,指針變量中只能存放地址與指針變量有關的兩個運算符:
& 取地址運算符
* 間接訪問運算符
&a為取變量a的地址,*P為指針變量P所指向的變量。如下:
int i , x, y;
int *pi,*px,*py;
pi=&i; //將變量i的地址賦給指針變量pi,也即pi指向i
px=&x;
py=&py;
*pi=0; //等價于i=0
*px+=6; //等價于 i+=6
(*py)++; //等價于 i++
注:指向同類數據的指針之間可以相互賦值。如pi=px;
(3).指針變量作為函數的參數
函數的參數不僅可以是整型,字符型等數據,還可以是指針類型,指針變量作為函數的參數的作用是將一個變量的地址傳到另一個函數中去,地址傳遞是雙向的,即主調用函數不僅可以向被調用函數傳遞參數,而且還可以從被調用函數返回其結果。下面通過一個簡單的示例來進行說明。
#include
swap(int *pi,int *pj)
{
int temp;
temp=*pi;
*pi=*pj; //把指針變量pj所指向的變量的值送給pi所指向的變量
*pj=temp;
}
main( )
{
int a,b;
int *pa, *pb;
a=9;
b=7;
pa=&a;
pb=&b;
if(a
printf(“n max=%d,min=%d n”,a,b);
}
上程序上定義了一個swap( )函數,兩個形參為指針變量,在調用函數時,所用的實參也是指針變量,在調用開始,實參變量將它的值傳遞給形參變量,采取的仍然是“值傳遞”方式,但這時傳遞的是指針的值(地址),傳遞后,形參pi的值為&a,pj的值為&b,即指針變量*pi 和*pa都指向了a, *pj和*pb指向了b。接著使*pj與*pi的值互換,從而達到了實現了a,和b值的互換。雖然函數返回時,pi pj被釋放而不存在,但main函數中a 與b的值已經交換。
(4).數組的指針
在C語言中,指針與數組有著十分密切的關系,任何能夠用數組實現的運算都可以通過指針來完成,例如定義一個具有十個元素的整形數據可以寫成:
int a[10];
數組名a表示元素a[0]的地址,而*a 則表示a所代表地址中的內容,即a[0].
如果定義一個指向整形變量的指針pa并賦以數組a中的第一個元素a[0]的地址;
int *pa;
pa=&a[0]; //也可寫成pa=a;
則可通過指針pa來操作數組a了,即可用*pa代表a[0];*(pa+i)代表a[i],也可以上pa[0];pa[1];pa[2]……pa[9]的形式
(5).字符數組的指針
用指針來描述一個字符數組是十分方便的,字符串是以字符數組的形式給出的,并且每個字符數組都是以轉義字符‘ 主站蜘蛛池模板: 洞头县| 浦北县| 天柱县| 博白县| 达日县| 兰坪| 洪洞县| 东乌珠穆沁旗| 三都| 深水埗区| 邯郸市| 隆子县| 乐山市| 阜宁县| 黑龙江省| 阿合奇县| 霍城县| 福鼎市| 绿春县| 怀柔区| 隆安县| 淳化县| 乌海市| 永安市| 榕江县| 会泽县| 图片| 静乐县| 惠安县| 潜山县| 安仁县| 大冶市| 博罗县| 阆中市| 炉霍县| 社旗县| 汉沽区| 黄平县| 太仓市| 历史| 新安县|