c語言基礎 (6) 指標
一.概述
在計算機中,所有的資料都是存放在儲存器中的。一般把儲存器中的一個位元組稱為一個記憶體單元,不同的資料型別所佔用的記憶體單元數不等,如整型量佔2個單元,字元量佔1個單元等,在前面已有詳細的介紹。為了正確地訪問這些記憶體單元,必須為每個記憶體單元編上號。根據一個記憶體單元的編號即可準確地找到該記憶體單元。記憶體單元的編號也叫做地址。既然根據記憶體單元的編號或地址就可以找到所需的記憶體單元,所以通常也把這個地址稱為指標。 記憶體單元的指標和記憶體單元的內容是兩個不同的概念。 可以用一個通俗的例子來說明它們之間的關係。我們到銀行去存取款時,銀行工作人員將根據我們的帳號去找我們的存款單,
嚴格地說,一個指標是一個地址,是一個常量。而一個指標變數卻可以被賦予不同的指標值,是變數。但常把指標變數簡稱為指標。為了避免混淆,我們約定:“指標”是指地址,是常量,“指標變數”是指取值為地址的變數。定義指標的目的是為了通過指標去訪問記憶體單元
二.變數的指標和指向變數的指標變數
1.變數的指標就是指的變數的地址。存放變數的地址的變數即存放指標的變數就是指標變數。
2.為了表示指標變數和它所指向的變數之間的關係,在程式中用“*”符號表示“指向”,例如,i_pointer代表指標變數(用以存放變數的地址),而*i_pointer是i_pointer所指向的變數。
因此,下面兩個語句作用相同:
i=3;
*i_pointer=3;
第二個語句的含義是將3賦給指標變數i_pointer所指向的變數
3.對指標變數的定義包括三個內容:
(1)指標型別說明,即定義變數為一個指標變數;
(2)指標變數名;
(3)變數值(指標)所指向的變數的資料型別。
其一般形式為:
型別說明符 *變數名;
其中,*表示這是一個指標變數
4.指標變數同普通變數一樣,使用之前不僅要定義說明,而且必須賦予具體的值。未經賦值的指標變數不能使用,否則將造成系統混亂,甚至宕機。指標變數的賦值只能賦予地址,決不能賦予任何其它資料,否則將引起錯誤。在C語言中,變數的地址是由編譯系統分配的,對使用者完全透明,使用者不知道變數的具體地址。
1)&:取地址運算子。
2)*:指標運算子(或稱“間接訪問”運算子)。
其一般形式為:
&變數名;
如&a表示變數a的地址,&b表示變數b的地址。變數本身必須預先說明。
(1)指標變數初始化的方法
int a;
int *p=&a;
(2)賦值語句的方法
int a;
int *p;
p=&a;
不允許把一個數賦予指標變數,故下面的賦值是錯誤的:
int *p;
p=1000;
被賦值的指標變數前不能再加“*”說明符,如寫為*p=&a 也是錯誤的。
假設:
int i=200, x;
int *ip;
我們定義了兩個整型變數i,x,還定義了一個指向整型數的指標變數ip。i,x中可存放整數,而ip中只能存放整型變數的地址。我們可以把i的地址賦給ip:
ip=&i;
此時指標變數ip指向整型變數i,假設變數i的地址為1800,這個賦值可形象理解為下圖所示的聯絡。
以後我們便可以通過指標變數ip間接訪問變數i,例如:
x=*ip;
運算子*訪問以ip為地址的存貯區域,而ip中存放的是變數i的地址,因此,*ip訪問的是地址為1800的存貯區域(因為是整數,實際上是從1800開始的兩個位元組),它就是i所佔用的存貯區域, 所以上面的賦值表示式等價於
x=i;
三.指標做引數
1.通過指標交換兩個變數的值
swap(int *p1,int *p2)
{int temp;
temp=*p1;//注意這裡交換的是指標所指向的記憶體區域的值
*p1=*p2;
*p2=temp;
}
main()
{
int a,b;
int *pointer_1,*pointer_2;
scanf("%d,%d",&a,&b);
pointer_1=&a;pointer_2=&b;
if(a<b) swap(pointer_1,pointer_2);
printf("\n%d,%d\n",a,b);
}
說明:
swap是使用者定義的函式,它的作用是交換兩個變數(a和b)的值。swap函式的形參p1、p2是指標變數。程式執行時,先執行main函式,輸入a和b的值。然後將a和b的地址分別賦給指標變數pointer_1和pointer_2,使pointer_1指向a,pointer_2指向b。
接著執行if語句,由於a〈b,因此執行swap函式。注意實參pointer_1和pointer_2是指標變數,在函式呼叫時,將實參變數的值傳遞給形參變數。採取的依然是“值傳遞”方式。因此虛實結合後形參p1的值為&a,p2的值為&b。這時p1和pointer_1指向變數a,p2和pointer_2指向變數b。
接著執行執行swap函式的函式體使*p1和*p2的值互換,也就是使a和b的值互換。
函式呼叫結束後,p1和p2不復存在(已釋放)如圖
最後在main函式中輸出的a和b的值是已經過交換的值。
2.指標變數可以進行某些運算,但其運算的種類是有限的。它只能進行賦值運算和部分算術運算及關係運算。
1.指標運算子
1)取地址運算子&:取地址運算子&是單目運算子,其結合性為自右至左,其功能是取變數的地址。在scanf函式及前面介紹指標變數賦值中,我們已經瞭解並使用了&運算子。
2)取內容運算子*:取內容運算子*是單目運算子,其結合性為自右至左,用來表示指標變數所指的變數。在*運算子之後跟的變數必須是指標變數。
需要注意的是指標運算子*和指標變數說明中的指標說明符*不是一回事。在指標變數說明中,“*”是型別說明符,表示其後的變數是指標型別。而表示式中出現的“*”則是一個運算子用以表示指標變數所指的變數。
2.指標變數的運算
1)賦值運算:指標變數的賦值運算有以下幾種形式。
①指標變數初始化賦值,前面已作介紹。
②把一個變數的地址賦予指向相同資料型別的指標變數。
例如:
int a,*pa;
pa=&a; /*把整型變數a的地址賦予整型指標變數pa*/
③把一個指標變數的值賦予指向相同型別變數的另一個指標變數。
如:
int a,*pa=&a,*pb;
pb=pa; /*把a的地址賦予指標變數pb*/
由於pa,pb均為指向整型變數的指標變數,因此可以相互賦值。
④把陣列的首地址賦予指向陣列的指標變數。
例如:
int a[5],*pa;
pa=a;
(陣列名錶示陣列的首地址,故可賦予指向陣列的指標變數pa)
也可寫為:
pa=&a[0]; /*陣列第一個元素的地址也是整個陣列的首地址,也可賦予pa*/
當然也可採取初始化賦值的方法:
int a[5],*pa=a;
⑤把字串的首地址賦予指向字元型別的指標變數。
例如:
char *pc;
pc="C Language";
或用初始化賦值的方法寫為:
char *pc="CLanguage";
這裡應說明的是並不是把整個字串裝入指標變數,而是把存放該字串的字元陣列的首地址裝入指標變數。在後面還將詳細介紹。
⑥把函式的入口地址賦予指向函式的指標變數。
例如:
int (*pf)();
pf=f; /*f為函式名*/
2)加減算術運算
對於指向陣列的指標變數,可以加上或減去一個整數n。設pa是指向陣列a的指標變數,則pa+n,pa- n,pa++,++pa,pa--,--pa運算都是合法的。指標變數加或減一個整數n的意義是把指標指向的當前位置(指向某陣列元素)向前或向後移動n個位置。應該注意,陣列指標變數向前或向後移動一個位置和地址加1或減1在概念上是不同的。因為陣列可以有不同的型別,各種型別的陣列元素所佔的位元組長度是不同的。如指標變數加1,即向後移動1 個位置表示指標變數指向下一個資料元素的首地址。而不是在原地址基礎上加1。例如:
int a[5],*pa;
pa=a; /*pa指向陣列a,也是指向a[0]*/
pa=pa+2; /*pa指向a[2],即pa的值為&pa[2]*/
指標變數的加減運算只能對陣列指標變數進行,對指向其它型別變數的指標變數作加減運算是毫無意義的。
3)兩個指標變數之間的運算:只有指向同一陣列的兩個指標變數之間才能進行運算,否則運算毫無意義。
①兩指標變數相減:兩指標變數相減所得之差是兩個指標所指陣列元素之間相差的元素個數。實際上是兩個指標值(地址)相減之差再除以該陣列元素的長度(位元組數)。例如pf1和pf2是指向同一浮點陣列的兩個指標變數,設pf1的值為2010H,pf2的值為2000H,而浮點陣列每個元素佔4個位元組,所以pf1-pf2的結果為(2010H-2000H)/4=4,表示pf1和 pf2之間相差4個元素。兩個指標變數不能進行加法運算。例如,pf1+pf2是什麼意思呢?毫無實際意義。
②兩指標變數進行關係運算:指向同一陣列的兩指標變數進行關係運算可表示它們所指陣列元素之間的關係。
例如:
pf1==pf2表示pf1和pf2指向同一陣列元素;
pf1>pf2表示pf1處於高地址位置;
pf1<pf2表示pf1處於低地址位置。
指標變數還可以與0比較。
設p為指標變數,則p==0表明p是空指標,它不指向任何變數;
p!=0表示p不是空指標。
空指標是由對指標變數賦予0值而得到的。
例如:
#define NULL 0
int *p=NULL;
對指標變數賦0值和不賦值是不同的。指標變數未賦值時,可以是任意值,是不能使用的,否則將造成意外錯誤。而指標變數賦0值後,則可以使用,只是它不指向具體的變數而已。