2013-04-28 49 views
3

我嘗試了兩種方法將值傳遞給函數,並且兩者似乎具有相同的效果。傳遞變量作爲參數並獲得所需的返回值與傳遞指針基本相同?

1)Passing pointers

int foo(int *bar){ 
    *bar = doSomething(); 
} 

int main(void){ 
    int foobar = 0; 

    foo(&foobar); 
} 

2)Passing variables

int foo(int bar){ 
    //do stuff 
    return bar; 
} 

int main(void){ 
    int foobar = 0; 

    foobar = foo(foobar); 
} 

那麼這是否意味着使用適當的return我可以模擬,當我使用指針作爲參數相同的效果?它們是否基本相同,只是選擇和樣式/語法的問題?

我聽說指針使代碼複雜化,所以一般應該避免使用指針。

+2

請閱讀更多關於指針的信息,這是一個非常強大的工具,它不僅僅是語法問題。 – Guy 2013-04-28 04:35:06

+3

我想你的意思是'foo(&foobar)'在第一個例子中。 – 2013-04-28 04:35:31

+0

通過使用指針,您不會返回,因爲您傳遞參數的地址,並且它被修改。但是使用方法,一旦它們退出函數,就會離開堆棧,因此您需要返回值。 – Sunny 2013-04-28 04:38:02

回答

2

在這個特定的例子中,兩者都具有相同的效果:它們都更新foobar的值,但是它們更新的方式完全不同。

在案例1中,您將main中的foobar的地址傳遞給函數foofoobar的該地址被存儲到函數foobar自變量變量中,並且bar指向foobar。因爲現在你的地址爲foobar,並且對象的生命週期還沒有完成,你可以直接訪問foobar的存儲並修改它。

 +--------+          
     | 55 |  +------call foo(&foobar)-----+ 
     +--------+  |       |        
int | foobar |  |       V 
     +--------+  | +--points to--------+--------+ 
     | 0x1234 |-----+ |     | 0x1234 | 
     +--------+<---------+     +--------+ 
           bar ( int * | bar |  ) 
               +--------+ 
               | 0xabcd | 
               +--------+ 
          At this moment bar holds the address of foobar 
          *bar = whatever will assign whatever to the address 
          which is held by bar, and thus changing the value 
          of foobar in main 

在情況2中,情況不同。您將foobar的值傳遞給foo,該值保留在變量bar中。在foo中,您無法直接訪問對象foobar。您可以使用foo以內的值執行任何操作,但不會影響foobar。當您返回某個值時,foobar = foo(foobar);語句將返回的值分配給foobar,這是foobar得到更新時的值。

 +--------+          
     | 55 |-------------call foo(foobar)-----+ 
     +--------+         |        
int | foobar |<-+        V 
     +--------+ |       +--------+ 
     | 0x1234 | |       | 55 | 
     +--------+ |       +--------+ 
        |    bar ( int * | bar |  ) 
        |       +--------+ 
     foobar = foo(foobar);     | 0xabcd | 
        |       +--------+ 
        |    { 
        |    //change bar 
        | 
        +-------------- return bar; 
           } 

           Here bar does not point to anything, it just has 
           copy of the value of foobar. Whatever is returned 
           by foo will be assigned in foobar in main 

要注意的一點是,當你通過地址,實際的值複製到bar,這是後來解釋爲地址。我建議閱讀更多關於指針的知識,這是一個強大的工具。

1

亞都相同。你可以使用任何東西。但是,您的第一個代碼必須是

int foo(int *bar){ 
    *bar = doSomething(); 
} 

int main(void){ 
    int foobar = 0; 

    foo(&foobar); 
} 

因爲bar是指針類型,它需要變量的地址作爲參數。 因此,foobar將會是一個變量,而foobar將會成爲指針。

通常,這些東西將被稱爲按價值調用,按地址調用。 請參閱Wiki按價值查詢,並按地址致電。

//Call by Address 
    void SwapIntAddr(int* ptmp1, int* ptmp2) 
    { 
     int ptmp; 
     ptmp = *ptmp1; 
     *ptmp1 = *ptmp2; 
     *ptmp2 = ptmp; 
    } 

//Call by Value 

    void SwapIntVal(int tmp1, int tmp2) 
    { 
     int tmp; 
     tmp = tmp1; 
     tmp1 = tmp2; 
     tmp2 = tmp; 
    } 


int main(){ 
    int a = 3, b= 5; 

    SwapIntVal(a,b); // This will have no effect 
    printf("%d %d\n",a,b); 

    SwapIntAddr(&a,&b); // This will have effect 
    printf("%d %d\n",a,b); 

} 

有關更詳細的示例,請參閱我的回答here

+1

C.在C中沒有'按引用調用'或'按地址調用'。只有'按值調用'。 – 2013-04-28 05:31:55

+0

@SheerFish ya'referenece'是一個C++特性。但是,'按地址調用'存在於C:Sample.C中。void swap(int * a,int * b){ } int main(){ int a = 56,b = 59; 交換(&a,&b); } '執行得很好。 – 2013-04-28 06:48:42

