C語言筆記(四)--- 指針


第十章 指針

1. 地址指針的基本概念:

在計算機中,所有的數(shù)據(jù)都是存放在存儲器中的。
一般把存儲器中的一個字節(jié)稱為一個內(nèi)存單元,不同的數(shù)據(jù)類型所占用的內(nèi)存單元數(shù)不等,如整型占2個單元,字符型占1個單元等。
為了正確訪問這些內(nèi)存單元,必須為每個內(nèi)存單元編上號。
根據(jù)一個內(nèi)存單元的編號即可準確地找到該內(nèi)存單元。
內(nèi)存單元的編號也叫做地址。
既然根據(jù)內(nèi)存單元的編號或地址就可以找到所需的內(nèi)存單元,所以通常也把這個地址稱為指針。
內(nèi)存單元的指針和內(nèi)存單元的內(nèi)容是兩個不同的概念。
在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。
因此,一個指針變量的值就是某個內(nèi)存單元的地址或稱為某內(nèi)存單元的指針。

2. 變量的指針和指向變量的指針變量:

變量的指針就是變量的地址。
存放變量地址的變量是指針變量。
即在C語言中,允許用一個變量來存放指針,這種變量稱為指針變量。
因此,一個指針變量的值就是某個變量的地址或稱為某變量的指針。
為了表示指針變量和他所指向的變量之間的關(guān)系,在程序中用 "*" 符號表示“指向”,例如,i_pointer 代表指針變量,而 *i_pointeri_pointer 所指向的變量。
因此下面兩個語句作用相同:

i=3;
*i_pointer=3;
// 第二個語句的含義是將3賦給指針變量i_pointer所指向的變量。

3. 定義一個指針變量:

對指針變量的定義包括三個內(nèi)容:

  • 1)指針類型說明,即定義變量為一個指針變量;
  • 2)指針變量名;
  • 3)變量值(指針)所指向的變量的數(shù)據(jù)類型。

其一般形式為:
類型說明符 *變量名;

其中,* 表示這是一個指針變量,變量名即為定義的指針變量名,類型說明符表示本指針變量所指向的變量的數(shù)據(jù)類型。例如:
int *p1; 表示p1是一個指針變量,他的值是某個整型變量的地址。
或者說p1指向一個整型變量。
至于p1究竟指向哪一個整型變量, 應由向p1賦予的地址來決定。

注意:一個指針變量只能指向同類型的變量,如p3只能指向浮點變量,不能時而指向一個浮點變量,時而又指向一個字符變量。

4. 指針變量的引用:

指針變量在使用前,需要先聲明,并且賦值,否則將造成系統(tǒng)混亂,甚至死機。
指針變量的賦值只能賦予地址,絕不能賦予任何其他數(shù)據(jù),否則將引起錯誤。
在C語言中,變量的地址是由編譯系統(tǒng)分配的,對用戶完全透明,用戶不知道變量的具體地址。

兩個有關(guān)的運算符:

  • 1)& : 取地址運算符。
  • 2)* : 指針運算符(或稱“間接訪問”運算符)

指針變量初始化的方式:

  • 1)
 int a;
 int *p=&a;
  • 2)
 int a;
 int *p;
 p=&a;

不允許把一個數(shù)賦予指針變量,故下面的賦值是錯誤的:

int *p;
p=1000;

被賦值的指針變量前不能再加 "*" 說明符,如 *p=&a 也是錯誤的。

請對下面的關(guān)于 “&" 和 "*" 的問題進行考慮:

    1. 如果已經(jīng)執(zhí)行了 pointr_1=&a; 語句,則 &*pointer_1 是什么含義? 指代 pointer_1 自己,即變量a的地址
  • 2)*&a 含義是什么? 指代a自己,即變量a的值。
  • 3)(*pointer_1)++*pointer_1++ 的區(qū)別?

5. 指針變量做為函數(shù)參數(shù):

例題:輸入的兩個整數(shù)按大小順序輸出。

