點進這篇文章的朋友們,如果對「指針」沒有概念,那么請面壁思過。
你不是一個正統(tǒng)的程序員,你是野路子,是faker,在技術(shù)這條路上注定走不遠。
?閑話少述,正文開始。
1、從操作符說起
要看「引用」和「指針」的區(qū)別,首先要看操作符。
在c/c++中,指針相關(guān)的操作符有3個:& -> *
在Java中,引用相關(guān)的操作符有1個:.
What,引用就一個操作符???那我們就來看下,操作符各有什么作用
注:指針使用結(jié)構(gòu)體來舉例,便于和引用的對象來比較
1.1、C/C++中指針操作符 & -> * 的作用
定義一個結(jié)構(gòu)體和變量
-
?typedef stuct {
? ? ?int sex;
? ? ?int age;
?} student_t;
??
?student_t stu1 = {1, 20};
操作符&,將數(shù)據(jù)的地址讀取到一個指針:
- ?int* p_addr = &stu1; // 創(chuàng)建一個指針p_addr,p_addr存儲了stu1的地址
操作符->,讀/寫一個指針所指向結(jié)構(gòu)體地址的成員數(shù)據(jù)
-
?int age = p_addr->age;
?p_addr->age = 44;
操作符*,讀/寫一個指針中地址的數(shù)據(jù):
-
?student_t stu2 = *p_addr; // 將p_addr地址的數(shù)據(jù)讀取到stu2
?*p_addr = {2, 8}; // 將數(shù)據(jù)寫入p_addr地址
注:c++中也有引用,但不在本文討論范圍內(nèi)
1.2、Java中引用操作符 . 的使用
定義一個類和對象
-
?public class Student{
? ? ?public Integer sex;
? ? ?public Integer age;
? ? ?
? ? ?Student(Integer s, Integer a) {
? ? ? ? ?sex = s;
? ? ? ? ?age = a;
? ? ?}
?}
?Student stu1 = new Student(1, 20); // 創(chuàng)建一個引用stu1,stu1中存儲的不是對象的數(shù)據(jù),而是是對象的地址,也即引用
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // stu1存放在stack,對象存放在heap
將數(shù)據(jù)的地址讀取到一個引用
- ?Student p_addr = stu1; // 創(chuàng)建一個引用p_addr,把stu1中存儲的對象的地址,賦給p_addr
讀/寫一個引用所指向對象地址的成員數(shù)據(jù)
-
?Integer age = stu.age;
?stu.age = 44;
?注:Java只有引用,沒有指針,而引用弱化了地址和數(shù)據(jù)的概念,所以程序員們更要深刻理解引用的本質(zhì),寫出更健壯的代碼。
如此看來,C/C++指針的操作符 * 能干的活,Java的引用干不了,也就是指針能直接對地址的數(shù)據(jù)進行讀/寫,引用則不能。
那咱就來看看,具體那些活是指針能干,引用干不了。。。
?
?
2、指針能干,引用干不了的活~
2.1、指針可以指向任意一個地址,引用只能指向一個對象
指針可以給用操作符&給其一個數(shù)據(jù)的地址,也可以直接給其一個地址,甚至空地址
-
?student_t* p_addr = &stu1;
?student_t* p_addr = 0x12000;
?student_t* p_addr = NULL;
指針可以對地址進行加減操作,從而修改相鄰地址的數(shù)據(jù),比如修改一個數(shù)組
-
?int data[4] = {1,2,3,4};
?int* p_addr = data;
?*p_addr = 6;
?p_addr += 1;
?*p_addr = 7;
?p_addr += 1;
?*p_addr = 8;
?p_addr += 1;
?*p_addr = 9;
?// 此時數(shù)組內(nèi)數(shù)據(jù)為:{6,7,8,9}
引用只能指向一個對象,不能直接給其一個地址,也不能空引用
-
?Student stu = new Student();
?Student stu = 0x12000; // 對不起,編譯不通過。。。
2.1.1、有什么用?
-
在底層驅(qū)動開發(fā)時,寄存器的地址是固定的,
- 想要修改寄存器的數(shù)據(jù),需創(chuàng)建一個指針,把寄存器地址賦給指針,然后去修改寄存器。
-
?// 點亮一個LED
?int* p_led_addr = 0x1233; // LED寄存器地址是0x1233,將其賦給指針p_led_addr
?*p_led_addr = 1; // LED亮
?*p_led_addr = 0; // LED滅
?int state = *p_led_addr; // 讀取LED的亮滅狀態(tài) - 修改連續(xù)地址的多個寄存器
-
?// 點亮多個LED
?int* p_led_addr = 0x1233; // LED寄存器地址是0x1233,將其賦給指針p_led_addr
?*p_led_addr = 1; // LED亮
?*(p_led_addr+1) = 1; // LED2亮
?*(p_led_addr+2) = 1; // LED3亮
- 顯而易見,引用能不能干???干不了!
?
2.2、指針可以隨意修改所指向地址的數(shù)據(jù)
- 指針大法
-
?student_t* p_addr = &stu1; // 創(chuàng)建Student類型的指針,指向一個stu1
?*p_addr = 24242; // 將24242寫入stu1的地址 - 引用只能修改所指向?qū)ο蟮?strong>固定成員,或者通過所指向?qū)ο筇峁┑?strong>固定方法來修改數(shù)據(jù)
- 有什么用?
好像沒啥用。。。
3、指針的缺陷
3.1、野指針
- 定義
指針在創(chuàng)建時,未初始化,此時指向的地址是隨機的!此時指針讀寫,破壞程序運行!
指針?biāo)赶虻刂返臄?shù)據(jù)已經(jīng)被釋放,此時指針讀寫,則破壞程序運行!
- 原因
指針可以指向任意一個地址;而引用必須指向一個確定的對象
指針不能自動解除指向;而引用在指向的對象銷毀時,會自動解引用
- 后果
程序奔潰、不能按預(yù)期運行、代碼漏洞
3.2、C語言強制類型轉(zhuǎn)換造成的內(nèi)存誤修改
- 定義
將類型A的變量s,強制轉(zhuǎn)換成類型B,然后將其s的地址賦給指向類型B的指針p,對指針p讀寫
此時類型B的數(shù)據(jù)結(jié)構(gòu)可能并不兼容類型A,導(dǎo)致對變量s的誤修改
- 原因
C語言強制類型轉(zhuǎn)換的不嚴格檢查,過于粗魯
這是C++為什么要引入四個轉(zhuǎn)換符的原因
- 后果
程序奔潰、不能按預(yù)期運行、代碼漏洞
4、總結(jié)
4.1、引用能做到的,指針都能無損的做到——反之則不行
-
指針的操作符 * 能干的活,引用干不了,也就是指針能直接對地址的數(shù)據(jù)進行讀寫,引用則不能
- 指針可以指向任意一個地址(甚至空地址),引用只能指向一個對象(不可空引用)
- 指針可以對地址進行加減操作,從而修改相鄰地址的數(shù)據(jù),比如修改一個數(shù)組
- 指針不能自動解除指向;而引用在指向的對象銷毀時,會自動解引用
-
指針可以隨意修改所指向地址的數(shù)據(jù)
- 引用只能修改所指向?qū)ο蟮?strong>固定成員,或者通過所指向?qū)ο筇峁┑?strong>固定方法來修改數(shù)據(jù)
4.2、指針的靈活帶來缺陷,引用的不靈活帶來安全
引用避免了對地址的直接讀寫,增強了內(nèi)存操作的規(guī)范,從而增強了語言內(nèi)存安全性,降低了對開發(fā)者的要求。
指針和引用,各有各的用途,我們理解本質(zhì)后,在不同的場景選擇合適的工具即可!
??
4.3、題外話:C++引用和Java引用的區(qū)別
C++中一個引用指向的地址不會改變,改變的是指向地址的內(nèi)容,然而Java中引用指向的地址在變!!
如果非要對比著看,那么Java中的“引用”倒是和C/C++的指針更像一些,和C++的“引用”很不一樣。
java去除指針概念,就用引用羅...
你看 java:
?
A a = new A(1);?
A b = new A(2);?
b = a;?
沒有問題,a 和 b引用同一個對象A(2),原來的A(1)成為沒有被引用的對象。 垃圾回收機制會在之后的某個時刻把A(1)干掉。
而C++則不然。C++的引用就語義上說是“別名”【本質(zhì)是個const指針,又叫指針常量】,而并不是指針的另一種用法:
?
A a = A(1);?
A b = A(2);?
A& c = b; //c 是 b的別名?
c = a; //并不是 c 引用 a,而是拷貝操作 c.operator= ( a )?
就語言機制來說,java的引用是用來管理和命名對象;
而,C++的引用機制是很純粹的,就是別名而已,一旦定義就無法修改,即無法再指向其他變量。
每種語言的特性都是整體的有機部分。
我們知道, java的引用機制是一個很復(fù)雜的機制。他必須區(qū)分“基本對象”和“復(fù)合對象”,你可以想象一下,如果其中沒有基本對象,那么我們?nèi)绾瓮瓿蓪ο蟮膹?fù)制? 唯一的解決方案是提供兩個等于號,或者一律用構(gòu)造函數(shù).... 但是綜合來看,他和垃圾回收形成了相當(dāng)完美的組合方案。
而C++ 的引用機制為運算符重載提供了大幅度的支持。C++ 的引用是用類“模擬”基本對象的根本要求。 如果C++使用java那種引用,那么原本漂亮的 operator[]、 proxy class 等就很難實現(xiàn)了。 更進一步, C++ 的運算符重載對 C++ 的模版機制提供了強力的支持
審核編輯:湯梓紅
評論
查看更多