读书人

作用域联接存储类型

发布时间: 2012-11-05 09:35:12 作者: rapoo

作用域,连接,存储类型

转载http://blog.csdn.net/ts_54eagle/archive/2009/08/06/4418627.aspx

作用域,连接,和存储类型

# 1 "main.n" 1
int n;
# 5 "main.c" 2
int main()
{

????? printf("n=%d\n",n);
????? system("pause");
return 1;
????? }以上的示例应该能比较明显解释#include的作用,和使用方法了。但是在实际开发中,这种使用方式是严重的不规范行为,强烈建议不要使用。同样下边的例子也是一样的建议。

例2:

(1)包含.c文件:
//file1: main.c
#include <stdio.h>
#include <stdlib.h>
#include "test.c"
int main(int argc, char *argv[])
{
m=5;
for(int i=0;i<5;i++)
{
add();
m++;
test();
}
system("PAUSE");
return 0;
}
12: //end of file1
//file2:test.c
static int n;
int m;
int add();
void test()
{
int t_sum;
printf("m = %d\n",m);
printf("n = %d\n",n++);
t_sum = add();
printf("add = %d\n",t_sum);
}
int add()
{
static int sum;
sum++;
return sum;
}
//end of file2这个例子是采用 包含.c文件 的方法实现的。 在编译时,直接去编译main.c文件,预处理器会先把test.c文件中的内容复制到main.c中来,然后再对新的main.c进行编译。
编译命令:
gcc main.c -o main
可以看到,这里并没有对test.c进行编译,但还是生成了最终的main可执行程序。 也可以通过命令来观察一下预处理的结果: 编译命令:
gcc -E main.c -o main.cpp(仅预处理)
在main.cpp文件末尾可以看来下面一段代码:
# 3 "main.c" 2
# 1 "test.c" 1


static int n;//此处是test.c的内容
int m;
int add();
void test()
{
???? int t_sum;
???? printf("m = %d\n",m);
???? printf("n = %d\n",n++);
???? t_sum = add();
???? printf("add = %d\n",t_sum);
???? }
int add()
{
??? static int sum;
??? sum++;
??? return sum;
??? }
# 4 "main.c" 2//此处是main.c的内容
int main(int argc, char *argv[])
{
m=5;
for(int i=0;i<5;i++)
{
??????????? add();
m++;
test();
}可见,其实就是将test.c文件中的内容添加到了main函数之前,然后对新的文件进行编译,生成最终的可执行程序。
这次如果还是按照上面的方法只编译main.c的话就会出错,因为变量m和函数add并没有在main.c中定义,所以编译时需要将test.c一起编译:

编译命令:

gcc -c main.c -o main.o #编译main.c

gcc -c fun.c -o fun.o #编译fun.c

gcc main.o fun.o -o main #用main.o fun.o生成main

到这里大家应该已经理解包含#include文件和多文件程序的本质区别了。

包含文件仅仅是在c预编译时进行再次整合,最终的还是合并成一个文件编译,生成执行文件。

而多文件的编译,是多个文件分别编译,(也可能是在编译时添加必须的标识),然后通过链接器将各个文件链接后加载形成可执行文件。

这种方式会使得我们的定义和声明分开,不容易产生重定义。而且也利于模块化,仅通过头文件来给出接口,而隐藏具体的实现。
预处理时会把头文件中的内容复制到包含它的文件中去,而复制的这些内容只是声名,不是定义,所以它被复制再多份也不会出现"重定义"的错误。。。

前面说了头文件的方法也是模块化程序设计中的一种非常有利的手段。把同一类功能写到一个.c文件中,这样可以把他们划为一个模块,另外再对应的写上一
个.h文件做它的声明。这样以后再使用这个模块时只需要把这两个文件添加进工程,同时在要使用模块内函数或变量的文件中包含.h文件就可以了。

举个很实际的例子,在单片机、ARM或其他嵌入式开发中,每一个平台可能本身都有多种不同的硬件模块,使用时需要去写相应的驱动程序,

这样就可以把各个硬 件模块的驱动程序作为一个模块(比如lcd驱动对对应lcd.c和lcd.h,IIC驱动对应I2C.c和I2C.h等),当具体使用到某个模块时,

只需 要在将对应的.c和.h文件添加进工程,并在文件中包含对就的.h文件即可。

根据以上的原理理解和实际中使用的一些问题及模块化的原则,对头文件写法给出以下几点个人建议作为基础:

(1) 按相同功能或相关性组织.c和.h文件,同一文件内的聚合度要高,不同文件中的耦合度要低。接口通过.h文件给出。

(2) 对应的.c文件中写变量、函数的定义,并指定链接范围。对于变量和函数的定义时,仅本文件使用的变量和函数,要用static限定为内部链接防止外部调用。

(3) 对应的.h文件中写变量、函数的声明。仅声明外部需要的函数,和必须给出变量。有时可以通过使用设定和修改变量函数声明,来减少变量外部声明。

(4) 如果有数据类型的声明 和 宏定义 ,请写的头文件(.h)中,这时也要注意模块化问题,如果数据类型仅本文件使用则不必在写头文件中,而写在源文件(.c)中,会提高聚合度。减少不必要的格式外漏。

(5) 头文件中一定加上#ifndef...#define....#endif之类的防止重包含的语句

(6) 头文件中不要包含其他的头文件,头文件的互相包含使的程序组织结构和文件组织变得混乱,同时给会造成潜在的错误,同时给错误查找造成麻烦。如果出现,头文件中类型定义需要其他头文件时,将其提出来,单独形成全局的一个源文件和头文件。

(7)模块的.c文件中别忘包含自己的.h文件以上几点仅是个人观点,供大家讨论,如果有意见或是认为不合理或是有更合理的方式请讨论指出。

补充1:

按照c语言的规则,变量和函数必须是先声明再使用。可以多次声明,但不可以多次定义。
补充2:变量的定义和声明。

“声明”仅仅是告诉编译器某个标识符是:变量(什么类型)还是函数(参数和返回值是什么)。要是在后面的代码中出现该标识符,编译器就知道如何处理。记住最重要的一点:声明变量不会导致编译器为这个变量分配存储空间。 C语言专门有一个关键字(keyword)用于声明变量或函数:extern。带有extern的语句出现时,编译器将只是认为你要告诉它某个标识符是什么,除此之外什么也不会做(直接变量初始化除外)。
编译器在什么情况下将语句认为是定义,什么情况下认为是声明。这里给出若干原则: #1 带有初始化的语句是定义 例如:

int a = 1; //定义
#2 带有extern的语句是声明(除非对变量进行初始化) 例如:

extern int a; //声明 extern int b = 2; //定义
#3既没有初始化又没有extern的语句是“暂时定义”(tentative definition) C语言中,外部变量只能被(正式)定义一次:

int a = 0; int a = 0; //错误!重复定义
又或者:

int a = 0; double a = 0.1; //错误!标识符a已经被使用
暂时定义有点特殊,因为它是暂时的,我们不妨这样看: 暂时定义可以出现无数次,如果在链接时系统全局空间没有相同名字的变量定义,则暂时定义“自动升级”为(正式的)定义,这时系统会为暂时定义的变量分配存储空间,此后,这些相同的暂时定义(加起来)仍然只算作是一个(正式)定义。 例如: /*Example C code*/ int a; //暂时定义 int a; //暂时定义 int main(void) {

a = 1; return 0;
} int a; //暂时定义

读书人网 >编程

热点推荐