swap(int *p1,int *p2) 
{ int temp;
  temp=*p1;
  *p1=*p2;
  *p2=temp;
}
main(){
int a,b,*pointer_1,*pointer_2;
a=5;b=10;
pointer_1=&a;
pointer_2=&b;
if (a<b) swap(pointer_1,pointer_2);
}

對程序的說明:
swap是用戶定義的函數(shù),他的作用是交換兩個變量(a和b)的值。
swap函數(shù)的形參p1和p2是指針變量。
注意實參pointer_1和pointer_2是指針變量,在函數(shù)調(diào)用時,將實參變量的值傳遞給形參變量。
采取的依然是“值傳遞”方式。
因此虛實結(jié)合后形參p1的值為&a,p2的值為&b。
這時p1和pointer_1指向變量a,p2和pointer_2指向變量b.
接著執(zhí)行swap函數(shù)的函數(shù)體是 *p1*p2 的值互換,也就是使a和b的值互換。
函數(shù)調(diào)用結(jié)束后,p1和p2不復存在。
但是pointer_1和pointer_2所指向的變量a和b的值已經(jīng)互換。

如果swap函數(shù)如下則達不到這個效果:

swap(int x,int y)
{ int temp;
  temp=x;
  x=y;
  y=temp;
}

請注意,不能企圖通過改變指針形參的值而使指針實參的值改變。

swap(int *p1,int *p2){
 int *p;
 p=p1;
 p1=p2;
 p2=p;
}

上面說的,只要是理解了指針,都不算什么難理解的內(nèi)容。

6. 指針變量幾個問題的進一步說明:

指針變量可以進行某些運算,但其運算的種類是有限的。
他只能進行賦值運算和部分算數(shù)運算及關(guān)系運算。

  • 1)指針運算符:

    • a)取地址運算符 & : 取地址運算符 & 是單目運算符,其結(jié)合性為自右至左,其功能是取變量的地址。
      在scanf函數(shù)及前面介紹指針變量賦值中,我們已經(jīng)了解并使用了 & 運算符。
    • b)取內(nèi)容運算符 * : 取內(nèi)容運算符 * 是單目運算符,其結(jié)合性為自右至左,用來表示指針變量所指的變量。
      * 運算符之后跟的變量必須是指針變量。
      需要注意的是,指針運算符*指針變量說明中的說明符* 不是一回事。
  • 2)指針變量的運算:

    • a)賦值運算:有以下幾種形式:
      i)指針變量初始化賦值,前面已經(jīng)作了介紹。
      ii)把一個變量的地址賦予指向相同數(shù)據(jù)類型的指針變量。例如:int a,*pa;pa=&a;
      iii)把一個指針變量的值賦予指向相同類型變量的另一個指針變量。如:int a*pa=&a,*pb; pb=pa;
      iv)把數(shù)組的首地址賦予指向數(shù)組的指針變量:例如:int a[5],*pa=a; 也可以寫為 *pa=&a[0]; 因為a=&a[0]; 即數(shù)組名就是數(shù)組的首地址
      v)把字符串的首地址賦予指向字符類型的指針變量。例如:char *pc="C Language"; 這里應說明的是并不是把整個字符串裝入指針變量,而是把存放該字符串的字符數(shù)組的首地址裝入指針變量
      vi)把函數(shù)的入口地址賦予指向函數(shù)的指針變量。例如:int (*pf)(); pf=f;
    • b)加減算數(shù)運算:對于指向數(shù)組的指針變量,可以加上或減去一個整數(shù)n。表示把指針向前或向后移動一個位置,和地址加1或減1在概念上是不同的。 指針變量的減運算只能對數(shù)組指針變量進行,對指向其他類型變量的指針變量做加減運算是毫無意義的。
    • c)兩個指針變量之間的運算:
      只有指向同一數(shù)組的兩個指針變量才能進行運算,否則運算毫無意義。
      i)兩指針變量相減:所得之差是兩個指針所指數(shù)組元素之間相差的元素個數(shù)。實際上是兩個指針值(地址)相減只差再除以該數(shù)組元素的長度(字節(jié)數(shù))。兩指針指針變量不能進行加法運算,因為沒有實際含義。
      ii)兩指針變量進行關(guān)系運算:指向同一數(shù)組的兩指針變量的關(guān)系運算可表示他們所指數(shù)組元素之間的關(guān)系。
      例如:
      pf1==pf2表示pf1和pf2指向同一數(shù)組元素
      pf1>pf2表示pf1處于高地址位置
      指針變量還可以與0比較。
      設(shè)n為指針變量,則 n==0 表示n是空指針,他不指向任何變量。p!=0 表示p不是空指針。
      例如:
