天天看點

【C語言】第六章-指針

第1節

什麼是指針

  在計算機科學中,指針時程式設計語言中的一個對象,利用位址,它的值直接指向存在在電腦存儲器當中的另一個地方的值。簡而言之,指針是一個存儲變量位址的變量。在32位系統上,位址是32個0或1組成的序列,是以要求用4個位元組來存儲,是以指針在32位機器上是4個位元組的大小。 是以推而廣之,在64位機器上,指針是8個位元組大小的。

指針和指針類型

指針的基本使用

  我們可以使用

int* num;

的語句來定義一個指針類型的變量,并直接在其後進行指派,但是要注意我們要賦給指針變量的一定是一個變量的位址,此時我們就需要用到

&

取位址符。

  而如果我們我們想要取到指針中的值時則需要用到

*

解引用操作符。以下的例子展示了指針的基本使用。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int num = 10;
	int*p = &num;//建立指針變量并讓其指向num變量
	printf("%d\n", p);//列印指針p所指向的記憶體位址
	printf("%d\n", *p);//列印p所指向變量的值
	system("pause");
}
           

  但是我們不能将非法的記憶體位址賦給指針變量。那麼什麼是非法的記憶體位址呢?所謂非法的記憶體位址即我們并未向系統申請過的記憶體位址。我們每定義一個變量系統都會分給我們一塊記憶體空間以便我們存放資料,而如果我們對指針直接進行指派,那麼我們就無法保證賦給指針的變量是合法的,此時如果再通路指針則是未定義行為(在C标準中并未明确說明的操作行為),這是十分危險的。比如說我們使用這樣的語句

int* p = 0x100;

則是十分不安全的寫法。是以對于指針我們不要對其進行亂指派,防止其指向非法的記憶體

  為了防止防止指針指向不合法的記憶體,對于我們定義了但暫時不用的指針變量,出于安全起見,我們可以将其暫時指派為

NULL

。如

int* p = NULL

指針常見注意事項

  1、不可将字面值常量的位址賦給指針。如

int* p = &10

這樣的寫法就是錯的。

  2、不可給指針直接複制記憶體位址,以免造成通路非法記憶體的錯誤。

  3、對于暫時不使用的指針将其值賦為

NULL

防止造成野指針,發生意想不到的麻煩。

  4、在32位機器下指針的大小為32位,4個位元組,指針具體大小視系統而變。

指針和數組名

  之前數組講解時有提到過數組名就是元素的首位址,那麼我們是否可以了解為數組名就是一個指向首元素的指針呢?其實這麼了解實在欠妥,雖然數組和指針十分相似,有着千絲萬縷的聯系,但數組可千萬不能和指針一概而論。

  在C語言中,數組和指針可以進行轉換,這為我們很多操作行了友善,但數組和指針永遠屬于兩個不同的資料類型。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	int* p = arr;
	printf("%d\n",sizeof(arr));//數組的大小
	printf("%d\n", sizeof(arr + 0));//數組隐式轉換為指針,參與運算
	printf("%d\n", sizeof(p));//一個和數組指向相同元素的指針
	system("pause");
}
           

  在這個例子中我們很明顯就能看出指針和數組的差別,當我們取數組的大小時,會進行計算得出數組所有元素大小之和,而我們取指針的大小時,永遠都隻會是4,尤其是我們在列印

arr + 0

的大小的時候由于數組名不能參與運算,于是數組名隐式轉換為指針後才參與運算,從結果是4也能看得出來此時的

arr + 0

已經是一個指針了,哪怕它所指向的記憶體位址并沒有發生改變。

  初次之外我們可以利用數組名和指針可以互相轉換的特點和指針可以參與運算的特點利用指針來通路數組中的元素。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	printf("%d\n", *(arr + 1));
	printf("%d\n", arr[1]);
	system("pause");
}
           

  在這個例子中我們可以看到兩種取數組中元素的方式都可得到數組中的第二個元素,是以我們不難得出結論

*(arr + 1) <=> arr[1]

,即這兩種方式是等價的。是以我們很多情況下可以利用指針靈活使用數組。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	int* p = arr + 1;
	printf("%d\n", p[-1]);//等價于*(p - 1);
	system("pause");
}
           

  尤其是對于一個指針,下标的方式依然使用,并且不同于數組,在指針的下表中甚至可以使用負數作為下标。

指針運算

指針 ± 整數

  指針加減一個整數等同于跳過了幾個元素,而絕對不是記憶體位址加減整數,這點我們之前的例子中已經深有體會了,這裡不再詳細說明了。

指針 - 指針

  指針雖然不能相加,但是可以相減,指針相減計算的是兩個指針間所偏移的元素個數。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	int* p1 = arr + 1;
	int* p2 = arr + 2;
	printf("%d\n", p2 - p1);//得出p2與p1間相偏移了一個元素
	printf("%d\n", p1 - p2);//向左偏移則為負數
	system("pause");
}
           

指針的關系運算

  指針間也可也進行普通的關系比較,比如指針相等,指針大小等等,其中指針大小比較時,指針相比于另一個指針向右便宜則為大,向左則為小。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	int* p1 = arr + 1;
	int* p2 = arr + 1;
	int* p3 = arr + 2;
	printf("%d\n", p1 == p2);//得出p2與p1間相偏移了一個元素
	printf("%d\n", p3 > p2 ? 1 : -1);//向左偏移則為負數
	system("pause");
}
           

  不過在此值得說明的是:隻有在連續的一段記憶體上指針的相減以及指針的關系運算才有意義,否則都是無意義的。

指針微進階

  C語言指針博大精深,有着很多的門路,在此我們挑其中最好了解的兩點進行講解。

數組指針

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加頭檔案
#include <stdlib.h>

int main()//主函數,函數入口
{
	int arr[] = { 1,2,3,4 };
	printf("%p\n", arr);
	printf("%p\n", &arr + 1);
	system("pause");
}
           

  從運作結果大家不難發現,

&arr + 1

arr

的結果要多了16個位元組,也就正好是整個數組的長度。也就是說

&arr

我們取到了整個數組的指針,在我們對這個指針進行

+1

時,指針跳過了整個數組,于是我們稱指向整個數組的指針為數組指針。

常量指針和指向常量的指針

  常量我相信大家都不陌生,也就是在初始化後無法再進行改變的量,但是在指針中常量也分為常量指針和指向常量的指針兩種。

  指向常量的指針:對指針所指向的變量無法進行更改的指針。

         

const int* p = &num

         

int const* p = &num(等價)

  這樣的指針在我們不想讓指針修改一個值卻又友善傳入的時候經常使用。

  常量指針:指針一旦指向某個記憶體位址無法再更改其指向。

         

int* const p = &num

  這樣的指針與數組名十分類似,自身的值都無法進行改變,但是在強調一遍數組和指針是兩種資料類型,不可搞混。

  剩下指針更為高階的知識和用法将在***C語言進階***篇進行講解。

  歡迎來我的部落格閑逛:https://misakifx.github.io/

繼續閱讀