最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++指針與數(shù)組:指針詳解

 更新時間:2021年09月08日 15:55:27   作者:追道者  
本文從初學者的角度,深入淺出地講解C++中的指針、數(shù)組指針,對最常混淆的引用傳遞、值傳遞和指針傳遞做了區(qū)處,需要的朋友可以參考下

本文主要介紹C語言中指針的基本概念與用法,本博文從零開始講解指針,只要你了解基本的C語法,相信你看完本文后一定會有所收獲。

一. What(什么是指針)

1. 地址初了解

要搞明白什么是指針,首先我們得先了解一個概念:地址。

什么是地址呢?

我們知道,計算機的內存是一塊連續(xù)的大塊存儲空間,為了提高 CPU 查找數(shù)據(jù)的效率,我們將這塊連續(xù)的存儲空間以字節(jié)為單位進行編號,每個字節(jié)都一一對應了一個編號,這個編號就叫做地址。

舉個形象的例子:

如果我們把一座宿舍樓看做是內存,那么宿舍樓里的房間我們就可以看成是內存里的每一個字節(jié),那么每個房間的門牌號就可以看做是地址。

為什么要有地址?

通過上面的例子,我們很容易了解到地址存在的意義。試想,如果宿舍樓里的房間都沒有門牌號,我們就很難描述一指定房間的具體位置。CPU 對內存的處理也是如此,如果沒有地址,CPU 就沒有辦法確定指定內存的具體位置,從而正確且快速的訪問到該內存空間。

2. 指針概念

  • 指針就是地址
  • 指針就是地址
  • 指針就是地址

重要的事情說三遍,我們必須明確的知道一件事,指針就是地址,可以理解為,指針是地址在C語言里的一種特殊叫法。

#include <stdio.h>
int main()
{
	int a = 10;
	int *p = &a;		// p是不是指針?
	printf("%p\n", p);  // %p 可以格式化輸出地址。
	return 0;
}

既然如此,上述代碼中 p 是不是指針呢?

我們說過指針就是地址,而 p 是不是地址?當然不是!地址是一個單獨的數(shù)據(jù),就像 int a = 10; 我們能說 a 就是 10 嗎?當然不能,10是一個整形常量,而 a 是一個整形變量,我們只能說 a 里面存放的值是 10,而不能簡單的說 a 就是 10。

同理,上述代碼中的 p 是一個變量,里面存放的是 a 的地址,我們稱它為指針變量。所以嚴格意義上來說,p 不是指針,而是指針變量。就像 a 不是整數(shù)10,而是整形變量一樣。

那么什么是指針呢,我們把這段代碼運行起來:

結果

沒錯,嚴格意義上來說,這段數(shù)字才真正的叫做指針 (地址值在計算機中一般以16進制顯示)。

3. 指針與指針變量

通過上面的講解,相信大家可以理解,指針是地址,是一個常量,而指針變量是一個變量,里面存放的是指針,這兩者之間有著本質區(qū)別。

看到這,相信有些同學就有疑問了:明明上課的時候,或者某些書上都將指針變量統(tǒng)稱為指針,就像 上例中的 p 平時都叫做指針,為什么現(xiàn)在又說 p 不是指針呢?

請大家注意,我說 p 不是指針是在嚴格意義上來說。

在日常使用中,我們經常把指針和指針變量混為一談。這似乎成為了一種習慣,甚至我在下面的表述中都可能將指針變量表述為指針。

結論是:我們在日常使用中,可以模糊指針與指針變量的概念,把指針變量統(tǒng)稱為指針,但是,我們心里必須清楚的意識到,指針和指針變量的本質區(qū)別,以及你叫的這個東西到底是指針還是指針變量。

二. Why(為什么要有指針)

經過上面的講解,相信這個問題就非常簡單了。指針就是地址,那么為什么要有地址?究其原因就是,為了提高查找效率

試想如果沒有地址的存在,我們寫下 int a = 10; 的時候,系統(tǒng)會自動在內存中開辟一4字節(jié)的空間存放10這個數(shù)值。而我們 CPU 在訪問的時候,由于沒有地址的存在只能在內存中一塊一塊的遍歷對比查找,而有了地址,內存就可以把地址值告訴 CPU,CPU 就可以根據(jù)地址值直接定位到該內存,大大提高了 CPU 訪問內存的效率與準確性。

