【C語言】第8章 善於利用指標
第8章 善於利用指標
指標是什麼
如果在程式中定義了一個變數,在對程式進行編譯時,系統就會給該變數分配記憶體單元
編譯系統根據程式中定義的變數型別,分配一定長度的空間
例如,VC++為整型變數分配4個位元組,對單精度浮點型變數分配4個位元組,對字元型變數分配1個位元組
地址指向該變數單元
將地址形象化地稱為“指標”
直接存取
int i=3,j=6,k;
k=i+j;
間接存取 int i=3,j=6,k; [ 定義特殊變數 i_pointer ] i_pointer=&i; *i_pointer=50; 指向就是通過地址來體現的 假設i_pointer中的值是變數i的地址(2000),這樣就在i_pointer和變數i之間建立起一種聯絡,即通過i_pointer能知道i的地址,從而找到變數i的記憶體單元
例如,地址2000是變數i的指標
如果有一個變數專門用來存放另一變數的地址(即指標),則它稱為“指標變數”
i_pointer就是一個指標變數。指標變數就是地址變數,用來存放地址的變數,指標變數的值是地址(即指標)
“指標”和“指標變數”是不同的概念
可以說變數i的指標是2000,而不能說i的指標變數是2000
指標是一個地址,而指標變數是存放地址的變數
通過指標變數訪問整型變數。 思想: 先定義2個整型變數,再定義2個指標變數,分別指向這兩個整型變數,通過訪問指標變數,可以找到它們所指向的變數,從而得到這些變數的值。 #include <stdio.h> int main() { int a=100,b=10; int *pointer_1, *pointer_2; pointer_1=&a; pointer_2=&b; printf(“a=%d,b=%d\n”,a,b); printf(“*pointer_1=%d,*pointer_2= %d\n”,*pointer_1,*pointer_2); //此處*與指標變數一起使用。此時代表指標變數所指向的變數 return 0; }
定義指標變數的一般形式為:
型別 * 指標變數名;
如:int *pointer_1, *pointer_2;
int是為指標變數指定的“基型別”
基型別指定指標變數可指向的變數型別
如pointer_1可以指向整型變數,但不能指向浮點型變數
在引用指標變數時,可能有三種情況:
給指標變數賦值。如:p=&a;
引用指標變數指向的變數。如有
p=&a; *p=1;
則執行printf(“%d”,*p); 將輸出1
引用指標變數的值。如:printf(“%o”,p);
運算子
(1) & 取地址運算子。
&a是變數a的地址
(2) * 指標運算子(“間接訪問”運算子)
如果: p指向變數a,則*p就代表a。
k=*p; (把a的值賦給k)
*p=1; (把1賦給a)
輸入a和b兩個整數,按先大後小的順序輸出a和b。
思路:
用指標方法來處理這個問題。不交換整型變數的值,而是交換兩個指標變數的值。
#include <stdio.h>
int main()
{ int *p1,*p2,*p,a,b;
printf(“integer numbers:");
scanf(“%d,%d”,&a,&b);
p1=&a; p2=&b;
if(a<b)
{ p=p1; p1=p2; p2=p; }
printf(“a=%d,b=%d\n”,a,b);
printf(“%d,%d\n”,*p1,*p2);
return 0;
}
題目要求同上例,即對輸入的兩個整數按大小順序輸出。現用函式處理,而且用指標型別的資料作函式引數。
思路
定義一個函式swap,將指向兩個整型變數的指標變數作為實參傳遞給swap函式的形參指標變數,在函式中通過指標實現交換兩個變數的值。
#include <stdio.h>
int main()
{ void swap(int *p1,int *p2);
int a,b; int *pointer_1,*pointer_2;
printf("please enter a and b:");
scanf(“%d,%d”,&a,&b);
pointer_1=&a;
pointer_2=&b;
if (a<b) swap(pointer_1,pointer_2);
printf(“max=%d,min=%d\n”,a,b);
return 0;
}
void swap(int *p1,int *p2)
{ int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
輸入3個整數a,b,c,要求按由大到小的順序將它們輸出。用函式實現。#include <stdio.h>
int main()
{ void exchange(int *q1, int *q2, int *q3);
int a,b,c,*p1,*p2,*p3;
scanf("%d,%d,%d",&a,&b,&c);
p1=&a; p2=&b; p3=&c;
exchange(p1,p2,p3);
printf(“%d,%d,%d\n",a,b,c);
return 0;
}
void exchange(int *q1, int *q2, int *q3)
{ void swap(int *pt1, int *pt2);
if(*q1<*q2) swap(q1,q2);
if(*q1<*q3) swap(q1,q3);
if(*q2<*q3) swap(q2,q3);
}
void swap(int *pt1, int *pt2)
{ int temp;
temp=*pt1; *pt1=*pt2; *pt2=temp;
}
通過指標引用陣列
一個變數有地址,一個數組包含若干元素,每個陣列元素都有相應的地址
指標變數可以指向陣列元素(把某一元素的地址放到一個指標變數中)
所謂陣列元素的指標就是陣列元素的地址
可以用一個指標變數指向一個數組元素
int a[10]={1,3,5,7,9,11,13,15,17,19};
int *p;
p=&a[0];
等價於p=a;
等價於int *p=a;
或int *p=&a[0];
運算
在指標指向陣列元素時,允許以下運算:
加一個整數(用+或+=),如p+1,同一陣列中的下一個元素
減一個整數(用-或-=),如p-1,同一陣列中的上一個元素
自加運算,如p++,++p
自減運算,如p--,--p
兩個指標相減,如p1-p2 (只有p1和p2都指向同一陣列中的元素時才有意義)
有一個整型陣列a,有10個元素,要求輸出陣列中的全部元素。
思路:
引用陣列中各元素的值有3種方法:
(1) 下標法;
(2) 通過陣列名計算陣列元素地址,找出元素的值;
(3) 用指標變數指向陣列元素
分別寫出程式,以資比較分析。
(1) 下標法
#include <stdio.h>
int main()
{ int a[10]; int i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++) scanf("%d",&a[i]);
for(i=0;i<10;i++) printf(“%d ”,a[i]);
printf("%\n");
return 0;
}
(2) 通過陣列名計算陣列元素地址,找出元素的值
#include <stdio.h>
int main()
{ int a[10]; int i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++) scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf(“%d ”,*(a+i));
printf("\n");
return 0;
}
(3) 用指標變數指向陣列元素
#include <stdio.h>
int main()
{ int a[10]; int *p,i;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++) scanf("%d",&a[i]);
for(p=a; p<(a+10); p++)
printf(“%d ”,*p);
printf("\n");
return 0;
}
通過指標變數輸出整型陣列a的10個元素。
#include <stdio.h>
int main()
{ int *p,i,a[10];
p=a;
printf(“enter 10 integer numbers:\n");
for(i=0;i<10;i++) scanf(“%d”,p++);
for(i=0;i<10;i++,p++)
printf(“%d ”,*p);
printf("\n");
return 0;
}
將陣列a中n個整數按相反順序存放
#include <stdio.h>
int main()
{ void inv(int x[ ],int n);
int i, a[10]={3,7,9,11,0,6,7,5,4,2};
for(i=0;i<10;i++) printf(“%d ”,a[i]);
printf("\n");
inv(a,10);
for(i=0;i<10;i++) printf(“%d ”,a[i]);
printf("\n");
return 0;
}
void inv(int x[ ],int n)
{ int temp,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)
{ j=n-1-i;
temp=x[i]; x[i]=x[j]; x[j]=temp;
}
}
void inv(int x[ ],int n)
{ int temp,*i,*j;
i=x; j=x+n-1;
for( ; i<j; i++,j--)
{ temp=*i; *i=*j; *j=temp; }
}
改寫
用指標變數作實參。
#include <stdio.h>
int main()
{ void inv(int *x,int n);
int i, arr[10],*p=arr;
for(i=0;i<10;i++,p++)
scanf(“%d”,p);
inv(p,10);
for(p=arr;p<arr+10;p++)
printf(“%d ”,*p);
printf("\n");
return 0;
}
用指標方法對10個整數按由大到小順序排序。
思路:
在主函式中定義陣列a存放10個整數,定義int *型指標變數p指向a[0]
定義函式sort使陣列a中的元素按由大到小的順序排列
在主函式中呼叫sort函式,用指標p作實參
用選擇法進行排序
#include <stdio.h>
int main()
{ void sort(int x[ ],int n);
int i,*p,a[10];
p=a;
for(i=0;i<10;i++) scanf(“%d”,p++);
p=a;
sort(p,10);
for(p=a,i=0;i<10;i++)
{ printf(“%d ”,*p); p++; }
printf("\n");
return 0;
}
void sort(int x[], int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{ k=i;
for(j=i+1;j<n; j++)
if(x[j]>x[k]) k=j;
if(k!=i)
{ t=x[i];x[i]=x[k];x[k]=t; }
}
}
通過指標引用多維陣列
- 多維陣列元素的地址
int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
a代表第0行首地址
a+1代表第1行首地址
a+2代表第2行首地址
a+i代表行號為i的行首地址(按行變化)
a[0]代表a[0][0]的地址
a[0]+1代表a[0][1]的地址
a[0]+2代表a[0][2]的地址
a[0]+3代表a[0][3]的地址
例8.11 二維陣列的有關資料(地址和值)
#include <stdio.h>
int main()
{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf(“%d,%d\n”,a,*a);
printf(“%d,%d\n”,a[0],*(a+0));
printf(“%d,%d\n”,&a[0],&a[0][0]);
printf(“%d,%d\n”,a[1],a+1);
printf(“%d,%d\n”,&a[1][0],*(a+1)+0);
printf(“%d,%d\n”,a[2],*(a+2));
printf(“%d,%d\n”,&a[2],a+2);
printf(“%d,%d\n”,a[1][0],*(*(a+1)+0));
printf(“%d,%d\n”,*a[2],*(*(a+2)+0));
return 0;
}
有一個3×4的二維陣列,要求用指向元素的指標變數輸出二維陣列各元素的值。
#include <stdio.h>
int main()
{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
for(p=a[0];p<a[0]+12;p++)
{ if((p-a[0])%4==0) printf(“\n”);
printf(“%4d”,*p);
}
printf("\n");
return 0;
}
輸出二維陣列任一行任一列元素的值。
#include <stdio.h>
int main()
{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4],i,j;
p=a;
printf(“enter row and colum:");
scanf(“%d,%d”,&i,&j);
printf(“a[%d,%d]=%d\n”, i,j,*(*(p+i)+j));
return 0;
}
有一個班,3個學生,各學4門課,計算總平均分數以及第n個學生的成績。
#include <stdio.h>
int main()
{ void average(float *p,int n);
void search(float (*p)[4],int n);
float score[3][4]={{65,67,70,60},
{80,87,90,81},{90,99,100,98}};
average(*score,12);
search(score,2);
return 0;
}
void average(float *p,int n)
{ float *p_end;
float sum=0,aver;
p_end=p+n-1;
for( ;p<=p_end; p++)
sum=sum+(*p);
aver=sum/n;
printf("average=%5.2f\n",aver);
}
void search(float (*p)[4],int n)
{ int i;
printf("The score of No.%d are:\n",n);
for(i=0;i<4;i++)
printf("%5.2f ",*(*(p+n)+i));
printf("\n");
}
在上題基礎上,查詢有一門以上課程不及格的學生,輸出他們的全部課程的成績。
#include <stdio.h>
int main()
{ void search(float (*p)[4],int n);
float score[3][4]={{65,57,70,60}, {58,87,90,81},{90,99,100,98}};
search(score,3);
return 0;
}
void search(float (*p)[4],int n)
{ int i,j,flag;
for(j=0;j<n;j++)
{ flag=0;
for(i=0;i<4;i++)
if(*(*(p+j)+i)<60) flag=1;
if (flag==1)
{ printf("No.%d fails\n",j+1);
for(i=0;i<4;i++)
printf(“%5.1f ”,*(*(p+j)+i));
printf("\n");
}
}
}
通過指標引用字串
定義一個字元陣列,在其中存放字串“I love China!”,輸出該字串和第8個字元。
#include <stdio.h>
int main()
{
char string[]=“I love China!”;
printf(“%s\n”,string);
printf(“%c\n”,string[7]);
return 0;
}
通過字元指標變數輸出一個字串。
#include <stdio.h>
int main()
{
char *string=“I love China!”;
printf(“%s\n”,string);
return 0;
}
將字串a複製為字串b,然後輸出字串b。
#include <stdio.h>
int main()
{ char a[ ]=“I am a student.”, b[20];
int i;
for(i=0;*(a+i)!='\0';i++)
*(b+i)=*(a+i);
*(b+i)=‘\0’;
printf(“string a is:%s\n”,a);
printf("string b is:");
for(i=0;b[i]!='\0';i++)
printf(“%c”,b[i]);
printf("\n");
return 0;
}
char *format;
format=”a=%d,b=%f\n”;
printf(format,a,b);
相當於
printf(“a=%d,b=%f\n”,a,b);
指向函式的指標
用函式求整數a和b中的大者。
思路:
定義一個函式max,實現求兩個整數中的大者。在主函式呼叫max函式,除了可以通過函式名呼叫外,還可以通過指向函式的指標變數來實現。分別程式設計並作比較。
(1) 通過函式名呼叫函式
#include <stdio.h>
int main()
{ int max(int,int);
int a,b,c;
printf("please enter a and b:");
scanf("%d,%d",&a,&b);
c=max(a,b);
printf(“%d,%d,max=%d\n",a,b,c);
return 0;
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
return(z);
}
(2) 通過指標變數訪問它所指向的函式
#include <stdio.h>
int main()
{ int max(int,int);
int (*p)(int,int); int a,b,c;
p=max;
printf("please enter a and b:");
scanf("%d,%d",&a,&b);
c=(*p)(a,b);
printf(“%d,%d,max=%d\n",a,b,c);
return 0;
}
定義指向函式的指標變數的一般形式為
資料型別 (*指標變數名)(函式引數表列);
例如:
int (*p)(int,int);
p=max; 對
p=max(a,b); 錯
p+n,p++,p-- 等運算無意義
輸入兩個整數,然後讓使用者選擇1或2,選1時呼叫max函式,輸出二者中的大數,選2時呼叫min函式,輸出二者中的小數。
#include <stdio.h>
int main()
{ int max(int,int); int min(int x,int y);
int (*p)(int,int); int a,b,c,n;
scanf("%d,%d",&a,&b);
scanf("%d",&n);
if (n==1) p=max;
else if (n==2) p=min;
c=(*p)(a,b);
printf("a=%d,b=%d\n",a,b);
if (n==1) printf("max=%d\n",c);
else printf("min=%d\n",c);
return 0;
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
return(z);
}
int min(int x,int y)
{ int z;
if(x<y) z=x;
else z=y;
return(z);
}
指向函式的指標變數的一個重要用途是把函式的地址作為引數傳遞到其他函式
指向函式的指標可以作為函式引數,把函式的入口地址傳遞給形參,這樣就能夠在被呼叫的函式中使用實參函式
有兩個整數a和b,由使用者輸入1,2或3。如輸入1,程式就給出a和b中大者,輸入2,就給出a和b中小者,輸入3,則求a與b之和。
#include <stdio.h>
int main()
{ void fun(int x,int y, int (*p)(int,int));
int max(int,int); int min(int,int);
int add(int,int); int a=34,b=-21,n;
printf("please choose 1,2 or 3:");
scanf(“%d”,&n);
if (n==1) fun(a,b,max);
else if (n==2) fun(a,b,min);
else if (n==3) fun(a,b,add);
return 0;
}
int fun(int x,int y,int (*p)(int,int))
{ int resout;
resout=(*p)(x,y);
printf(“%d\n”,resout);
}
int max(int x,int y)
{ int z;
if(x>y) z=x;
else z=y;
printf("max=" );
return(z);
}
int min(int x,int y)
{ int z;
if(x<y) z=x;
else z=y;
printf("min=");
return(z);
}
int add(int x,int y)
{ int z;
z=x+y;
printf("sum=");
return(z);
}
定義返回指標值的函式的一般形式為
型別名 *函式名(引數表列);
例:int *a(int x, int y);
有a個學生,每個學生有b門課程的成績。要求在使用者輸入學生序號以後,能輸出該學生的全部成績。用指標函式實現。
#include <stdio.h>
int main()
{ float score[ ][4]={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search(float (*pointer)[4], int n);
float *p; int i,k;
scanf("%d",&k);
printf("The scores of No.%d are:\n",k);
p=search(score,k);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
printf("\n");
return 0;
}
float *search(float (*pointer)[4],int n)
{ float *pt;
pt=*(pointer+n);
return(pt);
}
找出其中有不及格的課程的學生及其學生號。
……
float *search(float (*pointer)[4]);
float *p; int i,j;
for(i=0;i<3;i++)
{ p=search(score+i);
if(p==*(score+i))
{ printf("No.%d score:",i);
for(j=0;j<4;j++)
printf(“%5.2f ”,*(p+j));
printf("\n");
}
}
……
float *search(float (*pointer)[4])
{ int i=0;
float *pt;
pt=NULL;
for( ;i<4;i++)
if(*(*pointer+i)<60)
pt=*pointer;
return(pt);
}
指標陣列
一個數組,若其元素均為指標型別資料,稱為指標陣列,也就是說,指標陣列中的每一個元素都存放一個地址,相當於一個指標變數。
定義一維指標陣列的一般形式為
型別名*陣列名[陣列長度];
int *p[4];
將若干字串按字母順序(由小到大)輸出。
#include <stdio.h>
#include <string.h>
int main()
{ void sort(char *name[ ],int n);
void print(char *name[ ],int n);
char *name[ ]={“Follow”,“Great”, “FORTRAN”,“Computer”};
int n=4;
sort(name,n);
print(name,n);
return 0;
}
void sort(char *name[ ],int n)
{ char *temp; int i,j,k;
for (i=0;i<n-1;i++)
{ k=i;
for (j=i+1;j<n;j++)
if(strcmp(name[k],name[j])>0) k=j;
if (k!=i)
{ temp=name[i]; name[i]=name[k];
name[k]=temp;
}
}
}
void print(char *name[ ],int n)
{ int i;
for(i=0;i<n;i++)
printf(“%s\n”,name[i]);
}
使用指向指標資料的指標變數。
char *name[]={“Follow”,,“Great”, “FORTRAN”,“Computer”};
char **p; int i;
for(i=0;i<5;i++)
{ p=name+i; printf("%s\n",*p); }
有一個指標陣列,其元素分別指向一個整型陣列的元素,用指向指標資料的指標變數,輸出整型陣列各元素的值。
#include <stdio.h>
int main()
{ int a[5]={1,3,5,7,9};
int *num[5]={&a[0],&a[1],&a[2], &a[3],&a[4]};
int **p,i;
p=num;
for (i=0;i<5;i++)
{ printf("%d ",**p);
p++;
}
printf("\n"); return 0;
}
動態記憶體分配與指向它的指標變數
記憶體的動態分配
非靜態的區域性變數是分配在記憶體中的動態儲存區的,這個儲存區是一個稱為棧的區域
C語言還允許建立記憶體動態分配區域,以存放一些臨時用的資料,這些資料需要時隨時開闢,不需要時隨時釋放。這些資料是臨時存放在一個特別的自由儲存區,稱為堆區
對記憶體的動態分配是通過系統提供的庫函式來實現的,主要有malloc,calloc,free,realloc這4個函式。
malloc函式
其函式原型為:void *malloc(unsigned int size);
其作用是在記憶體的動態儲存區中分配一個長度為size的連續空間
函式的值是所分配區域的第一個位元組的地址,或者說,此函式是一個指標型函式,返回的指標指向該分配域的開頭位置
malloc(100);
開闢100位元組的臨時分配域,函式值為其第1個位元組的地址
注意指標的基型別為void,即不指向任何型別的資料,只提供一個地址
如果此函式未能成功地執行(例如記憶體空間不足),則返回空指標(NULL)
calloc函式
其函式原型為:void *calloc(unsigned n,unsigned size);
其作用是在記憶體的動態儲存區中分配n個長度為size的連續空間,這個空間一般比較大,足以儲存一個數組。
用calloc函式可以為一維陣列開闢動態儲存空間,n為陣列元素個數,每個元素長度為size。這就是動態陣列。函式返回指向所分配域的起始位置的指標;如果分配不成功,返回NULL。如:
如: p=calloc(50,4); 開闢50×4個位元組的臨時分配域,把起始地址賦給指標變數p
free函式
其函式原型為:void free(void *p);
其作用是釋放指標變數p所指向的動態空間,使這部分空間能重新被其他變數使用。p應是最近一次呼叫calloc或malloc函式時得到的函式返回值。
例如:free(p);
釋放指標變數p所指向的已分配的動態空間
free函式無返回值
realloc函式
其函式原型為:void *realloc(void *p,unsigned int size);
如果已經通過malloc函式或calloc函式獲得了動態空間,想改變其大小,可以用recalloc函式重新分配。
用realloc函式將p所指向的動態空間的大小改變為size。p的值不變。如果重分配不成功,返回NULL。
例如:realloc(p,50);
將p所指向的已分配的動態空間改為50位元組
以上4個函式的宣告在stdlib.h標頭檔案中,在用到這些函式時應當用“#include <stdlib.h>”指令把stdlib.h標頭檔案包含到程式檔案中。
例8.30 建立動態陣列,輸入5個學生的成績,另外用一個函放數檢查其中有無低於60分的,輸出不合格的成績。
思路:用malloc函式開闢一個動態自由區域,用來存5個學生的成績,會得到這個動態域第一個位元組的地址,它的基型別是void型。用一個基型別為int的指標變數p來指向動態陣列的各元素,並輸出它們的值。但必須先把malloc函式返回的void指標轉換為整型指標,然後賦給p1
#include <stdio.h>
#include <stdlib.h>
int main()
{ void check(int *);
int *p1,i;
p1=(int *)malloc(5*sizeof(int));
for(i=0;i<5;i++)
scanf("%d",p1+i);
check(p1);
return 0;
}
void check(int *p)
{ int i;
printf("They are fail:");
for(i=0;i<5;i++)
if (p[i]<60)
printf("%d ",p[i]);
printf("\n");
}