main里面有一个a,值是100。它调用test,test里面直接打印a,可以吗?
#include <stdio.h>
void test(void)
{
printf("%d\n", a); /* 错误:此处没有可见的 a 声明 */
}
int main(void)
{
int a = 100;
test();
return 0;
}
如果顺着运行过程想,会觉得main的a已经有值了,怎么还报没有声明?这里先别背“局部变量”四个字。我们把“代码写在哪里”和“执行时从哪里过来”分开。
test被调用了,但它没有写进main的大括号
test的函数体是独立定义的。编译器检查那行printf时,这个位置没有可见的a声明;运行时谁调用它,不能倒过来改变名字的查找范围。
对照一下:main里面再套一个普通代码块,这层确实写在main内,可以用外层的a。test要用这个数,则可以让调用处把值交给它:
#include <stdio.h>
static void test(int value)
{
printf("test:%d\n", value);
}
int main(void)
{
int a = 100;
{
int b = 20;
printf("inner:%d,%d\n", a, b);
}
/* 这里能使用 a,不能使用上面那个 b。 */
test(a);
return 0;
}
输出是inner:100,20和test:100。普通内层块可以看到外层a,独立函数使用的是自己的形参value。调用处的test(a)把100交过去,并没有让test获得main里面那个名字。

这时如果把形参也命名为a,它仍是test自己的变量。名字相同,不能把两个对象合并。
那我原来这段“函数”为什么能打印100
#include <stdio.h>
int main(void)
{
int a = 100;
void test(void);
{
printf("%d\n", a);
}
return 0;
}
注意void test(void);后面的分号。到这里是一条函数声明,已经结束了。后面的花括号是main内的普通代码块,不是test的函数体;程序里也没有调用test。
所以输出100本身没错,错的是把它当成“独立函数可以访问调用者局部变量”的证据。
标准C不允许在一个函数体内再定义另一个函数。先辨认这段代码到底声明了什么、执行了什么,再解释它的输出。
同名变量出现时,到底修改了哪一个
再看一个更容易混淆的情况:
#include <stdio.h>
int main(void)
{
int a = 100;
{
int a = 20;
printf("inside=%d\n", a);
}
printf("outside=%d\n", a);
return 0;
}
先在内层打印20,再在外层打印100。看起来像a先变20,出了块又恢复100,但这次根本没有“恢复”动作。
内层那句有int,新声明了另一个对象。在内层使用a,找到的是这个新声明;离开它的范围后,再使用a,找到的是外层声明。外层那个100一直没被改过。
把内层的int a = 20;换成a = 20;,再运行一次。这次没有新声明,就会修改原来的对象,外层也打印20。
| 内层写法 | 内层a对应谁 | 外层最后的值 |
|---|---|---|
int a = 20; | 新声明的内层对象 | 100 |
a = 20; | 已经可见的外层对象 | 20 |
所以看到赋值,先找到这一行里的名字绑定到哪个声明。不能只看整篇程序里有没有另一个也叫a的变量。
名字看不见了,对象就一定没了吗
刚才讨论的是“在哪能使用这个名字”。但函数返回后,对象还能不能保留,是另一个问题。
#include <stdio.h>
static int next_count(void)
{
static int count;
return ++count;
}
int main(void)
{
int first = next_count();
int second = next_count();
printf("%d,%d\n", first, second);
return 0;
}
输出是1,2。局部static的count不会每次调用都重新从0开始;第一次加到1,第二次接着加到2。
可是在main里直接写printf("%d", count),依然看不见这个名字。对象在两次调用之间保留着,不等于这个名字对整个程序都可见。
这时再区分两个词就有具体对象了:作用域决定名字在哪可见,存储期决定对象存在多久。
回到开头,test究竟需要什么
如果test只需要100这个值,把它作为参数传入即可。如果test还要修改main里的对象,单纯复制一个整数就不够了,需要另外传递访问那个对象的方式。
这正好接到下一课:函数改了参数,外面的值为什么有时变、有时不变。先把名字找对,再追踪复制与修改,就不会把调用、变量同名和共享对象混在一起。
6675



被折叠的 条评论
为什么被折叠?



