C語言指針(中)(指針常量)(小端序和大端序)(指針變量)(深度理解指針操作內存)【指針】(18)

指針常量

常量指針是有類型的地址常量

我們首先提出一個概念:
常量指針是有類型的地址常量,對變量取地址,取出的地址就是一個指針,且是常量指針

對於變量使用取地址符號 & 取到了變量的地址,再對取到的地址進行解引用,也就是取內容 * 操作,得到了變量的數據,可以理解爲,先找到變量地址,再通過變量的地址訪問到變量所保存的數據。

代碼演示:

#include <stdio.h>
int main()
{
    char a = 1;
	short b = 2; 
	int c = 10; 
	double d = 123.45;
	//打印變量的地址
	printf("%x\t%d\n", &a,&a);
	printf("%x\t%d\n", &b,&b);
	printf("%x\t%d\n", &c,&c);
	printf("%x\t%d\n", &d,&d);
	
	//通過變量的地址,解引用取到變量的內容
	printf("a = %d\n",*(&a));
	printf("b = %d\n",*(&b));
	printf("c = %d\n",*(&c));
	printf("d = %f\n",*(&d));
	return 0;
}

運行結果爲:
打印地址和通過地址取數據
上面執行代碼中對變量取地址,取出來的地址就是常量指針。

那麼既然我們之前說到了指針就是地址,現在我們知道了a,b,c,d的地址,那我們能不能直接通過16進制的地址訪問到變量呢?

我們通過獲得的16進制地址數據通過解引用獲得指針指向的數據,查看是否能取到地址所對應的數據。

