首页 技术 正文
技术 2022年11月11日
0 收藏 868 点赞 2,546 浏览 3073 个字

多目标文件的链接

stack.c

#include <stdio.h>#define STACKSIZE 1000typedef struct stack {
int data[STACKSIZE];
int top;
} stack;stack s;
int count = 0;void pushStack(int d)
{
s.data[s.top ++] = d;
count ++;
}int popStack()
{
return s.data[-- s.top];
}int isEmpty()
{
return s.top == 0;
}

link.c

#include <stdio.h>int a, b;int main()
{
a = b = 1;pushStack(a);
pushStack(b);
pushStack(a);while (! isEmpty()) {
printf("%d\n", popStack());
}return 0;
}

编译方式:
gcc -Wall stack.c link.c -o main
提示出错信息如下:
c的链接详解

但是代码是可以执行的

定义和声明

static和extern修饰函数

上述编译出现错误的原因是:编译器在处理函数调用代码时没有找到函数原型,只好根据函数调用代码做隐式声明,把这三个函数声明为:

int pushStack(int);
int popStack(void);
int isEmpty(void);

编译器往往不知道去哪里找函数定义,像上面的例子,我让编译器编译main.c,而这几个函数定义却在stack.c里,编译器无法知道,因此可以用extern声明。修改link.c如下:

#include <stdio.h>int a, b;extern void pushStack(int d);
extern int popStack(void);
extern int isEmpty(void);int main()
{
a = b = 1;pushStack(a);
pushStack(b);
pushStack(a);while (! isEmpty()) {
printf("%d\n", popStack());
}return 0;
}

这样编译器就不会报警了。这里extern关键字表示这个标识符具有External Linkage.pushStack这个标识符具有External Linkage指的是:如果link.c和stack.c链接在一起,如果pushStack在link.c和stack.c中都声明(在stack.c中的声明同时也是定义),那么这些声明指的是同一个函数,链接后是同一个GLOBAL符号,代表同一个地址。函数声明中的extern可以省略不写,不屑extern的函数声明也表示这个函数具有External Linkage。
如果用static关键字修饰一个函数声明,则表示该标识符具有Internal Linkage,例如有以下两个程序文件:

/* foo.c */static void foo(void) {}
/*main.c*/void foo(void);int main(void) { foo(); return 0;}

编译链接在一起会出错,原因是:
虽然在foo.c中定义了函数foo,但是这个函数是static属性,只具有internal Linkage。如果把foo.c编译成目标文件,函数名foo在其中是一个LOCAL的符号,不参与链接过程,所以在链接时,main.c中用到一个External Linkage的foo函数,链接器却找不到它的定义在哪,无法确定它的地址,也就无法做符号解析,只好报错。
凡是被多次声明的变量或函数,必须有且只有一个声明是定义,如果有多个定义,或者一个定义都没有,链接器就无法完成链接

static和extern修饰变量

如果我想在link.c中访问stack.c中定义的int变量count,则可以用extern声明

#include <stdio.h>int a, b;extern void pushStack(int d);
extern int popStack(void);
extern int isEmpty(void);
extern int count;int main()
{
a = b = 1;pushStack(a);
pushStack(b);
pushStack(a);printf("%d\n", count);while (! isEmpty()) {
printf("%d\n", popStack());
}return 0;
}

变量count具有external linkage,它的存储空间是在stack.c中分配的,所以link.c中的变量声明extern int count;不是变量定义,因为它不分配存储空间。
如果不想在stack.c外让外界访问到count,则可以用static关键字将count声明为Internal Linkage

区别

变量生命和函数声明有一点不同,函数声明的extern可写可不写,而变量声明如果不写extern,意思就完全变了。如果上面的例子不写extern就表示在main函数中定义一个全局变量count。
用static关键字声明具有Internal Linkage的函数和关键字是处于保护内部状态的目的,也是一种封装(Encapsulation)的思想。一个模块中,有些函数是提供给外界使用的,也称为导出(Export)给外界使用,这些函数用extern声明为External Linkage的。

头文件

为了防止每次函数extern声明,例如又有一个foo.c也使用pushStack等函数,又需要在foo.c中写多个extern声明,为了避免这种重复麻烦的操作,可以自己定义一个stack.h头文件:

#ifndef STACK_H
#define STACK_H#define STACKSIZE 1000typedef struct stack {
int data[STACKSIZE];
int top;
} stack;extern void pushStack(int d);
extern int popStack(void);
extern int isEmpty(void);#endif

这样,在link.c里就只需要包含这个头文件就可以了,而不需要写三个函数声明了:

#include <stdio.h>
#include "stack.h"int a, b;extern int count;int main()
{
a = b = 1;pushStack(a);
pushStack(b);
pushStack(a);printf("%d\n", count);while (! isEmpty()) {
printf("%d\n", popStack());
}return 0;
}

为什么#include <stdio.h>用角括号,而#include “stack.h”用引号?原因

  • 对于用角括号包含的头文件,gcc首先查找-I选项指定的目录,然后查找系统的头文件目录(通常是/usr/include)
  • 对于用“”包含的头文件,gcc首先查找包含头文件的.c文件所在的目录,然后查找-I选项指定的目录,然后查找系统的头文件目录

用#ifndef #define #endif是为了防止头文件的重复包含,头文件重复包含的问题如下:

  1. 使预处理的速度变慢了,要处理很多本来不需要处理的头文件
  2. 如果a.h包含了b.h,然后b.h又包含了a.h的情况,预处理就陷入死循环了
  3. 头文件按有些代码不允许重复出现

头文件中的变量和函数声明一定不能是定义。如果头文件中出现变量或函数定义,这个头文件又被多个.c文件包含,那么这些.c文件就不能链接在一起



相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,111
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,584
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,431
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,203
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,838
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,922