指針是 C 語言中的一個特點,也是內存地址,是內存單元的編號,指針變量是用來存放內存地址的變量,不同類型的指針變量所占用的存儲單元長度是相同的,而存放數據的變量因數據的類型不同,所占用的存儲空間長度也不同。
有了指針以后,不僅可以對數據本身,也可以對存儲數據的變量地址進行操作;一般把指針稱為指針變量,指向的對象可以是變量或者數組等;指針指向數組時,它內容存儲的是數組的首地址,所以數組和指針就產生了一定的關系。那什么是數組呢?具有相同類型的若干元素按有序的形式組織起來的一種集合就叫做數組,下面會對指針、指針和數組相結合的一些用法進行分析。

1、指針
1、1 定義 int * 類型的指針變量以及修改這個指針變量,其他類型的指針變量寫法也類似
int * p; //p是變量的名字,int * 表示 p 變量存放的是 int 類型變量的地址;它並不是表示定義了名叫 *p 的變量
int i = 5;
p = &i; //p 保存了 i 的地址,所以 p 指向 i
int i2 = 6;
p = &i2; //修改 p 的值不影響 i 的值
int * p2 = &i;
i = 9; //修改 i 的值不影響 p2 的值
printf("%d\n",*p2); //*p2 輸出為9,*p2 等同於 i 的值,p2 等同於 i 的地址;*p2 可以理解為以 p2 的內容為地址的變量,p2 的內容就是 i 的地址
1、2 指針與指針變量是不同的概念
系統為每一個內存單元分配一個地址值,C 語言中把這個地址值稱為“指針”。例如 int j = 6; int p=&j; 存放變量 j 的內存單元的編號 &j 被稱為指針,它的本質是一個操作受限的非負整數。“指針變量”則是存放前述“地址值”的變量,也可以表述為,“指針變量”是存放變量所占內存空間“首地址”的變量。把 j 的指針 &j 賦給了int 型指針變量p,就是說 p 存入着 &i,因此說指針變量是存放指針的變量。
1、3 當某一類型的指針變量沒有指向任何一個地址時,它的值為垃圾值
int i = 6;
int * p;
int * p2;
p = &i;
*p = p2; //這里語法編譯錯誤,因為 *p 和 p2 是兩個不同的類型,*p 表示 int 類型的內容,p2 表示 int * 類型變量的地址
*p = *p2; //錯誤的,因為 p2 是垃圾值,所以 *p2 也是垃圾值
p = p2; //p2 是垃圾值,p2 賦值給 p,p 也變成了垃圾值
printf("%d\n",*p); //p 的空間是屬於當前程序的,因此當前程序可以讀寫 p 的內容,如果 p 的內容是垃圾值,那么當前程序不能讀寫 *p 的內容,因為 *p 所代表的內存單元的控制權限並沒有分配給當前程序,所以運行到這一行會出錯
1、4 在當前函數中通過獲取2個某一類型(一般是基本數據類型)變量的地址並傳入另外一個函數進行內容交換,可實現2個某一類型變量內容的交換
void swop(int * p1,int * p2)
{
int t = 0; //這里的 t 必須定義為 int 類型的,因為 *p1 和 *p2 都是 int 類型的
t = *p1;
*p1 = *p2;
*p2 = t;
/*
** 以下這種交換的寫法是錯誤的,只是交換了 p1 和 p2 指向的地址
** 並沒有交換 以 p1 和 p2 的內容為地址的變量,即 i1 和 i2
**
** int * t;
** t = p1;
** p1 = p2;
** p2 = t;
*/
}
int main()
{
int i1 = 2;
int i2 = 3;
swop(&i1,&i2);
printf("i1 = %d,i2 = %d\n",i1,i2); //這個時候輸出時,i1 = 3,i2 = 2;真正完成了交換
return 0;
}