三. How(如何使用指針)

1. 基本定義

指針變量的定義格式較簡單,只要在類型和變量名之間加上 * 操作符,就可以定義該類型的指針變量。

int *a = NULL;				//定義整形指針變量
double *d = NULL;			//定義雙精度浮點型指針變量
struct stdnt *ps = NULL;	//定義結構體指針變量

注: NULL 是指針變量初始化的時候常用的宏定義,稱其為空指針,本質為 0 值。

如果定義時不初始化,如:

int *a;

我們知道,變量如果不初始化,其中的值就是隨機值,也就是此時的指針變量 a 里面存放的是隨機值,如果此時訪問 a 變量就會以這個隨機值作為地址訪問對應的內存,這種操作是非法的。這種不初始化的指針我們稱之為野指針。

所以,為了避免野指針的出現(xiàn),我們在定義指針變量時盡量對其進行初始化。

2. 取地址操作符 &

我們使用指針就是使用地址,那么地址從何而來?我們可不可以這么寫代碼:

int *a = 0x11223344;

我們把 0x11223344 看做是地址值交給整形指針變量 a 來存儲。語法上沒有問題,但是這樣寫毫無意義!因為 0x11223344 這個地址對于我們來說是未知的,我們并沒有讀取和訪問的權限,這時候的 a 變量就相當于一個野指針。

事實上,我們的地址是由 & 取地址符取出來的。

#include <stdio.h>
int main()
{
	int a = 10;
	int *p = &a;
	return 0;
}

這里我們定義了整形變量 a,取地址符取出了 a 的地址并把它賦給整形指針變量 p。

這里就有一個疑問,a 是一個整形變量,占4字節(jié)的空間,而通過上面的介紹,我們知道內存的地址編號是以字節(jié)為單位的,這就意味著變量 a 其實存在4個地址,那么 &a 究竟取出的是哪一個地址呢?

上結論:

在C語言中,對任意類型的變量或者數(shù)組取地址,永遠取得是數(shù)值上最小的那個地址。

我們畫一下上面代碼的內存分布圖:

內存分布圖

假設從左向右地址依次升高,那么 p 里面存放的就是 a 變量4字節(jié)中地址最低的那個字節(jié)的地址。

3. 解引用操作符 *

我們上面說到,對變量取地址取得是地址最低的字節(jié)即首字節(jié)地址,那么我們如何通過這一個字節(jié)的地址訪問到原變量里的數(shù)據(jù)呢,這里就需要使用 * 操作符:

#include <stdio.h>
int main()
{
	int a = 10;
	int *p = &a;
	*p = 20;     // 等價于 a = 20
	printf("%d\n", *p);
	printf("%d\n", a);
	return 0;
}

看上面的代碼,我們把 a 變量的地址賦值給指針變量 p,通過 *p 即可訪問到該變量的內容。

那么 * 操作符到底是如何使用的呢,下面給出結論:

對指針解引用,就是指針指向的目標。

就像上例中 *p 是什么?*p 就是 a,*p 就是 a,*p就是a,重要的事情說三遍。所以,如果我改變 *p 的值,完全等價于直接改變 a 變量的值。

上面的代碼運行結果:

運行結果

那么,我們看下面這段代碼:

int main()
{
	int a = 0x11223344;
	int *p = &a;
	printf("0x%x\n", *(char*)p);
	return 0;
}

注意,我們說 *p 就是 a,但是,這里的 *p 被強制類型轉化為 char* 類型之后再進行的解引用操作,此時的 p 就是一個字符類型的指針,因為字符 char 類型在C中只占一個字節(jié),對其進行解引用只能訪問一個字節(jié)的內容。而我們說過 p 中存放的是 a 變量的首字節(jié)的地址,即 44 的地址 (至于為什么,請讀者自己了解大小端的內容),解引用就只訪問到了 44:

運行結果

4. 結構體指針

下面定義一個結構體:

typedef struct Student
{
	int age;
	char name[20];
	char sex[2];
}STD;

