C 語言 指標 學習心得



我個人用指標到現在,覺得指標的用處 :
1 . 修改外部變量
2 . 增加傳遞變數的效率實際上是copy位址值給 function

學習指標的時候要注意:
1.我們要更改的變數他的type是什麼? 
2.要更改的變數他的指標的type是什麼?
心中一定要一直反問這兩句話

初學者可以理解成這樣:
*****指標變數的內容是儲存位址******
*****一般變數的內容是儲存值value *****


以下我會使用 scope 的概念來幫助解說

簡單而言,以 C language 為例子,大括號之間就是一個 scope

在括號之內的東西,到最後一個括號,生命週期就結束了

像是你在  while loop 宣告int變數num 
while (1) {
int num;
}
view raw gistfile1.c hosted with ❤ by GitHub

這個 num 出了 } 之後就沒辦法再使用了

所以 function 才會有return value 去取得 function scope 裡面的值

為什麼要用指標才可以在function內改到 caller(call function的地方)變數的值?

我們可以這樣看 :

void func(int n1) { .... }
int main(){
func(5);
return 0;
}
view raw gistfile1.txt hosted with ❤ by GitHub

func 被 call 了時候 其實在 {} 內是這樣做
{
int n1; //在 func scope裡面宣告變數
n1 = 5; //將傳入的參數 assign. 美其言是傳入參數 實際上是將 5 複製 給 n1
.......... //才做 { } 內要做的statements
}
view raw gistfile1.c hosted with ❤ by GitHub

所以func結束之後n1就不見,無法在取得

所以假如我們不使用指標,而用下列的方法傳遞參數
int main() {
int number = 10;
func(number);
return 0;
}
view raw gistfile1.c hosted with ❤ by GitHub
其實是在func 內實際上先做了這樣的事情 :
{
// 只是單純的assign
//複製number的值然後給num 這個區域變數,沒有更變number
int num;
num = number;
statement
...............
}
view raw gistfile1.c hosted with ❤ by GitHub
可是假如我們改成
void func(int *num){
.....
}
int main(){
int number = 10;
func(&number);
return 0;
}
view raw gistfile1.c hosted with ❤ by GitHub

傳入變數的位置 (指標變數的內容是 指到的變數 的位址

fun內:
{
int *num; //指向int的pointer , 它的內容就是 就是int的address
// 0x12345678之類的
num = &number;
// 以下是 function body
*num = XXXX ; 直接改到外部變數值
}
view raw gistfile1.c hosted with ❤ by GitHub

藉由這個方法

我們才可以真正存取到外面的變數 (因為知道複製外面變數的位址給 num )
ex : 0x12345678 之類

否則只是在自己scope ( 中文叫做作用域 ) 操作

所以才會有指標這種東西。由此可見,指標有一種功能:更改外部變數內容

指標另一個功能是方便傳遞變數

—————————-—————————-—————————- 小結 —————————-—————————-—————————- 


利用 func 更改 main 中 int type 的變數 


//這裡的int *整個是一個 type 代表int指標 指標內容是位址


void func ( int *int_addr ) {
//這裡的* 是用來dereference(取得某個位址中的內容),是一個operator
*int_addr = 你想要改的數值 ;
return ;
}
int main () {
int int_num;
func ( &int_num );
return 0;
}
view raw gistfile1.c hosted with ❤ by GitHub

實際上的動作:

func
{
int *int_addr;
int_addr = &int_num; //( int_addr 的內容是 &num 像是
//0x12345678 )
*(&int_num) = 你想要改的數值;
return ;
}
view raw gistfile1.c hosted with ❤ by GitHub
—————————-—————————-指標的第二個用途:傳遞大量數據—————————-—————————-——

當我們定義一個 

struct strudent{
int array[100];
};
view raw gistfile1.c hosted with ❤ by GitHub

我們要把這種struct student( 整個是一個type ) 的變數傳入給function

也是用 “複製“的概念傳入 ( pass by value )

那我們在開始執行function之前 就要整整複製100個元素 這是很沒有效率的行為

所以,我們假如不要這樣做就可以傳入指標
function( struct student *name){
// name 是一個指向 struct student 的指標
// name的 type 是 struct student *
statements
}
view raw gistfile1.c hosted with ❤ by GitHub

這樣就會傳入 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 是人為定義的名詞,但是定義得十分巧妙
文章一開頭曾說過,我們在學習指標的時候要一直反問兩個問題:
1.我們要更改的變數他的type是什麼? 
2.要更改的變數他的指標的type是什麼?

因為我們要用function更改不是在function scope的資料,必須用pass by address的方式傳入變數。也就是我們要將 資料的位址 傳給function,在function中藉由索引到那個位址中存放的內容來間接存取數值,使用的方式就是 *address (dereference)


—————————- 比較 string 傳入和 普通 array 傳入 —————————- 

我們在傳入 char * 的時候,都需要跟 function 說明 array 的大小,

所以實際上傳 array 我們在function的第二個參數要指名大小

int main() {
int array[10];
func ( array, 10 );
return 0;
}
view raw gistfile1.c hosted with ❤ by GitHub
但是在傳 C string 時不用,是因為他會去搜尋第一個碰到的 '\0' 做為結尾
因此你在使用 string.h header file 裡面的函數都不用特別指名size大小。

———————————————————————— 結論 —————-—————————-————————


funcition 要改到外部的變數的值則要傳入外部變數的位址
同時,function 接收到的變數,其type要和外部變數的位址相符

ex :

我們要利用function修改一個 type 為 int 的變數:int_num 
要傳入 int_num 的位址給 function :

function ( &int_num ) ; // &取址符號
view raw gistfile1.c hosted with ❤ by GitHub

function  接收變數的 type 要和 接收的資料 互相 match 
所以是這樣宣告的 :

void function ( int * ); 口訣:指標的 type 多一顆星星,指標存放的是位址
view raw gistfile1.c hosted with ❤ by GitHub

———————————————————————— 問題 ————————————————————————

所以假設今天我們要用function修改一個外面的變數 他的 type  是FILE *

ex:

int main() {
FILE *f;
func ( arg1 );
}
function ( type1 var_name ) {
...
}
view raw gistfile1.c hosted with ❤ by GitHub

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寫成下面的情況會發生什麼事情?
func( FILE * var_name ) {
statement
}
int main () {
FILE *f;
func( f );
}
view raw gistfile1.c hosted with ❤ by GitHub

在function內只是先這樣做

func (f) {
FILE *var_name;
var_name = f;
//再次以scope的觀念來說 : 我們創立一個var_name的local var.
//但是出了這個 func 就消失了! 所以更改不到任何東西
statement
}
view raw gistfile1.c hosted with ❤ by GitHub

———————————————————————— 常見範例 ————————————————————————

剛學指標的時候,老師一定會要我們想怎麼寫一個swap function去交換兩個變數的內容
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;
}
view raw gistfile1.c hosted with ❤ by GitHub


這個網誌中的熱門文章

Vim 自動 補完 插件 YouCompleteMe 教學 心得 新手入門 C++ C

怎麼建立一個 Https server?

C pointer to function 函式指標 學習心得