C语言_12 全局变量

简介: 全局变量

12.1.1 全局变量:定义在函数之外的变量,全局的生存期和作用域

定义在函数内部的是本地变量。而定义在外面的是全局变量。
本地变量最大特点是生存期、作用域都在函数内部,出来就用不了了。

int gAll=12;
int main()
{
    printf("in %s, gAll is %d",__func__,gAll);//__func__是输出当前函数名称
    return 0;
}

如果在主函数中先输出gAll,然后执行函数f,在函数f中把gAll+2,然后返回主函数,再次输出gAll,会发现输出结果是12 14。可见我们可以在任何函数中直接访问、改变全局变量。
如果全局变量没赋初值,会自动被赋予0值,不像本地变量会出现奇怪的值。
如果是指针的话,没赋初值会得到NULL,因为只能用编译时已知的值来初始全局变量。而初始化发生在main函数前。
如果int gAll=f();不可以,因为这时候电脑表示他还不知道f()是什么
如果int gAll=12; int g2=gAll;也不行(Dev C++可以)
但是如果const int gAll=12; int g2=gAll;就可以了。但是不建议这样做。
如果函数内部有和全局变量同名的变量,那么全局变量会被隐藏。
比如在主函数中输出gAll,然后进入函数f,int gAll=1,输出gAll,再回到主函数再次输出gAll,会得到12 1 12(因为f函数里的gAll生存期就是f)

12.1.2 静态本地变量:能在函数结束后继续保持原值的本地变量

在本地变量前加上static修饰符,成为静态本地变量。离开函数之后,静态本地变量还会存在。
静态本地变量的初始化只会在第一次进入这个函数时做,以后再进入函数时还是会保持上次离开的值。就是即便输入:

f(){
    static int a=1;
    a+=2;
}

f()连续执行三次,不是每次都会a=1,而是1 3 3 5 5 7。
静态本地变量实际是特殊的全局变量。他们位于相同的内存区域;(可以试着看下全局变量、本地变量、静态本地变量的地址%p,静态本地变量和全局变量地址都是一样的)
静态本地变量有全局内的生存期,但是只有函数内的局部作用域(static在这里的意思是局部作用域,本地可访问。)

12.1.3 后记:返回指针的函数,使用全局变量的贴士

关于指针以前讲过,如果指针返回本地变量的地址,这是一件很危险的事情。因为离开函数之后本地变量就不存在了。

int *f();
void g();

int main()
{
    int *p=f();
    printf("*p=%d",*p);
    g();
    printf("*p=%d",*p);
    return 0;
}
int *f()
{
    int i=12;
    return &i;
}
void g()
{
    int k=24;
    printf("k=%d",k);
    return 0;
}

输出p=12,k=24,p=24.
可以得知,在函数f中本地变量i那个地址又给了k使用。这就容易出问题。
但是全局变量和静态本地变量可以。
返回函数在malloc内的地址是安全的,但是也容易出问题。最好的办法是返回传入的指针。
tips
1.不要用全局变量在函数之间传递参数和结果(可以,但是有问题。详见丰田汽车案??)。使用全局变量和静态本地变量的函数是线程不安全的,尽量避免全局变量。

12.2.1 宏定义

编译预处理指令
我们在第一节课就见过了。就是#开头的(#include)。他们不是C语言的一部分,但是C语言离不开他们。
现在要定义PI=3.14159,我们可以定义一个const的全局变量;但以前没有const的时候,用#define来定义一个宏

#define PI 3.14159

这样C语言在编译预处理时会把所有PI变成3.14159。
我们可以通过下面的方法看在编译过程中留下来的零始文件
gcc:

gcc xxx.c --save-temps
ls-l

可以看到会出现xxx. c, xxx. i, xxx. s, xxx. o, xxx. out
文件c->i这一步做了π的替换,i->s这一步产生汇编代码文件,s->o产生目标代码文件 ,最后再和其他可链接的东西链接起来生成可执行的out。
可以发现.i比.c大很多
如果用tail来输出c和i的结尾部分

tail xxx.c;
tail xxx.i;

