指針變量是包含內存地址的變量,它指向內存中的一塊區域,通過指針的值,可以間接訪問到相應的內存單元的數據,並做相應的修改。
1、指針的定義和簡單使用
定義一個指針變量和定義一般的變量類似,只需在變量名前面加一個“*”。對一個指針變量賦值可以用取地址符&來獲取到一個變量的地址,如果要獲得指針指向的內存區域的數據,用解參考運算符*(也稱為間接運算符,它返回其操作數指向的對象的值)。指針的值為NULL(NULL是stdio.h中定義的符號變量,實際上是0)說明其不指向任何的內存單元,0是唯一直接可以賦值給指針變量的整數值。實際上,*和&是互補的,當兩個運算符連續應用於一個指針變量時,無論順序如何,運算結果相同。同時可以用printf中的格式化字符串%p來輸出指針變量的值,下面是一個簡單的程序。
[cpp]
#include <stdio.h>
int main()
{
int a;
a=9;
//定義並初始化一個指針,命名就可以看出
int *aPtr=NULL;
//將指針指向變量a
aPtr=&a;
printf("The address of a is %p"
"\nThe value of aPtr is %p",&a,aPtr);
printf("\n\nThe value of a is %d"
"\nThe value of *aPtr is %d",a,*aPtr);
printf("\n\nShowing that * and & are complements of "
"each other\n&*aPtr = %p"
"\n*&aPtr = %p\n",&*aPtr,*&aPtr);
return 0;
}
2、用指針做函數的參數
2.1 通過指針實現的引用傳遞
程序設計語言的參數傳遞方式,大致分兩種:值傳遞和引用傳遞。C語言中沒有引用傳遞,但是C語言通過指針間接實現了引用傳遞。通過用指針變量作為函數的參數,可以傳遞變量的地址(只需要在變量前面加上&運算符就可以),這樣,用該地址就可以訪問到主調函數中的該變量的內存地址,並可以進行相應的修改。這樣,在函數執行完畢之後,修改仍然可以得到保留。
2.2 const
const限定符可以告訴編譯器特定的變量的值是不能被修改的。如果想確保函數不會修改傳遞進來的參數值,應該將參數聲明為const。這樣對於C語言中用指針實現的引用傳遞,有四種情況:指向非常量數據的非常量指針(int *aPtr),指向非常量數據的常量指針(int *const aPtr),指向常量數據的非常量指針(const int *aPtr)和指向常量數據的常量指針(const int * const aPtr)。簡單的說,就是指針變量自身和指針指向的變量都有可能是const,這樣就產生了四種情況,這四種情況提供了四種不同的訪問權限,下面分別解釋。
指向非常量數據的非常量指針(int *aPtr):指針的值本身和指針指向變量的值都可以在函數中被修改。
指向非常量數據的常量指針(int *const aPtr):指針的值不能被修改,但是指針指向的變量的值可以被修改。
指向常量數據的非常量指針(const int *aPtr):指針指向的值不能被修改,但是指針本身的值可以被修改。
指向常量數據的常量指針(const int * const aPtr):指針本身和指針指向變量的值都不能被修改。
3、sizeof和指針運算
3.1 sizeof
sizeof是C語言中特殊的一元運算符,可以應用在變量名稱、數據類型和常量之前,它在程序編譯期間以字節為單位來確定數組或其他數據類型的大小。當應用於數組時,sizeof返回數組中的字節總數。如float a[20],sizeof(a)的值應該是4*20,80。當然,如果想獲得數組的大小可以采用sizeof(a)/sizeof(float)。
3.2 指針運算
實際上,指針變量可以進行的算術操作是有限的:遞增,遞減,將指針和整數相加,從指針中減去一個整數或者一個指針減去另一個指針。需要注意的是,對於指針的算術運算,其單位長度並不是一般意義上的1,而是sizeof(TYPE)。這樣,如果float a[14]; float *aPtr=a;(或者int *aPtr=&a[0]); aPtr++;這樣aPtr應該指向的是數組a的第二個元素,也就是a[1],這裡的單位長度就是sizeof(float)。同樣地,如果aPtr=aPtr+5;,這樣aPtr又指向了數組a的第6個元素。如果aPtr-a,這樣可以得到兩個指針之間的元素間隔個數。應該是6。
進行指針運算要注意:
(1)如果將一個指針的值賦給另外一個指針,那麼這兩個指針的類型必須相同,否則應該用類型轉換運算符進行類型轉換。但是,有一個例外就是指向void類型的指針,它是通用指針,可以代表任何指針類型。因此,所有指針類型都可以賦值給void指針,而void指針也可以賦值給任何類型的指針,而不需要任何類型轉換運算符。但是,void指針不能解參考,編譯器知道指向int類型的指針引用的是32位計算機上的4個字節內存,但指向void的指針僅包含未知數據類型的內存位置,也就是說,編譯器不知道指針所引用的字節數。編譯器必須知道數據類型,才能確定所引用的字節數。
(2)除非兩個指針變量都指向的是一個數組中的元素,否則對它們相減的結果沒有任何意義,因為我們不能假設兩個變量在內存中是連續的。
4、指針和數組
4.1 數組和指針的共性
實際上,數組名稱的本質是一個常量指針。因此,int a[6]; int *aPtr;定義一個數組和指針之後,通過a[3],*(a+3)和*(aPtr+3)都可以訪問到數組的第四個元素的值。但是區別在於,aPtr=aPtr+3;這樣aPtr就指向了a數組的第四個元素,但是,不能a=a+3;,因為a是一個數組名,它是一個常量指針它的值不能被修改,更加具體地說,它應該是一個指向非常量數據的常量指針。
4.2 指針數組
數組元素也可以是指針類型,指針數組常見的用途就是構成由字符串組成的數組,簡單地說就是字符串數組,數組中的每一個元素都是字符串。下面是一個例子,const限定符說明不能修改每個元素指針所指向的字符串。
[cpp]
#include<stdio.h>
int main()
{
const char *suit[4]={"Hearts","Diamonds","Clubs","Spades"};
int i;
for(i=0;i<4;i++)
printf("sizeof pointer to \"%s\" :%d\n",suit[i],sizeof(suit[i]));
printf("\nsizeof suit:%d\n",sizeof(suit));
return 0;
}
運行結果如下:
從這個例子中,可以看出const char *suit[4]={"Hearts","Diamonds","Clubs","Spades"};定義了一個指針數組,但是,其中的每個元素只是一個指針,而不是數組名(如果是數組名的話,sizeof的結果不應該都是4)。這樣定義字符串數組可以節省空間。
5、函數指針
和數組名實際上就是數組第一個元素在內存中的地址類似,函數迷你實際上就是執行函數任務的代碼在內存中的起始地址。函數指針包含函數在內存中的地址,可以傳遞給函數、從函數返回、存儲在數組中或者是賦值給其它的函數指針,下面是兩個函數指針的例子。
(1) 用函數指針實現升序/降序排序
[cpp]
#include <stdio.h>
#define SIZE 10
int ascending(int a,int b)
{
return a>b;
}
int descending(int a,int b)
{
return a<b;
}
void swap(int *aPtr,int *bPtr)
{
int temp=*aPtr;
*aPtr=*bPtr;
*bPtr=temp;
}
void bubble(int work[],const int size,int(*compare)(int a,int b))
{
int pass;
int count;
for(pass=1;pass<size;pass++)
for(count=0;count<size-pass;count++)
{
if((*compare)(work[count],work[count+1]))
swap(&work[count],&work[count+1]);
}
}
int main()
{
int order;
int counter;
int a[SIZE]={2,6,4,8,10,12,89,68,45,37};
printf("\nData items in original order:\n");
for(counter=0;counter<SIZE;counter++)
printf("%5d",a[counter]);
printf("\nEnter 1 to sort in accending order,\n"
"Enter 2 to sort in descending order: ");
scanf("%d",&order);
if(order==1)
{
bubble(a,SIZE,ascending);
printf("\nData items in ascending order:\n");
}
else
{
bubble(a,SIZE,descending);
printf("\nData items in descending order:\n");
}
for(counter=0;counter<SIZE;counter++)
printf("%5d",a[counter]);
printf("\n");
return 0;
}
(2) 函數指針數組
[cpp]
#include<stdio.h>
void function1(int a);
void function2(int a);
void function3(int a);
int main()
{
void (*f[3])(int)={function1,function2,function3};
int choice;
printf("Enter a number between 0 and 2, 3 to end: ");
scanf("%d",&choice);
while(choice >=0 && choice<3)
{
(*f[choice])(choice);
printf("Enter a number between 0 and 2, 3 to end: ");
scanf("%d",&choice);
}
printf("Program execution completed.\n");
return 0;
}
void function1(int a)
{
printf("You entered %d so funtion1 was called\n\n",a);
}
void function2(int a)
{
printf("You entered %d so funtion2 was called\n\n",a);
}
void function3(int a)
{
printf("You entered %d so funtion3 was called\n\n",a);
}