當前位置:才華齋>計算機>C語言>

C語言基礎知識

C語言 閱讀(1.01W)

引導語:指標一般出現在比較近機器語言的語言,以下是本站小編分享給大家的C語言指標概念,幫助大家學習C語言基礎知識,歡迎閱讀!

C語言基礎知識

指標簡介

指標是C語言中廣泛使用的一種資料型別。運用指標程式設計是C語言最主要的風格之一。利用指標變數可以表示各種資料結構; 能很方便地使用陣列和字串; 並能象組合語言一樣處理記憶體地址,從而編出精練而高效的程式。指標極大地豐富了C語言的功能。 學習指標是學習C語言中最重要的一環, 能否正確理解和使用指標是我們是否掌握C語言的一個標誌。同時, 指標也是C語言中最為困難的一部分,在學習中除了要正確理解基本概念,還必須要多程式設計,上機除錯。只要作到這些,指標也是不難掌握的。

 指標的基本概念

計算機中,所有的資料都是存放在儲存器中的。 一般把儲存器中的一個位元組稱為一個記憶體單元, 不同的資料型別所佔用的記憶體單元數不等,如整型量佔2個單元,字元量佔1個單元等, 在第二章中已有詳細的介紹。為了正確地訪問這些記憶體單元, 必須為每個記憶體單元編上號。 根據一個記憶體單元的編號即可準確地找到該記憶體單元。記憶體單元的編號也叫做地址。 既然根據記憶體單元的編號或地址就可以找到所需的記憶體單元,所以通常也把這個地址稱為指標。 記憶體單元的指標和記憶體單元的內容是兩個不同的概念。 可以用一個通俗的例子來說明它們之間的關係。我們到銀行去存取款時, 銀行工作人員將根據我們的帳號去找我們的存款單, 找到之後在存單上寫入存款、取款的金額。在這裡,帳號就是存單的指標, 存款數是存單的內容。對於一個記憶體單元來說,單元的地址即為指標, 其中存放的資料才是該單元的內容。在C語言中, 允許用一個變數來存放指標,這種變數稱為指標變數。因此, 一個指標變數的值就是某個記憶體單元的地址或稱為某記憶體單元的指標。圖中,設有字元變數C,其內容為“K”(ASCII碼為十進位制數 75),C佔用了011A號單元(地址用十六進數表示)。設有指標變數P,內容為011A, 這種情況我們稱為P指向變數C,或說P是指向變數C的指標。 嚴格地說,一個指標是一個地址, 是一個常量。而一個指標變數卻可以被賦予不同的指標值,是變。 但在常把指標變數簡稱為指標。為了避免混淆,我們中約定:“指標”是指地址, 是常量,“指標變數”是指取值為地址的變數。 定義指標的目的是為了通過指標去訪問記憶體單元。

既然指標變數的值是一個地址, 那麼這個地址不僅可以是變數的地址, 也可以是其它資料結構的地址。在一個指標變數中存放一

個數組或一個函式的首地址有何意義呢? 因為陣列或函式都是連續存放的。通過訪問指標變數取得了陣列或函式的首地址, 也就找到了該陣列或函式。這樣一來, 凡是出現數組,函式的地方都可以用一個指標變數來表示, 只要該指標變數中賦予陣列或函式的首地址即可。這樣做, 將會使程式的概念十分清楚,程式本身也精練,高效。在C語言中, 一種資料型別或資料結構往往都佔有一組連續的記憶體單元。 用“地址”這個概念並不能很好地描述一種資料型別或資料結構, 而“指標”雖然實際上也是一個地址,但它卻是一個數據結構的首地址, 它是“指向”一個數據結構的,因而概念更為清楚,表示更為明確。 這也是引入“指標”概念的一個重要原因。

指標變數的型別說明

對指標變數的型別說明包括三個內容:

(1)指標型別說明,即定義變數為一個指標變數;

(2)指標變數名;

(3)變數值(指標)所指向的變數的資料型別。

其一般形式為: 型別說明符 *變數名;

其中,*表示這是一個指標變數,變數名即為定義的指標變數名,型別說明符表示本指標變數所指向的變數的資料型別。

例如: int *p1;表示p1是一個指標變數,它的值是某個整型變數的地址。 或者說p1指向一個整型變數。至於p1究竟指向哪一個整型變數, 應由向p1賦予的地址來決定。

再如:

staic int *p2; /*p2是指向靜態整型變數的指標變數*/

float *p3; /*p3是指向浮點變數的指標變數*/

