lecture1-4:通用swap函數(shù)

課程地址
????前4個(gè)lecture主要就講了指針肮帐,不同類型之間轉(zhuǎn)換時(shí)的內(nèi)存是如何變化的。下面將通過描述通用swap函數(shù)的執(zhí)行過程記錄下我學(xué)習(xí)課程的收獲。
????顧名思義翘骂,swap函數(shù)是將傳入的兩個(gè)對(duì)象的值進(jìn)行交換棋弥,在c++中核偿,有現(xiàn)成的使用模板實(shí)現(xiàn)的swap函數(shù)可以直接調(diào)用,當(dāng)然自己實(shí)現(xiàn)一個(gè)也不難顽染,使用引用可以很方便地實(shí)現(xiàn)對(duì)象之間內(nèi)容的交換宪祥,但是在c中,就只能使用指針實(shí)現(xiàn)家乘。話不多說蝗羊,直接開始吧。

版本1.0
void swap(int *pa,int *pb)
{
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

接下來我們來看看調(diào)用函數(shù)時(shí)到底發(fā)生了什么仁锯,首先我們應(yīng)該明白的是傳入函數(shù)的參數(shù)是什么耀找,兩個(gè)指針,指針是什么?指針本質(zhì)上可以理解為存地址的變量(你心里可能會(huì)產(chǎn)生疑問:既然指針都是存地址野芒,那為什么指針還有類型之分蓄愁,難道地址還有不同的類型?狞悲,后面會(huì)解釋這個(gè)問題)撮抓,它和int,double類型的變量本質(zhì)上其實(shí)沒有什么區(qū)別摇锋,都是變量丹拯,都用來存儲(chǔ)信息,但是荸恕,為什么指針能讓人望而生畏乖酬?慢慢看下去你就會(huì)發(fā)現(xiàn)它的獨(dú)特之處。
下面我們通過圖來理解下swap 1.0函數(shù)里到底發(fā)生了什么融求,

swap1.0.png

????a,b表示兩個(gè)想要被交換的int類型的數(shù)咬像,pa,pb表示兩個(gè)指針變量生宛,這里為什么使用箭頭將pa县昂,pb指向a,b的起始地址陷舅,因?yàn)閜a七芭,pb的值,其實(shí)就是a蔑赘,b變量在內(nèi)存空間的起始地址狸驳。在函數(shù)中int temp = *pa,我們定義了一個(gè)變量temp缩赛,將pa指針指向的值取出(可以理解為先將pa變量中存儲(chǔ)的地址取出耙箍,再用該地址去取值,即得到圖中的a的值)酥馍,這個(gè)過程也叫解引用辩昆,存入temp中,然后同樣將pb指向的值取出旨袒,放到pa指向的值中汁针,最后將變量temp中存的值存入pb指向的值中,也就是下圖中的1砚尽,2施无,3步:
swap1.0.png

????這是1.0版的swap,理解了指針的含義函數(shù)的執(zhí)行過程還是不難理解必孤。我們發(fā)現(xiàn)猾骡,這個(gè)函數(shù)它只能用來交換int類型的值,如果我想交換兩個(gè)float類型的數(shù),怎么辦兴想,沒辦法只能再寫一個(gè)一樣的函數(shù)幢哨,將函數(shù)中出現(xiàn)的int全都變?yōu)閒loat,但是可以看出這種解決方式并不優(yōu)雅嫂便,會(huì)使得代碼重復(fù)累贅捞镰,所以我們換一種方式來解決這個(gè)問題。

swap2.0
void swap(void *vp1,void *vp2,int size)
{
    int buffer[size];
    memcpy(buffer,vp1,size);
    memcpy(vp1,vp2,size);
    memcpy(vp2,buffer,size);
}

