




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
課程提要
第一章緒論第二章C語言基礎第三章結構化程序設計第四章選擇結構第五章循環結構程序設計第六章函數第七章編譯預處理
第八章數組第九章結構體、共用體和枚舉類型
第十章指針第10章指針10.0指針概述10.1地址與指針變量10.2指針與函數10.3指針與數組10.4指針與結構體(*)10.0指針概述指針的地位和重要意義:指針是
C
語言中的一個重要概念,也構成
C
語言的一個重要特色;指針可以有效地表達復雜的數據結構;能動態地分配內存,更有效地利用內存空間;能方便地表示數組、字符串、樹、圖等多種數據結構,提高數據處理效率;指針作為函數參數,能使函數調用得到多于一個的值;在程序中適當使用指針,會使程序變得靈活高效。10.1地址與指針變量10.1.1內存單元地址10.1.2指針10.1.3指針變量的定義和初始化10.1.4指針的運算10.1.5指向指針的指針(*)10.1.1內存單元地址計算機主存儲器由一個一個的存儲單元組成。計算機以字節作為存儲的計量單位,一個字節8位,不同計算機的字長可能不同,有單字節的,有多字節的。每個存儲單元具有唯一的地址,存儲單元的地址是一個無符號整數,主存儲器的所有存儲單元的地址是連續的。程序中處理的數據需要在內存中占用存儲單元,編譯系統根據變量的數據類型,為變量分配若干個存儲單元(字節)。例如VC++系統為每個字符變量分配一個字節,為每個整型變量分配4個字節,為每個單精度實數分配4個字節。一個變量所占用存儲區域的所有字節都有各自的地址,C
編譯系統把變量存儲區域中第一個字節的地址作為變量的地址。要訪問變量中的數據,就要知道該變量的內存地址。10.1.1內存單元地址前面章節使用了大量的變量,包括整型、字符型、數組和構造型變量,但并沒有直接使用變量的存儲地址。直接訪問:C
語言屏蔽了編譯系統底層的實現細節。在程序中定義的變量,編譯時系統給這個變量分配適當的內存單元,并把變量名和變量存儲地址對應起來,于是就可以直接在程序中通過變量名訪問存儲單元中的數據。通過變量名訪問變量中的數據,這種變量存取方式稱為“直接訪問”。此外,還有間接訪問。間接訪問:變量的地址也是數據。如果定義一種特殊的變量(即指針變量),用指針變量存放某變量的地址,就可以由一個指針變量的值得到另一個變量的地址,然后通過該地址取得后一個變量的值。這就是所謂的“間接訪問”。10.1.2指針
存儲整數的變量稱為整型變量,存儲字符的變量稱為字符變量,存儲某個變量地址的變量應該稱為“地址變量”。C語言把地址變量稱為“指針變量”。指針變量存儲另一個變量的地址,即“指向”另一個變量對應的存儲區域。這種指向是通過地址來體現的,因此地址也被稱為指針。于是,存放某個變量地址的變量就被稱為“指針變量”。變量的地址在程序編譯時由系統分配,變量地址一經分配就不能改變,因此,變量地址是一個常量。變量地址可由運算符“&”和變量名運算獲得。如輸入函數scanf(“%d”,&a),其表達式“&a”的值就是變量a的地址,執行該函數從鍵盤讀取一個整數,存入變量a在存儲區域地址為&a的存儲單元。數組名本身就是地址,它代表該數組首元素的地址。設a為一維數組,則有a==&a[0],即a代表a[0]的地址。
問題1:
chara[6]=“Hello”;printf(“%s”,a)和printf(“%s”,&a[0])結果是否相同?
問題2:
chara[]=“123”,b[]=“abcd”;if(a>b)printf(“%s”,a)elseprintf(“%s”,b);
該代碼運行結果?(第2版教材P221選擇題第12題)10.1.2指針例10.1.1
變量地址輸出示例,程序如下:#include<stdio.h>voidmain(){ inta,b[4]; printf
("a的地址(十進制): %d\n",&a); printf
("b的地址(十進制): %d\n",b);//b的地址就是b[0]的地址
printf
(“b[1]的地址(十進制):%d\n",&b[1]); printf
(“a的地址(十六進制):%x\n",&a); printf
(“b的地址(十六進制):%x\n",b); printf
(“b[1]的地址(十六進制):%x\n",&b[1]);}a的地址(十進制):1245052b的地址(十進制):1245036b[1]的地址(十進制):1245040a的地址(十六進制):12ff7cb的地址(十六進制):12ff6cb[1]的地址(十六進制):12ff7010.1.3指針變量的定義和初始化
指針變量屬于簡單變量,一個指針變量存儲一個變量的地址。定義指針變量的一般形式為:
基類型*指針變量名;其中,“基類型”是已定義的數據類型,可以是系統預定義類型,也可以是用戶自定義類型;可以是簡單類型,也可以是構造類型。符號“*”表示定義一個指向“基類型”的指針變量。例如:intvar=10;int*
pointer=&var;值:1310588變量名:pointer地址:1310584值:10變量名:var地址:1310588圖10.1.1指針變量示意圖注意:指針變量的變量名是pointer,而不是*pointer。指針變量pointer的基類型為int,表示變量pointer只能存儲一個整型變量的地址,不能存儲其它類型變量的地址。例如下面的初始化是錯誤的:floatf;int*p=&f;即不能把一個實型變量的地址賦給一個基類型為整型的指針變量。10.1.3指針變量的定義和初始化可以在一個語句中同時定義整型變量和指針變量,例如:
inta,b,*
p1,*
p2;當定義一個指針變量而沒有初始化時,指針變量的值沒有確定,即該指針不指向特定的變量。這時使用這個變量是危險的,可能造成不可預料的錯誤結果。可以把指針變量初始化為0,即不指向任何變量。例如:
int*
pinter=0;
或
int*
pointer=NULL;10.1.4指針的運算
指針專用運算符
賦值運算
算術運算
關系運算(*)
指針專用運算符
(1)
取地址運算符&
運算符&是一個單目運算符,結合方向是右結合。&的運算對象只能是變量名(包括簡單變量和構造型變量)、數組元素或結構體成員,不能是表達式。&運算結果就是運算對象(變量)的地址。例如:int*p1,*p2,a,b[10];p1=&a;//把整型變量a的地址賦給指針變量p1p2=&b[0];//把數組元素b[0]的地址賦給指針變量p2
指針專用運算符
(2)指向運算符*運算符*在算術運算中表示乘法運算,是雙目運算符。在這里*稱為指向運算符或間接訪問運算符,是單目運算符。*的運算對象只能是指針變量,*的運算結果得到運算對象(指針變量)所指的變量。例如,當p1=&a時,*p1就表示變量a,是對變量a的間接引用。*p1是整型變量,不是指針,*p1可以像一般整型變量一樣使用。注意:符號*出現在不同的位置,含義也不同。在變量定義語句中(如int
*
p;),int*是類型名,p是變量名;在表達式中,符號*既可以表示乘法,也可表示指向。
指針專用運算符例10.1.2輸入兩個整數,按從大到小的順序輸出。程序如下:
#include<stdio.h>voidmain(){int*p1,*p2,*p,a,b;printf("輸入任意兩個整數:");scanf("%d%d",&a,&b);p1=&a;p2=&b; //如圖10.1.2(a)所示
if(a<b){p=p1;p1=p2;p2=p;//如圖10.1.2(b)所示
}printf("a=%d\tb=%d\n",a,b);printf("max=%d\tmin=%d\n",*p1,*p2);}&ap1&bp23a5b圖10.1.2(a)&bp1&ap23a5b圖10.1.2(b)程序運行結果:輸入任意兩個整數:35a=3b=5max=5min=3
賦值運算
可以把指針常量或另一個同類型的指針變量賦給指針變量。例如:inta,*p1,*p2=0;//對p2初始化p1=p2;
//把p2的值賦給p1p2=&a;//把變量a的地址賦給p2注意:基類型相同的指針才能進行賦值運算。指針變量的值雖然是一個整數,但不能把整數(0除外)直接賦給指針變量,也不能把指針賦給整型變量。例如,下面的運算是錯誤的,它們類型不同:p1=100;//錯誤a=p1;//錯誤
算術運算指針和整數進行加減運算只有當指針指向數組時,這種運算才有意義。例如,當指針p指向數組時,下面運算是合法的:p++;//p指向下一個數組元素p=p+2;//p指向當前元素后的第二個元素#include<stdio.h>voidmain(){int*p1,b[4]={1,2,3,4};p1=b;//把數組變量b的首地址賦給指針變量p1printf("value=%d\n",*p1);p1++;printf("value=%d\n",*p1);p1=p1+2;printf("value=%d\n",*p1);}運行結果:value=1value=2value=4
算術運算例10.1.3指針算術運算示例,程序清單如下:#include<stdio.h>voidmain(){inti,a[]={1,3,5,7,9},*p=a;for(i=0;i<5;i++)printf("a[%d]=%d\t",i,a[i]);printf("\n");printf(“a=%d\n”,p);//輸出數組a的首地址,即a[0]的地址
printf(“p+2=%d\n”,p+2);
//輸出a[2]的地址
printf(“*p+3=%d\n”,*p+3);//輸出a[0]+3的值
printf(“*(p+3)=%d\n”,*(p+3));//輸出a[3]的值
printf("*p++=%d\n",*p++);//輸出a[0]的值后,使p指向a[1]p=a;
printf(“*++p=%d\n”,*++p);//使p指向a[1]后,輸出a[1]的值printf(“++*p=%d\n”,++*p);//輸出++a[1]的值
}程序運行結果:a[0]=1a[1]=3a[2]=5a[3]=7a[4]=9a=1310568p+2=1310576*p+3=4*(p+3)=7*p++=1*++p=3++*p=4
關系運算(*)兩指針相等或不相等關系運算,用于確定這兩個指針是否指向同一對象。兩指針大小關系運算,用于確定它們所指數據對象存儲位置的前后關系。兩指針大小比較,只有對指向數組元素的指針才有意義,指向前面數組元素的指針變量小于指向后面數組元素的指針變量。inta[5],*p=a;下面關系運算是有意義的:p<a+5//判斷指針p是否指向數組a的元素,值為真或假p!=a//判斷指針p是否不指向a[0],值為真或假p==0//判斷指針p是否為空,值為真或假10.1.5指向指針的指針指針變量也是變量,它在內存中也分配存儲空間,因此,指針變量也有地址。可以定義一個指針變量pp存儲另一個指針變量p的地址,這個變量pp,就是指向指針變量p的指針變量。定義指向指針的指針的一般形式:類型名**變量名;例如:inta=10,*p=&a,**pp=&p;利用指針變量對數據進行訪問是“間接訪問”,普通的指針變量用的是“單級間址”,而指向指針的指針使用的則是“二級間址”。值:10變量名:a地址:1310588值:1310588變量名:p地址:1310584值:1310584變量名:pp地址:1310580圖10.1.3多重指針關系示意圖10.2指針與函數
10.2.1指針變量作為函數參數
10.2.2函數的返回值為指針(*)10.2.3指向函數的指針(*)10.2.1指針變量作為函數參數在C語言中,函數的形式參數不僅可以是整型變量、實型變量或字符型變量,也可以是指針變量。使用指針作為函數參數,主調函數將變量的地址傳遞給被調函數。用指針做參數,實際參數和形式參數之間仍然是值傳遞,即在調用函數時,把實際參數的值賦給形式參數。被調函數中雖然不能改變作為實際參數的指針變量的值,但可以改變指針變量所指變量的值,從而達到在被調函數中修改主調函數中變量的目的。用指針變量作為函數參數,可以獲得從函數中帶回多于一個值的客觀效果。10.2.1指針變量作為函數參數例10.2.1調用函數,交換兩個變量的值。
//程序1,使用整型參數:#include<stdio.h>voidswap(intx,inty){inttemp; temp=x; x=y;y=temp; //交換形參x,y的值 }voidmain(){inta,b;printf(“請輸入兩個整數a和b:”);scanf("%d%d",&a,&b);printf("調用函數前:a=%d\tb=%d\n",a,b);swap(a,b);printf("調用函數后:a=%d\tb=%d\n",a,b);}程序運行結果:請輸入兩個整數a和b:35調用函數前:a=3b=5調用函數后:a=3b=510.2.1指針變量作為函數參數例10.2.1調用函數,交換兩個變量的值。
//程序2:使用指針參數#include<stdio.h>voidswap(int*p1,int*p2){inttemp;
temp=*p1; *p1=*p2;*p2=temp;//交換指針變量所指變量的值
}voidmain(){inta,b;printf(”請輸入兩個整數a和b:”);scanf("%d%d",&a,&b);printf("調用函數前:a=%d\tb=%d\n",a,b);swap(&a,&b);//實參為變量的地址
printf("調用函數后:a=%d\tb=%d\n",a,b);}程序運行結果:請輸入兩個整數a和b:35調用函數前:a=3b=5調用函數后:a=5b=310.2.1指針變量作為函數參數例10.2.2已知長方體長、寬、高,求其體積和三個側面的面積。程序如下:#include<stdio.h>intvolume(intl,intw,inth,int*ps1,int*ps2,int*ps3){*ps1=l*w; *ps2=l*h; *ps3=w*h; returnl*w*h;}voidmain(){inta,b,c,v,s1,s2,s3; printf("請輸入長方體的三條邊:"); scanf("%d%d%d",&a,&b,&c); v=volume(a,b,c,&s1,&s2,&s3); printf("volume=%d\n",v); printf("s1=%d\ts2=%d\ts3=%d\n",s1,s2,s3);}請輸入長方體的三條邊:123volume=6s1=2s2=3s3=610.3指針與數組10.3.1一維數組與指針
指向數組元素的指針
數組名作為函數參數
10.3.2二維數組與指針
10.3.3字符串與指針
10.3.4指針數組
指向數組元素的指針一個數組的所有元素在內存中是連續存儲的,數組元素也是變量,各數組元素都有各自的地址。數組第一個元素(下標為0的元素)的地址稱為數組首地址。在C語言中,數組名就代表該數組首地址。例如,有如下定義:
inta[10],*p;則表達式p=a和表達式p=&a[0]等價,都表示指針變量p指向數組a的首地址。數組首地址是系統編譯時確定的,是一個常量。因此,不能對數組首地址賦值,例如a=p、a++等都是錯誤的。由10.1節介紹的指針運算規則可知:a代表數組元素a[0]的地址;a+1就是數組元素a[1]的地址;…;a+i就是數組元素a[i]的地址。因此,*(a+i)就表示a+I所指的變量,即數組元素a[i]。用a[i]方式訪問數組元素,稱為下標引用法;而用*(a+1)方式訪問數組元素,稱為指針引用法。
指向數組元素的指針例10.3.1指針法引用數組元素示例,程序清單如下:
#include<stdio.h>voidmain(){inta[]={1,2,3,5,7,9},i,*p; printf("用下標引用數組元素:\n"); for(i=0;i<6;i++) printf("%d\t",a[i]); printf("\n"); printf("用數組名引用數組元素:\n"); for(i=0;i<6;i++) printf("%d\t",*(a+i)); printf("\n"); printf("用指針引用數組元素:\n"); for(p=a;p<a+6;p++) printf("%d\t",*p); printf("\n");}程序中3組輸出結果相同,可見a[i]和*(a+1)完全等價。用下標引用數組元素:123579用數組名引用數組元素:123579用指針引用數組元素:123579
指向數組元素的指針例10.3.2數組輸入示例,程序如下:#include<stdio.h>voidmain(){inta[5],i,*p;printf("用下標引用數組元素:\n");for(i=0;i<5;i++)scanf("%d",&a[i]);for(i=0;i<5;i++)printf("%d\t",a[i]);printf("\n");//在右欄接續代碼//接續左欄的代碼printf("用數組名引用數組元素:\n");for(i=0;i<5;i++)scanf("%d",a+i);for(i=0;i<5;i++)printf("%d\t",a[i]);printf("\n");printf("用指針引用數組元素:\n");for(p=a;p<a+5;p++)scanf("%d",p);for(i=0;i<5;i++)printf("%d\t",a[i]);printf("\n");}
數組名作為函數參數
數組名可以作為函數參數。因為數組名代表數組首地址,所以用數組名作為參數實際上就是用指針作為函數參數。例10.3.3求數組元素的平均值,程序清單如下:
#include<stdio.h>voidmain(){intaverage(inta[]); //函數原型聲明
intscore[5]={60,80,90,70,50},i,aver;printf("Scores:\n");for(i=0;i<5;i++)printf("%d\t",score[i]);printf("\n");aver=average(score); //調用求平均值函數
printf("Average=%d\n",aver);}intaverage(inta[]) //定義求平均值函數
{inti,sum=0;for(i=0;i<5;i++)sum+=a[i];returnsun/5;}
Scores:6080907050Average=70
數組名作為函數參數說明:數組作為函數的形式參數,數組長度可以省略,但方括號不能省略。即:
intaverage(inta[5])//正確可以寫成
intaverage(inta[])//正確但是不能寫成
intaverage(inta)//錯誤!數組作為函數的實際參數,只能寫數組名。即把
aver=average(score)//正確寫成以下形式都是錯誤的
aver=average(score[])//錯誤!
aver=average(score[5])//錯誤!數組作為函數參數,函數調用時,系統把實參數組的首地址傳遞給形參,而不是把實參數組各元素的值傳遞給形參。因此,可以把數組形參寫成指針形參。即可以把intaverage(inta[])寫成intaverage(int*a),函數體不變。同理,實參的數組名也可以由已賦值的指針變量代替。
數組名作為函數參數
例10.3.4逆序輸出數組元素,程序清單如下:#include<stdio.h>voidmain(){ voidlist(int*a,intn); //函數原型聲明
intscore[5]={1,2,3,4,5},i,*p=score; printf(“scores:\n"); for(i=0;i<5;i++) //輸出原數組
printf("%d\t",*(p+i)); printf("\n"); list(p,5); //調用倒序函數
for(i=0;i<5;i++) //輸出倒序后數組
printf("%d\t",*(p+i)); printf("\n");}voidlist(int*a,intn) //倒序函數定義{ inti,t; for(i=0;i<n/2;i++) {t=a[i];a[i]=a[n-1-i];a[n-1-i]=t;}}10.3.2二維數組與指針
二維數組的地址
指向一維數組的指針
二維數組作為函數參數(*)
二維數組的地址
設二維數組a定義如下:
inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};C語言允許把二維數組看成多個一維數組,即可以把a理解為一維數組,它包含3個元素a[0]、a[1]和a[2];而每個元素又是一個一維數組,即a[0]包含a[0][0]、a[0][1]、a[0][2]和a[0][3]共4個元素,a[1]和a[2]也各包含4個元素。a是二維數組,a就代表二維數組的首地址,即&a[0][0]。(打印a[0][0]元素的值,用printf(“%d\n”,*a);
還是printf(“%d\n”,**a);?)a[0]、a[1]和a[2]是a的元素,但也是一維數組,因此,它們分別代表各一維數組(二維數組的行)的首地址。a[0]是元素a[0][0]的地址,即&a[0][0];a[1]是元素a[1][0]的地址,即&a[1][0];數組元素有下標法和指針法兩種引用方式,即a[0]等價于*a,a[1]等價于*(a+1),因此,a、a[0]、*a和&a[0][0]都是表示元素a[0][0]的地址,a+1、a[1]和*(a+1)和&a[1][0]都是表示元素a[1][0]的地址。a[i][j]的地址是a[i]+j、*(a+i)+j或&a[i][j]。
二維數組的地址例10.3.5二維數組元素地址示例,程序如下:#include<stdio.h>voidmain(){ inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; printf("%d\n",a);//輸出&a[0][0] printf("%d\n",a+1); //輸出&a[1][0] printf("%d\n",*a+1);//輸出&a[0][1] printf("%d\n",a[0]+1);//輸出&a[0][1] printf("%d\n",*a[0]+1);//輸出a[0][0]+1}程序運行結果:12450081245024124501212450122
指向一維數組的指針C語言允許定義指向二維數組中的行(一維數組)的指針,然后應用指針變量靈活地處理二維數組。定義格式為:
類型說明符(*變量名)[長度];
其中,“類型說明符”是數組元素的類型,“長度”是二維數組分解為多個一維數組時,每個一維數組的長度,即二維數組的行寬。定義語句中的圓括號是必須的,否則就成了“指針數組”。
指向一維數組的指針程序中語句“p=a;”表示把a[0]的地址賦給指針變量p,如果寫成“p=a[0];”(會發生編譯錯誤),則表示把a[0][0]的地址賦給指針變量p(這時p+1是什么?printf(“%d\n”,a);和printf(“%d\n”,a[0]);是否相同?),顯然a[0][0]的地址與p的類型不相符。*(p+i)表示a[i]的首地址,*(p+i)+j則表示a[i][j]的地址,所以,*(*(p+i)+j)表示a[i][j]的值。例10.3.7指向行的指針變量應用示例:#include<stdio.h>voidmain(){inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}},i,j,(*p)[4];p=a;for(i=0;i<3;i++){for(j=0;j<4;j++)printf("%d\t",*(*(p+i)+j)
);printf("\n");}}10.3.3字符串與指針在C語言中,使用字符數組存儲字符串。例如:
charstr1[]=“Hello”;printf(“str=%s\n”,str1);C語言還允許用字符指針表示字符串,即定義一個指針,然后用指針指向字符串中的首字符。例如:
char*str2=“Hello”;printf(“str=%s\n”,str2);Hello\0Hello\0str1str2
用字符數組表示字符串,系統為字符數組分配存儲空間,數組名就是該存儲空間的首地址;用字符指針變量表示字符串,指針變量只保存了字符串的首地址,而字符串本身并沒有保存在字符指針中(實際上也無法存儲),系統為字符串常量專門開辟了一塊連續的存儲空間,然后把該空間的首地址賦給字符指針。10.3.3字符串與指針1.賦值運算對字符數組不能進行賦值運算,因為字符數組名是指針常量。而對字符指針可以進行
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 專利技術咨詢服務合同
- 以房抵債合同范本
- 商品混凝土供貨居間合同協議書10篇
- 終止施工合同書協議書
- 裝修分期合同書二零二五年
- 二零二五版房地產經紀合同書范文
- 2025年合同違約的處理方式及其法律效力
- 2025商務英語考試:商務合同英譯關鍵注意問題
- 2025工程承包合同范本示例
- 版人防承包合同范例
- 《企業經營決策實戰模擬》教學大綱
- 抗菌藥物合理使用培訓
- 變壓器絕緣油試驗資料課件
- 籃球運動的簡介
- 如何幫助孩子有效應對壓力
- 分布式光伏高處作業專項施工方案
- 中華人民共和國突發事件應對法
- 鞘內注射化療護理課件
- 郵政社區團購怎么做流程
- 建筑施工電動運輸車輛進場驗收表
- Unit2Let'sCelebrate!Developingideas作業設計-2023-2024學年高中英語(精修版)
評論
0/150
提交評論