C語言關於指針的複習整理
一、指針的定義
計算機中所有的數據都必須放在內存中,不同類型的數據佔用的字節數不一樣,例如 int 佔用 4 個字節,char 佔 用 1 個字節。爲了正確地訪問這些數據,必須爲每個字節都編上號碼,就像門牌號、身份證號一樣,每個字節的編 號是唯一的,根據編號可以準確地找到某個字節。
我們將內存中字節的編號稱爲地址(Address)或指針(Pointer)。地址從 0 開始依次增加,對於 32 位環境,程 序能夠使用的內存爲 4GB,最小的地址爲 0,最大的地址爲 0XFFFFFFFF
二、指針變量
1、定義
數據在內存中的地址也稱爲指針,如果一個變量存儲了一份數據的指針,我們就稱它爲指針變量。
int a=1;
int *p;
p=&a;
printf("%d\n", *p);
printf("%#X, %#X\n", &a, p);
輸出爲
1
0X28FF10 , 0X28FF10
由第一個輸出可以看出,p指向a的地址,利用p可以得到a的取值
%#X 表示以十六進制形式輸出,並附帶前綴 0X,由輸出結果可以看出兩者的到相同的值,證明p爲指針變量,指向並保存a的地址。
2、取址符、解引用符
定義一個指針變量,應該用 *,然後用 **數據類型+***的格式來定義
int *p
由定義可以看出,要想給指針變量賦值,應該用取地址符“&”:
int *p=&a;
1、指針變量也可以連續定義,例如:
1. int *a, *b, *c;
a、b、c 的類型都是 int* 注意每個變量前面都要帶*。
2. 如果寫成下面的形式,那麼只有 a 是指針變量,b、c 都是類型爲 int 的普通變量:
int *a, b, c;
3、通過指針變量取得數據
int a=1;
int *p;
p=&a;
printf("%d\n", *p);
輸出a=1。
看起來*p和a是等價的,通過 *p 和 a 獲取到的數據一樣,但它們的運行過程稍有不同:a 只需要一次運算就能夠取得數據,而 *p 要經過兩次 運算,多了一層“間接”。
4、通過指針變量賦值
1 int a=1;
2 int *p=&a;
3 *p=100;
4 printf("%d\n", a);
輸出 a=100.
第2行代碼中 * 用來指明 p 是一個指針變量,第3 行代碼中 * 用來給指針指向的數據賦值。
三、數組指針
1、定義
數組(Array)是一系列具有相同類型的數據的集合,每一份數據叫做一個數組元素(Element)。數組中的所有元 素在內存中是連續排列的,整個數組佔用的是一塊內存。
如果一個指針指向了數組,我們就稱它爲數組指針(Array Pointer)。
int arr[] = { 1, 2, 3, 4, 5 };
int *p = arr;
arr 本身就是一個指針,可以直接賦值給指針變量 p。arr 是數組第 0 個元素的地址,因此不用加 &,
也可以寫作:
int arr[] = { 1, 2, 3, 4, 5 };
int *p = &arr[0];
//也可以改變指針指向
p=&arr[1] ;
arr、p、&arr[0] 這三種寫法都是等價的,它們都指向數組第 0 個元素,或者說指向 數組的開頭。
但是“arr 就是一個指針”這種表述並不準確,嚴格來說應該是“arr 被轉換成了一個指針”。
2、用法
如果一個指針變量 p 指向 了數組的開頭,那麼 p+i 就指向數組的第 i 個元素;如果 p 指向了數組的第 n 個元素,那麼 p+i 就是指向第 n+i 個元素。
要記住數組的下標是從零開始的,如果p開始指向的是arr[0],那麼p+2指向的是arr[2]
由此得出一些利用下標和指針遍歷數組的方法:
- 使用下標
也就是採用 arr[i] 的形式訪問數組元素。如果 p 是指向數組 arr 的指針,那麼也可以使用 p[i] 來訪問數組元素, 它等價於 arr[i]。 - 使用指針
也就是使用 *(p+i) 的形式訪問數組元素。另外數組名本身也是指針,也可以使用 *(arr+i) 來訪問數組元素,它等價 於 *(p+i)。
如下代碼:
#include <stdio.h>
int main()
{
int arr[] = { 99, 15, 100, 888, 252 };
int *p = &arr[0]; //也可以寫作 int *p = arr+0;
//第一種方法
printf("%d, %d, %d, %d, %d\n", *(p), *(p+1), *(p+2), *(p+3), *(p+4) );
//第二種方法
for(int i=0;i<5;i++)
{
printf("%d",*(a+i));
}
//第三種方法
for(int i=0;i<5;i++)
{
printf("%d",*(p+i));
}
//第三種方法
for(int i=0;i<5;i++)
{
printf("%d",*(p+i));
}
//第四種
for(int i=0;i<5;i++)
{
printf("%d",p[i]);
}
return 0;
}
3、關於p++ ,++p, (*p)++
*p++ 等價於 *(p++),表示先取得第 n 個元素的值,再將 p 指向下一個元素,即爲將p的地址向後移
*++p 等價於 *(++p),會先進行 ++p 運算,使得 p 的值增加,指向下一個元素,整體上相當於 *(p+1),所以會 獲得第 n+1 個數組元素的值。
(*p)++ 就非常簡單了,會先取得第 n 個元素的值,再對該元素的值加 1。假設 p 指向第 0 個元素,並且第 0 個元素的值爲 99,執行完該語句後,第 0 個元素的值就會變爲 100。
4、字符串數組
字符串數組的用法與數組用法類似:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "hello world";
char *p = str;
int len = strlen(str), i;
for(i=0; i<len; i++){
printf("%c", *(p+i));
}
printf("\n");
//使用p[i]
for(i=0; i<len; i++)
{
printf("%c", p[i]);
}
printf("\n");
//使用*(str+i)
for(i=0; i<len; i++)
{
printf("%c", *(str+i));
}
//使用%s輸出
printf("%s\n",p);
printf("\n");
return 0;
}
除了字符數組,C 語言還支持另外一種表示字符串的方法,就是直接使用一個指針指向字符串,例如:
char *str = “hello world”;
或者:
char *str;
str = “hello world”;
四、指針數組
1、定義
如果一個數組中的所有元素保存的都是指針,那麼我們就稱它爲指針數組。
一般形式爲:
int *p[3];
2、用法
1. #include <stdio.h>
2. int main(){
3. int a = 1, b = 22, c = 130;
4. //定義一個指針數組
5. int *arr[3] = {&a, &b, &c};//也可以不指定長度,直接寫作 int *arr[]
6. printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
7. return 0;
8. }
arr 是一個指針數組,它包含了 3 個元素,每個元素都是一個指針,在定義 arr 的同時,我們使用變量 a、b、c 的 地址對它進行了初始化,這和普通數組類似。
也可以和字符串結合
9. #include <stdio.h>
10. int main(){
11. char *str[3] = {
12. "afdsf",
13. "qtedggew",
14. "wbfdhdn"
15. };
16. printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
17. return 0;
18. }
五、二級指針
1、定義
1. int a =100;
2. int *p1 = &a;
3. int **p2 = &p1;
- 指針變量也是一種變量,也會佔用存儲空間,也可以使用&獲取它的地址。
- C 語言不限制指針的級數,每增加一級 指針,在定義指針變量時就得增加一個星號*。p1 是一級指針,指向普通類型的數據,定義時有一個*;p2 是二級 指針,指向一級指針 p1,定義時有兩個*。
2、獲取指針數據
想要獲取指針指向的數據時,一級指針加一個*,二級指針加兩個*,三級指針加三個*,以此類推,
如:
1. int a =100;
2. int *p1 = &a;
3. int **p2 = &p1;
4. printf("%d,%d\n",*p1,**p2);
六、指針函數和函數指針
1、指針函數
C語言允許函數的返回值是一個指針(地址),我們將這樣的函數稱爲指針函數。
常用代碼形式:
1. #include <stdio.h>
2. #include <string.h>
3. int *fun1(int x, int y){
4. x+=y;
5. return &x;
6. }
7. int *fun2(int *x, int* y){
8. x+=y;
9.
10. return x;
11. }
12. int main(){
13. int m=10,n=20;
14. int *p;
15. p=fun1(m,n);
16. printf("%d\n",*p);
17. p=fun1(&m,&n);
18. printf("%d\n",*p);
19. return 0;
20. }
用指針作爲函數返回值時需要注意的一點是,函數運行結束後會銷燬在它內部定義的所有局部數據,包括局部變量、 局部數組和形式參數,函數返回的指針請儘量不要指向這些數據, C語言沒有任何機制來保證這些數據會一直有效, 因此在調用完該函數後,應該及時使用運行輸出。
2、函數指針
一個函數總是佔用一段連續的內存區域,函數名在表達式中有時也會被轉換爲該函數所在內存區域的首地址,這和 數組名非常類似。我們可以把函數的這個首地址(或稱入口地址)賦予一個指針變量,使指針變量指向函數所在的 內存區域,然後通過指針變量就可以找到並調用該函數。這種指針就是函數指針。
用法:
1. #include <stdio.h>
2.
3. //返回兩個數中較大的一個
4. int max(int a, int b){
5. return a>b ? a : b;
6. }
7. int main(){
8. int x, y, num;
9. //定義函數指針
10. int (*pmax)(int, int) = max; //也可以寫作int (*pmax)(int a, int b)
11.
12. scanf("%d %d", &x, &y);
13. num = (*pmax)(x, y);
14. printf("Max value: %d\n",num);
15. return 0;
16. }