網站首頁 編程語言 正文
一、引用的意義
- 引用作為變量別名而存在,因此在一些場合可以代替指針
- 引用相對于指針來說具有更好的可讀性和實用性
下面通過代碼來進行說明,在C語言中,可以這么寫:
#include <stdio.h>
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
int main()
{
int a = 1;
int b = 2;
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
下面為輸出結果,可以看到a,b被交換。
若采用C++用的引用,則采用下面的代碼:
#include <stdio.h>
void swap(int& a, int& b)
{
int t = a;
a = b;
b = t;
}
int main()
{
int a = 1;
int b = 2;
swap(a, b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
下面為輸出結果,需要注意的是,引用作為函數的形參時,不需要進行初始化,初始化發生在函數調用的時候(形參一旦被初始化后,就代表兩個具體的外部變量)。
二、特殊的引用
const 引用
- 在C++中可以聲明const 引用
- const Type& name = var ;
- const 引用讓變量擁有只讀屬性
- 當使用常量對const引用進行初始化時,C++編譯器會為常量值分配空間,并將引用名作為這段空間的別名
所以上面那段代碼,b = 5 是不正確的,因為 b 已經是只讀變量了,但是依舊可以通過指針改變這個只讀變量的值。
結論:使用常量對const 引用初始化后將生成一個只讀變量
下面看一段代碼,加深理解:
#include <stdio.h>
void Example()
{
printf("Example:\n");
int a = 4;
const int& b = a;
int* p = (int*)&b;
//b = 5;
*p = 5;
printf("a = %d\n", a);
printf("b = %d\n", b);
}
void Demo()
{
printf("Demo:\n");
const int& c = 1;
int* p = (int*)&c;
//c = 5;
*p = 5;
printf("c = %d\n", c);
}
int main(int argc, char *argv[])
{
Example();
printf("\n");
Demo();
return 0;
}
下面為輸出結果:
如果把那兩行(b = 5,c = 5)取消注釋,則就會輸出下面結果,編譯器會報 b 和 c 都是只讀變量的錯誤。
三、引用是否占用存儲空間
下面看一段代碼:
#include <stdio.h>
struct TRef
{
char& r;
};
int main(int argc, char *argv[])
{
char c = 'c';
char& rc = c;
TRef ref = { c };
printf("sizeof(char&) = %d\n", sizeof(char&)); //char類型占用一個字節 1
printf("sizeof(rc) = %d\n", sizeof(rc)); //sizeof(c) => 1
printf("sizeof(TRef) = %d\n", sizeof(TRef)); //?
printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); //sizeof(c) => 1
return 0;
}
下面為輸出結果,可以看到sizeof(TRef)占用的內存空間為4,我們知道指針占用的內存空間為4,那么指針和引用到底有什么關系呢?第4節來分析。
四、引用的本質
引用在C++中的內部實現是一個指針常量
注意:
- C++編譯器在編譯過程中用指針常量作為引用的內部實現,因此引用所占用的空間大小與指針相同。
- 從使用的角度,引用只是一個別名,C++為了實用性而隱藏了引用的存儲空間這一細節。
通過下面的代碼,也可以很好的理解引用所占的字節數:
#include <stdio.h>
struct TRef
{
char* before;
char& ref;
char* after;
};
int main(int argc, char* argv[])
{
char a = 'a';
char& b = a;
char c = 'c';
TRef r = {&a, b, &c};
printf("sizeof(r) = %d\n", sizeof(r));
printf("sizeof(r.before) = %d\n", sizeof(r.before));
printf("sizeof(r.after) = %d\n", sizeof(r.after));
printf("&r.before = %p\n", &r.before);
printf("&r.after = %p\n", &r.after);
return 0;
}
下面為輸出結果,可以看到結構體占用12個字節,before 和 after 指針各占用4個字節,所以 ref 引用當然也占用4個字節,通過 after 的起始內存地址減上 before 的起始內存地址得8,而 before 指針占用4個字節,從這個層面也能知道 ref 引用占用4個字節。
為了深入理解引用的本質,可以在 Visual Studio 2012 中進行反匯編,如下圖,現在return 0那里打個斷點,然后點擊本地 Windows 調試器,開始執行代碼。
執行完代碼后,鼠標右擊空白區域,選擇轉到反匯編。
下面看一下反匯編的部分代碼,主要看引用那部分的匯編代碼,lea eax,[a] 表示取a的地址,存到 eax 寄存器中,mov dword ptr [b],eax表示把a 的地址保存到 b 所對應的4個內存空間里面去。可以這么說,引用的內部實現就是指針,所以引用占用內存空間,且占用內存空間大小和指針一樣。
五、引用的注意事項
C++中的引用旨在大多數的情況下代替指針
- 功能性:可以滿足多數需要使用指針的場合
- 安全性∶可以避開由于指針操作不當而帶來的內存錯誤
- 操作性∶簡單易用,又不失功能強大
下面通過一個函數返回引用,介紹一下引用的注意事項。
#include <stdio.h>
int& demo() //從內部實現來看,想返回一個指針 int* const
{
int d = 0;
printf("demo: d = %d\n", d);
return d; //本質上,相當于 return &d
}
int& func()
{
static int s = 0;
printf("func: s = %d\n", s);
return s; //本質上,相當于 return &s
}
int main(int argc, char* argv[])
{
int& rd = demo();
int& rs = func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
rd = 10;
rs = 11;
demo();
func();
printf("\n");
printf("main: rd = %d\n", rd);
printf("main: rs = %d\n", rs);
printf("\n");
return 0;
}
下面為輸出結果,可以看到編譯的時候開始發出警告說不能返回局部變量,如果繼續運行,可以看到 rd = 9658356,rd 為 d 的別名,按理說應該輸出 0 的,為什么輸出9658356 呢?這個因為 rd 所代表的的變量在 demo 函數調用返回的時候被摧毀了,其所代表的是一個不存在的變量,所以 rd 沒有意義了。
引用中必須遵守的規則:不要返回局部變量的引用。 如果局部變量是靜態的,則可以。因為靜態局部變量的存儲區是全局的存儲區,所以它的空間不會因為函數的返回而被摧毀。
六、小結
- 引用作為變量別名而存在旨在代替指針
- const 引用可以使得變量具有只讀屬性
- 引用在編譯器內部使用指針常量實現
- 引用的最終本質為指針
- 引用可以盡可能的避開內存錯誤
原文鏈接:https://blog.csdn.net/weixin_43129713/article/details/120925975
相關推薦
- 2022-08-11 boost.asio框架系列之調度器io_service_C 語言
- 2022-04-14 詳細聊聊C#的并發機制優秀在哪_C#教程
- 2022-11-14 Python?查看數據類型與格式_python
- 2022-05-01 Entity?Framework系統架構與原理介紹_基礎應用
- 2022-05-27 C++?動態規劃算法使用分析_C 語言
- 2022-05-14 Centos8安裝docker報錯(錯誤提示:All?mirrors?were?tried)的問題_
- 2022-03-16 Oracle表空間管理和用戶管理介紹(oracle表空間與用戶的關系)
- 2022-05-25 在Windows平臺安裝Jenkins_win服務器
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支