#define NULL 0
int *p=NULL;
  對指針變量賦0值和不賦值是不同的。指針變量未賦值時,可以是任意值,是不能使用的。否則將造成意外錯誤。而指針賦0值后,則可以使用,只是他不指向任何變量而已。

7. 數(shù)組指針和指向數(shù)組的指針變量

(1)通過指針引用數(shù)組元素

如果p的初值為 &a[0] ,則:

  • a)p+ia+i 就是 a[i] 的地址,或者說他們指向a數(shù)組的第i個元素。
  • b)*(p+i)*(a+i) 就是 p+ia+i 所指向的數(shù)組元素,即 a[i]
  • c)指向數(shù)組的指針變量也可以帶下標,如 p[i]*(p+i) 等價。類似于 a[i] ?

根據(jù)以上敘述,引用一個數(shù)組元素可以用:

  • a)下標法:a[i]p[i]
  • b) 指針法:即 *(a+i)*(p+i)

幾個注意的問題:

  • a)指針變量可以實現(xiàn)本身的值的改變,如 p++ 是合法的,而 a++ 是錯誤的,因為a是地址常量。
  • b)要注意指針變量的當前值。
  • c)雖然定義數(shù)組時指定它包含10個元素,但指針變量可以指到數(shù)組以后的內(nèi)存單元,系統(tǒng)并不認為違法。
  • d)*p++ ,由于 ++* 同優(yōu)先級,結(jié)合方向自右至左,等價于 *(p++)
  • e)*(p++)*(++p) 作用不同。若p的初值為a,則 *(p++) 等價于 a[0],*(++p) 等價 a[1] 。
  • f)(*p)++ 表示p所指向的元素值加1.
  • g)如果p當前指向a數(shù)組中的第i個元素,則
    *(p--) 相當于 a[i--];
    *(++p) 相當于 a[++i];
    *(--p) 相當于 a[--i]

8. 數(shù)組名作為函數(shù)參數(shù):

函數(shù)定義:

f(int arr[],int n){
...
}

函數(shù)調(diào)用:

int array[10];
f(array,10);

數(shù)組名就是數(shù)組的首地址,實參向形參傳送數(shù)組名實際上就是傳送數(shù)組的地址,形參得到該地址后也指向同一數(shù)組。
這就好像同一件物品有兩個彼此不同的名稱一樣。
同樣,指針變量的值也是地址,數(shù)組指針變量的值即為數(shù)組的首地址,當然也可以做為函數(shù)的參數(shù)使用。
例如:

float aver(float *pa);
main() {
 float sco[5],av,*sp;
 int i;
 sp=sco;
 ...
 av=aver(sp);
}

float aver(float *pa) {
 int i;
 float av,s=0;
 for (i=0;i<5;i++) s=s+*pa++;
 av=s/5;
 return av;
}

例題:將數(shù)組a中的n個整數(shù)按相反順序存放算法為:將 a[0]a[n-1] 對換,再 a[1]a[n-2] 對換,直到將 a[(n-1)/2]a[n-int((n-1)/2)] 對換。
今用循環(huán)處理此問題,設(shè)兩個“位置指示變量” i 和 j,i 的初值為 0,j 的初值為 n-1.將 a[i]a[j] 交換,然后是i的值加1,j的值減1,再將 a[i]a[j] 交換,直到 i=(n-1)/2 為止。
程序如下:

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;
 }
 return;
}

對此程序可以做一些改動,將 函數(shù)inv 中的 形參x 改成指針變量。