char *p4; /*p4是指向字元變數的指標變數*/ 應該注意的是,一個指標變數只能指向同類型的變數,如P3 只能指向浮點變數,不能時而指向一個浮點變數, 時而又指向一個字元變數。

指標變數的賦值

指標變數同普通變數一樣,使用之前不僅要定義說明, 而且必須賦予具體的值。未經賦值的指標變數不能使用, 否則將造成系統混亂,甚至宕機。指標變數的賦值只能賦予地址, 決不能賦予任何其它資料,否則將引起錯誤。在C語言中, 變數的地址是由編譯系統分配的,對使用者完全透明,使用者不知道變數的具體地址。 C語言中提供了地址運算子&來表示變數的地址。其一般形式為: & 變數名; 如&a變示變數a的地址,&b表示變數b的地址。 變數本身必須預先說明。設有指向整型變數的指標變數p,如要把整型變數a 的地址賦予p可以有以下兩種方式:

(1)指標變數初始化的方法 int a;

int *p=&a;

(2)賦值語句的方法 int a;

int *p;

p=&a;

不允許把一個數賦予指標變數,故下面的賦值是錯誤的: int *p;p=1000; 被賦值的指標變數前不能再加“*”說明符,如寫為*p=&a 也是錯誤的

指標變數的運算

指標變數可以進行某些運算,但其運算的種類是有限的。 它只能進行賦值運算和部分算術運算及關係運算。

1.指標運算子

(1)取地址運算子&

取地址運算子&是單目運算子,其結合性為自右至左,其功能是取變數的地址。在scanf函式及前面介紹指標變數賦值中,我們已經瞭解並使用了&運算子。

(2)取內容運算子*

取內容運算子*是單目運算子,其結合性為自右至左,用來表示指標變數所指的變數。在*運算子之後跟的變數必須是指標變數。需要注意的是指標運算子*和指標變數說明中的指標說明符* 不是一回事。在指標變數說明中,“*”是型別說明符,表示其後的變數是指標型別。而表示式中出現的“*”則是一個運算子用以表示指標變數所指的變數。

main(){

int a=5,*p=&a;

printf ("%d",*p);

}

......

表示指標變數p取得了整型變數a的地址。本語句表示輸出變數a的值。

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="C Language"; 這裡應說明的是並不是把整個字串裝入指標變數, 而是把存放該字串的字元陣列的首地址裝入指標變數。 在後面還將詳細介紹。

⑥把函式的入口地址賦予指向函式的指標變數。例如: 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的結果為(2000H-2010H)/4=4,表示pf1和 pf2之間相差4個元素。兩個指標變數不能進行加法運算。 例如, pf1+pf2是什麼意思呢?毫無實際意義。

②兩指標變數進行關係運算

指向同一陣列的兩指標變數進行關係運算可表示它們所指陣列元素之間的關係。例如:

pf1==pf2表示pf1和pf2指向同一陣列元素

pf1>pf2表示pf1處於高地址位置

pf1  main(){

int a=10,b=20,s,t,*pa,*pb;

pa=&a;

pb=&b;

s=*pa+*pb;

t=*pa**pb;

printf("a=%dnb=%dna+b=%dna*b=%dn",a,b,a+b,a*b);

printf("s=%dnt=%dn",s,t);

}

......

說明pa,pb為整型指標變數

給指標變數pa賦值,pa指向變數a。

給指標變數pb賦值,pb指向變數b。

本行的意義是求a+b之和,(*pa就是a,*pb就是b)。

本行是求a*b之積。

輸出結果。

輸出結果。

......

指標變數還可以與0比較。設p為指標變數,則p==0表明p是空指標,它不指向任何變數;p!=0表示p不是空指標。空指標是由對指標變數賦予0值而得到的。例如: #define NULL 0 int *p=NULL; 對指標變數賦0值和不賦值是不同的。指標變數未賦值時,可以是任意值,是不能使用的。否則將造成意外錯誤。而指標變數賦0值後,則可以使用,只是它不指向具體的變數而已。

main(){

int a,b,c,*pmax,*pmin;

printf("input three numbers:n");

scanf("%d%d%d",&a,&b,&c);

if(a>b){

pmax=&a;

pmin=&b;}

else{

pmax=&b;

pmin=&a;}

if(c>*pmax) pmax=&c;

if(c<*pmin) pmin=&c;

printf("max=%dnmin=%dn",*pmax,*pmin);

}

......

pmax,pmin為整型指標變數。

輸入提示。

輸入三個數字。

如果第一個數字大於第二個數字...

