一、指針與數組和字符串
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
#include
#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);
請關注以下兩點:
數組名表示的是第0個元素的地址。結構體變量的數組名也是如此;
運算符“->”、“.”、“()”和“[]”的有先級最高。正確理解優先級及關聯性非常重要。