void inv(int *x,int n) {
 int *p,temp,*i,*j,m=(n-1)/2;
 i=x;j=x+n-1;p=x+m;
 for(;i<p;i++,j--) {
  temp=*i;*i=*j;*j=temp;
 }
 return;
}

從n個數(shù)中找出其中最大值和最小值
調(diào)用一個函數(shù)只能得到一個返回值,今用全局變量在函數(shù)之間“傳遞”數(shù)據(jù)。

int max,min;
void max_min_value(int array[],int n) {
 int *p,*array_end;
 array_end=array+n;
 max=min=*array;
 for(p=array+1;p<array_end;p++)
   if (*p>max) max=*p;
   else if (*p<min) min=*p;
 return;
}

main() {
 int i,number[10];
 printf("enter 10 integer numbers:\n");
 for (i=0;i<10;i++) {
  scanf("%d",&number[i]);
 }
 max_min_value(number,10);
 printf("\nmax=%d,min=%d\n",max,min);
}

9. 指向多維數(shù)組的指針和指針變量

(1)多維數(shù)組的地址

設(shè)有整型二維數(shù)組 a[3][4] 如下:

0 1 2 3
4 5 6 7
8 9 10 11

他的定義為

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

設(shè)數(shù)組a的首地址為1000,各下標變量的首地址為:

1000 1002 1004 1006
1008 1010 1012 1014
1016 1018 1020 1022

前面介紹過,C語言允許把一個二維數(shù)組分解為多個一維數(shù)組來處理。
因此數(shù)組a可分解為三個一維數(shù)組,即a[0],a[1],a[2]。
每一個一維數(shù)組又含有4個元素。
從二維數(shù)組的角度來看,a是二維數(shù)組名,a代表整個二維數(shù)組的首地址,也就是二維數(shù)組0行的首地址,等于1000。
a+1 代表第一行的首地址,等于1008。
a[0]是第一個一維數(shù)組的數(shù)組名和首地址,因此也為1000。*(a+0)*a 是與 a[0] 等效的,他表示一維數(shù)組a[0] 0號元素的首地址,也為1000.
&a[0][0] 是二維數(shù)組a的0行0列元素首地址,同樣是1000.
因此,a a[0] *(a+0) &a[0][0] 是相等的。

同理,a+1 是二維數(shù)組1行的首地址,等于1008.
a[1] 是第二個一維數(shù)組的數(shù)組名和首地址,因此也是1008.
&a[1][0] 是二維數(shù)組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[0] 也可以看成是 a[0]+0 ,是一維數(shù)組a[0]的0號元素的首地址,而 a[0]+1 則是a[0]的1號元素首地址,由此可得出 a[i]+j 則是一維數(shù)組 a[i] 的 j號元素 的首地址,他等于&a[i][j] 。(這里有疑問了,a+i是第i行的一維數(shù)組的首地址,a+i+j 呢?)
a[i] = *(a+i)a[i]+j=*(a+i)+j . 由于 *(a+i)+j 是 二維數(shù)組a 的 i行j列 元素的首地址,所以,該元素的值等于 *(*(a+i)+j)

(2)指向多維數(shù)組的指針變量

把二維數(shù)組a分解為一位數(shù)字 a[0],a[1],a[2] 之后,設(shè)p為指向二維數(shù)組的指針變量。
可定義為:

int (*p)[4];

他表示p是一個指針變量,他指向包含4個元素的一維數(shù)組。
若指向第一個一維數(shù)組a[0],其值等于a,a[0],或&a[0][0]等。
而p+i則指向一維數(shù)組a[i]。
從前面的分析可得出 *(p+i)+j 是二維數(shù)組i行j列的元素的地址,而 *(*(p+i)+j) 則是 i行j列 元素的值。

二維數(shù)組指針變量說明的一般形式為:
類型說明符 (*指針變量名)[長度]

其中,
"類型說明符"為所指數(shù)組的數(shù)據(jù)類型,
* 表示其后的變量是指針類型。
“長度”表示二維數(shù)組分解為多個一維數(shù)組時,一維數(shù)組的長度,也就是二維數(shù)組的列數(shù)。
應注意 () 不能少,如缺少括號則表示是指針數(shù)組,意義就完全不同了。

