程序结构
今天继续学习c语言语法,相对之前的知识而言,这次的知识就比较重要了,算是C语言的核心基础了,所以得好好学它,下面正式开始:
存储类别: 它指定了变量生存期(时间)与作用域(空间)
![](http://images0.cnblogs.com/blog/324374/201312/05210512-a36214b996ea497599db148703aad9c2.png)
对于下面这个函数:
![](http://images0.cnblogs.com/blog/324374/201312/05210924-994c7c2670ac468ca78e66f4a99b3031.png)
![](http://images0.cnblogs.com/blog/324374/201312/05211019-9f58b17b00ed48d9a24cf5f551ead3ba.png)
![](http://images0.cnblogs.com/blog/324374/201312/05211220-3633a93cba7e4e04a3be19cfbb90ce12.png)
![](http://images0.cnblogs.com/blog/324374/201312/05211319-4ef957b47aef4eeeb4d0a2637d264df4.png)
![](http://images0.cnblogs.com/blog/324374/201312/05212048-d55799fd68fe427e83ddb09bff32a0d3.png)
运行结果:
![](http://images0.cnblogs.com/blog/324374/201312/05212133-ebe40d1b5f57466d9d1a56e8e367c4e1.png)
解析:
静态变量,只分配一次内存,离开作用域的时候,生存期并未结束,它的生存期是整个程序的执行期[这也说明作用域和生存期是不一样的],所以上面执行了两次的结果累加了。
另外,需要注意:
![](http://images0.cnblogs.com/blog/324374/201312/05212950-2f241e142e774bc6a0a62695c08c5b6b.png)
关于这个可以参考博文:http://www.cnblogs.com/webor2006/p/3427567.html,里面有详细的说明。
![](http://images0.cnblogs.com/blog/324374/201312/05213232-6c216e70c1bc4f57985e4693c495a5e8.png)
下面有两个C文件:
![](http://images0.cnblogs.com/blog/324374/201312/05213909-1138f837f9ac4e4aa06b0fe83caf4595.png)
test.c的内容:
![](http://images0.cnblogs.com/blog/324374/201312/05214019-02ea8a0f2d3f4721b7486d855c6429e5.png)
1_9.c的内容:
![](http://images0.cnblogs.com/blog/324374/201312/05214147-a6f94ee20c94457bb50fc695d42be646.png)
这时编译两个c到一个可执行文件:
![](http://images0.cnblogs.com/blog/324374/201312/05214335-12804150a51a4809932ab2094e046fc9.png)
解决之道,就是将test.c的文件中的全局变量声明为static的,因为static修饰全局变量,它的作用域只限本文件中,对于1_9.c是看不到的:
![](http://images0.cnblogs.com/blog/324374/201312/05214637-1cb523b9434e4c42b4ba87376ccd4485.png)
这时编译就成功了:
![](http://images0.cnblogs.com/blog/324374/201312/05214804-3a56f32350774b2a84ebf08d43af2457.png)
![](http://images0.cnblogs.com/blog/324374/201312/05214913-ccf47d6bedad4c84a83b1b8751be5168.png)
这个依照静态全局变量的,就很容易理解了:
test.c:
![](http://images0.cnblogs.com/blog/324374/201312/05215612-b2cf09cb7df24075bd081dcc75c7cc61.png)
1_9.c:
![](http://images0.cnblogs.com/blog/324374/201312/05215706-4d695bdd3aa5462e822e2a6c46446bfd.png)
这时编译:
![](http://images0.cnblogs.com/blog/324374/201312/05215756-4c82174de0a74e66b5b1273b2d48e09d.png)
将test.c的函数声明为静态的,这时就成功了:
![](http://images0.cnblogs.com/blog/324374/201312/05215901-3136adec39504f30b53fc96182d71a89.png)
![](http://images0.cnblogs.com/blog/324374/201312/05215949-9e9f5d313a294a5e83c09ef91908516d.png)
提示:不管是局静态局部变量还是静态全局变量生存期是整个程序执行期
![](http://images0.cnblogs.com/blog/324374/201312/05220739-802e90665da248c093ba205a1b6d641e.png)
修饰变量:
还是两个.c文件:
test.c声明一个全局变量:
![](http://images0.cnblogs.com/blog/324374/201312/05214019-02ea8a0f2d3f4721b7486d855c6429e5.png)
而如果1_9.c想直接用这个变量会怎么样呢?
![](http://images0.cnblogs.com/blog/324374/201312/05221236-0f112fecf04f42b09661c12be1077bcd.png)
![](http://images0.cnblogs.com/blog/324374/201312/05221348-8b9c31215d0e4aaa8a9d850c3d662d27.png)
那如果就是想用test.c定义的全局变量呢,这时extern就派上用场了:
![](http://images0.cnblogs.com/blog/324374/201312/05221706-acada28fc67e46e88a535f005fb5d9b6.png)
再次编译,这回就成功了:
![](http://images0.cnblogs.com/blog/324374/201312/05221823-33c93440fad74e71aa0e0eeedc5d94cf.png)
修饰函数:
test.c:
![](http://images0.cnblogs.com/blog/324374/201312/05215612-b2cf09cb7df24075bd081dcc75c7cc61.png)
1_9.c:
![](http://images0.cnblogs.com/blog/324374/201312/05222621-6cd0cbf3f85f4e1fb655045d9cb7de13.png)
编译貌似没有报错:
![](http://images0.cnblogs.com/blog/324374/201312/05222744-9bf90ec8a4b14ed8850dcb931eb4e2b0.png)
但如果把警告打开再编译一下:
![](http://images0.cnblogs.com/blog/324374/201312/05222839-88357bd1da694cf68b99d568013ca614.png)
所以,并非直接能调用其它文件声明的全局函数,而还得用上extern来解决:
![](http://images0.cnblogs.com/blog/324374/201312/05223123-f015d1f6142241808bd42f6f3773b5e2.png)
这时再编译:
![](http://images0.cnblogs.com/blog/324374/201312/05223231-a24aec67663e4c1d8b832eb12e2ccac6.png)
实际上,对于函数,extern关键字可以省略,但是变量则不行,如下:
![](http://images0.cnblogs.com/blog/324374/201312/05223615-21f523e1ff2a41bea6c8f218248e4400.png)
![](http://images0.cnblogs.com/blog/324374/201312/05223725-478a85d3faca45b68a2d9ffb15afbeee.png)
另外,还有两个修饰关键字,可以了解一下:
![](http://images0.cnblogs.com/blog/324374/201312/05224000-c62e4e2bfd7e4413a98de3ce01fde0b1.png)
标识符的五种作用域[了解下既可!]:
![](http://images0.cnblogs.com/blog/324374/201312/06211004-717cabe52ef84ab8aa09c39579e92837.png)
![](http://images0.cnblogs.com/blog/324374/201312/06211155-019389e7fd4d45a29de5694088894207.png)
![](http://images0.cnblogs.com/blog/324374/201312/06211301-ec48b59df18643aeac649a2ffc669978.png)
①只能在函数体中引用
②标签号(用于goto)是唯一具有函数作用域的标识符
![](http://images0.cnblogs.com/blog/324374/201312/06212007-1e19541034094e8d8aac9784eaf8fa00.png)
![](http://images0.cnblogs.com/blog/324374/201312/06212106-2ded83caaf244062a0a20de55d02362d.png)
①在程序块中声明的标识符具有块作用域,块作用域开始于声明处,结束于程序块的右花括号
很容易理解,平常只是我们没将它理论化而已:
![](http://images0.cnblogs.com/blog/324374/201312/06212825-44300927cdc34ccbb70b0d9dee9d4a8c.png)
![](http://images0.cnblogs.com/blog/324374/201312/06213033-3b19262f66a544c49811c2e38934d43d.png)
②函数的形式参数、函数的局部变量、在复合语句中声明的变量具有块作用域
![](http://images0.cnblogs.com/blog/324374/201312/06213333-c0ceafdb70474b9bba2220142abe0df2.png)
③如果外层块和内层块有相同名字的变量,外层说明会被内层说明屏蔽(最近嵌套原则)
![](http://images0.cnblogs.com/blog/324374/201312/06213508-ff7474e9ca79403e832b12307421506f.png)
在一个程序文件的所有函数定义之外定义的标识符,则该标识符具有全局作用域,即该标识符在整个程序包括的所有文件中都有效,都是可见的,都是可以访问的。
也就是extern全局变量, extern函数
![](http://images0.cnblogs.com/blog/324374/201312/06213625-9f2f1512212b4eddbdf104bb7d79682e.png)
在函数外面定义的标识符并且用static修饰,能够被从标识符说明到文件结束之间的所有函数引用
也就是static全局变量、static函数定义或函数原型
预处理指令:
![](http://images0.cnblogs.com/blog/324374/201312/06213944-50c5e81ecc4b4f6bb8d739c00ee1a3e4.png)
![](http://images0.cnblogs.com/blog/324374/201312/06220043-28403b5978604d299e194242d9abc396.png)
下面有三个文件:test.c、test.h、1_9.c,来演示一下预编译指今的用法:
![](http://images0.cnblogs.com/blog/324374/201312/06220226-bd03d51b6a3a4720851aa4aa7c154f7c.png)
它们的关系如下:
![](http://images0.cnblogs.com/blog/324374/201312/06220615-aeba0d6ab0a440a6b8afdb952378d329.png)
test.h:
// ifndef defing endif 是为了防止重复包含头文件 #ifndef _TEST_H_ #define _TEST_H_ void fun(void);//声明了函数 #endif /* _TEST_H_ */
test.c:
#include <stdio.h> void fun(void) { printf("hello fun test ...\n"); }
1_9.c:
#include <stdio.h> #include "test.h"//包含头文件 int main(void) { fun(); return 0; }
运行:
将函数放到.h里面声明的好处,可以复用,需要调用的.c文件中只需包含一下.h文件既可,另外,全局变量最好不要定义.h头文件中,而应该放到.c文件中:
为了说明这个原因,再增加一个.c文件,如下:
里面啥都不做,只包含头文件:
这时在.h中声明一个全局变量,注意:不是static的:
test.h:
我们知道1_9.c也包含了test.h头文件,也就是test.h现在被test2和1_9.c所包含,这时编译:
这也就是为什么全局变量最好不要定义.h头文件中的原因了,当被多个文件包含时就会出现上面的错误,虽说在.h中已经有重复定义的判断了,那怎么解决呢?
解决方法如下:
test.c:
test.h:
这时,其它都不用变,test.h还是被两个文件同时包含,再编译就成功了:
条件编译:
![](http://images0.cnblogs.com/blog/324374/201312/06224014-88ce46df773a4f7e9e7e7925b84d07c7.png)
下面举一个在程序中使用条件编译的用法:
![](http://images0.cnblogs.com/blog/324374/201312/06224625-2b8edc8d838245dd8d2997a326435c6e.png)
默认编译运行:
![](http://images0.cnblogs.com/blog/324374/201312/06224812-db1ee0aa7cc14684bcda1f9c431c9040.png)
这时我们在文件中定义一个DEBUG宏:
![](http://images0.cnblogs.com/blog/324374/201312/06225034-7094283229074969a01d7ac38092e5f1.png)
![](http://images0.cnblogs.com/blog/324374/201312/06225148-89d56dea3c45431c85d73429376fa9b9.png)
另外,也可以不在程序中加入这个宏的定义,而在gcc编译时携带参数来编译出带这个宏的可执行文件:
先将宏定义从源文件去掉:
![](http://images0.cnblogs.com/blog/324374/201312/06225426-88d79f0588fd4f779d0ecde72fb7906f.png)
![](http://images0.cnblogs.com/blog/324374/201312/06225651-6f2d75aefcf24687ab034aeeb3ce5145.png)
![](http://images0.cnblogs.com/blog/324374/201312/06225803-08e7d1671a374551a0d094d234c0cf9d.png)
好了,这次的内容就学到这,里面的东西需细细体会,这也基础知识也是助自己深入学习C很有利的因素,之后就已经到了C语言的最核心的部分了,所以需不骄不躁,一步一个脚印学习。