可以看到.i中把已经所有的PI替换为3.14159。
同样的,我们也可以#define FORMAT "%f\n"FORMAT是按格式输出。
在main函数中写:printf(FORMAT,2*PI);编译出来也是没有问题的。不过注意如果FORMAT是写在双引号里面,printf输出的就是FORMAT这六个字母了。

define会先对程序进行预处理,把宏都替换掉;

#define 单词 值

define会对后面的值原封不动地进行文本替换,所以千万小心结尾不要加分号。因为这不是c的语句,c的语句结尾才需要加分号。

如果一个宏中嵌套有其他宏的名字,还会再次被替换;
如果一个宏的值超过一行,最后一行之前的行末需要加\,应该是处理回车的问题。
宏的结尾可以有注释。

#define prt printf("123");\
            printf("456");

我们还可以定义没有值的宏#define -DEBUG用来做条件编译,例如如果存在编译这一部分代码;如果不存在编译其他部分代码。
c编译器里有一些预先定义的宏,可以直接用的

__LINE__//行号
__FILE__//文件名
__DATE__//编译时日期
__TIME__//编译时时间
__STDC__

12.2.2 带参数的宏

还可以定义像函数一样的宏,带参数。
#define cube (x)((x)*(x)*(x))
第一个括号内是单词名字,后面是它的值。

printf("%d\n",cube(5));//输入tail 文件名,输出((5)*(5)*(5))

也可以括号内做运算,如输出cube(i+2)。
一些细节
x要加括号。否则比如:(rad to deg 弧度制转化为角度制)

RADTODEG(x) (x*57.29578);
RADTODEG(x) (x)*57.29578;
RADTODEG(x) ((x)*57.29578);

如果x=5+2,第一个计算的就是5+2*57.29578;
如果计算180/RADTODEG(1),第二个计算的就是180/1*57.29578。这样就能看出来如果宏带有参数,整个宏的值和每个参数都要有括号。
可以带有多个函数
#define MIN(a,b) ((a)>(b)? (b):(a))
也可以嵌套、组合使用其他宏。
千万不要加分号,这不是c的语句,比如if和else中间多了个加分号的宏,展开时就会有两个分号;第二个分号相当于有个空行,就把if和else分开了。
大型的参数中,带参数的宏非常常见,运行效率比函数高。(牺牲空间换取效率)
宏有一个缺点:没有类型检查。
C语言有inline机制,有类型检查,也许会逐渐取代宏。
还有很多编译预处理指令,比如条件编译、error等等,这些补充内容本课中不会讲到了。

目录
相关文章
|
4月前
|
程序员 编译器 C语言
嵌入式 C 语言中的全局变量问题
嵌入式 C 语言中的全局变量问题
40 0
|
4月前
|
存储 C语言
C语言中的局部变量与全局变量
C语言中的局部变量与全局变量
45 1
|
4月前
|
C语言
全局变量在C语言中的使用
全局变量在C语言中的使用
38 1
|
4月前
|
存储 C语言
C语言中的局部变量与全局变量详解
C语言中的局部变量与全局变量详解
46 0
|
4月前
|
存储 C语言
【C语言】全局变量与局部变量
【C语言】全局变量与局部变量
24 1
|
4月前
|
C语言
C语言5🔥:复合赋值,递增递减运算符,局部变量与全局变量
C语言5🔥:复合赋值,递增递减运算符,局部变量与全局变量
49 0
|
存储 编译器 C语言
【C语言】关键字static——static修饰局部变量、全局变量和函数详解!
【C语言】关键字static——static修饰局部变量、全局变量和函数详解!
296 0
|
4月前
|
编译器 C语言 开发者
【新手解答4】深入探索 C 语言:全局变量声明、全局函数声明 + 宏定义
【新手解答4】深入探索 C 语言:全局变量声明、全局函数声明 + 宏定义
334 0
|
10月前
|
C语言 C++
07 C++ - 全局变量检测增强(比较C语言)
07 C++ - 全局变量检测增强(比较C语言)
50 0
|
11月前
|
C语言
初识C语言的static关键字(修饰局部变量、全局变量和函数)
初识C语言的static关键字(修饰局部变量、全局变量和函数)