变量,在高级语言中是非常常见的,C语言中也是如此,因此深入的了解变量对编写优质的代码有很大的裨益!由于C++和C语言的天然血缘关系,本文中的原理和例子同样适用于C++语言中。现在,我们主要从以下几个方面来谈谈变量:
第一、 变量的作用域
变量的作用域,换句话说就是变量的作用范围。变量是有范围的,并不是你定义了变量以后,你就可以在程序中的任何位置使用它。例如:
int a;
void f1()
{
int b;
b = a; // 正确
}
void f2()
{
a = 10; // 正确
b = 10; // 错误
}
在上面的代码中,变量a、b都有自己的作用域,a的作用域是全局的,在a的定义之后的任何地方都可以使用它,我们称之为全局变量;b的作用域是局部的,称之为局部变量,只在函数f1之内有效,所以,当在函数f2中给变量b赋值就是错误的。通常我们可以这样以为,变量的作用域是以{}为界限的,例如:
void f3()
{
int c;
c = 10; // 正确
{
int d;
d = c; // 正确
}
d = 1; // 错误
}
变量c在函数f3里定义的,所以它在f3的整个函数范围内都是有效的,而变量d是在函数f3种的一对{}中定义的,它的定义域也就只能在这对{}中,当在{}之后再使用变量d就是非法的了,在VC中编译,会提示“error C2065: 'd' : undeclared identifier”错误,就是表示标识符d没有声明。
另外,当全局变量和局部变量重名的时候,则当两个变量的作用域发生重叠的时候,则以局部变量的作用域为准,例如:
int a = 0;
void main()
{
int a = 1;
{
int a = 2;
printf(“a = %d\n”, a);
}
printf(“a = %d\n”, a);
}
则该程序的输出结果是:
a = 2
a = 1
第二、 全局变量与局部变量
刚才已经提到了全局变量和局部变量,但是在所有{}外面定义的变量就是全局变量吗?我们看以下的程序:
// file1.c
#include <stdio.h>
void f5(int);
static int e;
void f4(int a)
{
e = a;
}
void main()
{
f5(10);
}
// file2.c
void f4(int);
int e;
void f5(int b)
{
e = 5;
f4(b);
printf(“e = %d\n”, e);
}
首先,main函数中以10为参数调用函数f5,在函数f5中,给变量e赋值为5,接下来调用函数f4,参数b的值为10,在函数f4中给变量e赋值为参数a的值10,注意:此e非彼e。我们先来看一看变量e在两个文件中的定义,唯一的区别就是文件file1.c中多了一个关键字:static。然而就是这个static使得file1.c中的变量e变成了局部变量,换句话说这个变量e的作用域已经不是全局的了,它的作用范围值在file1.c这个文件中有效,在file2.c中它是不起作用的。因此不难看出,这个程序的结果是:
e = 5
当然,如果想要使变量e作用域是全局的,程序可修改如下:
// file1.c
#include <stdio.h>
void f5(int);
int e;
void f4(int a)
{
e = a;
}
void main()
{
f5(10);
}
// file2.c
void f4(int);
extern int e;
void f5(int b)
{
e = 5;
f4(b);
printf(“e = %d\n”, e);
}
文件file2.c中的int e前面多了一个关键字:extern,表明这个变量是外部的,而且,
extern int e;
不是变量的定义,它只是声明一个变量,定义和声明的区别我们将在下面介绍。修改以后的程序运行结果是:
e = 10
在实际的工作中,我们都应该尽量少的使用全局变量,尤其在一个多人共同编码的项目中,每个人负责一部分代码的编写,有可能造成全局变量命名的冲突,当要在一个文件中使用一个该文件内多个函数都要用到的变量时,应在该变量定义前面加上关键字:static,使得该变量只在本文件内有效,避免和其他人命名的变量名冲突。
第三、 变量的定义和声明
一般情况下,在C程序中变量的定义和声明是合二为一的,如:
int a;
我们一般称之为变量的定义,这一点上,不如函数来的明显。当我们确实想声明一个变量的时候,我们用以下语句来完成:
extern int a;
关键字extern表示变量是外部的,换句话说,这里只是声明该变量的存在,以便在程序中使用,而变量的定义可以在其他文件,也可以在本文件内部,如:
#include <stdio.h>
extern int a;
void main()
{
printf(“a = %d\n”, a);
}
int a = 5;
在这段代码中,如果没有上面的extern int a;这句话对变量a的声明,则在main函数中使用变量a则是非法的,因为编译程序在编译的时候没有找到a的定义;相反,如果没有下面的这句int a = 5;虽然程序能够通过编译,但是连接(link)的时候也会出现错误,提示没有找到变量a的定义。
变量的定义和声明最重要的区别就是定义变量的时候要为变量分配存储空间,而声明一个变量则不需要。因此,extern int a;这个语句并没有为变量a分配内存空间,如果没有变量a的定义,那么任何对变量a的操作都是非法的,因为这个变量还不存在。
变量的定义和声明另一个区别就是定义变量是可以给变量赋初值,如:
int a = 5;
而声明则不可以,如:
extern int a = 5; // 错误
这个错误显而易见,因为这个5没有存储的空间。
第四、 变量的存储空间
简单的说,在函数中定义的变量一般情况下都在栈(Stack)中,除非你把变量定义成static型的,此时,他和其他在函数外面定义的变量一样,都分配在全局内存中。例如:
#include <stdio.h>
int a, b;
static int c;
main()
{
static int d;
int e;
a = 1;
b = 2;
c = 3;
d = 4;
e = 5;
}
在上面的程序中,变量a、b、c、d都是存储于全局内存中的,而变量e则是存储于栈中。由于函数执行结束之后,会进行还原栈的动作,因此在函数内部定义的变量的存储空间在函数返回以后就不再保留了。
所以,我们会看到需要保留函数内变量的值的时候,一般都把变量定义成static类型,此时变量存储于全局内存中,当函数的占被还原的时候,变量的值还存在,也就是这个道理了。
第五、 变量的初始化
变量的初始化对于大家都是相当熟悉了,形式也比较简单:
int a = 5;
这就是最简单的初始化语句了,如果是下面的语句:
int a;
那么大家知道变量a的默认初始值是多少吗?答案是不确定。当然不是说a的默认值是任意值,而是说变量变量a定义的位置、操作系统以及和使用的编译器相关。例如,在Windows平台上使用VC++6.0编译程序,如果int a;出现在所有函数的外面,那么a的默认初始值就是0,但是如果int a;出现在函数的里面,那么一般情况下a的默认初始值就是0xCC,大家会比较奇怪,为什么微软会使用这么奇怪的初始值呢?我想因为此时a在栈中被分配,二战是不应该被破坏的,如果有非法的语句莫名其妙的跳到这里来执行的话,0xCC是汇编语句中的(int 3)的二进制代码,所以程序就会中断执行,防止不可预料的结果发生。但是在Linux平台上,变量的默认初始值一般都是0。
因此,但我们在写程序的时候,要养成为变量添加初始值的好习惯。
我们再谈一谈数组的初始化,
char a[100];
char a[100] = {‘a’};
这两条语句都是定义一个字符数组,第二条语句是对数组进行了初始化。下面我们来讨论一个有意思的话题,就是这两条语句对编译连接以后的可执行文件的大小有什么影响?
当然了,如果数组的初始化是在函数中,则影响不明显,但是在所有函数的外面定义的话就不一样了,不信你可以试试下面的程序:
#include <stdio.h>
char [100000000] = {‘a’};
void main()
{
}
编译的时候你可要先确认一下你的磁盘剩余空间呀,如果少于100M那么请勿试验,否则后果自负!说到这里你应该明白了吧,编译连接以后的可执行文件有100多M呀,吓人吧!其实这是由于在Windows平台上的可执行文件的格式所决定的,有兴趣的朋友可以研究一下Windows的PE文件的结构。
以上我们讨论了C语言中关于变量的一些小问题,也是我在实际工作和学习中遇到了,当然其中不免有一些错误,希望你能告诉我,我将非常高兴。