#include <stdio.h>
int main()
{
	char a = 1;
	short b = 2; 
	int c = 10; 
	double d = 123.45;
	printf("%x\t%d\n", &a,&a);
	printf("%x\t%d\n", &b,&b);
	printf("%x\t%d\n", &c,&c);
	printf("%x\t%d\n", &d,&d);


	printf("a = %d\n",*(&a));
	printf("b = %d\n",*(&b));
	printf("c = %d\n",*(&c));
	printf("d = %f\n",*(&d));
	
	printf("a = %d\n",*(0x62fe1f));
	printf("b = %d\n",*(0x62fe1c));
	printf("c = %d\n",*(0x62fe18));
	printf("d = %f\n",*(0x62fe10));

上面最後一種方式是否都能打印出來變量的值呢?
我們在編譯的時候直接出現錯誤:
編譯出現錯誤
說明是不允許的。

我們得出常量指針和地址是不等價的,也就是說常量指針不僅僅只是地址。

我們換一種方式:
把其中:

#include <stdio.h>
int main()
{
	printf("a = %d\n",*(0x62fe1f));
	printf("b = %d\n",*(0x62fe1c));
	printf("c = %d\n",*(0x62fe18));
	printf("d = %f\n",*(0x62fe10));
	return 0;
}

換成:

#include <stdio.h>
int main()
{
	printf("a = %d\n",*((char*)0x62fe1f));
	printf("b = %d\n",*((short*)0x62fe1c));
	printf("c = %d\n",*((int*)0x62fe18));
	printf("d = %f\n",*((double*)0x62fe10));
	return 0;
}

打印結果爲:
有類型的指針

我們可以看到:
變量地址的等價關係

我們通過以上方式證明:
①常量指針並不直接等價於地址
②說明十六進制地址的時候必須要說明指針的類型。

同時也就證明了我們剛開始提到的:
常量指針不是一個單純的地址,而是有類型的。
也就是說常量指針是有類型的地址常量
常量指針 = 地址 + 類型

地址是 0000 0000 到 1111 1111 一共4G的範圍對應的內存編址單元的數值,
那麼我們如何理解類型呢?
類型對於指針有什麼意義呢?

接下來我們通過圖解進行說明:
指針的類型
如果我們只是給了一個地址,那麼我們從這個地址開始到底取到多少個字節,我們是沒有辦法確定的,這也就是上面沒有給指針類型編譯出錯的原因。
指針變量的類型
其他類型變量證明類似。

常量指針不是一個單純的地址,而是有類型的。(重點理解)

結論:
指針的類型,決定了該指針的尋址能力。即從指針所代表的地址處的尋址範圍。

小端序和大端序

那麼我們在這裏就類型展開討論:
在這裏我們提到一個小端序和大端序的問題:
小端序就是內存中的小地址存儲的是小數據,大地址存儲的是大數據。
大端序就是內存中小地址存儲的的是大數據,大地之存儲的是小數據。

例如:

int  data = 0x12345678;

那麼在內存中的存儲爲:
內存存儲數據
也就是說,最小的78存儲在最低位,最大的12存儲在了最高位,這就是我們的小端序,我們的計算機內存存儲就是這種方式,手機的內存存儲方式爲大端序。

接下來我們通過代碼進行驗證:

#include <stdio.h>
int main()
{
	int data = 0x12345678;
	printf("%x\n",&data);
	printf("%x\n",*((char*)0x62fe1c));//起始地址相同 
	printf("%x\n",*((short*)0x62fe1c));//類型決定大小 
	printf("%x\n",*((int*)0x62fe1c));//同樣的地址類型不同獲取的長度也不同
	return 0;
}

當我們取到變量data的地址之後,我們可以通過指針類型限定我們訪問的字節數。

例如我們通過char * 訪問一個字節,short * 訪問兩個字節,並且進行打印。

打印結果爲:
小端序
我們通過圖解方式進行說明:
不同類型的指針對應不同的尋址能力

上面的三行打印結果都是依託於地址:0x62fe1c 求得,只不過地址類型不同,所以尋址的範圍不同。同一個地址,char * 類型尋址範圍只有1個字節,short * 類型尋址範圍只有4個字節,double * 類型尋址範圍只有8個字節。

所以同樣是一個地址,類型不同,尋址能力就不同,那麼取到變量的數值就不同。

以上也就證明了打印出來:
78
5678
12345678
的原因,
所以我們最終對於指針的結論就是:
指針的本質是一個有類型的地址。

地址我們已經早都知道了,32位機能夠放出的地址總數,類型決定了從這個地址開始的尋址能力。
(也就是從一個地址開始能夠尋址多少個字節)。

那麼我們說明指針常量,既然是常量,也就是說指針常量的地址是不允許被賦值修改的:

#include <stdio.h>
int main()
{
    char a;
	short b;
	int c;
	double d;
	&a = 0x12345678;
	return 0;
}

編譯出現錯誤
出現錯誤:不允許a的地址被改變
我們接下來說明指針變量。

指針變量

指針是一個有類型地址,是一個有類型的常量。用以存放指針的變量,我們叫作指針變量。一個指針變量卻可以被賦予不同的指針值,把不同的指針值存儲在指針變量中,可以能過指針變量改變指向和間接操作。

嚴格意義上來說,指針即是指的指針常量,而我們通常意義上所說的指針,多指的指針變量。

我們前面知道常量是有類型的。

#include <stdio.h>
int main()
{
	int a = 12;
	char b = 'c';
	float d = 1.234f;
	return 0;
}

上面的 12 ,c ,1.234 ,我們認爲是輸入的常量數據,那麼輸入的常量數據,我們就需要找一個合適的變量進行存儲。
對於一個指針來說:

#include <stdio.h>
int main()
{
	char a;
	short b;
	float f;
	double d;
	//取地址的時候取出來是常量地址
	//常量地址不允許被賦值
	&a = (char *)0x12345678;
	&b;
	&f;
	&d;
	return 0;
}

上面代碼編譯就會出現錯誤:
編譯錯誤
&a 的地位就等價於 12,c,1.234 ,&a是一個常量指針,常量不允許被賦值。
那麼最終我們需要找一個變量把 a 的地址存起來,那麼就需要聲明一個指針變量,下面我們給出指針變量的定義。

定義

聲明一個指針變量必須保存:①地址數據,②類型
因爲要完整的表達一個指針就必須要有地址和類型。

指針變量的定義:
指針變量的定義

解析

  • 表明該變量是一個指針變量。
    type 決定該指針變量中存儲的地址的尋址能力。

指針變量大小

指針 = 地址 + 類型

聲明一個指針變量用於存放常量指針的時候, 定義帶 * 的變量決定了該變量是一個指針類型,我們都知道類型決定了變量所佔字節的大小,那麼指針類型也就決定了指針變量變量所佔字節的大小。

定義帶有 * 的變量都是指針類型變量,對於32位機來說總共釋放2^32個地址,所以32位二進制數就可以尋址到內存地址 從 0000 0000 到 1111 1111 中的每一個字節,那麼對於32位機來說所有指針類型所佔內存的大小都是32位也就是4個字節。那麼同理在64位機的情況下,所有指針類型所佔內存的大小相等且佔8個字節,我們下面代碼在64位機下面進行打印,證明指針的大小是一樣的。

#include <stdio.h>
int main()
{
	char a;
	short b;
	int c;
	double d;

	char* aa;
	short* bb;
	int* cc;
	double* dd;
	printf("sizeof(&a) = %d\n", sizeof(&a));
	printf("sizeof(&b) = %d\n", sizeof(&b));
	printf("sizeof(&c) = %d\n", sizeof(&c));
	printf("sizeof(&d) = %d\n", sizeof(&d));

	printf("sizeof(aa) = %d\n", sizeof(aa));
	printf("sizeof(bb) = %d\n", sizeof(bb));
	printf("sizeof(cc) = %d\n", sizeof(cc));
	printf("sizeof(dd) = %d\n", sizeof(dd));

	printf("sizeof(char *) = %d\n", sizeof(char*));
	printf("sizeof(short *) = %d\n", sizeof(short*));
	printf("sizeof(int *) = %d\n", sizeof(int*));
	printf("sizeof(double *) = %d\n", sizeof(double*));
	return 0;
}

這裏我們在64位平臺下驗證,64位平臺下,每個指針佔8個字節。
打印結果爲:
64位平臺測試指針變量所佔字節數
有興趣的讀者可以把上面代碼放在32位機下面打印,所有的打印結果大小都爲4。

之前我們在訪問地址指向的變量時候是通過下面形式訪問的:

#include <stdio.h>
int main()
{
	char a = 1;
	short b = 2;
	int c = 3;
	double d = 5.6;
	printf("%d\t",*(&a));
	printf("%d\t",*(&b));
	printf("%d\t",*(&c));
	printf("%f\t",*(&d));
	return 0;
}

打印結果爲:
通過指針訪問數據

初始化及間接訪問

那麼在定義了指針變量之後,我們可以換一種方式訪問變量。

#include <stdio.h>
int main()
{
	char a = 1;
	short b = 2;
	int c = 3;
	double d = 5.6;
	
	char*aa = &a;
	short*bb = &b;
	int*cc = &c;
	double*dd = &d;
	
	printf("%d\t",*(&a));
	printf("%d\t",*(&b));
	printf("%d\t",*(&c));
	printf("%f\n",*(&d));
	
	printf("%d\t",*aa);
	printf("%d\t",*bb);
	printf("%d\t",*cc);
	printf("%f\n",*dd);
	return 0
}

打印結果爲:
指針變量的初始化和間接訪問
我們可以看出兩種方式打印出來完全相同。

那麼接下來我們要區分一下不同位置 * 的作用:

我們在定義的時候使用的 * 只是聲明變量是指針變量,例如:int * p;
printf("%d\t", * aa); 這裏的*表示取值,也就是我們理解的解引用。

那麼之後我們就可以這樣使用:
例如:

#include <stdio.h>
int main()
{
	int data = 0x12345678;
	int *pa = &data;  
	printf("%x\n",&data); 
	printf("%x\n",*(int *)pa);
	printf("%x\n",*(short*)pa);
	printf("%x\n",*(char*)pa);
	
	//上面方法就不需要使用地址打印輸出
	printf("%x\n",*(int *)0x62fe14);
	printf("%x\n",*(short*)0x62fe14);
	printf("%x\n",*(char*)0x62fe14);
	//使用地址進行打印
	return 0;
}

打印結果爲:
指針變量itn類型變量的地址

通過使用指針變量存儲變量的地址,我們就不用直接看見變量的地址,就可以對於變量進行操作。

把變量的地址放在指針變量裏面,就可以對於變量的地址進行操作,這就是爲什麼所有數據類型能夠和指針產生關係的重要紐帶。

把變量的地址取出來放在指針變量裏面,就可以通過地址操縱內存。

小結

強調
有了指針變量之後,我們就可以不需要知道變量的地址,直接對於指針變量存放的變量地址進行訪問和操作。

我們可以定義一個指針變量把變量的地址存放在指針變量裏面,就可以通過指針變量對於地址進行操作。並且可以通過地址操縱內存。

我們強調:指針的本質是一個有類型的地址。

單純的說指針只是一個地址是沒有意義的,說明指針時必須要有類型,才能確定在從這個地址開始尋址的內存空間大小也就是尋址能力。 也就是從指針所代表的地址處開始尋址的尋址範圍。

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