问题描述
void doSomething(int b){
b = 6;
printf("%d",b);
}
int a = 5;
int main(void){
doSomething(a);
printf("%d",a);
return 0;
}
我应该得到输出 65
,编译和执行都没有错误。通过跟踪代码,这是我的看法:
- 整数
a
被赋值为5
。 - 由于 C 是严格按值传递的,因此
doSomething(a) == doSomething(5)
。
现在在运行 b = 6;
行之前,我相当确定 b == 5
。因此,通过运行该行,程序可以有效地读取:
5 = 6;
一个常量(因为我没有更好的术语)被分配给另一个常量。在 Python 中,这会因语法错误而失败,并且出现错误是有道理的。为什么不引发语法错误?
解决方法
如果您查看调试器,您会发现这里有两个变量在起作用,a
在您的代码中是全局作用域,而 b
是局部变量到 doSomething()
函数。
这是两个完全独立的变量。当您调用 doSomething()
函数时,想象一下发生以下情况:
stack_push(a);
call(doSomething);
编译器在 doSomething()
的内部执行如下操作:
int b = stack_pop();
...
这就是制作“副本”的方式。
请注意,这个考虑变量的模型是一个近似值,并且优化编译器可能会做一些完全不同的事情,只要达到相同的结果,根据 C 规范。
,5 = 6;
与 b = 6;
b = 6;
将值 6 赋给变量 b
,b
具有自动存储,这是另一回事
5 = 6;
不是 C
中的有效表达式,您误解了这个概念,认为两者是相同的。
运行以下程序以查看错误:
void doSomething(int b){
b = 6;
5 = 6;
printf("%d",b);
}
int a = 5;
int main(void){
doSomething(a);
printf("%d",a);
return 0;
}
现在你会得到第三行的错误:
错误:左值需要作为赋值的左操作数
5 = 6;
我决定将 5 = 6;
放在 main()
中。它产生了一个类似于 Python 中的编译错误,Johnny Mopp 和 Martin James 的评论现在有意义了。
b
确实是一个变量而不是实际的常量。我的印象是传值是只传递变量所包含的内容,并且完全不考虑 b
是一个变量的想法,认为该函数只允许整数,而 {{1} } 只不过是一个占位符,不同于变量。
我的印象是 b
是完全 b
,而不是变量分配值 5
。
我目前在脑海中看到的是:
- 整数
5
被赋值为a
。 - 由于 C 是严格按值传递的,因此
5
。 -
doSomething(a) == doSomething(5)
在调用b
时被赋予值5
。 -
doSomething(5)
按照行b
分配了值6
。 - ...等等等等。
感谢大家的评论和回答!
,C 2018 标准在 6.5.2.2 4 中说明了如何执行函数调用:
在准备调用函数时,会评估参数,并为每个参数分配相应参数的值……
这意味着,在带有 void doSomething(int b)
的函数定义中,b
本身被定义为一个变量(技术上是一个对象),并且当函数使用 {{ 1}} 或 doSomething(a)
,doSomething(5)
或 a
的值被分配给 5
,就像您执行了 b
或 b = a
。这只是一项任务;它不会在 b = 5
和 b
或 a
之间建立任何持久的联系。
在函数内部,5
是一个变量,所以当然可以执行赋值b
。这只是将 b = 6
分配给变量 6
。
这里有更多关于参数如何成为它们自己的变量。 3.16 定义参数为:
…作为函数声明或定义的一部分声明的对象,在函数入口处获取值,…
6.9.1 9 说:
每个参数都有自动存储时长;它的标识符是一个左值……