C語言中任何數(shù)據(jù)類型都有其對于的指針類型,結構體也不例外,如下:

int main()
{
	STD s = { 18, "張三", "男" };
	STD *ps = &s;		// 定義結構體指針
	printf("%s\n%d\n%s\n", s.name, s.age, s.sex);
}

我們定義一個結構體變量并初始化,通過 . 操作符可以很容易訪問到結構體的內容。那么我們如何通過結構體指針變量 ps 來訪問結構體的內容呢?

我們說過對指針解引用,就是指針指向的目標,那么請讀者思考,*ps 是什么呢?沒錯 *ps 就是 s變量,既然如此,我們就可以這么訪問:

	printf("%s\n%d\n%s\n", (*ps).name, (*ps).age, (*ps).sex);
	//注:因 . 操作符優(yōu)先級高于 * 操作符,故 (*ps) 必須加括號

結果

C語言可能認為這樣寫太麻煩,于是對于結構體指針我們可以使用 -> 操作符直接訪問到結構體的內容:

	printf("%s\n%d\n%s\n", ps->name, ps->age, ps->sex);

ps->name 寫法完全等價于 (*ps).name 這樣的寫法。
注:-> 操作符只在結構體指針變量訪問結構體成員時使用。

5. 多級指針

首先問大家一個問題,指針變量是不是變量?是變量。既然是變量,那么就有地址,我們依然可以把指針變量的地址存入一個新的變量,此變量我們可以稱為二級指針變量:

int a = 10;
int *pa = &a;
int **ppa = &pa;  // 定義二級指針變量
*ppa;  //等價于 pa
**ppa; //等價于 *pa,即等價于 a

二級指針大家不要看的多么神秘,所謂二級指針其實也就是一個指針變量,只不過里面存放的是另一個指針變量的地址而已。
既然如此,二級指針變量是不是變量呢?它能不能取地址存入另外一個三級指針變量呢?那么,三級指針變量是不是變量呢?… 如果你愿意,可以一直這么套娃下去~

6.指針變量的命名規(guī)范

之所以把這塊單獨分出來講,是因為對指針變量進行一個好的命名規(guī)范,不僅有利于提高代碼的可讀性,更有助于我們理解一些復雜的數(shù)據(jù)結構的代碼。

指針的命名習慣上在原變量名前加字母 p。 如定義整形變量 a ,其指針變量就命名為 pa, 定義結構體變量 std 其對應指針變量命名為 pstd,如果對 pstd 再次取地址存入二級指針變量,那么該二級指針變量就應該命名為 ppstd。

四. 我對指針的一些理解

都說指針是C語言的靈魂,那么指針的魅力究竟在何處?

我們看這段交換兩個數(shù)的代碼:

#include <stdio.h>
void Swap(int a, int b)
{
	int t = a;
	a = b;
	b = t;
}
int main()
{
	int a = 10;
	int b = 20;
	Swap(a, b);
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	return 0;
}

我們應該知道,這段代碼是無法完成 a, b 的交換的,因為 Swap() 里的 a, b 只不過是 main() 函數(shù)里 a, b 的一份拷貝。即,Swap() 函數(shù)里 a, b 的改變是不會影響 main() 函數(shù)的。這種傳參的方式我們稱之為傳值。

可是我們這么寫:

#include <stdio.h>
void Swap(int *pa, int *pb)
{
	int t = *pa;
	*pa = *pb;
	*pb = t;
}
int main()
{
	int a = 10;
	int b = 20;
	Swap(&a, &b);
	printf("a = %d\n", a);
	printf("b = %d\n", b);
	return 0;
}

main() 傳參時傳 a, b 的地址,Swap() 函數(shù)使用指針進行接收,內部使用解引用方式進行交換,我們就可以完成真正的交換功能。這種傳參的方式我們稱之為傳址。

我在一篇文章上看到這樣一個說法,可謂是對指針理解到了本質:

  • 傳值就是傳值。
  • 傳址就是傳值本身。

值和值本身兩個概念希望大家能夠深刻理解。

如同我們 Swap() 函數(shù)的第一種寫法,main() 函數(shù)僅僅是將 a, b 的值傳遞給了 Swap() 函數(shù)進行處理,可無論 Swap() 函數(shù)對其做什么樣的處理,也依然無法改變原來 a, b 的值。

