linux开发套件-gcc_makefile_ctags_cscope

文章目录

  • 1. gcc编译
    • 1.1 编译步骤
    • 1.2 静态库
      • 制作步骤
      • 优缺点
    • 1.3 动态库
      • 制作步骤
  • 2. makefile
    • 2.1 指定编译器
    • 2.2 冲突情况
  • 3. ctags
    • 常用快捷键
  • 4. cscope

1. gcc编译

gcc foobar.c会生成a.out可执行文件。

windows环境变量中配好mingw/gcc.exe,则默认生成a.exe-o a则生成a.exe

一个c项目的目录大概如下:

  • src
  • main.c
  • include
  • res
  • bin(lib+so)
  • debug
  • release

1.1 编译步骤

c编译成可执行文件有4步:

  1. 预处理:宏定义展开、头文件展开、条件编译、删除注释等,gcc -E a.c -o a.i
  2. 编译:检查语法,生成汇编文件,gcc -S a.i -o a.s
  3. 汇编:由汇编文件生成二进制文件,gcc -c a.s -o a.o
  4. 链接: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特点:

  • 可移植,支持多种平台
  • 不仅是个本地编译器,还能跨平台交叉编译
  • 可解析多种语言
  • 模块化
  • 自由软件

1.2 静态库

命名规则:lib+库的名字+.a

发布时除了.a,还要有头文件以提供接口查询。

制作步骤

以库test为例:

  1. 编译c文件-c生成.o文件
  2. src目录下,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步要选择性地编译静态库。

1.3 动态库

命名规则:lib+库的名字+.so

制作步骤

  1. 生成与位置无关的.ogcc -fPIC -c *.c
  2. .o打包成共享库
  3. 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.confsudo ldconfig [-v]更新。

现在,ldd就不会显示not found了。

2. makefile

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

2.1 指定编译器

如果某天不用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

2.2 冲突情况

如果有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

3. ctags

apt-get install ctags

ctags *.c *.h添加新文件。

vim打开后,:set tags=tag文件路径

常用快捷键

在项目根目录下执行ctags -R。代码变量处,ctrl+]转到定义,ctrl+t跳回。

4. cscope

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

你可能感兴趣的:(#,C/C++,linux)