前言
這是一篇跟着FishC大佬的C++指針部分學習筆記,以前一直對指針折騰的雲裡霧裡,希望這篇能對大家有所幫助。如有纰漏,望各位大佬指正。
指針(一)
想了解指針,必須先了解位址。
首先需要了解:程式是在硬碟上以檔案的形式存在的,但它們的運作卻是發生在計算機的記憶體中。
示範一下變量在記憶體中的存放情況。
- int a = -12;
- char b = M;
- float c =3.14;
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLzIDO5IzMzkTM0ATMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
這裡需要來讨論一下對齊,為什麼上圖中c要從8開始存儲呢??
其實,在C++裡,變量類型是根據它們的自然邊界進行對齊的!不過這個我們隻需要知道即可,因為編輯器會自動幫我們處理這類問題。另外,對齊問題會因為系統平台不同而不同。
接下來談一下尋址,對于變量可以通過兩種方法來對它進行索引。
一種是通過變量名
一種是通過位址。
這裡引入一個新的操作符,叫做:取址操作符:&,它的作用是獲得變量的位址。
我們習慣這麼使用:
- int var = 123;
- std::cout <<"Address is:"<<&var;
使用指針
上邊我們對位址這個概念是了解和使用指針的基礎。位址是計算機記憶體中的某個位置,而指針是專門用來存放位址的特殊類型變量。一般情況下,我們用下面的形式來聲明指針變量:
type *pointerName;
例如:
- int *p;
- int pp = 123;
- p = &pp;
在建立指針時,空格放在哪裡都是沒關系的,下邊的文法都是可以接受的。
- int *p1;
- int * p1;
- int* p1;
指針變量前邊的類型是用來說明指針指向的資料的類型,請務必比對來使用。
另外,允許void類型的指針變量:void*p
指針(二)
溫故而知新:
- 建立變量時,系統會配置設定一些記憶體塊用來存儲它們的值;
- 每個記憶體塊都擁有一個獨一無二的位址;
- 變量的位址可以用&variablename文法來取得;(注:&我們稱為“取位址”操作符)
- 可以把位址指派給一種稱為指針的特殊變量;
- 指針的類型必須與由它儲存其位址的變量的類型一緻。
接下來舉一個栗子:
- int a = 456;
- char b = ‘C’;
- int *aPointer = &a;
- char *bPointer = &b;
這會讓程式保留四個記憶體塊,兩個為變量保留,兩個為指針保留。變量a和變量b中存放的是變量的值(456和‘C’的ASCII碼值);兩個指針變量存放着的是指針的值,這些值是其他變量的位址。
為什麼aPointer指針變量中存儲的是0而不是0-3呢,因為既然知道存儲的a為整形變量,那麼隻需要存儲它的首位址再加上sizeof(a)即可
當我們知道了某個變量在記憶體中的位址(通過指針),就可以利用指針通路位于改位址的資料。
這需要對指針進行“解引用(Deference)”處理:即在指針名的前面加上型号(*)。
例如:std::count << *aPointer;
這裡我們了解一下:把整數變量a的位址存在在aPointer指針之後,*aPointer和變量a将代表同一個值
是以:*aPointer = 123;将會導緻下圖所示結果:
一定要牢記:指針所儲存的是記憶體中的一個位址。它并不儲存指向的資料的值的本身。是以,務必確定指針對應一個已經存在的變量或者一塊已經配置設定的記憶體。
重點部分:關于星号的用途
第一種是用于建立指針:
int *myPointer = &myInt;
第二種是對指針進行解引用:
*myPointer = 3998;
Tips:
C++允許多個指針指向同一個位址,就是多個指針有相同的值,任意一個進行了修改,其從放的值都會發生修改。
C++支援無類型(void)指針,就是沒有被聲明為某種特定類型的指針,例如
void *vPointer;
注意:對一個無類型指針進行解引用前,必須先把它轉換成一種适當的資料類型。
指針和數組(三)
在上述兩講中關于位址和指針的栗子中,我們使用的都是标量類型:整數、實數和字元。
是以當我們遇到一個标量類型的變量時,我們可以建立一個與其類型相同的指針來存放它的位址。如果我們遇到的是數組(是一組數,而不是一個數)該怎麼辦呢?
我們知道,計算機把數組是以一組連續的記憶體塊儲存的
例如:int myArray[3] = {1,2,3};
存儲形式如下圖所示:
這就說明了數組擁有很多個位址,每個位址對應着一個元素。數組的名字其實也是一個指針(指向數組的基位址,也就是第一個元素的位址)
比如上述那個栗子,一下兩句話做的事同一件事情:
- int *ptr1 = &myArray[0];
- int *ptr2 = myArray;
我們可以輕易地将數組的基位址用指針變量儲存起來,那如果我們要通過指針通路其他數組元素,如何辦到呢?
試試:ptr1++;
以上運算并不是将位址值簡單的做+1處理,而是安裝指向的數組的資料類型來遞增的,也就是說+sizeof(int)
思考:
如有:
int Array[5] = {1,2,3,4,5};
int *ptr = Array;
則
*ptr + 1;
*(ptr + 1);
二者有什麼差別呢??
#include <iostream>
int main()
{
int Array[5] = {1,2,3,4,5};
int *ptr = Array;
std::cout << *ptr + 1 << '\n';
std::cout << *(ptr + 1) << '\n';
return 0;
}
程式運作之後得到的的結果為2,難道二者沒有差別嗎?
答案肯定是有差別的。
*ptr+1是指針變量ptr(數組)指向的首位址(第一個)元素的值加1。
*(ptr+1)是指針變量ptr(數組)指向的首位址後的(第二個)元素的值;
注:有括号先算括号裡面的,也就是*(ptr+1)中先将指針指向ptr+1,再求值;而*ptr+1,先求*ptr,再加1。是以二者值相等僅僅是巧合而已~
小結:
指針運算的重要性在進階和抽象的程式設計工作中展現的非常明顯,就目前而言,隻需要記住數組的名字同時也是指向其第一個元素(基位址)的指針。
數組可以是任意一種資料類型,這意味着我們完全可以建立一個以指針為元素的數組。
未完待續……
參考資料
FishC的《C++快速入門》大家可以在B站上搜到
https://www.bilibili.com/video/av7595819?from=search&seid=12323634976003104863