gcc foobar.c
会生成a.out
可执行文件。
windows环境变量中配好mingw/gcc.exe
,则默认生成a.exe
,-o a
则生成a.exe
。
一个c项目的目录大概如下:
c编译成可执行文件有4步:
gcc -E a.c -o a.i
;gcc -S a.i -o a.s
gcc -c a.s -o a.o
;gcc a.o -o a_elf
。记法:参数esc,后缀iso。
头文件中的反斜杠是路径的意思。
-I
指定头文件目录路径
-L
指定库的目录,-l
指定库的名字,注意名字不带lib
和.a
-D
编译时指定一个宏。
//-D DEBUG编译特使
#ifdef DEBUG
printf("define DEBUG\n");
#endif
-O#
指定优化等级,等级#
范围0-3。
-Wall
编译时输出提示信息,比如有变量未使用。
常用的是-g
,保存调试信息,然后才能gdb
调试。
gcc特点:
命名规则:lib
+库的名字+.a
发布时除了.a
,还要有头文件以提供接口查询。
以库test为例:
-c
生成.o
文件ar rcs libtest.a *.o
记着把库移到库目录,把头文件和动态库发布给用户。
用户操作:gcc main.c -I include -L bin -l test -o myapp
或者gcc main.c -I include bin/libtest.a -o myapp
然后学一下nm
(names)命令,nm litest.a
可以查看有哪些.o
文件;也可以跟可执行文件nm myapp
。符号类型如下:
符号类型 | 说明 |
---|---|
A | 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。 |
B | 该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中 |
C | 该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。 |
D | 该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。 |
G | 该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。 |
I | 该符号是对另一个符号的间接引用。 |
N | 该符号是一个debugging符号。 |
R | 该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。 |
S | 符号位于非初始化数据区,用于small object。 |
T | 该符号位于代码区text section。 |
U | 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。 |
V | 该符号是一个weak object。 |
W | The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. |
- | 该符号是a.out格式文件中的stabs symbol。 |
? | 该符号类型没有定义 |
优点:发布程序时不需提供库;加载库快。
缺点:程序大;升级时要重新编译。
所以,制作.a
第2步要选择性地编译静态库。
命名规则:lib
+库的名字+.so
.o
,gcc -fPIC -c *.c
.o
打包成共享库gcc -shared -o libtest.so *.o [-I ../include]
同样移到库目录,把头文件和动态库发布给用户。
用户操作:gcc main.c -I include bin/libtest.so -o myapp
与位置有无关:
静态库的代码用到了绝对地址,编译时确定,每次运行都放在text段的固定位置;
c标准库、系统io函数还有动态库在运行时会加载进共享库段,用到了相对地址,每次位置不一样。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fNkLlzI3-1579073704037)(http://r.photo.store.qq.com/psb?/V11kR0B91ocBaQ/2clrAijkI8uvDse3ZrHc1y*XhdOwzrxrwCFXuxLJVJo!/r/dL8AAAAAAAAA)]
动态库可以用ldd
查看so
;windows中可以用depends.exe
查看dll
。
可以看到动态链接器ld-linux-x86-64.so
,它会根据$PATH
调用其它动态库。所以我们可以临时提供一个环境变量export LD_LIBRARY_PATH=./lib
做测试,或把这个命令的路径改为绝对路径后添加进.bashrc
。但通常不这样做。
不要把库直接复制进用户的lib目录,可能覆盖重名库。
通常使用下面这种方法:把动态库的路径写进动态链接器的配置文件/etc/ld.so.conf
,sudo ldconfig [-v]
更新。
现在,ldd
就不会显示not found
了。
main: main.c foo.o bar.o
gcc main.c foo.o bar.o -o main
foo.o: foo.c
gcc -c foo.c
bar.o: bar.c
gcc -c bar.c
clean:
rm *.o main
解释:目标文件main依赖于main.c和func.o,由gcc...
生成。func.c一样。
执行make
,则执行前两段;如果有错误或者重新编译,就执行make clean; make
。
如果某天不用gcc了,上面的脚本就得手动一个个改。所以有下面的方法:
CC = g++
main: main.c foo.o
$(CC) main.c foo.o -o main
foo.o: foo.c
$(CC) -c foo.c
clean:
rm *.o main
其它参数也可以作为变量。
CC = g++
CFLAGS = -lm -Wall -g
main: main.c
$(CC) $(CFLAGS) main.c -o main
如果有main1.c和main2.c各有一个main,就只编译第一个。
# 执行
main1: main1.c func.o
gcc main1.c foo.o -o main1
# 不执行
main2: main2.c func.o
gcc main2.c foo.o -o main2
# 执行
func.o: func.c
gcc -c func.c
clean:
rm *.o main1 main2
使用all就可以生成两个了.
all: main1 main2
main1: main1.c func.o
gcc main1.c foo.o -o main1
main2: main2.c func.o
gcc main2.c foo.o -o main2
func.o: func.c
gcc -c func.c
clean:
rm *.o main1 main2
apt-get install ctags
ctags *.c *.h
添加新文件。
vim打开后,:set tags=tag文件路径
在项目根目录下执行ctags -R
。代码变量处,ctrl+]
转到定义,ctrl+t
跳回。
ctags的强化版,同属sourceforge。
在项目根目录下执行cscope -Rbqk
,生成cscope.out, cscope.in.out, cscope.po.out
cscope.out是基本的符号索引,后两个文件是使用"-q"选项生成的,可以加快cscope的索引速度。
ctrl + \ , s
,列出引用;ctrl+], ctrl+t
和ctags一样。ctrl+space, s
,search-------split in two horizontally.`CTRL-spacebar CTRL-spacebar s
(just hold down the CTRL key and tap the spacebar twice), search----split vertically.:cscope find symbol foo
(or, more tersely, “:cs f s foo”)—horizontal split:spl[it] filename
–open a file in a new window via:scscope
,vertically。
except “s”:
g
finds the global definition(s) of a symbol,c
finds all calls to a function,f
opens the filename under the cursor (note: since Cscope by default parses all C header files it finds in /usr/include, you can open up most standard include files with this).look in the cscope_maps.vim file for all of them, or read the Cscope man page