Linux编程环境介绍(3) -- linux下的c/c++程序开发
目录:1. 编辑器( Vi )【vi 与 vim】vi(visual interface)是linux系统最重要的文本编辑器, 所有的 Unix-Like 系统都会内置vi文本编辑器. vim则是从 vi 发展出来的. 它完全兼容vi, 并且扩展了很多额外的强大功能. vim 的官方网站 (http://www.vim.org) 自己也说 vim 是一个『程序开发工具』, 而非
目录:
1. 编辑器( Vi )
【vi 与 vim】
vi(visual interface)是linux系统最重要的文本编辑器, 所有的 Unix-Like 系统都会内置vi文本编辑器.
vim则是从 vi 发展出来的. 它完全兼容vi, 并且扩展了很多额外的强大功能. vim 的官方网站 (http://www.vim.org) 自己也说 vim 是一个『程序开发工具』, 而非简单的文字处理软件.
小故事:
Bram Moolenaar 在 80 年代末购入了一台Amiga计算机, 这台计算机上并没有vi编辑器. 于是Bram 把Stevie ( Atari ST电脑上的VI克隆 ) 移植到了Amiga计算机上. 最初的目标只是完全复制 vi 的功能,那个时候的 Vim 意思是Vi IMitation (仿制品). 由于后续版本的vim越来越好用, 在1992年1.22 版本的 Vim 被移植回了 UNIX上。从那个时候开始,Vim 的全名就变成 Vi IMproved (改良) 了, 从这之后, vim进入了更加快速的发展, 加入了不计其数的新功能.
【文本编辑器那么多, 为何要学 vim】
a. 所有的 Unix Like 系统都会内建 vi 文本编辑器,其他的文本编辑器 则不一定会存在
b. 很多个别软件的编辑接口都会主动呼叫 vi (例如 crontab, visudo,edquota 等指令);
c. vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确 性,方便程序设计
d. 因为程序简单,编辑速度相当快速
【vi的三种工作模式】
【vi的常用指令】
命令模式 末行模式 插入模式
h 左 :w 保存 在命令模式下, 按a/A, i/I,
j 下 :w! 强制保存 o/O等进入插入模式.
k 上 :q! 不保存离开
l 右 :wq! 保存后离开
0 移动到行首 :e! 还原原始档
$ 移动到行尾 :w filename 另存为
H 屏幕最上 :set nu 设置行号
M 屏幕中央 :set nonu 取消行号
dd 删除光标当前行 :nohlsearch 暂时关闭高亮显示
yy 复制当前行 :set nohlsearch 永久地关闭高亮显示
p,P 粘贴 :sp 同时打开两个文档
u 撤消
gg 移动到档案第一行
[Ctrl]+w 两个文档设换
【vi更进一步学习】
可参考: http://blog.csdn.net/zhuying_linux/article/details/6924805
2. 编译器( GCC / G++ )
【基本概念介绍】
1) GCC是GNU的官方编译器,现已被大多数Unix-like操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器,GCC同样适用于微软的Windows。
2) GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java(gcj), 以及 Ada(gnat)与其他语言。因此GCC早已有了新的定义: GCC(GNU Compiler Collection,GNU编译器集合),是一套由 GNU 开发的编程语言编译器。它是一套GNU编译器套装。
3) GCC和G++都是GNU的编译器。
【关于GCC和G++的常见误区】
误区一: gcc只能编译c代码, g++只能编译c++代码
两者都可以,但是请注意:
1) 后缀为.c的, gcc把它当作是c程序, 而g++当作是c++程序;
后缀为.C / .cc / .cpp的, 两者都会认为是c++程序.
(c和c++对语法的要求还是有区别的。C++的语法规则更加严谨一些)
2) 编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
误区二:gcc不会定义__cplusplus宏,而g++会
实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释,如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。
误区三: 对于c++程序, 编译只能用gcc,链接只能用g++
严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++(gcc -lstdc++也可以达到同样的效果, 只是需要显示去链接c++的库)。因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价。
误区四: extern "C"与gcc/g++有关系
实际上并无关系,无论是gcc还是g++,用extern "c"时,都是以C的命名方式来为symbol命名,否则,都以c++方式命名。
【Gcc的编译流程分为四个步骤】
< 预处理 >
处理头文件, 宏, 以及预编译语句等, 生成.i文件. 选项: -E
eg: gcc –E hello.c –o hello.i
< 编译 >
检查代码的规范性、是否有语法错误, 检查无误后把代码翻译成汇编语言, 即生成".s"文件. 选项: -S
eg: gcc –S hello.i –o hello.s
< 汇编 >
汇编是把编译阶段生成的”.s”汇编代码文件转成".o"的二进制目标文件.
选项: -c
eg: gcc –c hello.s –o hello.o
< 链接 >
链接需要的库, 生成最终的可执行程序.
eg: gcc hello.o –o hello
【Gcc命令】
Gcc指令的一般格式为:
gcc [选项] 要编译的文件 [选项] [目标文件]
eg: gcc -o0 main.cpp -o main
几个重要的编译选项:
-E: 仅作预处理,不进行编译、汇编和链接。
-S: 仅编译到汇编语言,不进行汇编和链接。
-c: 编译、汇编到目标代码,不进行链接。
-o: 输出到指定的文件。
-g:产生调试器GDB所需要的符号信息。
-w: 关闭警告信息.
-Wall: 打开一些很有用的警告选项,建议编译时加此选项。
-o[0~3]: 优化选项。
-Idir: 把dir 加入到搜索头文件的路径列表中。
-Ldir: 把dir 加入到搜索库文件的路径列表中。
eg: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test
-llibrary: 进行链接时搜索名为library的库。
eg: $ gcc test.c -lpthread -o test
-Dname: 预定义一个名为name 的宏,值为1。
eg: $ gcc -DTEST_CONFIG test.c -o test
-Dname=definition: 预定义名为name ,值为definition 的宏。
eg: $ gcc -DTEST_CONFIG=5 test.c -o test
【Gcc更进一步学习】
关于GCC及其参数选项的进一步学习, 可参考: http://www.cnblogs.com/xmphoenix/archive/2011/03/21/1989944.html
3. 调试器 ( gdb )
【什么是gdb】
GDB是GNU开源组织发布的一个强大的linux下的程序调试工具。
一般来说,GDB主要帮助你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。
【gdb常用的命令】
启动gdb : gdb 文件名
(gdb) l (list) : 从第一行开始列出源码
(gdb) break n : 在第n行处设置断点
(gdb) break func : 在函数func()的入口处设置断点
(gdb) info break : 查看断点信息
(gdb) r(run) : 运行程序
(gdb) n(next) : 单步执行, 不进入函数体
(gdb) s(step) : 单步执行, 进入函数内部
(gdb) finish : 退出函数
(gdb) c(continue) : 继续运行
(gdb) 直接回车为执行上一个命令
(gdb) p(print) 变量 :打印变量的值
(gdb) watch 表达式 :一旦表达式的值改变, 程序停在此处
(gdb) bt:查看函数堆栈
(gdb) until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序, 直到退出循环体。
(gdb) kill:将强行终止当前正在调试的程序
(gdb) quit:简记为 q ,退出gdb
【gdb深入学习】
关于GCC及其参数选项的进一步学习, 可参考:
http://blog.chinaunix.net/uid-9525959-id-2001805.html
http://fanqiang.chinaunix.net/program/other/2006-07-14/4834.shtml
4. make和makefile
【makefile产生的背景】
1) 一个软件项目通常包含多个源码文件,每个源代码的编译和可执行文件的链接都要书写大量的命令.如Linux 下进行编译要大量调用gcc来处理
2) 如果用IDE开发环境, 这编译和链接一般由IDE自动完成.但绝大部分Linux和开源项目并不使用IDE, 而是使用gcc之类命令行工具来编译. 编译Linux 内核也是如此.
3) 在一项目里, 代码通常都有引用的关系.因此需要指定谁先编译,谁后编译,甚至是更复杂的功能操作.
4) Makefile就为解决上述一系统问题而创造的.可以把Makefile理解成是一种由make 程序进行解释的一种特殊脚本.
5) Linux 绝大多数的项目都是通过Makefile方式编译的,如 MySQL, Apache和操作系统本身. 因此Linux 下开发必须掌握Makefile的编写和使用.
【什么是makefile】
1) makefile是一个供make工具来进行解释操作的文本文件,它遵循一定的语法规则,有自己的书写格式、关键字、函数,而且在 Makefile 中可以使用系统 shell 所提供的任何命令来完成想要的工作。
2) Makefile定义了源文件之间的依赖关系以及整个工程的编译、链接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些中间文件以及如何创建这些中间文件、如何产生最终我们想要得到的可执行文件。Makefile 在绝大多数的 IDE 开发环境中都在使用,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile已经成为一种工程方面的编译方法。
3) 使用makefile的好处:
尽管看起来可能是很复杂的事情,但是为工程编写 Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供了正确的 Makefile,编译整个工程你所要做的唯一的一件事就是输入 make 命令。整个工程完全自动编译, 极大提高了软件开发的效率。
【什么是make工具】
1) make 是专门解释 Makefile 文件的一个命令工具。
2) 它解释 Makefile 中的规则。一般情况下, 当你在shell中敲入“make”时, make工具会在当前目录下寻找“Makefile”或“makefile”, 进行解析.
3) 如果makefile脚本名称不是缺省名称,则需要用-f参数来通知make. eg: make –f hello.mk
4) make工具默认在当前目录下查找makefile文件, 如果要指定其它目录,则使用-C参数. eg:make -C /home/xxx
5) 当对工程中的若干源文件修改以后,只需要重新执行 make 命令,就会自动根据修改情况完成源文件的对应目标文件的更新、中间文件的更新、最终的可执行程序的更新。make 通过比较对应文件的最后修改时间,来决定哪些文件需要更新、那些文件不需要更新。对需要更新的文件 make 就执行相应的命令来重建它,对于不需要重建的文件 make 什么也不做。
【makefile规则】
一个简单的Makefile文件包含一系列的“规则”,其样式如下:
目标(target) : 依赖(prerequiries)…
<tab>命令(command)
这是一个文件的依赖关系,也就是说,target这个目标文件依赖于prerequisites中的文件,其生成规则定义在 command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是makefile的规则。也就是makefile中最核心的内容。
"目标("target)通常是要产生的文件的名称,目标的例子是可执行文件或OBJ文件。目标也可是一个执行的动作名称,这样的目标通常称为伪目标(PHONY)诸如‘clean’ 。
"依赖"是用来输入从而产生目标的文件,一个目标经常有几个依赖。
"命令"是Make执行的动作,一个规则可以含有多个命令,每个命令占一行。(注意:每个命令行前面必须是一个Tab字符)
【makefile示例】
一个简单的makefile文件:
hello: a.o b.o
gcc a.o b.o -o hello
a.o: a.c
gcc –c a.c
b.o: b.c
gcc –c b.c
• 当运行make时,可以接一目标名(eg:make a.o)作为参数,表示要处理改目标。如没有参数,则处理第一个目标。
• 对上述例子执行make,则是处理hello这个目标。
• hello依赖于文件目标a.o和b.o,则先去处理a.o,调用gcc –c a.c来更新a.o,之后更新b.o,最后调用gcc a.o b.o -o hello 来更新hello.
【Makefile中的宏 / 变量】
• 在makefile中使用宏(也有人称为变量),要先定义,然后在makefile中引用。宏的定义格式为:
宏名= 宏的值(宏名一般习惯用大写字母)
例:
CC = gcc
hello: a.o b.o
$(CC) a.o b.o -o hello
a.o: a.c
$(CC) –c a.c
b.o: b.c
$(CC) –c b.c
【系统定义的宏】
• 还有一些设定好的内部变量,它们根据每一个规则内容定义。
$@ 当前规则的目的文件名
$< 依靠列表中的第一个依靠文件
$^ 整个依靠的列表(除掉了里面所有重复的文件名)。
$? 依赖中所有新于目标的
• 可以用变量做许多其它的事情,特别是当你把它们和函数混合使用的时候。如果需要更进一步的了解,请参考GNU Make 手册。('man make', 'man makefile')
【一个进化的makefile文件】
CC = gcc
CFLAGS = -O2
OBJS = a.o b.o
hello: $(OBJS)
$(CC) $^ -o $@
.c.o:
$(CC) $(CFLAGS) -c $<
clean:
rm -f *.o hello
【隐含规则】
• 请注意在上面的例子里,几个产生.o文件的命令都是一样的,都是从.c文件和相关文件里产生.o文件,这是一个标准的步骤。
• 其实make已经知道怎么做—它有一些叫做隐含规则的内置的规则,这些规则告诉它当你没有给出某些命令的时候,应该怎么办。
• 如果你把生成a.o和b.o的命令从它们的规则中删除,make将会查找它的隐含规则,然后会找到一个适当的命令。
• 它的命令会使用一些变量,因此你可以按照你的想法来设定它:它使用变量CC做为编译器,并且传递变量CFLAGS,CPPFLAGS,TARGET_ARCH,然后它加入‘-c’,后面跟变量$<,然后是‘-o’跟变量$@。一个C编译的具体命令将会是:
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
• 当然你可以按照你自己的需要来定义这些变量。
除了变量, makefile里还可以使用条件判断, 使用函数, 甚至包含其它头文件, 当然, 还有或简单或晦涩的各种隐含规则. 想真正掌握makefile确实有非常多的东西可以和需要去学习.
【makefile深入学习】
关于makefile的进一步学习, 可参考:
http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile
http://www.2cto.com/os/201203/122087.html
全文完.
更多推荐
所有评论(0)