????為了解決我們swap1.0中的存在的問題毙替,我們需要一個(gè)能交換任意類型數(shù)據(jù)的函數(shù)岸售,所以這次我們不能將傳入的參數(shù)的類型限制為特定的類型,而是使用void*類型蔚龙,即可以傳入任意類型的指針,最后一個(gè)參數(shù)是一個(gè)int類型的size映胁,這個(gè)有什么用?后面會(huì)解釋木羹。回想一下解孙,之前swap1.0的實(shí)現(xiàn)思路是聲明一個(gè)變量坑填,用于臨時(shí)存儲(chǔ)值,從而交換兩個(gè)指針指向的值弛姜,在swap2.0中脐瑰,我們將傳入的參數(shù)改為了void*,難道不能像swap1.0那樣直接定義個(gè)臨時(shí)變量實(shí)現(xiàn)值的交換嗎廷臼?答案是不能苍在,為什么?原因主要有以下兩點(diǎn):
1.不能聲明void類型的變量
2.在swap1.0中荠商,我們傳入的參數(shù)是int*類型的指針pa寂恬,當(dāng)我們解引用時(shí),編譯器知道這個(gè)指針是int*型(即知道它里面存儲(chǔ)的是一個(gè)int類型變量的地址)莱没,再一次強(qiáng)調(diào)指針類型的變量存的就是地址初肉,所以解引用時(shí)它將pa中存的地址取出,作為起始地址饰躲,從該地址開始接著后面取4個(gè)字節(jié)(int類型的值使用4個(gè)字節(jié)存儲(chǔ))牙咏,然后將其解釋為一個(gè)int類型的數(shù)值,這樣我們就得到的pa解引用的值嘹裂。但是在swap2.0中妄壶,指針沒有了類型,我們?cè)谌〕龅刂泛蠹睦牵瑹o法知道應(yīng)該取出該地址后的幾個(gè)字節(jié)盯拱,所以不能使用swap1.0的思路實(shí)現(xiàn)swap2.0。
????上面的原因2也回答了之前的問題,為什么都是存地址狡逢,指針卻有類型之分宁舰,原因就在于有類型的指針在解引用時(shí)指針的類型能讓編譯器知道應(yīng)當(dāng)取出幾個(gè)字節(jié),知道應(yīng)當(dāng)取出幾個(gè)字節(jié)后我們就能使用memcpy函數(shù)實(shí)現(xiàn)內(nèi)存間交換奢浑。
????目前為止我們的swap函數(shù)通用性是有了蛮艰,但是調(diào)用時(shí)也應(yīng)當(dāng)時(shí)刻注意,要交換的到底是什么數(shù)據(jù)雀彼,當(dāng)你將它用于交換字符串變量時(shí)壤蚜,可能會(huì)出現(xiàn)錯(cuò)誤(可以先自己嘗試寫一下交換字符串變量的函數(shù)調(diào)用再看下面的解釋)。

char * husband = "husband";
char * wife = "wife";
swap2(husband,wife,sizeof(char*));

可能一開始會(huì)寫出這樣的代碼徊哑,并且輸出結(jié)果可能就是你想要的袜刷,然后你覺得就是這樣的,但是這段代碼從邏輯上就是錯(cuò)的莺丑,下面我們看看是為什么可能輸出對(duì)的結(jié)果著蟹,以及為什么它是錯(cuò)的。
一開始可能覺得字符串指針存的就是字符串首地址梢莽,我們想交換字符串的話直接傳入字符串的首地址萧豆,傳入字符串長(zhǎng)度,swap2就能實(shí)現(xiàn)數(shù)據(jù)交換昏名,但是這樣的話交換的內(nèi)容是什么涮雷,是字符串,這里有一個(gè)問題是直接交換字符串所在的內(nèi)存的話轻局,我們?nèi)绾未_定交換的數(shù)據(jù)塊的大小洪鸭,如果兩個(gè)字符串長(zhǎng)度不一樣如何處理?所以這樣是行不通的仑扑,但是為什么這樣可能得到正確的結(jié)果卿嘲,因?yàn)槟銈魅氲膕izeof(char*)的值為8(在64位操作系統(tǒng)中),而初始化的兩個(gè)字符串長(zhǎng)度恰好又沒有超過8夫壁,所以可能輸出正確的結(jié)果(更大的看可能是程序崩掉)拾枣。正確的交換方式應(yīng)當(dāng)是這樣: swap2(&husband,&wife,sizeof(char*));,想一下為什么盒让,這樣調(diào)用的話我們交換的是什么梅肤,一開始husband,wife兩個(gè)變量分別存儲(chǔ)的是兩個(gè)字符串的首地址的值,而我們傳入husband邑茄,wife的地址姨蝴,即交換兩個(gè)變量中存的值,也就是字符串的首地址肺缕,交換完成后左医,husband變量中存的就是“wife”的首地址授帕,這樣我們交換字符串的目的也就達(dá)到了。
完整代碼如下:

#include <stdio.h>
#include <assert.h>
#include <string.h>
int swap1(int *pa,int *pb)
{
    int temp = *pa;
    *pa = *pb;
    *pb = temp;
}

void swap2(void *vp1,void *vp2,int size)
{
    int buffer[size];
    memcpy(buffer,vp1,size);
    memcpy(vp1,vp2,size);
    memcpy(vp2,buffer,size);
}


int main(int argc, char const *argv[])
{
    int a = 23,b = 44;

    //swap1 test
    printf("a : %d  b : %d\n", a , b);
    swap1(&a,&b);
    printf("after swap  a : %d  b : %d\n\n", a , b);
    swap1(&a,&b);

    //swap2 test for int
    printf("a : %d  b : %d\n", a , b);
    swap2(&a,&b,sizeof(int));
    printf("after swap  a : %d  b : %d\n\n", a , b);
    swap2(&a,&b,sizeof(int));

    //swap2 test for double
    double d1 = 32.1,d2 = 44.1;
    printf("d1 : %.2lf  d2 : %.2lf\n", d1 , d2);
    swap2(&d1,&d2,sizeof(double));
    printf("after swap  d1 : %.2lf  d2 : %.2lf\n\n", d1 , d2);
    swap2(&d1,&d2,sizeof(double));

    //swap2 test for string
    char * husband = (char*)"husband";
    char * wife = (char*)"wife";

    printf("husband : %10s    wife : %10s\n", husband,wife);
    swap2(&husband,&wife,sizeof(char*));
    printf("husband : %10s    wife : %10s\n\n", husband,wife);

    return 0;
}

參考博客:
void及void指針含義的深刻解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浮梢,一起剝皮案震驚了整個(gè)濱河市跛十,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秕硝,老刑警劉巖芥映,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異远豺,居然都是意外死亡奈偏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門躯护,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惊来,“玉大人,你說我怎么就攤上這事棺滞〔靡希” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵检眯,是天一觀的道長(zhǎng)厘擂。 經(jīng)常有香客問我昆淡,道長(zhǎng)锰瘸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任昂灵,我火速辦了婚禮避凝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眨补。我一直安慰自己管削,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布撑螺。 她就那樣靜靜地躺著含思,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甘晤。 梳的紋絲不亂的頭發(fā)上含潘,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音线婚,去河邊找鬼遏弱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛塞弊,可吹牛的內(nèi)容都是我干的漱逸。 我是一名探鬼主播泪姨,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼饰抒!你這毒婦竟也來了肮砾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤循集,失蹤者是張志新(化名)和其女友劉穎唇敞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咒彤,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疆柔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镶柱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旷档。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖歇拆,靈堂內(nèi)的尸體忽然破棺而出鞋屈,到底是詐尸還是另有隱情,我是刑警寧澤故觅,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布厂庇,位于F島的核電站,受9級(jí)特大地震影響输吏,放射性物質(zhì)發(fā)生泄漏权旷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一贯溅、第九天 我趴在偏房一處隱蔽的房頂上張望拄氯。 院中可真熱鬧,春花似錦它浅、人聲如沸译柏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鄙麦。三九已至,卻和暖如春镊折,著一層夾襖步出監(jiān)牢的瞬間胯府,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工腌乡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盟劫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓与纽,卻偏偏與公主長(zhǎng)得像侣签,于是被迫代替她去往敵國(guó)和親塘装。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 指針是C語(yǔ)言中廣泛使用的一種數(shù)據(jù)類型影所。 運(yùn)用指針編程是C語(yǔ)言最主要的風(fēng)格之一蹦肴。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,440評(píng)論 3 44
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 1,976評(píng)論 0 7
  • 1.寫一個(gè)NSString類的實(shí)現(xiàn) +(id)initWithCString:(c*****t char *)nu...
    韓七夏閱讀 3,765評(píng)論 2 37
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,802評(píng)論 1 10
  • 1.C和C++的區(qū)別猴娩?C++的特性阴幌?面向?qū)ο缶幊痰暮锰帲?答:c++在c的基礎(chǔ)上增添類,C是一個(gè)結(jié)構(gòu)化語(yǔ)言卷中,它的重...
    杰倫哎呦哎呦閱讀 9,515評(píng)論 0 45