变量重定义

2019-12-11 08:06栏目:bob体育平台
TAG:

出现变量重定义的情况?

一、简介

最近在Linux下编程发现一个诡异的现象,就是在链接一个静态库的时候总是报错,类似下面这样的错误:(.text+0x13): undefined reference to `func'

源文件与include的文件定义了同一个变量

main.c

1 #include <stdio.h>
2 #include "a.c"
3 
4 int a = 100;
5 
6 int main() {
7 
8     return 0;
9 }

a.c

 1 int a = 200; 

编译命令:

gcc main.c -o main

编译报错:

 图片 1

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序(简单将:管理工程的文件,决定先编译哪些文件,编译顺序)。

关于undefined reference这样的问题,大家其实经常会遇到,在此,我以详细地示例给出常见错误的各种原因以及解决方法,希望对初学者有所帮助。
1. 链接时缺失了相关目标文件(.o)
** **测试代码如下:

链接的两个文件都定义了同一个变量

main.c

1 #include <stdio.h>
2 
3 int a = 100;
4 
5 int main() {
6 
7     return 0;
8 }

a.c

int a = 200;

编译命令:

gcc -c main.c -o main.o

gcc -c a.c -o a.o

gcc main.o a.o -o main

最后一步链接会报错:

 图片 2

二、编写规则:

图片 3

目标1:目标依赖  然后回车+tab键
 命令;

目标2:目标依赖  然后回车+tab键
 命令;
...

然后编译。

目标n:目标依赖  然后回车+tab键
 命令;

gcc -c test.c

gcc –c main.c 

注意:命令必须是tab键开头的。

得到两个 .o 文件,一个是 main.o,一个是 test.o ,然后我们链接 .o 得到可执行程序:
gcc -o main main.o

三、Makefile演进
1、一个项目有main.c/a.c/a.h/b.c/b.h五个文件;main.c包含a.h和b.h并使用相关函数;然后建立一个新的Makefile文件,内容如下:
main:a.o b.o
 gcc -o main a.o b.o 
a.o:a.c
 gcc -c a.c -o a.o 
b.o:b.c
 gcc -c b.c -o b.o

这时,你会发现,报错了:
main.o: In function main': main.c:(.text+0x7): undefined reference totest'
collect2: ld returned 1 exit status

2、Makefile升级1
采用makefile变量:想用就用,没有类型,不需要定义(引用变量使用$(obj)来包含更多.o文件)
方法:obj:=a.o b.o
那么上面的Makefile程序升级如下:
obj:=a.o b.o
main:$(obj)
 gcc -o main a.o b.o 
a.o:a.c
 gcc -c a.c -o a.o 
b.o:b.c
 gcc -c b.c -o b.o

这就是最典型的undefined reference错误,因为在链接时发现找不到某个函数的实现文件,本例中test.o文件中包含了test()函数的实现,所以如果按下面这种方式链接就没事了。
gcc -o main main.o test.o

3、Makefile升级2
经过以上两个makefile的编译,项目执行是成功的,但是如果main.c需要引用更多文件中的函数时,是否要填写那么多的编译命令吗?显然这个方法不可取。
改进:makefile特殊变量和自动推导功能
知识点说明:
$@  代表目标名,
$^  代表依赖文件
%  代表任意字符
%.o  代表任意.o文件
%.c  代表任意.c文件

【扩展】:其实上面为了让大家更加清楚底层原因,我把编译链接分开了,下面这样编译也会报undefined reference错,其实底层原因与上面是一样的。
gcc -o main main.c //缺少test()的实现文件

以上Makefile升级如下:
obj:=a.o b.o
main:$(obj)
 gcc -o main $(obj) 
%.o:%c      #注释:模式通配,自动将.c文件编译成.o文件
 gcc -o $@ -c $^    #注释:通配符
clean:
 rm -rf *.o main

需要改成如下形式才能成功,将test()函数的实现文件一起编译。
gcc -o main main.c test.c //ok,没问题了

4、Makefile升级3
exe=main      #注释:最后的编译结果名字
obj:=main.o a.o b.o c.o   #注释:依赖文件
all:$(obj)
gcc -o $(exe) $(obj)
%.o:%.c
gcc -c $^ -o $@
clean:
rm -rf $(obj) $(exe)

2. 链接时缺少相关的库文件(.a/.so)
在此,只举个静态库的例子,假设源码如下。

以上程序看似没有什么问题的,但是clean有点瑕疵,要是也有一个文件叫clean那怎么办?如果make clean就没办法执行这条命令。

图片 4

5、Makefile升级4
使用伪目标.PHONY来解决clean瑕疵问题,升级Makefile如下:
exe:=main
obj:=main.o a.o b.o c.o
all:$(obj)
 gcc -o $(exe) $(obj)
%.o:%.c
 gcc -c $^ -o $@
.PHONY:clean     #注释:声明clean是伪目标
clean:
 rm -rf $(obj) $(exe)

注释#.PHONY:clean声明伪目标,避免当前目录存在名字为clean文件的时候命令不能执行的情况

先把test.c编译成静态库(.a)文件
gcc -c test.c
ar -rc test.a test.o

版权声明:本文由bob体育app发布于bob体育平台,转载请注明出处:变量重定义