一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - C++指針與數組:指針詳解

C++指針與數組:指針詳解

2021-12-31 15:13追道者 C/C++

本文從初學者的角度,深入淺出地講解C++中的指針、數組指針,對最常混淆的引用傳遞、值傳遞和指針傳遞做了區處,需要的朋友可以參考下

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

 

一. What(什么是指針)

1. 地址初了解

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

什么是地址呢?

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

舉個形象的例子:

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

為什么要有地址?

通過上面的例子,我們很容易了解到地址存在的意義。試想,如果宿舍樓里的房間都沒有門牌號,我們就很難描述一指定房間的具體位置。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 是不是地址?當然不是!地址是一個單獨的數據,就像 int a = 10; 我們能說 a 就是 10 嗎?當然不能,10是一個整形常量,而 a 是一個整形變量,我們只能說 a 里面存放的值是 10,而不能簡單的說 a 就是 10。

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

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

C++指針與數組:指針詳解

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

3. 指針與指針變量

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

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

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

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

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

 

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

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

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

 

三. How(如何使用指針)

1. 基本定義

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

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

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

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

int *a;

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

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

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字節的空間,而通過上面的介紹,我們知道內存的地址編號是以字節為單位的,這就意味著變量 a 其實存在4個地址,那么 &a 究竟取出的是哪一個地址呢?

上結論:

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

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

C++指針與數組:指針詳解

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

3. 解引用操作符 *

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

#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 變量的值。

上面的代碼運行結果:

C++指針與數組:指針詳解

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

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

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

C++指針與數組:指針詳解

4. 結構體指針

下面定義一個結構體:

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

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

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);
	//注:因 . 操作符優先級高于 * 操作符,故 (*ps) 必須加括號

C++指針與數組:指針詳解

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.指針變量的命名規范

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

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

 

四. 我對指針的一些理解

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

我們看這段交換兩個數的代碼:

#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() 函數里 a, b 的一份拷貝。即,Swap() 函數里 a, b 的改變是不會影響 main() 函數的。這種傳參的方式我們稱之為傳值。

可是我們這么寫:

#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() 函數使用指針進行接收,內部使用解引用方式進行交換,我們就可以完成真正的交換功能。這種傳參的方式我們稱之為傳址。

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

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

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

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

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

 

總結

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

原文鏈接:https://blog.csdn.net/qq_55135139/article/details/120151642

延伸 · 閱讀

精彩推薦
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
主站蜘蛛池模板: 精品国产一区二区三区国产馆 | 成人精品网| 极品 女神校花 露脸91 | 四虎现在的网址入口2022 | 无码一区二区三区视频 | 国产成人亚洲精品一区二区在线看 | 2020最新版的ab片 | 成年性香蕉漫画在线观看 | 国产欧美国产综合第一区 | 惩罚美女妲己的尤老师 | 精东影业传媒全部作品 | eeuss18影院www国产| 午夜国产精品视频 | 成人免费视频一区二区 | 韩国三级在线观看 完整版 韩国三级视频网站 | 777奇米影视一区二区三区 | 韩国三级做爰 | 久久精品热在线观看30 | 日本xxx在线观看免费播放 | 亚洲第五色综合网啪啪 | 青青草原国产一区二区 | 69人成网站色www | 4虎影院永久地址www | 亚洲第五色综合网啪啪 | 欧美日韩国产精品va | narutotsunade全彩雏田 | 亚洲国产精久久久久久久 | 国产色视频网站 | 无人在线高清观看 | 国亚洲欧美日韩精品 | 网站视频免费 | 全黄一级裸片视频免费 | 麻豆小视频在线观看 | 国产精品亚洲一区二区久久 | 日韩日日日 | 精品亚洲欧美中文字幕在线看 | 极品妖艳许清赵丽全文免费阅读 | 久久精品视频在线看 | 亚洲男女在线 | 99爱免费 | www.伊人|