我尝试了两种方法将值传递给函数,两者似乎都有相同的效果。
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
我可以模拟与使用指针作为参数时相同的效果?它们是否基本相同而且只是选择和样式/语法的问题?
我听说指针使代码复杂化,因此一般的经验法则应该避免使用指针。
答案 0 :(得分:3)
C的一半是指针,在掌握指针概念之前,你还没有得到C。
并不总是只使用返回值。实际上,许多API仅将返回值用作成功/错误指示符代码,而实际返回值则放在指针参数中。例如,网络库可能具有以下用途:
if (AcceptClient(&ClientInfo) == SUCCESS) { ... }
指针参数也用于多个返回值:
if (GetConfigString(&StrData,&StrLen) == OK) { ... }
在用C语言编程时,人们无法真正避免使用指针。但是,在支持指针抽象的语言中,这是指向许多不同对象的指针,并且程序员隐藏了复杂性,使用抽象而不是普通指针因为它减少了处理指针时可能出现的许多问题。例如,在Object Pascal中,您有动态数组,ansistrings,widestrings等。所有这些都是指针抽象。一个不需要处理存储(de)分配,元素访问,范围检查等。一切都由编译器注入的代码处理。因此,即使发生错误(例如访问有效索引之外的元素),错误也很容易被追踪。
答案 1 :(得分:2)
它们可能看起来相同但不存在!!即使两者都使用pass-by-value
来传递参数,它们的使用方式也大不相同。当您将指针作为参数传递给函数时,无论您执行何种操作通过取消引用该函数体中的那些指针反映在这些指针所指向的变量的原始值中,即使这些passed pointers
可能是main()
中声明的“原始”指针的副本。您看到,多个指针可以指向变量,因此可以通过解除引用任何指针来更改变量的值。
但是当您将变量作为参数而不是指针传递时,对这些变量的值的所有操作都不会反映在“原始”变量的值中。这些变化仅限于那些传递的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最优雅的功能之一,一旦你知道如何使用它们,它们是一个非常有用且不可或缺的工具。 / p>
答案 2 :(得分:2)
在这个特定示例中,两者都具有相同的效果:它们都更新foobar
的值,但它们的更新方式完全不同。
在案例1中,您将foobar
内的main
地址传递给函数foo
。 foobar
的地址存储在函数bar
中的foo
参数变量中,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
,后来将其解释为地址。我建议阅读有关指针的更多信息,这是一个强大的工具。
答案 3 :(得分:1)
int foo(int *bar){
*bar = doSomething();
}
int main(void){
int foobar = 0;
foo(&foobar);
}
因为bar是指针类型,它需要变量的地址作为参数。 因此,foobar将是一个变量,&foobar将成为指针。
通常,这些东西将被称为按值调用,按地址调用。 按价值推荐维基,并按地址打电话。
//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以获取更详细的示例。
答案 4 :(得分:1)
是的,两种风格都有效。各有利弊。
return
样式可以采用不变的参数。
int foo(int bar); foo(42); // works
void foo(int *bar); foo(42); // nope
void foo(int *bar); foo(&42); // still nope
传递地址样式(也称为&#34; out-parameters&#34;)减轻了内存管理的字符串函数(例如),并让它们返回错误代码。
int strcpy(char *src, char *dst) { /* nice simple while loop */ }
char *strcpy(char *src) { /* welcome to malloc hell */ }
此外,pass-by-address可以在使用结构时节省堆栈空间和复制开销。
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
,并且只在必要时使用out-parameters。
答案 5 :(得分:1)
这里讨论的不仅仅是“风格问题”。编写代码使机器正常工作,但99.9%的“真实世界”代码是为人类理解而编写的。
人类理解单词,句子,习语等。
C中常见的习语之一是:
“如果函数必须更改/更改对象的内容(变量,结构变量,联合变量等),则按值将句柄传递给对象。”
这意味着,您必须pass-the-object-by-reference
到该功能。这是你的方法#1运作良好的地方,经验丰富的程序员知道你正在尝试用它做什么。
另一方面,另一个成语说:
“如果函数的行为类似于纯数学函数,f(x)
总是等于y
,那么按值传递对象本身,并使用返回值”(不确定我是否陷害)好吧)
这意味着,对于每个x
,如果f(x)
的值永远不会更改,则使用方法#2。
这向其他程序员传达了你想要做的事情。我希望这个解释有所帮助。
无论如何,两种方法都做同样的工作,输出是相同的。