1、5 “*” 代表的含義
1.5.1 表示乘法
1.5.2 表示定義指針變量,舉個例子
int * p1; //定義了名叫 p1 的變量,int 代表 p1 只能存放 int 變量的地址
1.5.3 表示指針運算符,該運算符放在定義好的指針變量的面前,舉個例子
如果 p 是一個已經定義好的指針變量,那么 *p 代表以 p 的內容為地址的變量
1、6 假設 n >= 2 , n 級指針變量只能指向 n - 1 級指針變量的地址
int i = 3; //假設 i 的地址為 1001h
int * p = &i; //p 存放的內容是 i 的地址,假設 p 的地址為 1002h
int ** q = &p; //q 存放的內容是 p 的地址,假設 q 的地址為 1003h
int *** r = &q; //r 存放的內容是 q 的地址,假設 r 的地址為 1004h
//int *** r2 = &p; //error,只能存放二級指針變量的地址
printf("*p = %d\n", *p); //這里輸出 i 的內容 3
printf("*q = %d\n", *q); //這里輸出 p 存放的地址,即 i 的地址
printf("**q = %d\n", **q); //這里輸出 *p,因為 *(*q) 等同於 *p
printf("*r = %d\n", *r); //這里輸出 q 存放的地址
printf("**r = %d\n", **r); //這里輸出 p 存放的地址,因為 *r = q,**r = *q = p
printf("***r = %d\n", ***r); //這里輸出 i 的內容,即 3;***r = **q = *p = i
2 指針和數組
2、1 指針變量指向一維數組時,它存放的是一維數組的地址同時也是一維數組第一個元素的地址
int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[0];
int boolean = p == p2;
printf("boolean的值為%d\n",boolean); //boolean 的值為1,證明了 a 數組的地址和 a[0] 元素的地址相等
2、2 如果 p 是一個指針變量且指向一個一維數組,可以使用它輸出數組元素,且 p[i] 等價於 *(p + i)
int a[5] = {1,2,3,4,5};
int * p = a;
int i = 0;
for (i = 0; i < 5;i++) {
printf("p[i]的值為%d\n",p[i]); //p[i] 等價於 a[i]
printf("*(p+i)的值為%d\n",*(p+i)); //因為 a 數組的地址是連續的,p + i 等價於 a 的地址加上 i,即 p + i 等價於 a[i] 的地址,那么 *(p + i) 等價於 a[i] 的元素
}

2、3指針變量不能相加,不能相乘,也不能相除;如果兩個指針變量指向的是同一塊連續空間中的不同存儲單元,那么這兩個指針變量才可以相減
int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[3];
int j = p2 - p; //正確
int j2 = p2 + p; //error,不能相加
int j3 = p2 / p; //error,不能相除
int j4 = p2 * p; //error,不能相乘
2、4一個指針變量,無論它指向的地址的變量占多少個字節,該指針變量本身只占4個字節
char c = 'c';
int i = 0;
double d = 2.0;
char * p = &c;
int * p2 = &i;
double * p3 = &d;
printf("p所占的字節數為%d\n",sizeof(p)); //sizeof(p) 輸出為 4
printf("p2所占的字節數為%d\n",sizeof(p2)); //sizeof(p2) 輸出為 4
printf("p3所占的字節數為%d\n",sizeof(p3)); //sizeof(p3) 輸出為 4
2、5靜態數組長度必須事先確定,而且只能是常整數,不可以是變量
int length = 4;
int a[2]; //正確
int a2[length]; //錯誤的,數組長度不可以是變量
2、6 使用 malloc 函數動態構造數組
int length = 0;
int * pArr;
printf("請輸入數組的長度\n");
scanf("%d",&length);
/**
** 1、使用 malloc 函數,要添加 malloc.h 頭文件
** 2、malloc 函數只有一個形參而且只能是整形的,它表示請求系統為當前程序分配的字節數
** 3、malloc 函數返回的是第一個字節的地址
** 4、一共分配了 8 * length 個字節,pArr 變量占 4 * length 個字節,pArr 指向的內存也占 4 * length 個字節
** 5、pArr 本身所占的內存是靜態分配的,pArr 指向的內存是動態分配的
** 6、動態構造一個一維數組,假設 length = 5,類似 int pArr[20] 數組
*/
pArr = (int *)malloc(4 * length);
int i = 0;
for(i = 0; i < length;i++) {
pArr[i] = 2 * i;
}
for(i = 0; i < length;i++) {
printf("%d\n",pArr[i]);
}

2、7 數組動態內存和靜態內存的比較
2、7、1 靜態數組內存是由系統自動分配,是在棧分配的,由系統自動釋放;靜態定義的數組,它的內存無法讓程序員手動釋放,在一個函數運行期間,系統為該函數中數組分配的空間會一直存在,直到該函數運行完畢時,數組的空間才會釋放;數組的長度一旦定義,其長度不可以更改;
函數中定義的數組,函數運行完后數組無法再給其他函數使用。
2、7、2 動態數組可以解決靜態數組的一些缺陷;動態數組內存是由程序員手動分配的,且是在堆中分配的,需要手動釋放。
好了,本篇文章寫到這里就結束了,由於本人技術水平有限,難免會有出錯的地方,歡迎批評指正,謝謝大家的閱讀。
文章來源:https://segmentfault.com/a/1190000039207472?utm_source=tuicool&utm_medium=referral
最后
特別推薦一個分享C/C++和算法的優質內容,學習交流,技術探討,面試指導,簡歷修改...還有超多源碼素材等學習資料,零基礎的視頻等着你!
還沒關注的小伙伴,可以長按關注一下:
