C++中的引用與C語(yǔ)言的指針的指針運(yùn)用
引言
其實(shí)早就想寫這個(gè)筆記了耕腾,尤其是看到越來越多同學(xué)上課不怎么聽講課后多次犯了這個(gè)錯(cuò)誤后。心里有點(diǎn)捉急,于是想動(dòng)筆了拴竹。網(wǎng)上關(guān)于C++引用類型的說明其實(shí)有很多,也很通熟易懂剧罩,這里推薦一篇文章栓拜。那么在這里,我將結(jié)合自己的知識(shí)儲(chǔ)備進(jìn)行更有針對(duì)性的講解,希望同學(xué)們能夠耐下心來看完幕与。敘述中或許有些不太嚴(yán)謹(jǐn)或是沒用更加精確的術(shù)語(yǔ)的地方亦或是有些許錯(cuò)誤挑势,希望讀者諒解并提出。
我的講解順序是:
變量的作用域->C語(yǔ)言的傳參規(guī)則及指針傳參的錯(cuò)誤認(rèn)識(shí)->C++的引用類型->兩種解決C語(yǔ)言沒有引用類型的方法
變量的作用域
關(guān)于這一點(diǎn)我簡(jiǎn)單講啦鸣,基本規(guī)則就是變量作用域就是所在的花括號(hào)所包含的范圍潮饱。如:
for(int i=0;i<MAX;i++){
......
}
則i的作用域就是這個(gè)for循環(huán)。所以:
#include <stdio.h>
int main(void){
for(int i=0;i<MAX;i++){
......
}
for(i=0;i<MAX;i++){
......
}
return 0;
}
編譯都會(huì)不通過诫给,因?yàn)閷?duì)于第二個(gè)for循環(huán)香拉,i是未定義的。你可以這樣簡(jiǎn)單理解:某個(gè)變量只在聲明時(shí)生成中狂,對(duì)所在花括號(hào)外面的語(yǔ)句塊沒有任何作用效果凫碌。等到執(zhí)行完到花括號(hào)末尾時(shí),變量就銷毀了胃榕。函數(shù)變量也是如此盛险,這里的函數(shù)里的變量指的是如下例
int Add(int a,int b){
int c;
c=a+b;
return c;
}
中的a、b勋又、c苦掘。當(dāng)程序執(zhí)行到調(diào)用Add函數(shù)時(shí),變量a赐写、b鸟蜡、c生成并相應(yīng)的占用一塊內(nèi)存。當(dāng)函數(shù)執(zhí)行完時(shí)挺邀,變量a揉忘、b、c就被銷毀了端铛。
C語(yǔ)言的傳參規(guī)則及指針傳參的錯(cuò)誤認(rèn)識(shí)
上個(gè)學(xué)期學(xué)習(xí)指針時(shí)泣矛,老師肯定大都舉過類似這樣的一個(gè)例子:
#include <stdio.h>
void Plus(int a){
a++;
}
void PPlus(int *a){
(*a)++;
}
int main(void){
int b=1;
Plus(b);
printf("%d\n",b);
PPlus(&b);
printf("%d\n",b);
return 0;
}
運(yùn)行結(jié)果為:
1
2
不知道你們當(dāng)時(shí)是怎樣想的,我只記得很驚訝禾蚕,一直在問自己?jiǎn)柺裁囱侥啵浚炕幌繛槭裁唇Y(jié)果不是
2
3
經(jīng)過老師的講解哗总,我大概知道了。
因?yàn)镻lus函數(shù)傳參是傳值調(diào)用倍试,函數(shù)里面的a變量和主函數(shù)里面的b變量是獨(dú)立讯屈,占有不同的內(nèi)存,只是變量所儲(chǔ)存的值相等都是1县习。于是乎你函數(shù)里面的操作只是對(duì)函數(shù)變量a的操作涮母,對(duì)主函數(shù)里面的b變量沒用任何影響谆趾。于是乎就可以理解為函數(shù)變量a就是主函數(shù)變量b的復(fù)制品,對(duì)復(fù)制品的操作對(duì)原變量沒有任何影響叛本。
但是指針傳參好像就不一樣了沪蓬,因?yàn)樗?strong>傳址調(diào)用。通過函數(shù)指針變量a可以根據(jù)地址進(jìn)行改變主函數(shù)變量b的值来候。
于是乎跷叉,指針的傳址調(diào)用便深入大腦。于是同學(xué)們就記住指針調(diào)用不用加取址符這個(gè)原則营搅。
確實(shí)性芬,在上個(gè)學(xué)期應(yīng)對(duì)大部分問題時(shí)這句話幾乎屢試不爽。但是一到這個(gè)學(xué)期學(xué)了數(shù)據(jù)結(jié)構(gòu)時(shí)(特指教材用C++但是老師要求作業(yè)要使用C)剧防,漏洞百出植锉。那么問題出在哪里呢?
推薦兩本書《C與指針》和《C陷阱與缺陷》峭拘,廖勇老師推薦的俊庇,據(jù)說是屬于C語(yǔ)言圣經(jīng)級(jí)別的那種。扯遠(yuǎn)了······《C與指針》書中指出:C語(yǔ)言的傳參規(guī)則很簡(jiǎn)單鸡挠,就是傳值調(diào)用辉饱。
數(shù)組參數(shù)的傳址調(diào)用看起來似乎和傳值調(diào)用相悖。但是拣展,此處其實(shí)并無矛盾之處——數(shù)組名的值實(shí)際上是一個(gè)指針彭沼,傳遞給函數(shù)的就是這個(gè)指針的一份拷貝。
什么意思呢备埃?我們用代碼并結(jié)合圖畫來說明:
#include <stdio.h>
#include <string.h>
#define M 5
void Func(int *p){
p[3]++;
return;
}
int main(void){
int a[M];
memset(a,0,sizeof(int)*M);
Func(a);
for(int i=0;i<M;i++){
printf("%d ",a[i]);
}
return 0;
}
如前文我所講姓惑,傳值調(diào)用就是說形參(這里例子中指p)是實(shí)參(這里例子指a)的復(fù)制品,而指針作為參數(shù)其實(shí)并不違背傳值調(diào)用的原則按脚。p在調(diào)用函數(shù)時(shí)生成于毙,在函數(shù)結(jié)束時(shí)銷毀。但是卻確確實(shí)實(shí)對(duì)主函數(shù)里的數(shù)組a的所包含的元素的值進(jìn)行了改變辅搬。
其實(shí)唯沮,我比較喜歡這樣理解。比如第一個(gè)例子:
#include <stdio.h>
void Plus(int a){
a++;
}
void PPlus(int *a){
(*a)++;
}
int main(void){
int b=1;
Plus(b);
printf("%d\n",b);
PPlus(&b);
printf("%d\n",b);
return 0;
}
調(diào)用Plus函數(shù)時(shí)首先執(zhí)行了
int a=b;
語(yǔ)句堪遂。而調(diào)用PPlus函數(shù)時(shí)首先執(zhí)行了
int *a=&b;
語(yǔ)句介蛉。
例子
#include <stdio.h>
#include <string.h>
#define M 5
void Func(int *p){
p[3]++;
return;
}
int main(void){
int a[M];
memset(a,0,sizeof(int)*M);
Func(a);
for(int i=0;i<M;i++){
printf("%d ",a[i]);
}
return 0;
}
當(dāng)調(diào)用Func函數(shù)時(shí)首先執(zhí)行了
int *p=a;
語(yǔ)句。
理解了以上部分溶褪,你就很容易理解這個(gè)學(xué)期學(xué)數(shù)據(jù)結(jié)構(gòu)時(shí)你一直不理解的問題了币旧。比如:
......
void List_Init(ListPtr p) {
p = (ListPtr)malloc(sizeof(ListNode));
if (p == NULL) {
exit(0);
};
p->next = NULL;
return;
}
......
int main(void){
ListPtr head;
List_Init(head);
//printf("OK\n");
List_Insert(head,......);
//printf("OK\n");
......
return 0;
}
你調(diào)試時(shí)發(fā)現(xiàn)程序在打印出第一個(gè)OK后就停止工作,于是就認(rèn)為時(shí)List_Insert函數(shù)有問題竿滨,于是絞盡腦汁結(jié)果還是找不出問題來佳恬,然后你就很煩,然后你就不想做了于游,然后你就希望去抄一下別人代碼交上去得了······其實(shí)你的錯(cuò)誤出在初始化函數(shù)······
如此真相大白毁葱。
C++的引用類型
前面提到的文章講的就挺好的(戳這里)。
因?yàn)镃++不是C贰剥,所以傳參規(guī)則就不是僅限于傳值調(diào)用倾剿。我們結(jié)合具體實(shí)例講解:
#include <iostream>
using namespace std;
void PPPlus(int &a){
a++;
return;
}
int main(void){
int b=1;
PPPlus(b);
cout<<b<<endl;
return 0;
}
運(yùn)行結(jié)果:
2
其中調(diào)用PPPlus函數(shù)時(shí)發(fā)生如圖所示情況:
如果還沒理解引用,再看第二個(gè)例子:
#include <iostream>
using namespace std;
void PPPPlus(int *&a){
(*a)++;
return;
}
int main(void){
int b=1;
int *c=&b;
PPPPlus(c);
cout<<b<<endl;
return 0;
}
運(yùn)行結(jié)果:
2
其中調(diào)用PPPPlus函數(shù)時(shí)發(fā)生如圖所示情況:
綜合兩個(gè)例子蚌成,我們可以清晰地看到前痘,所謂引用就是不像C那樣進(jìn)行復(fù)制,引用變量就是原變量地一個(gè)別名担忧,對(duì)引用變量的操作就是對(duì)原變量的操作芹缔。
于是乎,我們書上就用引用變量這個(gè)方法實(shí)現(xiàn)head的初始化:
......
void List_Init(ListPtr &p) {
p = (ListPtr)malloc(sizeof(ListNode));
if (p == NULL) {
exit(0);
};
p->next = NULL;
return;
}
......
int main(void){
ListPtr head;
List_Init(head);
//printf("OK\n");
List_Insert(head,......);
//printf("OK\n");
......
return 0;
}
兩種解決C語(yǔ)言沒有引用類型的方法
但是不幸的是瓶盛,老師要求我們用C來實(shí)現(xiàn)所有題目最欠。那么有什么辦法呢?下面直接給出代碼:
代碼1
......
ListPtr List_Init() {
ListPtr p ;
p = (ListPtr)malloc(sizeof(ListNode));
if (p == NULL) {
exit(0);
};
p->next = NULL;
return p;
}
......
int main(void){
ListPtr head;
head=List_Init();
//printf("OK\n");
List_Insert(head,......);
//printf("OK\n");
......
return 0;
}
代碼2
這個(gè)方法就有點(diǎn)技巧了惩猫,不過我比較喜歡用這種方法芝硬,看似比較復(fù)雜但是好處還是挺多的。
void List_Init(ListPtr **p) {
(*p) = (ListPtr)malloc(sizeof(ListNode));
if ((*p) == NULL) {
exit(0);
};
(*p)->next = NULL;
return;
}
......
int main(void){
ListPtr head;
List_Init(&head);
//printf("OK\n");
List_Insert(head,......);
//printf("OK\n");
......
return 0;
}
之所以喜歡用這個(gè)方法轧房,是因?yàn)檫@樣就保留了函數(shù)的返回值類型拌阴,我們可以令返回值為你想要的類型,比如bool或者status奶镶,以便后續(xù)調(diào)試迟赃。
其實(shí)指針的指針運(yùn)用可以完美去除帶頭節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。你們難道不覺得帶頭結(jié)點(diǎn)真的看起來很不舒服嗎厂镇?反正我就是有強(qiáng)迫癥捺氢,要我寫帶頭節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)我真的寫不下去。當(dāng)然并不是說頭結(jié)點(diǎn)沒一點(diǎn)好處剪撬,至少閱讀起來比較易懂摄乒。
后記
說句老實(shí)話,我覺得現(xiàn)在我們學(xué)校至少我們學(xué)院的同學(xué)們充滿了一種戾氣残黑。不怎么好描述馍佑,就是透露出一種很喪的心態(tài)。大學(xué)四年梨水,“革命”尚未結(jié)束拭荤,同志們?nèi)孕枧Π。?/p>
又到了推送小姐姐的時(shí)候了,送上新晉女神李惠利和一部劇《請(qǐng)回答1988》疫诽。