3

C的一半是指針,在掌握指針概念之前,你沒有得到C的內容。

並不總是可以使用返回值。實際上,許多API僅將返回值用作成功/錯誤指示器代碼,而將實際返回值放入指針參數中。例如,網絡庫可能有以下用途:

if (AcceptClient(&ClientInfo) == SUCCESS) { ... } 

指針參數還用於多個返回值:

if (GetConfigString(&StrData,&StrLen) == OK) { ... } 

一個不能真正避免指針在C與編程然而,在語言的時候指針抽象支持,即指向許多不同對象的指針被管理,並且複雜程序對程序員來說是隱藏的,所以最好使用抽象而不是普通指針,因爲它減少了處理指針時可能發生的很多問題。例如,在Object Pascal中,您有動態數組,ansistrings,widetrings等等。所有這些都是指針抽象。一個不需要處理存儲(de)分配,元素訪問,範圍檢查等等,一切都由編譯器注入的代碼處理。因此,即使發生錯誤(例如,在有效索引之外訪問元素),錯誤也很容易被觸發。

1

是的,兩種風格都適用。每個人都有優點和缺點。

return樣式可以採用不變的參數。

int foo(int bar); foo(42); // works 
void foo(int *bar); foo(42); // nope 
void foo(int *bar); foo(&42); // still nope 

的通逐地址方式(也稱爲「輸出參數」)可釋放存儲器管理的字符串功能(例如),並讓他們返回錯誤代碼。

int strcpy(char *src, char *dst) { /* nice simple while loop */ } 
char *strcpy(char *src)   { /* welcome to malloc hell */ } 

此外,傳遞地址可以節省堆棧空間並在使用結構時複製開銷。

struct quux { /* lots and lots of stuff */ }; 
struct quux foo(int bar) { ...; return s; /* have to fit a struct quux on stack */ } 
int foo(int bar, struct quux *result) { /* write stuff into *result */; } 

一般而言,優選的是其中return在所有可能的,並且只使用輸出參數在必要時。

2

雖然兩者都使用pass-by-value來傳遞參數,但它們的使用方式有很大的不同。當您將指針作爲參數傳遞給函數時,無論您通過取消引用在函數體指針反映在這些指針指向變量的原始值,儘管這些passed pointersmain()宣佈的「原始」的指針。你看,多個指針可以指向一個副本變量,因此變量的值可以通過解引用任何這些指針來改變。

但是,雖然您將變量作爲參數傳遞而不是指針,但對這些變量的值的所有操作都不會反映在「原始」變量的值中。這些更改僅限於那些作爲參數傳遞的copies

在您的例子中,如果你想分配的return of foo()值還是那doSomething()bar以外的變量,並打算不改變bar的值,那麼只有通過變量參數是可取的,不是指針。

下面是一些簡單的代碼示出它:

// Passing variables as arguments

#include<stdio.h> 

int foo(int bar){ 
    bar=303; 

} 

int main(void) 
{ 
    int foobar = 0; 
    printf("The value of foobar before function call is %d\n",foobar); 
    foo(foobar); 
    printf("TThe value of foobar after function call is %d",foobar); 
} 

輸出The value of foobar before function call is 0

 `The value of foobar after function call is 303` 

// Passing pointers to variables as arguments

#include<stdio.h> 
int doSomething(); 

int foo(int *bar){ 
    *bar = doSomething(); 
} 

int main(void){ 
    int foobar = 0; 
    printf("The value of foobar before function call is %d\n",foobar); 
    foo(&foobar); 
    printf("TThe value of foobar after function call is %d",foobar); 
} 

int doSomething() 
{ 
    return 303; 
} 

輸出The value of foobar before function call is 0

 `The value of foobar after function call is 0` 

,最後,約指針使代碼複雜,好了,指針是C的最優雅的特點之一,是一旦你一個非常有用的和不可缺少的工具知道如何很好地使用它們。

1

它比「的風格問題」,這裏的討論多一點。代碼是爲了讓機器工作而編寫的,但99.9%的「真實世界」代碼是爲人類理解而編寫的。

和人類理解詞,句,成語等。

一個在C公共習慣用法是:

「如果一個功能必須改變/修改的對象(變量,結構變量,聯合變量等)的內容,然後由值手柄傳遞到該對象。 「

這意味着,您必須pass-the-object-by-reference的功能。這是您的方法#1運行良好的地方,經驗豐富的程序員知道您正在嘗試使用它。

在另一方面另一個成語說:

「如果函數的行爲像一個純粹的數學函數,其中f(x)總是等於y,然後按值傳遞對象本身,並使用返回值」(不知道我是不是很好)

這意味着,對於每x,如果f(x)的值永不改變,則使用方法#2。

這傳達了其他程序員正是你想要做的。我希望這個解釋有幫助。

無論如何,兩種方法都做同樣的工作,而且輸出是一樣的。