對在程式設計和學習中遇到的問題在這裡做個總結:
1. 直接為一個指針指派是錯誤的 (能通過編譯,但是會出現段錯誤):
int *p;
*p = 5;
指針變量必須初始化後才能使用。
2.
char *p;
p = "abc";
p[0] = 'A'; //這句編譯通過但是段錯誤,c不允許字元串常量進行修改。對字元串常量修改的行為是未定義的。
3.
int a[] = {1, 2, 3}; //a為一個數組
int *p; //p為int型的指針
則 把數組下标為0的元素的位址指派給p的語句是: p = a; 不要使用p = &a; ,因為&a是一個指向數組的指針,而p是一個指向整型變量的指針,它們的類型不比對,編譯會出現警告”warning: assignment from incompatible pointer type“。
除了a被用做運算符sizeof的參數外,其他情況下數組名a都表示指向數組a中下标為0的元素的指針。sizeof(a)等于12(三個int型資料的大小)。
是以,在數組名a作函數形參時,在該函數中sizeof(a)的結果為4,因為此時數組名a作為指針,詳情見文章《C++多态》中“數組名做形參”這個例子
4.
int a[2][3] = { {1, 2, 3}, {4, 5, 6}}; //這裡聲明了數組的數組(c語言僅有一維數組,不存在二維數組的說法)
int (*p)[2]; //以下是聲明了一個指向這類數組的指針,并使用該指針擷取數組a的元素。
p = a; /* 不能用一個普通指針int *q;指向數組a,即q = a;是錯誤的,因為q是一個指向整型變量的指針
而a表示一個指向數組的指針。 */
printf("%d", **p);
5.
用單引号引起的一個字元實際表示一個整數;
用雙引号引起的一個字元串表示的卻是一個指向無名數組起始字元的指針。 該數組被雙引号之間的字元以及一個額外的二進制值為零的字元‘\0’初始化。
6.函數指針:
a. int *g(); 由于()的優先級高于*,是以*g()也就是*(g()) :g是一個函數,該函數的傳回值是一個指針(傳回值類型為指向整型數的指針)。
*p() 等價于 *(p()) 等價于 *((*p)())
b. int (*g)(); g是一個函數指針(指向函數的指針),g指針所指向的傳回值為整型。g是一個指向傳回值為整型的函數的指針。
那麼 (int (*)()) 表示的是”指向傳回值整型的函數的指針“的類型轉換。
c. 若ptr_func是一個函數指針,那麼調用ptr_func所指向的函數function的語句是:(*ptr_func)();
如:
void function() { //定義了一個函數function
printf("In function()\n");
}
void (*ptr_func)() = function; //定義了一個函數指針ptr_func,該指針指向函數function
(*ptr_func)(); // 對ptr_func所指向的函數進行調用;調用該函數可以簡寫為ptr_func();但是這僅僅是簡寫。
d. 對 (*(void(*)())0)() 的解析:
(void(*)()) 0 :将0轉化為”指向傳回值為void的函數的指針“;
是以參考c可以知道 (*(void(*)())0)() 是對上邊所提到的指針指向的函數的調用
此外, (*(void(*)())0)() 也可以用typedef簡化寫為:
typedef void (*ptr) (); //将ptr聲明為指向傳回值為void類型的函數的指針
(*(ptr)0) ();
e. 用signal庫函數處理信号 void (*signal(int sig, void (*func)(int))) (int); 對該函數的解析:
該函數接收一個int型的參數,傳回值為void類型。signal函數接收兩個參數:一個是需要捕獲或忽略的信号(整型值sig),另一個 是指向使用者提供的傳回值為void的函數的指針,該函數用于處理捕獲到的信号。
7. 空指針問題:
當常數0被轉化為指針使用時,該指針絕對不能被解除引用;即絕對不能企圖使用該指針所指向的記憶體中存儲的内容。
如下代碼是錯誤的:
int *p = NULL;
int *q = (int *)0;
printf ("%d",*p); //這裡是錯誤的,因為不能對空指針p解引用。
printf(“%d”,*q); //這裡也是錯誤的,因為q是将0轉化得來的指針,也是空指針。
同理,下面的也是錯誤的:
if (strcmp(p, (char *) 0) == 0) //這裡也是錯誤的,因為 strcmp函數的實作中會包含檢視它的指針參數所指向記憶體中的内容的操作。
8. 指針常量與常量指針
區分方法:看const在*的左邊還是右邊
指向const對象的指針(常量指針): 是一個指針,指向的是常量值,它指向的量的值不能改變、必須是常量,但是該指針可以指向不同的常量(記憶方法:int *p:是整型指針,指向整型數的指針;是以常量指針是指向常量的指針) const char *p
當指向const的指針指向一個非const對象時,系統會認為該指針所指向的對象時const類型。如果指向const的指針所指向的對象并不是const,則可以直接給該對象指派或間接利用普通非const指針修改其值:畢竟這個值不是const。不能保證指向const的指針所指的值一定不可修改。可以将指向const的指針了解為“自以為指向const的指針”,有助于了解該段。
const指針(指針常量):是一個指針,指針本身是常量,即指針指向的位址不能改變,但是所指的位址裡的内容可以改變。 指針常量必須在定義時同時指派。 char *const p;
const出現在星号左邊表示被指物是常量;const出現在星号右邊說明指針本身是常量。
9.指針和typedef
typedef string *ptr;
const ptr cstr;
問上面的語句的含義?
錯誤答案:const string *cstr,認為const ptr是一種指針,指向string類型的const對象。
正确了解:把cstr定義為指向string類型對象的const指針:string *const cstr; const修飾的是ptr的類型,這是一個指針。
10. 指向數組的指針和指針數組
首先看如下代碼:
int a[10], *tp1 = a; //合法的
int b[10][12]; *tp2 = b; //錯誤
第一行的聲明是合法的,将聲明一個整數數組,為其配置設定空間,并将指針tp1聲明為指向數組a第一個元素的指針。但是第二行的聲明是錯誤的,它正确的建立了數組b,但是tp2指針初始化錯誤。b是一個 指向整型數組的指針,此時,tp2聲明為一個指向整型數的指針,不能用指向整型數組的指針對tp2初始化。
那麼該如何聲明一個指向整型數組的指針呢? 應該為:
int (*p)[12];
下标操作符的優先級高于間接通路操作符,但是後者加了括号,是以先計算括号内的:p是一個指針,它指向什麼呢?接着執行的是下标操作,于是p指向的是數組且數組的元素是int類型。是以,p是指針、指向int型數組,即p是一個指向int數組的指針。
故開始第二行代碼應該為:
int (*p)[12] = b;
它使得p指向數組b的第一行。p是一個擁有12個整型元素的數組的指針(指向數組的指針)。
那麼,聲明一個整型指針,使其指向b的第一個元素的方法如下:
int *pi = &b[0][0];
int *pi = b[0];
看下面的例子:
void main() {
int ia[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int (*pi)[4] = ia;
cout << "**ia: " << **ia << endl; //1
cout << "*(*ia + 1): " << *(*ia + 1) << endl; //2
cout << "**(ia + 2): " << **(ia + 2) << endl; //9
cout << "*(*(ia + 2) + 3): " << *(*(ia + 2) + 3) << endl; // 12
}
指針數組,顧名思義,是一個數組,它的元素是指針。如下是一個指針數組:
int *p[10];
它先執行下标操作,p是數組,數組的類型是指向整型的指針。
void f(char **p){
*p += 2;
}
void main() {
char *a[]= {"123456","abcdef","7890"},**p;
cout << sizeof(a) << endl; //結果為12,可見數組存的是三個指針
cout << sizeof(*a) << endl; //輸出4,*a也是一個指針,該指針指向的是"123456"所在位置
p = a;
f(p); //該函數使得*p指針在字元串"123456"中移動了兩位
printf("%s\r\n",*p); //*p是一個指向字元串的指針,輸出2345
}
如果将上述代碼中的 p = a 換為p = a + 1; 那麼最後輸出的結果将是cdef。
上述程式的示意圖如下:
關于指針數組,見《c和指針》第162頁;指向數組的指針見《c和指針》第158頁。