例子:

main() {
 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("%2d  ",*(*(p+i)+j));
    printf("\n";
 }
}

10. 字符串的指針指向字符串的指針變量

(1)字符串的表示形式:

在C語言中,可以用兩種方式訪問一個字符串。

  • a)用字符數(shù)組存放一個字符串,然后輸出該字符串。
main(){
 char string[]="I love China!";
 printf("%s\n",string);
}
  • b)用字符串指針指向一個字符串,
main()
{char *string="I love China!";
 printf("%s\n",string);
}

字符串指針變量的定義說明與指向字符變量的指針變量說明是相同的。
只能按對指針變量的賦值不同來區(qū)別。
對指向字符變量的指針變量應賦予字符變量的地址。
例如:

char c,*p=&c;
// 表示p是一個指向字符變量c的指針變量。


char *s="C language";
// 則表示s是一個指向字符串的指針變量。把字符串的首地址賦予s。

例子:輸出字符串中n個字符后的所有字符。

main() {
 char *ps="this is a book";
 int n=10;
 ps=ps+n;
 printf("%s\n",ps);
}

例子2:在輸入的字符串中查找有無'k'字符

main() {
 char st[20],*ps;
 int i;
 printf("input a string:\n");
 ps=st;
 scanf("%s",ps);
 for(i=0;ps[i]!='\0';i++)
  if(ps[i]=='k') {
   printf("there is a 'k' in the string \n");
   break;
  }
 if (ps[i]=='\0') printf("There is no 'k' in the string\n");
}

例子3:本例是將指針變量指向一個格式字符串,用在printf函數(shù)中,用于輸出二維數(shù)組的各種地址表示的值。但在printf語句中用指針變量PF代替了格式串。這也是程序中常用的方法。

main() {
 static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
 char *PF;
 PF="%d,%d,%d,%d,%d\n";
 printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);
}

例子4:本例是把字符串指針做為函數(shù)參數(shù)的使用。
要求把一個字符串的內(nèi)容復制到另一個字符串中,并且不能使用 strcpy函數(shù)
。函數(shù)cprstr的形參為兩個字符指針變量。
pss指向源字符串,pds指向目標字符串。
注意表達式:(*pds=*pss)!='\0' 的用法。

cpystr(char *pss,char *pds) {
 while ((*pds=*pss)!='\0') {
   pds++;
   pss++;
 }
}

main() {
 char *pa="CHINA",b[10],*pb;
 pb=b;
 cpystr(pa,pb);
 printf("string a=%s\nstring b=%s\n",pa,pb);
}

函數(shù)還可以簡寫為:

cpystr(char *pss,char *pds) { 
  while ((*pds++=*pss++)!='\0') ; 
}

注意到 '\0' 的ASCII碼為0,對于while語句只看表達式的值為非0就循環(huán),為0則結(jié)束循環(huán),因此也可省去 "!='\0'" 這一判斷部分。而寫為:

cpystr(char *pss,char *pds) { 
  while(*pds++=*pss++) ;
}

表達式的意義可解釋為:源字符向目標字符賦值,移動指針,若所賦值為非0則循環(huán),否則結(jié)束循環(huán)。

11. 使用字符串指針變量與字符數(shù)組的區(qū)別:

char *ps; ps="C Language"; 正確
char st[20];st={"C Language"}; 錯誤; 

即指針變量可以先聲明再賦值,字符數(shù)組則不可以,只能逐個賦值。

12. 函數(shù)指針變量:

在C語言中,一個函數(shù)總是占用一段連續(xù)的內(nèi)存區(qū),而函數(shù)名就是該函數(shù)所占內(nèi)存區(qū)的首地址。
我們可以把函數(shù)的這個首地址(或稱入口地址)賦予一個指針變量,使該指針變量指向該函數(shù)。
然后通過指針變量就可以找到并調(diào)用這個函數(shù)。
我們把這種指向函數(shù)的指針變量稱為“函數(shù)指針變量”。
函數(shù)指針變量定義的一般形式為: 類型說明符 (*指針變量名)();
其中
“類型說明符”表示被指函數(shù)的返回值的類型。
(*指針變量名) 表示 * 后面的變量是定義的指針變量。
最后的空括號表示指針變量所指的是一個函數(shù)。

例如:int (*pf)(); 表示pf是一個指向函數(shù)入口的指針變量,該函數(shù)的返回值是整型。
例子:

int max(int a,int b) {
 if (a>b) return a;
 else return b;
}

main() {
 int max(int a,int b);
 int (*pmax)();
 int x,y,z;
 pmax=max;
 printf("input two numbers:\n");
 scanf("%d%d",&x,&y);
 z=(*pmax)(x,y);
 printf("maxmum=%d",z);
}

從上面的例子可以看出:

  • 1)先定義函數(shù)指針變量: int (*pmax)();
  • 2)賦值: pmax=max;
  • 3)調(diào)用:z=(*pmax)(x,y);

使用函數(shù)指針變量應注意以下兩點:

  • 1)函數(shù)指針變量不能進行算數(shù)運算,這是與數(shù)組指針變量不同的。
  • 2)函數(shù)調(diào)用中 (*指針變量名) 的兩邊的括號不能少,其中的 * 不應該理解為求值運算,在此處他只是一種表示符號。

13. 指針型函數(shù)

在C語言中允許一個函數(shù)的返回值是一個指針(即地址),這種返回指針值的函數(shù)稱為指針型函數(shù)。
定義指針型函數(shù)的一般形式為:

類型說明符 *函數(shù)名(形參表) {
  ...
}

其中函數(shù)名之前加了 * 號表明這是一個指針型函數(shù),即返回值是一個指針。
如:int *ap(int x, int y) { ... }

例子:本程序是通過指針函數(shù),輸入一個1-7之間的整數(shù),輸出對應的星期名:

main() {
 int i;
 char *day_name(int n);
 printf("intput Day No:\n");
 scanf("%d",&i);
 if (i<0) exit(1);
 printf("Day No:%2d-->%s\n",i,day_name(i));
}

char *day_name(int n) {
 static char *name[]={"Illegal day","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
 return ((n<1)||n>7)?name[0]:name[n]);
}

應該特別注意的是函數(shù)指針變量和指針型函數(shù)這兩者在寫法和意義上的區(qū)別。
int (*p)()int *p() 是兩個完全不同的量。
前者是函數(shù)指針變量說明,說明p是一個指向函數(shù)入口的指針變量,該函數(shù)的返回值是整型量, (*p) 的兩邊的括號不能少。
后者 int *p() 則不是變量說明,而是函數(shù)說明,說明p是一個指針型函數(shù)。

14. 指針數(shù)組和指向指針的指針:

(1)指針數(shù)組的概念

一個數(shù)組的元素值為指針則是指針數(shù)組。
指針數(shù)組是一組有序的指針的集合。
指針數(shù)組的所有元素都必須是具有相同存儲類型和指向相同數(shù)據(jù)類型的指針變量。
指針數(shù)組說明的一般形式為:

類型說明符 *數(shù)組名[數(shù)組長度]

例如: int *pa[3]; 表示pa是一個指針數(shù)組,他有3個數(shù)組元素,每個元素值都是一個指針,指向整型變量。
例子:通??捎靡粋€指針數(shù)組來指向一個二維數(shù)組。
指針數(shù)組中的每個元素被賦予二維數(shù)組第一行的首地址,因此也可理解為指向

一個一維數(shù)組。

main() {
 int a[3][3]={1,2,3,4,5,6,7,8,9};
 int *pa[3]={a[0],a[1],a[2]};
 int *p=a[0];
 int i;
 for (i=0;i<3;i++) 
   printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i));

 for (i=0;i<3;i++)
   printf(%d,%d,%d\n",*pa[i],p[i],*(p+i));
}

