青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

MyMSDN

MyMSDN記錄開發(fā)新知道

談void changeString(char **s),指向指針的指針

void changeString(char **t){
	*t = "world";
}

void changeString2(char *t[]){
	*t = "world2";
}

typedef char *String;
void changeString3(String *s){
	*s = "world3";
}

void Inc(int *i){
	(*i)++;
}

問題列表:
1、如何改變外部變量?
2、啥時候我們需要使用**?
前言:
先看Inc,我們知道int i是一個值類型,為了能夠達到修改它的目的,我們需要將i的實際地址通過值傳遞的方式從調(diào)用函數(shù)傳遞給被調(diào)用函數(shù),因為對相同的地址的變量進行操作,所以我們的Inc(&i)將如我們所愿,順利地遞增。
以上兩個版本的changeString都是可以達到修改調(diào)用函數(shù)中的字符串的。如果按照下面的代碼將得到不正確的結(jié)果。

錯誤代碼示例:
void errorChangeString(char *t){
	t = "change!";
}

int main(void){
	char *s = "Hello";
	errorChangeString(s);

	return EXIT_SUCCESS;
}

在錯誤示例代碼中,假設傳遞的s則為指向字面值"Hello"的首字母'H'所在的地址值,假設這個值為0x1000。在errorChangeString中,假設"change"字面值的首字母'c'所在的地址值為0x2000,s被拷貝給了t,t的任何改動和s沒有任何關聯(lián),因此,s仍然指向0x1000,而不是指向0x2000。

我們應如何看待char **t?

若要讓所謂的t的值能夠跟著s變化,我們則需要指向指針的指針來幫忙。我們知道要讓函數(shù)傳遞(C語言只有值傳遞)后,仍然指向相同的值(這里指s始終為0x4000(s指向0x1000,假設它自身地址為0x4000)),則我們需要將這個傳遞的值進行一次包裝,使我們通過形參也能夠控制相同的地址所在的變量(這里指0x4000),因此,我們對形參定義一個指針,形如 char* *t(等價于char **t),這樣*t與s就代表了相同的值而不會因為傳遞而無法操縱s,因此可以在被調(diào)用函數(shù)內(nèi)部使用*t來指代要操作的外部變量(通過參數(shù)傳遞),因此在內(nèi)部*t="world"(假設"world"的首地址為0x2000),則s的值就被修改為"world"的首地址。(如下圖所示)


我們應如何看待char *t[]?
在我們的changeString2(char *t[])中,我們用char *t[]取代了char **t,我們知道char *t[]代表t是一個數(shù)組,數(shù)組的每一個成員都是一個char*類型的指針。我們也成為指針數(shù)組。下面讓我們看一個調(diào)用:
void changeStrArr(char *t[]){
	*t = "World";
}
int main(void){
	char *sArr[] = {
		"Hello"
	};
	printf("%s",*sArr);
	changeStrArr(sArr);
	printf("%s",*sArr); //printf("%s",sArr[0]);

	return EXIT_SUCCESS;
}
這是教科書上比較常見的指針數(shù)組形式,甚至還會簡單不少(它們的數(shù)組通常會有多個元素并用*t++來控制移位)。sArr在這里就是這個數(shù)組,因此sArr[0]即為指向該數(shù)組第一個元素的指針(因為是指針數(shù)組,每一個元素都是一個指針),因此使用printf("%s",*sArr); 或者printf("%s",sArr[0]);都將標準輸出sArr的第一個元素所指向的字符串。
下面我們來看一下下面這段代碼:
void changeString2(char *t[]); //函數(shù)體見本文頂部
int main(void){
	char *s="Hello";
	printf("%s",s);
	changeString2(&s);
	printf("%s",s); 
	return EXIT_SUCCESS;
}
從這段代碼中我們主要講s換成了一個字符而不是上一段代碼中的字符指針數(shù)組sArr,從上一段代碼我們可以得知s和sArr之間的關系,*s==*sArr[0]==**sArr;(我們可以通過strcmp(q,qArr[0])或者strcmp(q,*qArr);進行判斷,我們知道strcmp(const char *_Str1, const char *_Str2);也就是我們傳遞的q和*qArr均為字符指針也就是它們的定義通常為char *q和char **qArr)。為此我們可以將其進行移項,也可以得到等價表達式(規(guī)律:==兩側(cè)同時添加相同符號等式依舊不變(在*和&的邏輯里成立),同時出現(xiàn)&*,兩符號起中和作用(先從一個地址中取值,再從值反求它的地址,因此最終結(jié)果還是地址本身))也就是&*s==&*sArr[0]==&**sArr <=> s==sArr[0]==*sArr,這樣,再進行一次,&s==&sArr[0]==&*sArr,也就是&s==&sArr[0]==sArr因此changeStrArr(sArr)<=>changeStrArr(&s),因此從上面的代碼段到下面代碼段的演化是成功的(changeString2和changeStrArr本質(zhì)上沒有差別)。
下面的示例圖則從本質(zhì)上分別分析了兩者的各自的理由(非上述推理):


