C 語言 指標 學習心得
我個人用指標到現在,覺得指標的用處 :
1 . 修改外部變量
2 . 增加傳遞變數的效率 (實際上是copy位址值給 function)
學習指標的時候要注意:
1.我們要更改的變數他的type是什麼?
2.要更改的變數他的指標的type是什麼?
心中一定要一直反問這兩句話
學習指標的時候要注意:
1.我們要更改的變數他的type是什麼?
2.要更改的變數他的指標的type是什麼?
心中一定要一直反問這兩句話
初學者可以理解成這樣:
*****指標變數的內容是儲存位址******
*****一般變數的內容是儲存值value *****
*****一般變數的內容是儲存值value *****
以下我會使用 scope 的概念來幫助解說
簡單而言,以 C language 為例子,大括號之間就是一個 scope
在括號之內的東西,到最後一個括號,生命週期就結束了
像是你在 while loop 宣告int變數num
其實是在func 內實際上先做了這樣的事情 :
可是假如我們改成
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
while (1) { | |
int num; | |
} |
這個 num 出了 } 之後就沒辦法再使用了
所以 function 才會有return value 去取得 function scope 裡面的值
為什麼要用指標才可以在function內改到 caller(call function的地方)變數的值?
我們可以這樣看 :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void func(int n1) { .... } | |
int main(){ | |
func(5); | |
return 0; | |
} |
func 被 call 了時候 其實在 {} 內是這樣做
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
int n1; //在 func scope裡面宣告變數 | |
n1 = 5; //將傳入的參數 assign. 美其言是傳入參數 實際上是將 5 複製 給 n1 | |
.......... //才做 { } 內要做的statements | |
} |
所以func結束之後n1就不見,無法在取得
所以假如我們不使用指標,而用下列的方法傳遞參數
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main() { | |
int number = 10; | |
func(number); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
// 只是單純的assign | |
//複製number的值然後給num 這個區域變數,沒有更變number | |
int num; | |
num = number; | |
statement | |
............... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void func(int *num){ | |
..... | |
} | |
int main(){ | |
int number = 10; | |
func(&number); | |
return 0; | |
} |
傳入變數的位置 (指標變數的內容是 指到的變數 的位址)
fun內:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
int *num; //指向int的pointer , 它的內容就是 就是int的address | |
// 0x12345678之類的 | |
num = &number; | |
// 以下是 function body | |
*num = XXXX ; 直接改到外部變數值 | |
} |
藉由這個方法
我們才可以真正存取到外面的變數 (因為知道複製外面變數的位址給 num )
ex : 0x12345678 之類
否則只是在自己scope ( 中文叫做作用域 ) 操作
所以才會有指標這種東西。由此可見,指標有一種功能:更改外部變數內容
指標另一個功能是方便傳遞變數
—————————-—————————-—————————- 小結 —————————-—————————-—————————-
利用 func 更改 main 中 int type 的變數
//這裡的int *整個是一個 type 代表int指標 指標內容是位址
實際上的動作:
—————————-—————————-—————————- 小結 —————————-—————————-—————————-
利用 func 更改 main 中 int type 的變數
//這裡的int *整個是一個 type 代表int指標 指標內容是位址
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void func ( int *int_addr ) { | |
//這裡的* 是用來dereference(取得某個位址中的內容),是一個operator | |
*int_addr = 你想要改的數值 ; | |
return ; | |
} | |
int main () { | |
int int_num; | |
func ( &int_num ); | |
return 0; | |
} |
實際上的動作:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func | |
{ | |
int *int_addr; | |
int_addr = &int_num; //( int_addr 的內容是 &num 像是 | |
//0x12345678 ) | |
*(&int_num) = 你想要改的數值; | |
return ; | |
} |
—————————-—————————-指標的第二個用途:傳遞大量數據—————————-—————————-——
當我們定義一個
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct strudent{ | |
int array[100]; | |
}; |
我們要把這種struct student( 整個是一個type ) 的變數傳入給function
也是用 “複製“的概念傳入 ( pass by value )
那我們在開始執行function之前 就要整整複製100個元素 這是很沒有效率的行為
所以,我們假如不要這樣做就可以傳入指標
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function( struct student *name){ | |
// name 是一個指向 struct student 的指標 | |
// name的 type 是 struct student * | |
statements | |
} |
這樣就會傳入 struct student 的第一個位址
在32位元系統中,傳 address 只是複製 4bytes的資料 比之前 100 * 4 bytes 要小
在64位元系統,是複製64位元的資料(一串位址 ex: 0x12345678 87654321) 到function變數中,這個方法就是pass by address
不過其實你會發現到 我在說 pass by address 也用了 “ 複製 ” 這個名詞
所以他其實也是一種pass by value只不過這個value是變數的位置而非變數的數值
因此弄清楚這個觀念之後,其實pass by value , pass by address 其實都只是複製一個 data 到 function裡面只是複製的資訊不同,造成修改的權限不同而已
在電腦實際操作只是 copy value 到 function 裡面
pass by value , pass by address 是人為定義的名詞,但是定義得十分巧妙
在電腦實際操作只是 copy value 到 function 裡面
pass by value , pass by address 是人為定義的名詞,但是定義得十分巧妙
文章一開頭曾說過,我們在學習指標的時候要一直反問兩個問題:
1.我們要更改的變數他的type是什麼?
2.要更改的變數他的指標的type是什麼?
因為我們要用function更改不是在function scope的資料,必須用pass by address的方式傳入變數。也就是我們要將 資料的位址 傳給function,在function中藉由索引到那個位址中存放的內容來間接存取數值,使用的方式就是 *address (dereference)
—————————- 比較 string 傳入和 普通 array 傳入 —————————-
我們在傳入非 char * 的時候,都需要跟 function 說明 array 的大小,
所以實際上傳 array 我們在function的第二個參數要指名大小
但是在傳 C string 時不用,是因為他會去搜尋第一個碰到的 '\0' 做為結尾
因此你在使用 string.h header file 裡面的函數都不用特別指名size大小。
———————————————————————— 結論 —————-—————————-—————————
1.我們要更改的變數他的type是什麼?
2.要更改的變數他的指標的type是什麼?
因為我們要用function更改不是在function scope的資料,必須用pass by address的方式傳入變數。也就是我們要將 資料的位址 傳給function,在function中藉由索引到那個位址中存放的內容來間接存取數值,使用的方式就是 *address (dereference)
—————————- 比較 string 傳入和 普通 array 傳入 —————————-
我們在傳入非 char * 的時候,都需要跟 function 說明 array 的大小,
所以實際上傳 array 我們在function的第二個參數要指名大小
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main() { | |
int array[10]; | |
func ( array, 10 ); | |
return 0; | |
} |
因此你在使用 string.h header file 裡面的函數都不用特別指名size大小。
———————————————————————— 結論 —————-—————————-—————————
funcition 要改到外部的變數的值則要傳入外部變數的位址
同時,function 接收到的變數,其type要和外部變數的位址相符
ex :
我們要利用function修改一個 type 為 int 的變數:int_num
要傳入 int_num 的位址給 function :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function ( &int_num ) ; // &取址符號 |
function 接收變數的 type 要和 接收的資料 互相 match
所以是這樣宣告的 :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void function ( int * ); 口訣:指標的 type 多一顆星星,指標存放的是位址 |
———————————————————————— 問題 ————————————————————————
所以假設今天我們要用function修改一個外面的變數 他的 type 是FILE *
ex:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main() { | |
FILE *f; | |
func ( arg1 ); | |
} | |
function ( type1 var_name ) { | |
... | |
} |
arg1, type1要怎麼填呢?
arg1 : &f , FILE *是 f 的 type 實際上變數名稱是 f,要讓 function 修改 f 要傳入 f 的位址也就是 &f
type1 因為接收到 f 的位址 所以type是 FILE* *var_name 分開*是因為:
現在我們告一個指向 FILE * 的指標(*)
如同指向int 的指標我們宣告為 int *int_pointer;
所以指向FILE *的指標 我們宣告為 FILE * *var_name 只是我們通常會把**寫在一起 : FILE **var_name
如果不幸的我們將function寫成下面的情況會發生什麼事情?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func( FILE * var_name ) { | |
statement | |
} | |
int main () { | |
FILE *f; | |
func( f ); | |
} |
在function內只是先這樣做
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func (f) { | |
FILE *var_name; | |
var_name = f; | |
//再次以scope的觀念來說 : 我們創立一個var_name的local var. | |
//但是出了這個 func 就消失了! 所以更改不到任何東西 | |
statement | |
} |
———————————————————————— 常見範例 ————————————————————————
剛學指標的時候,老師一定會要我們想怎麼寫一個swap function去交換兩個變數的內容
剛學指標的時候,老師一定會要我們想怎麼寫一個swap function去交換兩個變數的內容
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void swap ( int *a, int *b ) { | |
int tmp; | |
tmp = *a; | |
*a = *b; | |
*b = tmp; | |
} | |
int main() { | |
int a = 10; | |
int b = 20; | |
swap(&a, &b); | |
return 0; | |
} |