C语言头文件及多文件编程的理解
C语言头文件和多个文件之间相连接的问题只有在处理大型程序的时候才能用到,如果我们接下来学习MFC的知识,这个地方很容易理解 。
这个地方引用一下网上的一个介绍简单讲解一下。
文件名 First.c play2() {
……………….
play1();
………………..
} play1()
{ ……………..
play2(); …………………… }
main()
{
play1();
}
也许大部分都会看出来了,这就是经常用到的一种算法, 函数嵌套, 那么让我们看看, play1和play2这两个函数哪个放到前面呢?
这时就需要我们来使用声明.
文件名 First.c
play1();
play2();
play2()
{
……………….
play1();
……………….. } play1() { …………………….
play2();
…………………… }
main()
{
play1();
}
一个大型的软件项目,可能有几千个,上万个play, 而不只是play1,play2这么简单, 这样就可能有N个类似 play1(); play2(); 这样的声明, 这个时候就需要我们想办法把这样的play1(); play2(); 也另行管理, 而不是把他放在.c文件中, 于是.h文件出现了.
文件名 First.h
play1();
play2();
文件名 First.C
#include “first.h”
play2()
{
……………….
play1();
………………..
} play1();
{
……………………..
play2();
……………………
}
main()
{
play1();
}
如在second.c中还有一个函数需要调用first.c文件中的play1函数, 如何实现呢?
Second.h 文件
play1();
second.c文件
***()
{
…………….
Play();
……………….
}
在second.h文件内声明play1函数,怎么能调用到first.c文件中的哪个play1函数中呢? 是不是搞错了,没有搞错, 这里涉及到c语言的另一个特性:存储类说明符.
C语言的存储类说明符有以下几个, 我来列表说明一下
说明符
用 法
Auto
只在块内变量声明中被允许, 表示变量具有本地生存期.
Extern
出现在顶层或块的外部变量函数与变量声明中,表示声明的对象具有静态生存期, 连接程序知道其名字.
Static
可以放在函数与变量声明中,在函数定义时,只用于指定函数名,而不将函数导出到链接程序,在函数声明中,表示其后边会有定义声明的函数,存储类型static.在数据声明中,总是表示定义的声明不导出到连接程序.
无疑, 在例程中的second.h和first.h中,需要我们用extern标志符来修饰play1函数的声明,这样,play1()函数就可以被导出到连接程序, 也就是实现了无论在first.c文件中调用,还是在second.c文件中调用,连接程序都会很聪明的按照我们的意愿,把他连接到first.c文件中的play1函数的定义上去, 而不必我们在second.c文件中也要再写一个一样的play1函数.
但随之有一个小问题, 在例程中,我们并没有用extern标志符来修饰play1啊, 这里涉及到另一个问题, C语言中有默认的存储类标志符. C99中规定, 所有顶层的默认存储类标志符都是extern .
*****在.h文件中声明的函数,如果在其对应的.c文件中有定义,那么我们在声明这个函数时,不使用extern修饰符, 如果反之,则必须显示使用extern修饰符.
这样,在C语言的.h文件中,我们会看到两种类型的函数声明. 带extern的,还不带extern的, 简单明了,一个是引用外部函数,一个是自己生命并定义的函数.
最终如下:
Second.h 文件
Extern play1();
上面洋洋洒洒写了那么多都是针对函数的,而实际上.h文件却不是为函数所御用的. 打开我们项目的一个.h文件我们发现除了函数外,还有其他的东西, 那就是全局变量.
在大型项目中,对全局变量的使用不可避免, 比如,在first.c中需要使用一个全局变量G_test, 那么我们可以在first.h中,定义 TPYE G_test. 与对函数的使用类似, 在second.c中我们的开发人员发现他也需要使用这个全局变量, 而且要与first.c中一样的那个, 如何处理? 对,我们可以仿照函数中的处理方法, 在second.h中再次声明TPYE G_test, 根据extern的用法,以及c语言中默认的存储类型, 在两个头文件中声明的TPYE G_test,其实其存储类型都是extern, 也就是说不必我们操心, 连接程序会帮助我们处理一切. 但我们又如何区分全局变量哪个是定义声明,哪个是引用声明呢?这个比函数要复杂一些, 一般在C语言中有如下几种模型来区分:
1、初始化语句模型
顶层声明中,存在初始化语句是,表示这个声明是定义声明,其他声明是引用声明。C语言的所有文件之中,只能有一个定义声明。
按照这个模型,我们可以在first.h中定义如下TPYE G_test=1;那么就确定在first中的是定义声明,在其他的所有声明都是引用声明。
2、省略存储类型说明
在这个模型中,所有引用声明要显示的包括存储类extern,而每个外部变量的唯一定义声明中省略存储类说明符。
这个与我们对函数的处理方法类似,不再举例说明。
这里还有一个需要说明,本来与本文并不十分相关,但前一段有个朋友遇到此问题,相信很多人都会遇到,那就是数组全局变量
他遇到的问题如下:
在声明定义时,定义数组如下:
int G_glob[100];
在另一个文件中引用声明如下:
int * G_glob;
在vc中,是可以编译通过的,这种情况大家都比较模糊并且需要注意,数组与指针类似,但并不等于说对数组的声明起变量就是指针。上面所说的的程序在运行时发现了问题,在引用声明的那个文件中,使用这个指针时总是提示内存访问错误,原来我们的连接程序并不把指针与数组等同,连接时,也不把他们当做同一个定义,而是认为是不相关的两个定义,当然会出现错误。
正确的使用方法是在引用声明中声明如下:
int G_glob[100];
并且最好再加上一个extern,更加明了。
extern int G_glob[100];
另外需要说明的是,在引用声明中由于不需要涉及到内存分配,可以简化如下,这样在需要对全局变量的长度进行修改时,不用把所有的引用声明也全部修改了。
extern int G_glob[];
对于全局变量的定义和声明,其实还有另外一个解决的方法,聪明的你可能早已经猜到了:),没错,就是用宏定义的技巧实现.比如a.h文件当中有: #ifdef AAA int i=0; #else int i; #endif 那么,在a.c文件当中,有如下语句: ...... #define AAA #include "a.h" ...... 而对于其他的任何包含a.h文件的头文件或者.c源文件,只需要直接包含a.h就行了 ...... #include "a.h" ...... 这样就可以达到在a.c文件当中定义变量一次,而在其他的文件当中声明该变量的目的. 当然了,你完全可以根据自己的需要来决定在哪个需要包含a.h的文件当中定义宏AAA,但是我要说的是 在同一个工程的不同的需要包含a.h的文件当中,你只能定义AAA一次,否则在连接这些目标文件时会出现 重复定义的错误,即使你的单独目标文件编译没有任何的问题. 当然,这里说的仅仅是对全局变量的声明技巧,强烈的推介大家在头文件中使用宏定义实现对整个头文件的防止重复包含,当然了,这个技巧大多数的c语言程序员都懂. #ifndef XXX #define XXX #endif 这样做会让你的程序更加稳健,很大程度上减少了不必要的麻烦... 最后给出一点点全局变量使用需要注意的问题,这也仅仅是个建议,或者说一种编程习惯 ;) 1) 所有全局变量全部以g_开头,并且尽可能声明成static类型. 2) 尽量杜绝跨文件访问全局变量.如果的确需要在多个文件内访问同一变量,应该由该变量定义所在文件内提供GET/PUT函数实现. 3) 全局变量必须要有一个初始值,全局变量尽量放在一个专门的函数内初始化. 4) 如调用的函数少于三个,请考虑改为局部变量实现.
-----------------------------------------------------------------------------
【程序46】 题目:宏#define命令练习(1) 1.程序分析: 2.程序源代码: #include "stdio.h" #define TRUE 1 #define FALSE 0 #define SQ(x) (x)*(x) void main() { int num; int again=1; printf("Program will stop if input value less than 50./n"); while(again) { printf("Please input number==>"); scanf("%d",&num); printf("The square for this number is %d /n",SQ(num)); if(num>=50) again=TRUE; else again=FALSE; } } ----------------------------------------------------------------------------- 【程序47】 题目:宏#define命令练习(2) 1.程序分析: 2.程序源代码: #include "stdio.h" #define exchange(a,b) { / /*宏定义中允许包含两道衣裳命令的情形,此时必须在最右边加上"/"*/ int t;/ t=a;/ a=b;/ b=t;/ } void main(void) { int x=10; int y=20; printf("x=%d; y=%d/n",x,y); exchange(x,y); printf("x=%d; y=%d/n",x,y); } ----------------------------------------------------------------------------- 【程序48】 题目:宏#define命令练习(3) 1.程序分析: 2.程序源代码: #define LAG > #define SMA < #define EQ == #include "stdio.h" void main() { int i=10; int j=20; if(i LAG j) printf("%d larger than %d /n",i,j); else if(i EQ j) printf("%d equal to %d /n",i,j); else if(i SMA j) printf("%d smaller than %d /n",i,j); else printf(" No such value./n"); } ----------------------------------------------------------------------------- 【程序49】 题目:#if #ifdef和#ifndef的综合应用。 1. 程序分析: 2.程序源代码: #include "stdio.h" #define MAX #define MAXIMUM(x,y) (x>y)?x:y #define MINIMUM(x,y) (x>y)?y:x void main() { int a=10,b=20; #ifdef MAX printf("The larger one is %d/n",MAXIMUM(a,b)); #else printf("The lower one is %d/n",MINIMUM(a,b)); #endif #ifndef MIN printf(" The lower one is %d/n",MINIMUM(a,b)); #else printf(" The larger one is %d/n",MAXIMUM(a,b)); #endif #undef MAX #ifdef MAX printf(" The larger one is %d/n",MAXIMUM(a,b)); #else printf("/40: The lower one is %d/n",MINIMUM(a,b)); #endif #define MIN #ifndef MIN printf(" The lower one is %d/n",MINIMUM(a,b)); #else printf(" The larger one is %d/n",MAXIMUM(a,b)); #endif } ----------------------------------------------------------------------------- 【程序50】 题目:#include 的应用练习 1.程序分析: 2.程序源代码: test.h 文件如下: #define LAG > #define SMA < #define EQ == #include "test.h" /*一个新文件50.c,包含test.h*/ #include "stdio.h" void main() { int i=10; int j=20; if(i LAG j) printf(" %d larger than %d /n",i,j); else if(i EQ j) printf(" %d equal to %d /n",i,j); else if(i SMA j) printf("%d smaller than %d /n",i,j); else printf(" No such value./n"); }
-----------------------------------------------------------------------------
【程序51】 题目:学习使用按位与 & 。 1.程序分析:0&0=0; 0&1=0; 1&0=0; 1&1=1 2.程序源代码: #include "stdio.h" void main() { int a,b; a=077; b=a&3; printf("The a & b(decimal) is %d /n",b); b&=7; printf(" The a & b(decimal) is %d /n",b); } ----------------------------------------------------------------------------- 【程序52】 题目:学习使用按位或 | 。 1.程序分析:0|0=0; 0|1=1; 1|0=1; 1|1=1 2.程序源代码: #include "stdio.h" void main() { int a,b; a=077; b=a|3; printf("The a & b(decimal) is %d /n",b); b|=7; printf("The a & b(decimal) is %d /n",b); } ----------------------------------------------------------------------------- 【程序53】 题目:学习使用按位异或 ^ 。 1.程序分析:0^0=0; 0^1=1; 1^0=1; 1^1=0 2.程序源代码: #include "stdio.h" void main() { int a,b; a=077; b=a^3; printf(" The a & b(decimal) is %d /n",b); b^=7; printf(" The a & b(decimal) is %d /n",b); } ----------------------------------------------------------------------------- 【程序54】 题目:取一个整数a从右端开始的4~7位。 程序分析:可以这样考虑: (1)先使a右移4位。 (2)设置一个低4位全为1,其余全为0的数。可用~(~0<<4) (3)将上面二者进行&运算。 2.程序源代码:
#include void main() { unsigned a,b,c,d; scanf("%o",&a); b=a>>4; c=~(~0<<4); d=b&c; printf("%o/n%o/n",a,d); } ----------------------------------------------------------------------------- 【程序55】 题目:学习使用按位取反~。 1.程序分析:~0=1; ~1=0; 2.程序源代码: #include "stdio.h" void main() { int a,b; a=234; b=~a; printf("/40: The a's 1 complement(decimal) is %d /n",b); a=~a; printf("/40: The a's 1 complement(hexidecimal) is %x /n",a); } -----------------------------------------------------------------------------