用typedef char *String;改良后的程序具有更高的可讀性
可以看到第三段代碼中我們在函數(shù)聲明前用typedef語句定義了typedef char *String;首先從typedef的本質(zhì)來講,這種定義將導致使用它的changeString3與changeString函數(shù)具有相同的本質(zhì),但是從閱讀的習慣上來講,用String而不是用char *的方式,則顯得更加親切。首先我們從眾多起他語言中,比如C#中,C#實現(xiàn)了類型String/string的方式,我們知道String是一個引用類型,但我們同時也知道string類型有個顯著的特征,就是它雖然是引用類型,每次對它的操作總是像值類型一樣被復制,這時候,我們定義的任何(C#):ChangeString(string str);將不起作用,而我們需要增加ref關鍵字來告訴編譯器它是同一實例,而不進行重新申請空間重新分配等一系列復雜操作,于是ChangeString(ref string str);的語句就有類似值類型的一些地方了,同樣,在C語言中,changeString2(String *s)也達到了同樣的效果。這樣的方式也同時對我們更加了解第一種方式起到了輔助作用。(用C#來比喻可能不是太好,因為很多讀者通常都是先接觸C再有機會才接觸C#的,而且也沒有講解到本質(zhì))
void changeString3(String *s); //函數(shù)體見本文頂部
int main(void){
	char *s="Hello";
	printf("%s",s);
	changeString3(&s);
	printf("%s",s); 

	return EXIT_SUCCESS;
}
本質(zhì)呢?因為任何一次的"Hello",其中的"Hello"是常量,而不是變量,它的存儲空間在編譯時就已經(jīng)確定了,它放在了靜態(tài)常量區(qū)中,因此它的地址不會變也不能加減。因此String,也就是char *指向的是一個不可變的常量,而非變量。(例如我們一直假設char *s = "Hello",的首地址s==0x1000(s的值,不是s的地址),那么它始終是0x1000,但是s是變量,s可以拋棄0x1000指向別的字符串字面值(char literal),但是我們知道C語言中只有按值傳遞,因此我們必須用它的指針假設s的地址0x3000,那么,我們將0x3000進行傳遞,這樣內(nèi)部就可以對0x3000進行操作了,這樣可以用(0x3000)->value的方式修改value指向0x2000的地址(假設這個地址是"GoodBye"的值),這樣我們的s就被修改了。因為我們的常量在編譯時就已經(jīng)分配了地址,在程序加載后就長久存在,知道應用程序退出后會跟著宿主一并消失,所以我們同樣不需要free操作)。

