2013-04-28 43 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。

这传达了其他程序员正是你想要做的。我希望这个解释有帮助。

无论如何,两种方法都做同样的工作,而且输出是一样的。