1. 指針是什么2. 指針和指針類型3. 野指針4. 指針運算5. 指針和數組6. 二級指針7. 指針數組
目錄
1. 指針是什么?
2. 指針和指針類型
2.1 指針+-整數
2.2 指針的解引用
3. 野指針
3.1 野指針成因
3.2 如何規避野指針
4. 指針運算
4.1 指針+-整數
4.2 指針-指針
5. 指針和數組
6. 二級指針
7. 指針數組
1. 指針是什么?
指針是什么?
指針理解的 2 個要點:
1. 指針是內存中一個最小單元的編號,也就是地址
2. 平時口語中說的指針,通常指的是指針變量,是用來存放內存地址的變量
總結:指針就是地址,口語中說的指針通常指的是指針變量。
那我們就可以這樣理解:
內存

?
指針變量
我們可以通過 & (取地址操作符)取出變量的內存其實地址,把地址可以存放到一個變量中,這個
變量就是指針變量
#include <stdio.h>
int main()
{
int a = 10;//在內存中開辟一塊空間
int *p = &a;//這里我們對變量a,取出它的地址,可以使用&操作符。//a變量占用4個字節的空間,這里是將a的4個字節的第一個字節的地址存放在p變量
中,p就是一個之指針變量。
return 0;
}
總結:
指針變量,用來存放地址的變量。(存放在指針中的值都被當成地址處理)。
那這里的問題是:
一個小的單元到底是多大?(1個字節)
如何編址?
經過仔細的計算和權衡我們發現一個字節給一個對應的地址是比較合適的。
對于 32 位的機器,假設有 32 根地址線,那么假設每根地址線在尋址的時候產生高電平(高電壓)和低電平(低電壓)就是(1或者 0 );
那么 32 根地址線產生的地址就會是:
00000000 00000000 00000000 0000000000000000 00000000 00000000 00000001...11111111? 11111111? ?11111111? ?11111111
這里就有 2 的 32 次方個地址。
每個地址標識一個字節,那我們就可以給 ( 2^32Byte == 2^32/1024KB ==
2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB ) 4G 的空閑進行編址。
同樣的方法,那 64 位機器,如果給 64 根地址線,那能編址多大空間,自己計算。
這里我們就明白:
在 32 位的機器上,地址是 32 個 0 或者 1 組成二進制序列,那地址就得用 4 個字節的空間來存儲,所以
一個指針變量的大小就應該是 4 個字節。
那如果在 64 位機器上,如果有 64 個地址線,那一個指針變量的大小是 8 個字節,才能存放一個地
址。
總結:
指針是用來存放地址的,地址是唯一標示一塊地址空間的。
指針的大小在 32 位平臺是 4 個字節,在 64 位平臺是 8 個字節 。
2. 指針和指針類型
這里我們在討論一下:指針的類型
我們都知道,變量有不同的類型,整形,浮點型等。那指針有沒有類型呢?
準確的說:有的。
當有這樣的代碼:
int num = 10;
p = #
要將&num(num的地址)保存到p中,我們知道p就是一個指針變量,那它的類型是怎樣的呢? 我們給指針變量相應的類型。
char? ? ? ?? * pc = NULL ;int? ? ? ? ? ?? * pi = NULL ;short? ? ? ?* ps = NULL ;long? ? ? ? ? * pl = NULL ;float? ? ? ? ?* pf = NULL ;double? ? * pd = NULL ;
這里可以看到,指針的定義方式是: type + * 。
其實:
char* 類型的指針是為了存放 char 類型變量的地址。
short* 類型的指針是為了存放 short 類型變量的地址。
int* 類型的指針是為了存放 int 類型變量的地址。
那指針類型的意義是什么?
2.1 指針+-整數
#include <stdio.h>
//演示實例
int main()
{
int n = 10;
char *pc = (char*)&n;
int *pi = &n;printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return ?0;
}
總結: 指針的類型決定了指針向前或者向后走一步有多大(距離)。
2.2 指針的解引用
//演示實例
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0; ? //重點在調試的過程中觀察內存的變化。
*pi = 0; ? //重點在調試的過程中觀察內存的變化。
return 0;
}
總結:
指針的類型決定了,對指針解引用的時候有多大的權限(能操作幾個字節)。
比如: char* 的指針解引用就只能訪問一個字節,而 int* 的指針的解引用就能訪問四個字節。
3. 野指針
概念: 野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)
3.1 野指針成因
1. 指針未初始化
#include <stdio.h>int main (){? ? ?int * p ; // 局部變量指針未初始化,默認為隨機值* p = 20 ;? ? ?return 0 ;}
2. 指針越界訪問
#include <stdio.h>int main (){int arr [ 10 ] = { 0 };int * p = arr ;int i = 0 ;for ( i = 0 ; i <= 11 ; i ++ )? {// 當指針指向的范圍超出數組 arr 的范圍時, p 就是野指針* ( p ++ ) = i ;? }return 0 ;}
3. 指針指向的空間釋放
這里放在動態內存開辟的時候講解,這里可以簡單提示一下。
3.2 如何規避野指針
1. 指針初始化
2. 小心指針越界
3. 指針指向空間釋放即使置 NULL
4. 避免返回局部變量的地址
5. 指針使用之前檢查有效性
#include <stdio.h>int main (){int * p = NULL ;//....int a = 10 ;p = & a ;if ( p != NULL )? {* p = 20 ;? }return 0 ;}
4. 指針運算
指針 +- 整數
指針 - 指針
指針的關系運算
4.1 指針+-整數
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指針+-整數;指針的關系運算
for (vp = &values[0]; vp < &values[N_VALUES];)
{*vp++ = 0;
}
4.2 指針-指針
int my_strlen(char *s)
{char *p = s;while(*p != '\0' )p++;return p-s;
}
4.3 指針的關系運算
for ( vp = & values [ N_VALUES ]; vp > & values [ 0 ];){*-- vp = 0 ;}
代碼簡化 , 這將代碼修改如下:
for ( vp = & values [ N_VALUES - 1 ]; vp >= & values [ 0 ]; vp -- ){* vp = 0 ;}
實際在絕大部分的編譯器上是可以順利完成任務的,然而我們還是應該避免這樣寫,因為標準并不保證它可行。
標準規定:
允許指向數組元素的指針與指向數組最后一個元素后面的那個內存位置的指針比較,但是不允許與
指向第一個元素之前的那個內存位置的指針進行比較。
5. 指針和數組
我們看一個例子:
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};printf("%p\n", arr);printf("%p\n", &arr[0]);return 0;
}
運行結果:
可見數組名和數組首元素的地址是一樣的。
結論: 數組名表示的是數組首元素的地址 。(2種情況除外,數組章節講解了)
那么這樣寫代碼是可行的:
int arr [ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 0 };int * p = arr ; //p 存放的是數組首元素的地址
既然可以把數組名當成地址存放到一個指針中,我們使用指針來訪問一個就成為可能。
例如:
#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,0};int *p = arr; //指針存放數組首元素的地址int sz = sizeof(arr)/sizeof(arr[0]);for(i=0; i<sz; i++){printf("&arr[%d] = %p ? <====> p+%d = %p\n", i, &arr[i], i, p+i);}return 0;
}
運行結果:
?所以 p+i 其實計算的是數組 arr 下標為i的地址。
那我們就可以直接通過指針來訪問數組。
如下:
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int *p = arr; //指針存放數組首元素的地址
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i<sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
6. 二級指針
指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪里?
這就是 二級指針 。

?
對于二級指針的運算有:
*ppa 通過對 ppa 中的地址進行解引用,這樣找到的是 pa , *ppa 其實訪問的就是 pa .
int b = 20 ;* ppa = & b ; // 等價于 pa = &b;
**ppa 先通過 *ppa 找到 pa , 然后對 pa 進行解引用操作: *pa ,那找到的是 a .
** ppa = 30 ;// 等價于 *pa = 30;// 等價于 a = 30;
7. 指針數組
指針數組是指針還是數組?
答案:是數組。是存放指針的數組。
數組我們已經知道整形數組,字符數組。
int? ? ?? arr1 [ 5 ];char? ? arr2 [ 6 ];

?那指針數組是怎樣的?
int* arr3 [ 5 ];? ? ? // 是什么?
arr3 是一個數組,有五個元素,每個元素是一個整形指針。
?