應該注意到指針數(shù)組和二維數(shù)組指針變量的區(qū)別。
這兩者雖然都可以用來表示二維數(shù)組,但是其表示方法和意義是不同的。
二維數(shù)組指針變量是單個的變量,其一般形式中 (*指針變量名) 兩邊的括號不能少。
而指針數(shù)組類型表示的是多個指針(一組有序指針)在一般形式中 *指針數(shù)組名 兩邊不能有括號。
例如:
int (*p)[3]; 表示一個指向二維數(shù)組的指針變量。該二維數(shù)組的列數(shù)為3或分解為一維數(shù)組的長度為3;
int *p[3] 表示p是一個指針數(shù)組,有三個下標變量p[0] p[1] p[2]均為指針變量。

指針數(shù)組也常用來表示一組字符串,這時指針數(shù)組的每個元素被賦予一個字符串的首地址。
指向字符串的指針數(shù)組的初始化更為簡單,例如:

static char *name[]={"Illegal day","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};

指針數(shù)組也可以用作函數(shù)參數(shù)
例子:指針數(shù)組作指針型函數(shù)的參數(shù)。
在本例函數(shù)中,定義了一個指針數(shù)組name,并對name作了初始化賦值。
其每個元素都指向一個字符串。
然后又以name做為實參調(diào)用指針型函數(shù)day_name,在調(diào)用時把數(shù)組名name賦予形參變量name,輸入的整數(shù)i做為第2個實參賦予形參n。
在day_name函數(shù)中定義了兩個指針變量pp1和pp2,pp1被賦予name[0]的值(即 *name ),pp2被賦予name[n]的值,即*(name+n) 。
由條件表達式?jīng)Q定返回pp1或pp2指針給主函數(shù)中的指針變量ps。
最后輸出i和ps的值。

main() {
 static char *name[]={"Illegal day","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
 char *ps;
 int i;
 char *day_name(char *name[],int n);
 printf("input Day No:\n");
 scanf("%d",&i);
 if (i<0) exit(1);
 ps=day_name(name,i);
 printf("Day No:%2d-->%s",i,ps);
}

char *day_name(char *name[],int n) {
 char *pp1,*pp2;
 pp1=*name;pp2=*(name+n);
 return ((n<1||n>7)?pp1:pp2);
}

例子:輸入5個國名并按字母順序排列后輸出:
現(xiàn)編程如下:

#include "string.h"
main() {
 void sort(char *name[],int n);
 void print(char *name[],int n);
 static char *name[]={"CHINA","AMERICA","AUSTRALIA","FRANCE","GERMAN"};
 int n=5;
 sort(name,n);
 print(name,n);
}

void sort(char *name[],int n){
 char *pt;
 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) {
   pt=name[i];
   name[i]=name[k];
   name[k]=pt;
  }
 }
}

void print(char *name[],int n){
 int i;
 for (i=0;i<n;i++) printf("%s \n",name[i]);
}

(2)指向指針的指針

如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的指針變量。
前面已經(jīng)介紹過,通過指針訪問變量稱為間接訪問。
由于指針變量直接指向變量,所以稱為“單級間址”。
而如果通過指向指針的指針變量來訪問變量則構(gòu)成“二級間址”。

怎樣定義一個指向指針型數(shù)據(jù)的指針變量呢?如下:

char **p;
// p前面有2個*號,相當于*(*p)。

考慮指針數(shù)組name,他的每一個元素是一個指針型數(shù)據(jù),其值為地址。
name是一個數(shù)據(jù),他的每一個元素都有相應的地址。
數(shù)組名name代表該指針數(shù)組的首地址,name+iname[i] 的地址。
name+1 就是指向指針型數(shù)據(jù)的指針(地址)。
還可以設(shè)置一個指針變量p,使他指向指針數(shù)組元素。
p 就是指向指針型數(shù)據(jù)的指針變量。

如果有:

p=name+2;
printf("%o\n",*p);
printf("%s\n",*p);

則第一個printf函數(shù)輸出name[2]的值(他是一個地址),
第二個printf函數(shù)語句子字符串形式(%s)輸出字符串"Great WALL";

例子:使用指向指針的指針