而 Swap() 的第二種寫法,main() 函數(shù)傳的是 a, b 的地址,相當于傳的是 a, b 本身,這樣 Swap() 在進行處理時便可直接改變原來的 a, b 的值。

總結

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!

相關文章

  • C語言斷言函數(shù)assert()的學習筆記

    C語言斷言函數(shù)assert()的學習筆記

    在C語言庫函數(shù)中提供了一個輔助調試程序的小型庫,它是由assert()宏組成,本文就詳細的介紹了一下如何使用,感興趣的可以了解一下
    2021-11-11
  • C語言平衡二叉樹詳解

    C語言平衡二叉樹詳解

    這篇文章主要介紹了C語言平衡二叉樹的相關資料,需要的朋友可以參考下,小編覺得這篇文章寫的還不錯,希望能夠給你帶來幫助
    2021-11-11
  • C語言實現(xiàn)飛機游戲(進階版)的示例代碼

    C語言實現(xiàn)飛機游戲(進階版)的示例代碼

    在前文中,已經帶大家利用C語言實現(xiàn)了簡單的飛機游戲,但它還存在一些缺陷。因此,本文將給大家?guī)磉M階版的飛機游戲,需要的可以參考一下
    2022-10-10
  • C語言關于二叉樹中堆的創(chuàng)建和使用整理

    C語言關于二叉樹中堆的創(chuàng)建和使用整理

    大家好,這里是針對二叉樹中堆結構的順序儲存,整理出來一篇博客供我們一起復習和學習,如果文章中有理解不當?shù)牡胤?還希望朋友們在評論區(qū)指出,我們相互學習,共同進步
    2022-08-08
  • C++ 17標準正式發(fā)布! 更簡單地編寫和維護代碼

    C++ 17標準正式發(fā)布! 更簡單地編寫和維護代碼

    C++ 17 標準正式發(fā)布:終于能更簡單地編寫和維護代碼了!本文為大家介紹了C ++ 17 主要特性,感興趣的小伙伴們可以參考一下
    2017-12-12
  • OPENCV批量讀取圖片實現(xiàn)方法

    OPENCV批量讀取圖片實現(xiàn)方法

    下面小編就為大家?guī)硪黄狾PENCV批量讀取圖片實現(xiàn)方法。小編覺得挺不錯的。現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • C語言實現(xiàn)簡單餐飲管理與點餐系統(tǒng)

    C語言實現(xiàn)簡單餐飲管理與點餐系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)簡單餐飲管理與點餐系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++優(yōu)先隊列的使用小結

    C++優(yōu)先隊列的使用小結

    普通的隊列是一種先進先出的數(shù)據(jù)結構,元素在隊列尾追加,而從隊列頭刪除,在優(yōu)先隊列中,元素被賦予優(yōu)先級,本文主要介紹了C++優(yōu)先隊列的使用,感興趣的可以了解一下
    2023-11-11
  • C語言中.與->的區(qū)別詳細解析

    C語言中.與->的區(qū)別詳細解析

    這篇文章主要給大家介紹了關于C語言中.與->區(qū)別的相關資料,這雖然是個小問題,但有時候很容易讓人迷惑,因為有的時候用混淆了,程序編譯不通過,需要的朋友可以參考下
    2023-06-06
  • C++?測試框架GoogleTest入門介紹

    C++?測試框架GoogleTest入門介紹

    這篇文章主要為大家介紹了C++測試框架GoogleTest入門基礎,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04

最新評論

民丰县| 安图县| 伊吾县| 昆山市| 同江市| 和硕县| 天峻县| 兴宁市| 集贤县| 昂仁县| 普宁市| 永丰县| 沽源县| 紫金县| 寻甸| 高唐县| 仪陇县| 石楼县| 贵溪市| 武强县| 稷山县| 广宗县| 定安县| 永丰县| 金门县| 盈江县| 三明市| 洪雅县| 潍坊市| 上犹县| 天柱县| 成武县| 乐东| 辽中县| 金塔县| 伊吾县| 定安县| 襄樊市| 汕尾市| 杭锦旗| 昂仁县|