下一個問題:
啥時候我們需要用到**?
通過以上的幾個直觀的示例,我們大體了解了一個字符串通過一個函數(shù)參數(shù)進行修改的具體情況。這是一個很發(fā)散性的問題,我也沒有一個很肯定的100%的答案。
從void **v;(//void代表任意類型,可以是int/char/struct node等)定義的本質(zhì)上來觀察這個問題,我們可以推論void **v;,當我們需要獲取并改變*v的地址值得時候,我們都有這個需要(因為單從void *v的角度講,我們只能夠獲取v的地址改變v的值,但不能改變v的地址)。那我們什么需要獲取并改變*v的值呢?從上面的分析我們不難得出,我們在需要改變v的地址的時候即有這個需要。
下面是一個鏈表的例子:
#include <stdio.h>
#include <stdlib.h>

typedef struct node{
	int value;
	struct node *next;
} Node;

Node *createList(int firstItem){
	Node *head = (Node *)malloc(sizeof(Node));
	head ->value = firstItem;
	head ->next = NULL;
	return head;
}
void addNode(Node *head, Node **pCurrent,int item){
	Node *node = (Node *)malloc(sizeof(Node));
	node ->value = item;
	node ->next = NULL;

	(*pCurrent)->next=node;
	*pCurrent = node;
}
typedef void (*Handler)(int i);
void foreach(Node *head, Handler Ffront, Handler Flast){
	if(head->next!=NULL){
		Ffront(head->value);
		foreach(head->next,Ffront,Flast);
	}
	else
	{
		Flast(head->value);
	}
}
void printfFront(int i){
	printf("%d -> ",i);
}
void printfLast(int i){
	printf("%dn",i);
}


int main(void){
	Node *head, *current;
	current = head = createList(0);
	for(int i=1;i<10;i++)
		addNode(head,&current,i);
	foreach(head, printfFront, printfLast);

	return EXIT_SUCCESS;
}
//函數(shù)輸出
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
這個程序中的關鍵部分就是當前節(jié)點值current的確定,部分老師可能會圖方便采用全局變量進行當前值的確定,這個在拋棄型的示例中當然無傷大雅,也很好地描述了鏈表的本質(zhì),這本沒什么關系,但是鏈表是一個常用的數(shù)據(jù)結(jié)構(gòu),并發(fā)怎么辦?操作多個鏈表怎么辦?總之我們要秉承“方法共享,數(shù)據(jù)不共享的原則”,這樣就不太容易出現(xiàn)問題了。這里我們在main函數(shù)中定義了唯一的*head用于標識鏈表的頭,并希望它始終扮演鏈表頭的角色,不然我們最后將無法找到它了。我們用一個同樣類型的節(jié)點current指向了當前節(jié)點,并始終指向當前節(jié)點(隨著鏈表的移動,它將指向最后一個節(jié)點)。由于我們的current是主函數(shù)中定義的,而它的修改是在被調(diào)函數(shù)中進行的。因為我們需要改變的*current的值,根據(jù)我們的分析,對于要修改值的,我們有使用**的必要,而類似只需要讀取值的head,則沒有任何需要了。
這個程序代表了一種使用**的典型用法,也是大部分需要使用**的用法。

總結(jié):
不論它怎么變化,怎么復雜,我們需要把握幾點:
1、C語言中,函數(shù)傳遞永遠是值傳遞,若需要按地址傳遞,則需要用到指針(類似func(void *v){...});
2、在對于需要變化外部值的時候,直接尋址的使用*,間接尋址的使用**;
3、對于復雜的表達式,善于使用嵌套的思路去分析(編譯器亦或如此),注意各符號之間的優(yōu)先級。

posted on 2008-08-30 22:09 volnet 閱讀(3702) 評論(4)  編輯 收藏 引用 所屬分類: C/C++

評論

# re: 談void changeString(char **s),指向指針的指針[未登錄] 2008-09-01 10:12 raof01

第三條結(jié)論怎么得出的?

分析得不錯,就是有點麻煩。TCPL里指針講得比較好,簡明扼要。  回復  更多評論   

# re: 談void changeString(char **s),指向指針的指針 2008-09-02 11:30 volnet

@raof01
我怎么能跟TCPL比呢~~·人家是之父,我是之孫孫……呵呵  回復  更多評論   

# re: 談void changeString(char **s),指向指針的指針[未登錄] 2008-09-05 13:02 raof01

靠,別誤會了,只是說這篇應當參考TCPL,寫得簡潔點。  回復  更多評論   

# re: 談void changeString(char **s),指向指針的指針 2008-09-05 22:02 volnet

@raof01
被你說的一暈一暈的~  回復  更多評論   

特殊功能
 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久精品日韩欧美| 欧美一区二区三区四区在线观看地址 | 亚洲另类在线视频| 六月婷婷一区| 亚洲电影专区| 久久影院午夜片一区| 久久婷婷人人澡人人喊人人爽| 小辣椒精品导航| 亚洲女同在线| 久久在线播放| 亚洲乱码日产精品bd| 一区二区三区欧美激情| 欧美激情一区二区三区在线视频观看 | 亚洲国产一区在线| 亚洲欧美电影在线观看| 欧美特黄一区| 久久精品国产69国产精品亚洲| 欧美在线免费一级片| 欧美一区二区视频在线| 久久三级福利| 亚洲欧洲精品一区二区三区| 欧美激情在线| 一级成人国产| 欧美一区二区三区播放老司机| 羞羞视频在线观看欧美| 国产日韩欧美精品在线| 精品动漫3d一区二区三区免费版 | 亚洲国产日韩欧美在线图片| 亚洲大胆av| 亚洲午夜免费视频| 久久久99久久精品女同性| 嫩草影视亚洲| 午夜精品亚洲| 国产精品日韩一区二区| 亚洲片在线观看| 免费在线成人| 欧美韩国日本一区| 亚洲视频一区二区免费在线观看| 欧美国产丝袜视频| 欧美日韩国产精品一区二区亚洲| 亚洲免费观看在线视频| 亚洲国产精品一区二区第一页| 久久福利影视| 久久久久9999亚洲精品| 一区在线观看| 亚洲高清视频中文字幕| 久久精品91| 国语自产偷拍精品视频偷| 欧美在线观看天堂一区二区三区| 日韩小视频在线观看| 欧美激情视频在线免费观看 欧美视频免费一| 国产精品一区二区三区四区五区| 欧美在线亚洲在线| 亚洲欧美激情一区| 国产日韩亚洲欧美精品| 欧美一区二区精品久久911| 亚洲综合国产| 亚洲大胆av| 欧美在线观看一区| 亚洲一区二区三区精品视频 | 久久男女视频| 欧美亚日韩国产aⅴ精品中极品| 亚洲第一二三四五区| 亚洲国产日韩欧美| 欧美日韩成人综合| 亚洲在线视频一区| 久久国产精品久久久| 国产一区二区电影在线观看| 午夜精品久久久久久| 久久亚洲影音av资源网| 亚洲国产美女| 国产精品久久久久久影视| 亚洲精品小视频在线观看| 亚洲乱码国产乱码精品精天堂| 久久精品日韩欧美| 午夜精品视频在线观看| 久久尤物视频| 亚洲精品视频一区| 欧美激情亚洲自拍| 午夜精品久久久99热福利| 先锋影音国产一区| 伊人色综合久久天天| 另类天堂av| 在线亚洲美日韩| 久久福利一区| 亚洲人成在线播放| 欧美色图五月天| 午夜精品一区二区三区四区| 久久国产高清| 亚洲国产精品一区二区第一页| 欧美精品亚洲一区二区在线播放| 亚洲高清视频一区二区| 亚洲一区视频在线| 亚洲国内欧美| 国产私拍一区| 欧美午夜精品理论片a级大开眼界 欧美午夜精品理论片a级按摩 | 亚洲国产欧美国产综合一区| 亚洲一二三四久久| 亚洲精品自在在线观看| 国产亚洲亚洲| 国产精品一级| 国产精品久久99| 欧美午夜不卡在线观看免费 | 国产精品一区二区三区四区五区| 欧美高清在线视频观看不卡| 亚洲欧美日韩国产综合在线| 亚洲精品在线看| 在线日韩视频| 国产日韩一区二区| 国产乱码精品一区二区三区不卡| 久久在线免费观看| 久久伊伊香蕉| 乱人伦精品视频在线观看| 久久久国产精品一区二区中文| 亚洲综合国产精品| 久久九九久久九九| 欧美va亚洲va日韩∨a综合色| 乱码第一页成人| 免费在线成人av| 欧美精品久久99久久在免费线| 欧美日韩精品欧美日韩精品一| 欧美激情麻豆| 国产精品丝袜91| 国产日韩欧美另类| 精品不卡视频| 亚洲欧美日韩另类| 欧美激情视频一区二区三区在线播放| 欧美韩国在线| 久久国产一区二区| 欧美日韩在线一区| 狠狠综合久久av一区二区小说| 一个人看的www久久| 先锋影音国产一区| 夜色激情一区二区| 老司机成人网| 国产女人水真多18毛片18精品视频| 在线电影国产精品| 亚洲一区二区三区视频播放| 久久视频一区二区| 亚洲免费在线观看视频| 欧美a级一区| 亚洲第一中文字幕在线观看| 先锋影音国产一区| 午夜精品久久久久久久蜜桃app | 欧美日韩麻豆| 亚洲国产第一| 亚洲精品网址在线观看| 国产亚洲欧美一区二区三区| 欧美凹凸一区二区三区视频| 欧美性久久久| 亚洲一区久久| 一本色道久久88综合日韩精品| 你懂的成人av| 亚洲日本国产| 亚洲国产精品电影| 欧美精品激情| 亚洲欧美日韩精品久久| 午夜精品视频一区| 在线播放亚洲| 亚洲精品免费看| 欧美日韩亚洲综合一区| 亚洲一区二三| 久久精品免费电影| 在线亚洲免费视频| 久久av一区| 一本久道久久综合中文字幕| 亚洲乱码日产精品bd| 国产日韩av高清| 亚洲国产精品欧美一二99| 国产精品福利影院| 久久免费少妇高潮久久精品99| 欧美激情一区二区三区在线视频| 在线亚洲一区观看| 久久综合久久综合九色| 午夜视频在线观看一区二区| 久久久久久一区| 亚洲欧美日韩专区| 欧美极品欧美精品欧美视频| 午夜在线视频观看日韩17c| 欧美国产欧美综合| 老司机午夜精品视频在线观看| 国产精品免费在线| 亚洲精品美女免费| 99re6这里只有精品视频在线观看| 亚洲专区国产精品| 亚洲欧美综合v| 国产精品视频精品视频| 99精品欧美一区二区三区综合在线 | 亚洲精品三级| 麻豆精品一区二区综合av| 欧美在线在线| 1769国产精品| 欧美视频一区二区三区| 夜夜嗨av一区二区三区| 亚洲视频专区在线| 国产精品综合久久久| 久久精品国产精品亚洲综合| 裸体素人女欧美日韩| 在线午夜精品自拍| 国产精品一区免费在线观看|