main() {
 char *name[]={"Follow me","BASIC","Great Wall","FORTRAN","Computer design"};
 char **p;
 int i;
 for (i=0;i<5;i++) {
  p=name+i;
  printf("%s\n",*p);
 }
}

13. main函數(shù)的參數(shù)

C語言規(guī)定main函數(shù)的參數(shù)只能有兩個,習慣上把這兩個參數(shù)寫為 argcargv 。
因此,main函數(shù)的函數(shù)頭可寫為:main(argc,argv)

C語言還規(guī)定 argc(第一個形參)必須是整型變量,argv(第二個形參)必須是指向字符串的指針數(shù)組。
加上形參說明后,main函數(shù)的函數(shù)頭應寫為:

main(int argc,char *argv[])

main的參數(shù)通過命令行傳入,但是應特別注意的是,main 的兩個形參和命令行中的參數(shù)在位置上不是一一對應的。
因為,main的形參只有兩個,而命令行中的參數(shù)個數(shù)原則上未加限制。
argc參數(shù) 表示了命令行中參數(shù)的個數(shù)(注意:文件名本身也算一個參數(shù)),argc 的值是在輸入命令行時由系統(tǒng)按實際參數(shù)的個數(shù)自動賦予的。
例如:C:\E24 BASIC foxpro FORTRAN 連文件名本身共4個參數(shù),所以 argc 的值是4.
argv參數(shù) 是字符串指針數(shù)組,其各元素值為命令行中各字符串的首地址。
指針數(shù)組的長度即為參數(shù)的個數(shù)。
其中 argv[0] 為文件名本身。
例子:

main(int argc,char *argv) {
 while(argc-->1)
  printf("%s\n",*++argv);
}

14. 有關(guān)指針的數(shù)據(jù)類型和指針運算的小結(jié):

(1)有關(guān)指針的數(shù)據(jù)類型的小結(jié):

int i; int *p; // 定義整型變量i和指向整型數(shù)據(jù)的指針變量
int a[n];int *p[n]; // 定義整型數(shù)組a;定義指針數(shù)組p
int (*p)[n]; // 定義p為指向含n個元素的一維數(shù)組的指針變量
int f();int *p();int (*p)(); // 定義函數(shù);定義返回整型指針變量的函數(shù);定義指向函數(shù)的指針。
int **p; // 定義指向指針的指針

(2)指針運算的小結(jié):

  • a)指針變量加(減):將指針變量的原值(是一個地址)和他指向的變量所占用的內(nèi)存單元字節(jié)數(shù)加(減)。

  • b)指針變量賦值:p=&a;p=array ; p=&array[i] ; p=array+i ; p=max (max為已定義的函數(shù));p1=p2 ;
    注意:p=1000; //錯誤!

  • c)指針變量可以有空值。即該指針不指向任何變量:p=NULL (NULL為#define的值,為0);

  • d)兩個指針變量可以相減:表示兩個指針之間的元素個數(shù)。

  • e)兩個指針變量比較:

(3)void指針類型

ANSI新標準增加了一種 void 指針類型,即可以定義一個指針變量,但不指定他是指向哪一種類型數(shù)據(jù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,612評論 3 44
  • 1. 變量 不同類型的變量在內(nèi)存中占據(jù)不同的字節(jié)空間。 內(nèi)存中存儲數(shù)據(jù)的最小基本單位是字節(jié),每一個字節(jié)都有一個內(nèi)存...
    C語言學習閱讀 1,357評論 0 4
  • C語言是面向過程的,而C++是面向?qū)ο蟮?C和C++的區(qū)別: C是一個結(jié)構(gòu)化語言,它的重點在于算法和數(shù)據(jù)結(jié)構(gòu)。C程...
    小辰帶你看世界閱讀 1,021評論 0 6
  • 大學的每一年都會借著圣誕這個節(jié)日給舍友每人送上一個看去雖“粗制濫造”,實際卻充斥著我“滿滿熱血”的一個小禮...
    他特特key閱讀 431評論 0 0

友情鏈接更多精彩內(nèi)容