麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

函數(shù)返回值與不同存儲(chǔ)位置字符串的“坑”

2019-11-14 09:12:30
字體:
供稿:網(wǎng)友

通常,我們采用兩種方式從一個(gè)函數(shù)中(被調(diào)用者),將某個(gè)有用信息傳遞到另外一個(gè)函數(shù)中(調(diào)用者)。分別是傳參方式與返回值方式①傳參方式:傳參數(shù)我們傳遞的是地址(或者值,以傳地址為重點(diǎn)),地址作為傳出參數(shù),其思想是調(diào)用者將自己的??臻g給被調(diào)用者使用,然后在被調(diào)用者結(jié)束時(shí)調(diào)用者授權(quán)其使用的??臻g并不會(huì)被回收,而調(diào)用者就可以從自己的??臻g中取得有用的值;②返回值方式: 被調(diào)用者在結(jié)束是向調(diào)用者返回一個(gè)值或者是一個(gè)不會(huì)被回收的內(nèi)存地址(或者值)。當(dāng)然我們也可以使用定義局部變量的方式解決傳參與返回值的麻煩的步驟,但是一有所得,必有所失,全局變量由于其共享性而不是那么的安全。

1、傳參與返回值各自的演示:

我們以簡(jiǎn)單的求和([1,n]所有整數(shù)之和),實(shí)現(xiàn)主函數(shù)獲取求和函數(shù)的處理結(jié)果的例子來做演示:

#include<stdio.h>void add_para(int * sum, int n){ itn i; for(i=1; i<=n; i++) *sum += i; }int add_ret(int n){ return (n*(n+)/2); //返回值}int main(void){ int n = 0, sum = 0; scanf("%d",&n); add_para(&sum, n);//傳參方式 這里寫圖片描述 當(dāng)然,我們也可以采用參數(shù)與返回值雙重返回的方式來傳遞處理結(jié)果:

int * add_para_ret(int * sum, int n){//傳參方式 itn i; for(i=1; i<=n; i++) *sum += i; return &sum; //返回地址方式}

那么今天我們就重點(diǎn)說說返回值(地址)與傳參的那些問題。

2、返回值的重點(diǎn):地址返回與值返回

在返回值時(shí),我們經(jīng)常會(huì)返回一個(gè)變量的地址,該地址在函數(shù)被調(diào)函數(shù)結(jié)束后不會(huì)被回收。因?yàn)樵诓捎梅祷刂?狹義),而不是一個(gè)值的有效地址的時(shí)候,返回其實(shí)并不起作用(或者說是返回失效)。當(dāng)然返回地址也會(huì)失效,比如局部變量的棧空間在返回給主函數(shù)(調(diào)用者)時(shí)其返回是無效的。 舉個(gè)簡(jiǎn)單的例子(以上例為母本):

/*地址返回的誤區(qū)*/#include<stdio.h>int * add_ret(int n){ int sum = (n*(n+)/2); return &sum; //返回局部棧空間地址}void add_para(int * sum, int n){ itn i; for(i=1; i<=n; i++) *sum += i; }int main(void){ int n = 0,* sum = 0; scanf("%d",&n); sum = add_ret(n);//計(jì)算結(jié)果是5050 add_para(sum,n);//計(jì)算結(jié)果是5050的兩倍,當(dāng)然如果不是那也不意外,因?yàn)閟um指向的地址被來就已經(jīng)不屬于它,被別人修改也很正常 printf("sum = %d/n",*sum);//輸出不是正確結(jié)果 return 0;}

這里寫圖片描述

我們發(fā)現(xiàn)會(huì)有警告:函數(shù)返回局部變量地址。 并且打印的并不是我們想要的值,雖然這個(gè)結(jié)果是我們?nèi)藶樵斐傻?我們用main中的sum接收了一個(gè)不可控的局部變量地址,之前的計(jì)算結(jié)果5050還存在,導(dǎo)致最終結(jié)果是預(yù)期的兩倍),只需要在add_para()函數(shù)中將sum重新初始化,結(jié)果就會(huì)正確;但是很多情況下即使在add_para()中將sum中重新初始化為0,對(duì)于返回的局部變量地址(這塊地址在返回后并不屬于調(diào)用方,已被回收只是還沒重新利用,故原有內(nèi)內(nèi)容還在)我們不能人為控制。如果在main()輸出sum中內(nèi)容之前系統(tǒng)用已經(jīng)用回收的地址干了其他事,那結(jié)果就那沒有規(guī)律可循了。所以我們?cè)诜祷氐刂窌r(shí),絕對(duì)不能返回局部變量地址(??臻g)。

3、字符串地址返回與傳參的“坑”:

這塊才是今天的重頭戲,昨天晚上我寫了一段代碼,出現(xiàn)了段錯(cuò)誤提示,代碼如下:

#include<stdio.h>#include<errno.h>#include<pthread.h>#include<string.h>#include<stdlib.h>void * task(void *p){ printf("%s/n",(char *)p); p = "hello"; strcpy(p,"hello"); //char st[] = "hello";//st指向棧區(qū),返回的是無效的指針,不能使用 //return st; return p;}int main(void){ char str[] = "abcde"; pthread_t tid; pthread_create(&tid, NULL, task, str); char * retval = "world"; int ret = pthread_join(tid, (void **)&retval); if(ret){ printf("pthread_join error %s/n",strerror(ret)); exit(EXIT_FAILURE); } printf("str = %s/n",str);//str由strcpy(p,"hello");語句改變 printf("res = %s/n",retval);//retval由p="hello";語句改變 return 0;}

