指針專題-----------再探指針(二)

一、指針的那些事

    說起指針,關於指針的四個方面一定要清楚:

  1. 指針的類型
  2. 指針指向的類型
  3. 指針的值
  4. 指針本身所佔的內存空間

1、指針的類型

<1>  int* p;
<2>  int** p;
<3>  char* p;
<4>  double* p[3];

無論上面定義的指針類型你是否認識,有一個判斷指針的萬能公式----把指針名去掉,剩下的就是是指針的類型

比如int** p;  這個指針自身的類型就是 int**  (去掉指針名p).。

2、指針指向的類型

這個和上面的判斷方法類似,判斷一個指針的指向類型時,把指針名和前面的一個*去掉,剩下的就是指針指向的類型

比如 int** p;   這個指針指向的類型就是int * 。

作用:決定了指針變量所取空間的寬度,決定了指針變量+1時跳過的單位跨度(★★★)

關於上面的作用可能不是很好理解,等到下面講到指針的運算符會詳細的解釋上面的作用

3、指針的值

在定義指針會申請一塊內存空間在存放這個指針,這個內存空間裏面村的就是指針的值,通過指針的值一般都是第一個地址,這個地址都是指針所指向的的地址。定義指針時,僅僅是申請了一塊內存空間來存放指針的地址,但是這個內存空間並沒有初始化。

4、指針本身所佔的內存空間

在定義指針時,肯定要去內存空間中申請一塊內存空間來存放這個指針。

一開始發現了一個特別有趣的事:32位的系統中,無論什麼類型的指針,都是用4個字節進行存儲,64位的系統都是用8個字節進行存儲。

#include<iostream>
using namespace std;

int main()
{
	cout << sizeof(int **) << endl;
	cout << sizeof(char *) << endl;
	cout << sizeof(double *) << endl;
	cout << sizeof(long long int*) << endl;
	return 0;
}

上面的代碼在32位的系統中運行,輸出的都是4,在64位的系統中運行,輸出的都是8。

一開始特別困惑,不同的指針類型,它們怎麼可能佔用的內存空間一樣呢?

最後去網上搜了搜,又查了一些書,最後得到了一個說服自己的理由:

      假如是32的系統,那麼內存空間的地址編號長度肯定都是一樣長的,而指針的值存放的就是一個個的地址,那麼不同類型指針佔用的內存空間可以是一樣大的,因爲他們存放的地址長度都是一樣的。

總結:一旦遇到了一個指針,自己就應該問問自己?指針的類型是什麼?指針指向的類型是什麼?指針的值是什麼?

二、指針的算術運算

1、關於整形數的存儲

首先看一張圖片

假如我們定義了一個整型變量,又賦予了一個它一個初值(16進制的11223344).那他在系統中的存儲方式如上圖所示(採取的是小端存儲,至於是大端還是小端存儲,這個和操作系統有關,window的系統是小端存儲)。

變量a的首地址都是地址1。當使用到變量a是,他就會拿着a的地址去內存中找,從這個地址一次往後找四個字節(找幾個節和這個變量是什麼類型的有關)。然後根據這四個字節讀出來一個整數(至於怎樣讀的,就不是咱們考慮的,操作系統既然能存進去,它肯定也可以讀出來)。

2、關於指針的運算

首先看  代碼1

#include<iostream>
using namespace std;

int main()
{
	int a = 0x11223344;
	printf("%#x\n", a);//按16進制數字進行輸出

	int* p1 = &a;
	printf("%#x\n", *p1);//按16進制數字進行輸出

	short* p2 = (short*)&a;
	printf("%#x\n", *p2);//按16進制數字進行輸出

	char* p3 = (char*)&a;
	printf("%#x\n", *p3);//按16進制數字進行輸出
	return 0;
}

代碼1運行結果:

根據整數的存儲,那麼指針的訪問如下圖所示

下面再看 代碼2

#include<iostream>
using namespace std;

int main()
{
	int a = 0x11223344;
	printf("%#x\n", a);//按16進制數字進行輸出

	int* p1 = &a;
	printf("%#x\n", *p1);//按16進制數字進行輸出

	short* p2 = (short*)&a;
	p2++;
	printf("%#x\n", *p2);//按16進制數字進行輸出

	char* p3 = (char*)&a;
	p3++;
	printf("%#x\n", *p3);//按16進制數字進行輸出
	return 0;
}

代碼2 運行結果:

經過上面的代碼後,指針p2和p3指向的位置都變了,下面通過一張圖片看他們移動後的指向

先說指針p1,他的指向是地址1,指針指向的類型是int(4個字節),所以說指針p1的指向地址是地址1,讀取的寬度是4個字節,因此*p1的值就是0x11223344(因爲採取的小端存儲方式,倒着存倒着取)。

再說指針p2,這個指針一開始的指向的地址1,指針指向的類型是short ( 2個字節 ) ,指針讀取的寬度2個字節,因此指針p2移動前,*p2讀取的數據就是0x3344。採取p2++後,指針p2向後移動一個單位長度(單位長度就是2個字節,這個和指針指向的類型有關)。那麼首地址都變成了地址3,讀取寬度是2個字節,因此讀取的數值就是0x1122

最後說指針p3.這個指針一開始指向的地址1,讀取寬度是1,因此*p3讀取的數值就是0x44。 

p3++後,向後移動一個單位長度(一個字節),讀取長度是1,因此 *p3讀取的數值就是0x33.

三、指針的安全行問題

1、空指針

在定義指針時,我們一般都要給指針賦初值,如果還沒有地址給這個指針賦初值,可以賦給指針一個NULL。

上面是一個特別好的習慣,不過有一點需要注意的就是空指針不能別訪問,

原因:NULL其實就是內存空間的0號地址,操作系統中0-255的地址空間都不能被訪問,這個地址只能由操作系統進行管理,程序員不可以進行修改的讀取。

2、野指針

假設我們現在定義了一個指針,同時由賦給了它一個地址(直接進行指定的常量地址),那這個指針就稱爲野指針。

#include<iostream>
using namespace std;

int main()
{
	int* p = &(0x01020304);
	cout << *p << endl;
	return 0;
}

就像上面那樣,直接指定了一個地址賦給了指針,這就是野指針,我們在使用指針時千萬不要這樣做。

就像你去酒店開房一樣,只有你去前臺找管理的人,讓他給你選擇一個空的房間,這樣你才能入住,假如少了前面那一步,你可以直接找一個房間進去嗎,說不定裏面就有人在幹啥呢!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章