指標變數賦值

指標變數賦值

指標變數賦值

指標變數賦值

判斷並賦值

判斷並賦值

輸出結果

......

陣列指標變數的說明和使用

指向陣列的指標變數稱為陣列指標變數。 在討論陣列指標變數的說明和使用之前,我們先明確幾個關係。

一個數組是由連續的一塊記憶體單元組成的。 陣列名就是這塊連續記憶體單元的首地址。一個數組也是由各個陣列元素(下標變數) 組成的。每個陣列元素按其型別不同佔有幾個連續的記憶體單元。 一個數組元素的首地址也是指它所佔有的幾個記憶體單元的首地址。 一個指標變數既可以指向一個數組,也可以指向一個數組元素, 可把陣列名或第一個元素的地址賦予它。如要使指標變數指向第i號元素可以把i元素的首地址賦予它或把陣列名加i賦予它。

設有實陣列a,指向a的指標變數為pa,從圖6.3中我們可以看出有以下關係:

pa,a,&a[0]均指向同一單元,它們是陣列a的首地址,也是0 號元素a[0]的首地址。pa+1,a+1,&a[1]均指向1號元素a[1]。類推可知a+i,a+i,&a[i]

指向i號元素a[i]。應該說明的是pa是變數,而a,&a[i]都是常量。在程式設計時應予以注意。

main(){

int a[5],i;

for(i=0;i<5;i++){

a[i]=i;

printf("a[%d]=%dn",i,a[i]);

}

printf("n");

}

主函式

定義一個整型陣列和一個整型變數

迴圈語句

給陣列賦值

列印每一個數組的值

......

輸出換行

......

陣列指標變數說明的一般形式為:

型別說明符 * 指標變數名

其中型別說明符表示所指陣列的型別。 從一般形式可以看出指向陣列的指標變數和指向普通變數的指標變數的說明是相同的。

引入指標變數後,就可以用兩種方法來訪問陣列元素了。

第一種方法為下標法,即用a[i]形式訪問陣列元素。 在第四章中介紹陣列時都是採用這種方法。

第二種方法為指標法,即採用*(pa+i)形式,用間接訪問的方法來訪問陣列元素。

main(){

int a[5],i,*pa;

pa=a;

for(i=0;i<5;i++){

*pa=i;

pa++;

}

pa=a;

for(i=0;i<5;i++){

printf("a[%d]=%dn",i,*pa);

pa++;

}

}

主函式

定義整型陣列和指標

將指標pa指向陣列a

迴圈

將變數i的值賦給由指標pa指向的a[]的陣列單元

將指標pa指向a[]的下一個單元

......

指標pa重新取得陣列a的首地址

迴圈

用陣列方式輸出陣列a中的所有元素

將指標pa指向a[]的下一個單元

......

......

下面,另舉一例,該例與上例本意相同,但是實現方式不同。

main(){

int a[5],i,*pa=a;

for(i=0;i<5;){

*pa=i;

printf("a[%d]=%dn",i++,*pa++);

}

}

主函式

定義整型陣列和指標,並使指標指向陣列a

迴圈

將變數i的值賦給由指標pa指向的a[]的陣列單元

用指標輸出陣列a中的所有元素,同時指標pa指向a[]的下一個單元

......

......

陣列名和陣列指標變數作函式引數

在第五章中曾經介紹過用陣列名作函式的實參和形參的問題。在學習指標變數之後就更容易理解這個問題了。 陣列名就是陣列的首地址,實參向形參傳送陣列名實際上就是傳送陣列的地址, 形參得到該地址後也指向同一陣列。 這就好象同一件物品有兩個彼此不同的名稱一樣。同樣,指標變數的值也是地址, 陣列指標變數的值即為陣列的首地址,當然也可作為函式的引數使用。

float aver(float *pa);

main(){

float sco[5],av,*sp;

int i;

sp=sco;

printf("ninput 5 scores:n");

for(i=0;i<5;i++) scanf("%f",&sco[i]);

av=aver(sp);

printf("average score is %5.2f",av);

}

float aver(float *pa)

{

int i;

float av,s=0;

for(i=0;i<5;i++) s=s+*pa++;

av=s/5;

return av;

}

指向多維陣列的指標變數

本小節以二維陣列為例介紹多維陣列的指標變數。

一、多維陣列地址的表示方法

設有整型二維陣列a[3][4]如下:

0 1 2 3

4 5 6 7

8 9 10 11

設陣列a的首地址為1000,各下標變數的首地址及其值如圖所示。在第四章中介紹過, C語言允許把一個二維陣列分解為多個一維陣列來處理。因此陣列a可分解為三個一維陣列,即a[0],a[1],a[2]。每一個一維陣列又含有四個元素。例如a[0]陣列,含有a[0][0],a[0][1],a[0][2],a[0][3]四個元素。 陣列及陣列元素的地址表示如下:a是二維陣列名,也是二維陣列0行的首地址,等於1000。a[0]是第一個一維陣列的陣列名和首地址,因此也為1000。*(a+0)或*a是與a[0]等效的, 它表示一維陣列a[0]0 號元素的首地址。 也為1000。&a[0][0]是二維陣列a的0行0列元素首地址,同樣是1000。因此,a,a[0],*(a+0),*a?amp;a[0][0]是相等的。同理,a+1是二維陣列1行的首地址,等於1008。a[1]是第二個一維陣列的陣列名和首地址,因此也為1008。 &a[1][0]是二維陣列a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等同的。 由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。 此外,&a[i]和a[i]也是等同的。因為在二維陣列中不能把&a[i]理解為元素a[i]的地址,不存在元素a[i]。

C語言規定,它是一種地址計算方法,表示陣列a第i行首地址。由此,我們得出:a[i],&a[i],*(a+i)和a+i也都是等同的。另外,a[0]也

可以看成是a[0]+0是一維陣列a[0]的0號元素的首地址, 而a[0]+1則是a[0]的1號元素首地址,由此可得出a[i]+j則是一維陣列a[i]的j號元素首地址,它等於&a[i][j]。由a[i]=*(a+i)得a[i]+j=*(a+i)+j,由於*(a+i)+j是二維陣列a的i行j列元素的首地址。該元素的值等於*(*(a+i)+j)。

[Explain]#define PF "%d,%d,%d,%d,%d,n"

main(){

static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

printf(PF,a,*a,a[0],&a[0],&a[0][0]);

printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);

printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]);

printf("%d,%dn",a[1]+1,*(a+1)+1);

printf("%d,%dn",*(a[1]+1),*(*(a+1)+1));

}

二、多維陣列的指標變數

把二維陣列a 分解為一維陣列a[0],a[1],a[2]之後,設p為指向二維陣列的指標變數。可定義為: int (*p)[4] 它表示p是一個指標變數,它指向二維陣列a 或指向第一個一維陣列a[0],其值等於a,a[0],或&a[0][0]等。而p+i則指向一維陣列a[i]。從前面的分析可得出*(p+i)+j是二維陣列i行j 列的元素的地址,而*(*(p+i)+j)則是i行j列元素的值。

二維陣列指標變數說明的一般形式為: 型別說明符 (*指標變數名)[長度] 其中“型別說明符”為所指陣列的資料型別。“*”表示其後的變數是指標型別。 “長度”表示二維陣列分解為多個一維陣列時, 一維陣列的長度,也就是二維陣列的列數。應注意“(*指標變數名)”兩邊的括號不可少,如缺少括號則表示是指標陣列(本章後面介紹),意義就完全不同了。

[Explain]main(){

static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

int(*p)[4];

int i,j;

p=a;

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

for(j=0;j<4;j++) printf("- ",*(*(p+i)+j));

}

'Expain字串指標變數的說明和使用字串指標變數的定義說明與指向字元變數的指標變數說明是相同的。只能按對指標變數的賦值不同來區別。 對指向字元變數的指標變數應賦予該字元變數的地址。如: char c,*p=&c;表示p是一個指向字元變數c的指標變數。而: char *s="C Language";則表示s是一個指向字串的指標變數。把字串的首地址賦予s。

請看下面一例。

main(){

char *ps;

ps="C Language";

printf("%s",ps);

}

執行結果為:

C Language

上例中,首先定義ps是一個字元指標變數, 然後把字串的首地址賦予ps(應寫出整個字串,以便編譯系統把該串裝入連續的一塊記憶體單元),並把首地址送入ps。程式中的: char *ps;ps="C Language";等效於: char *ps="C Language";輸出字串中n個字元後的所有字元。

main(){

char *ps="this is a book";

int n=10;

ps=ps+n;

printf("%sn",ps);

}

執行結果為:

book 在程式中對ps初始化時,即把字串首地址賦予ps,當ps= ps+10之後,ps指向字元“b”,因此輸出為"book"。

main(){

char st[20],*ps;

int i;

printf("input a string:n");

ps=st;

scanf("%s",ps);

for(i=0;ps[i]!='