這里str采用傳參的方式改變字符串內(nèi)容,retval采用返回值的方式改變字符串內(nèi)容,按理說str與retval輸出應(yīng)該都是”hello”,結(jié)果卻總是那么出人意料讓人驚喜。。 這里寫圖片描述

編譯運(yùn)行之后,我們發(fā)現(xiàn)只有第一個(gè)printf()輸出了個(gè)”abcde”,而后面接著是段錯(cuò)誤。那么首先我想到的就是返回了局部變量地址,但是并非如此:參數(shù)指針p指向的地址并非本函數(shù)中的棧空間,pthread_join(tid, (void **)&retval);語句將main()函數(shù)中的地址傳給了被調(diào)函數(shù)(子線程),返回是不會(huì)出錯(cuò)的。而當(dāng)我仔細(xì)觀察了這兩句語句之后:

p = "hello";strcpy(p,"hello");

我發(fā)現(xiàn)了端倪所在:p本來指向主線程傳給其的主線程中的地址,單單就每個(gè)語句來看:①p = “hello”;這一語句改變了p的指向(改為指向字符常量區(qū)),返回p是沒有問題的,②strcpy(p,”hello”);只負(fù)責(zé)復(fù)制不負(fù)責(zé)修改指向,就像傳參一樣。但是strcpy(p,”hello”);放在了p = “hello”;的后面就引發(fā)了段錯(cuò)誤(strcpy(p,”hello”);放在了p = “hello”;的前面就不會(huì)引發(fā)),問題在于第一句修改p的指向,“hello”在只讀常量區(qū),也就是說其之不能修改,而后面緊接著就用strcpy修改其值,導(dǎo)致了段錯(cuò)誤。當(dāng)我們稍作修改如下,問題立馬解決:

/*修改strcpy(p,"hello");與p = "hello";的順序*/#include<stdio.h>#include<errno.h>#include<pthread.h>#include<string.h>#include<stdlib.h>void * task(void *p){ printf("%s/n",(char *)p); //p = "hello";//修改地址為只讀常量區(qū)地址 strcpy(p,"hello");//沒有修改地址,原有地址在main函數(shù)中 p = "hello"; /* *注意:第八行與第九行不能同時(shí)執(zhí)行,必須注釋掉一個(gè) *但是當(dāng)調(diào)換8、9行順序后可以同時(shí)執(zhí)行(即9、10行一起執(zhí)行) *這是因?yàn)?hello"在只讀區(qū) *p="hello"是修改p的地址,strcpy是修改p的指向的地址中的值 *8、9行的代碼順序試圖改變只讀區(qū)的值,引發(fā)段錯(cuò)誤而退出 *而9、10行的順序不會(huì)去修改只讀區(qū)的值 * */ //char st[] = "hello";//st指向棧區(qū),返回的是無效的指針,不能使用 return p; //return st;}int main(void){ char str[] = "abcde"; pthread_t tid; pthread_create(&tid, NULL, task, str); char * retval = "world"; int ret = pthread_join(tid, (void **)&retval); if(ret){ printf("pthread_join error %s/n",strerror(ret)); exit(EXIT_FAILURE); } printf("str = %s/n",str);//str由strcpy(p,"hello");語句改變 printf("res = %s/n",retval);//retval由p="hello";語句改變 return 0;}

結(jié)果如下(正確結(jié)果):

這里寫圖片描述

4、總結(jié):

關(guān)于函數(shù)/線程的返回值: ①可以以指針作返回值,但是不能直接以數(shù)組作返回類型; ②可以返回局部變量,但是不能返回局部變量的指針; ③加了static的局部變量指針可以返回(加static就不再是局部變量); ④函數(shù)/線程的返回值必須是一個(gè)有效的指針。 最后再加一點(diǎn):對(duì)于返回值不僅要注意??臻g,也要注意全局區(qū)、堆區(qū)。也就是說對(duì)于按址返回的方式,一定要注意在函數(shù)執(zhí)行中其地址是否改變,其地址是否被允許改變,其地址的指向是否被允許改變。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 日韩大片在线永久观看视频网站免费 | 亚洲影视中文字幕 | 看片一区 | 国产精品一区二区三区在线播放 | 国内精品视频饥渴少妇在线播放 | 91精品国产91久久久久久吃药 | 中文欧美日韩 | 日本在线视频免费观看 | 亚洲成人播放 | 久草网在线 | 国产成人在线观看免费网站 | 国产成人综合在线观看 | 91麻豆精品国产91久久久点播时间 | 国产一区网址 | 欧美成人精品一区二区 | 国产午夜小视频 | 久久精品亚洲欧美日韩精品中文字幕 | 黄色网址在线免费 | 婷婷久久久久久 | 日韩在线播放一区二区 | 国产1区2区3区在线观看 | 伊人成人免费视频 | 成年性羞羞视频免费观看 | 成人免费在线观看视频 | 欧美无限资源 | 毛片av网址 | 国产毛片毛片 | 在线播放免费视频 | 最污网站 | 国产精品久久久久久久久粉嫩 | 99爱福利视频在线观看 | 蜜桃av鲁一鲁一鲁一鲁 | 91精品国产乱码久 | 久久欧美亚洲另类专区91大神 | 激情夜色| 青青国产在线视频 | 国产一国产一级毛片视频在线 | 轻点插视频 | 午夜在线小视频 | 日韩做爰视频免费 | 久久久久久麻豆 |