1. 程式人生 > >C/C++ 指標小結——指標與其它資料型別(陣列、字串、函式、結構體)的關係

C/C++ 指標小結——指標與其它資料型別(陣列、字串、函式、結構體)的關係

一、指標與陣列和字串

1、指標與陣列

當宣告數時,編譯器在連續的記憶體空間分配基本地址和足夠的儲存空間,以容納陣列的所有元素。基本地址是陣列第一個元素(索引為0)的儲存位置。編譯器還把陣列名定義為指向第一個元素的常量指標。

元素的地址是通過索引和資料型別的比例因子來計算的;例如: x[3]的地址 = 基本地址 + (3 x 整型資料的比例因子)

如何表示元素a[i][j]的(其中:int *p; p = a;);

0 1 2 3 4 5
1
2
3
4
  • p----指向第 0 行的指標;
  • p + i----指向第 i 行的指標;
  • *(p+i)----指向第 i 行的第一個元素的指標;
  • *(p+i)+j----指向第 i 行第 j 個元素的指標;
  • *((p+i)+j)----儲存在(i, j)單元(即第 i 行第 j 列)的值。

陣列 a 的基本地址為 &a[0][0],從這個地址開始,編譯器按行為所有的元素分配連續的儲存空間。例如:

int a[3][4] ={
	{15,27,11,35},
	{22,19,31,17},
	{31,23,14,36}
}

陣列a的儲存如下:

|15 |27| 11| 35 |22 |19 |31 |17 |31 |23 |14 |36 | |--

如果把 p 宣告為整型指標,並且初始地址為a[0][0](),那麼:

a[i][j] = *(p+4*i+j);

2、指標與字串

C語言支援另一種建立字串的方法,即使用char型別的指標變數。例如:

char *str = "good";

上述宣告語句建立了一個文字字串,然後將其地址儲存在指標變數str中;這樣指標 str 就指向字串 “good” 的第一個字元,如下所示:

|g(str) | o | o | d | \0 | |--

由此,可以用指標訪問整個字串:

printf("%s",str);
put(str);

當然,也可以用指標來訪問字串中的單個字元。

3、指標陣列

指標的一項重要的應用就是處理字串表,特別是處理行的長度可變的“凹凸不平的陣列”時;例如:

char *name[3] = { "New Zealand", "Australia", "India" };

上面的宣告語句只分配了28個字元,這足以儲存所有的字元,具體如下:

|N| e |w | |Z |e |a |l |a| n| d| \0| |-- |A| u| s| t| r| a| l| i| a| \0| |I| n| d| i| a| \0| 下面的語句可以用來顯示著三個名稱:

for(i = 0; i <= 2; i++)  printf("%s\n",name[i]);


要訪問第 i 個名稱的第 j 個字元,可以這樣編寫語句:

char c = *(name[i]+j);

二、指標與函式

1、將指標作為函式的引數

使用指標傳遞變數地址的函式呼叫過程稱為引用呼叫(我們已經說過,傳遞變數實際值的過程稱為 “按值呼叫”)。引用呼叫提供了一種機制,讓被呼叫的函式可以修改呼叫函式中儲存的值。 請注意以下程式碼:

//例一
void exchange(int *a, int *b)
{
	int t;
	t=*a;
	*a=*b;
	*b=t;
}
void main()
{
	int x, y;
	x = 100;
	y = 200;
	printf("%d ,%d\\n",x,y);
	exchange(&x,&y);
	printf("%d ,%d\\n",x,y);
}
//例二
void exchange(int *a, int *b)
{
	int *t;
	t=a;
	a=b;
	b=t;
	printf("%d ,%d\n", *a, *b);
}

void main()
{
	int x, y;
	x = 100;
	y = 200;
	printf("%d ,%d\n", x, y);
	exchange(&x,&y);
	printf("%d ,%d\n", x, y);
}

程式碼例二並不會使x,y的值發生互換,因為當函式指標a,b獲得x,y的地址後,作為值進行儲存,交換a,b的值並沒有改變其值(x,y的地址)指向的x,y的值。

2、函式返回指標

指標是C語言的一種資料型別,因此也可以使用函式返回一個指向呼叫函式的指標。請看下面的程式碼:

int  *larger(int* , int*);/*prototype*/

main()
{
	int a = 10;
	int b = 20;
	int  *p;
	p = larger(&a, &b);/*Function call*/
	printf("%d",p);
}
int *larger(int *x, int *y)
{
	if(*x > *y)
		return(x);/*address of a*/
	else
		return(y);/*address of b*/
}

注意:返回的地址必須是呼叫函式中變數的地址。如果返回的是指向被呼叫函式中區域性變數的地址;將產生錯誤。

3、指向函式的指標

與變數一樣,函式也屬於某種資料型別,在記憶體中也需要有儲存地址。因此可以宣告一個指向函式的指標。如同指向字元陣列的指標,可以接收任意大小的字元陣列一樣,指向函式的指標可以指向任意的函式,如此可以減少函式的數量,使函式的功能更加強大。指向函式的指標宣告如下:

type (*fptr)();

該語句告訴編譯器,fptr為指向函式的的指標,返回type型別的值。用括號把*fptr括起來是必要的。記住,下面的語句:

type *gptr();

表示的是把gptr宣告為函式,它返回一個指向type型別的指標。

請仔細關注以下程式碼:

#include<stdio.h>
#include<math.h>
#define PI 3.1415926

double table(double (*f)(), double, double, double);
double y(double);
double cos(double);

double table(double (*f)(), double min, double max, double step)
{
    double a, value;
    for(a = min;a <= max; a+=step){
        value = (*f)(a);
        printf("%5.2f %10.4f\n,a,value");
    }
}

double y(double x)
{
    return (2*x*x-x+1);
}

void main()
{
    printf("table of y(x) = 2*x*x-x+1\n\n");
    table(y, 0.0, 2.0, 0.5);
    printf("table of cos(x)\n\n");
    table(cos, 0.0, PI, 0.5);
}

三、指標與結構體

在前面章節中,我們討論把結構體作為引數傳遞給函式。我們還看到這樣的例項,其中函式接收整個結構體的副本,並在執行後把他返回給呼叫函式。正如我們前面介紹的那樣,該方法無論是在執行速度還是在記憶體使用上都不是高效的。通過以指向結構體的指標作為傳遞引數,然後使用指標來操縱結構體成員,就可以克服該缺點。請看以下函式:

print_invent(struct *item)
{
    printf("Name: %s\n", item->name);
    printf("price:%f\n", item->price);
}

該函式可以用下面的語句來呼叫:

print_invent(&product);

請關注以下兩點:

  1. 陣列名錶示的是第0個元素的地址。結構體變數的陣列名也是如此;
  2. 運算子“->”、“.”、“()”和“[]”的有先級最高。正確理解優先順序及關聯性非常重要。