一、学习内容
1. 熟悉Linux系统下的开发环境
2. 熟悉vi的基本操作
3. 熟悉gcc编译器的基本原理
4. 熟练使用gcc编译器的常用选项
5 .熟练使用gdb调试技术
6. 熟悉makefile基本原理及语法规范
7. 掌握静态库和动态库的生成
二、学习记录
1、Linux下C语言编程环境概述
包含:编辑器(vim)、编译链接器(gcc)、调试器(gdb)、项目管理器
2、vi的模式
1)vi的模式:命令行模式、插入模式、底行模式
a.命令行模式:用户可以上下移动光标进行删除字符或者整行删除等,或进行复制、粘贴等,但无法编辑文字。
b.插入模式:用户可以进行文字编辑输入,用户按[esc]可回到命令行模式。
c.底行模式:光标位于屏幕的底行,用户可以进行文件保存或退出操作,也可以设置编辑环境,如寻找字符串、列出行号等。
3、vi的基本流程
(1)进入 vi,即在命令行下键入“vi hello”(文件名)。此时进入的是命令行模式,光标位于屏幕的上方
(2)在命令行模式下键入 i 进入插入模式,如图 3.3 所示。可以看出,在屏幕底部显示有“插入”表示插入模式中的输入状态,在该模式下可以输入文字信息。
(3)最后,在插入模式中,按“Esc”键,则当前模式转入命令行模式,并在底行行中输入“:wq”(存盘 退出)进入底行模式,如图 3.4 所示。
这样,就完成了一个简单的 vi 操作流程:命令行模式→插入模式→底行模式。
4、 vi的各模式功能键
1)命令行模式功能键:
2)插入模式功能键:按“Esc”键可回到命令行模式
3)底行模式功能键:
:wq!:强制保存并退出5、gcc编译器所支持编译源文件的后缀及其解释
6、gcc 编译流程解析:预处理、编译、汇编、链接
Linux的可执行文件并没有像 Windows 那样有明显的.exe后缀名, 只需向其分配x(可执行)权限即可 sudo chmod u+x excutefile
gcc的-E参数可以让gcc在预处理结束后停止编译过程。
将hello.i编译为目标代码,gcc默认将.i文件看成是预处理后的C语言源代码,因此它会直接跳过预处理,开始编译过程。
gcc预处理源文件的时候(第一步),不会进行语法错误的检查。语法检查会在第二步进行,比如花括号不匹配、行末尾没有分号、关键字错误等。
1)预处理阶段:在该阶段,对包含的头文件(#include)和宏定义(#define、#ifdef 等)进行处理。在上述代码的预处理过 程中,编译器将包含的头文件 stdio.h 编译进来,并且用户可以使用 gcc 的选项“-E”进行查看,该选项的 作用是让 gcc 在预处理结束后停止编译过程。
例如:[root@localhost gcc]# gcc –E hello.c –o hello.i
选项“-o”是指目标文件,“.i”文件为已经过处理的 C 程序。
(gcc 指令的一般格式为:gcc [选项] 要编译的文件 [选项] [目标文件] 其中,目标文件可缺省,gcc 默认生成可执行的文件,名为:编译文件.out)
2)编译阶段:接下来进行的是编译阶段,在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。用户可以使用“-S”选项来进行查看, 该选项只进行编译而不进行汇编,结果生成汇编代码。
[root@localhost gcc]# gcc –S hello.i –o hello.s
3)汇编阶段:汇编阶段是把编译阶段生成的“.s”文件转成目标文件,读者在此使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了。
[root@localhost gcc]# gcc –c hello.s –o hello.o
4)链接阶段:在成功编之后,就进入了链接段。 系统把函数的实现都放到名为 libc.so.6 的库文件中去了,在没有特别指定时, gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 数库中去,这样就能使用各类函数了,而这也正是链接的作用。
函数库有静态库和动态库两种。静态库是指编译链接时,将库文件的代码全部加入可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后名通常为“.a”。动态库与之相反,在编译链接时并没有将库文件的代码加入可执行文件中,而是在程序执行时加载库,这样可以节省系统的开销。一般动态库的后名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。 完成了链接之后,gcc 就可以生成可执行文件。
[root@localhost gcc]# gcc hello.o –o hello
[root@localhost gcc]# ./hello
7、gcc 编译选项
gcc 有超过 100 个可用选项,主要包括总体选项和出错选项、优化选项和体系结构相关选项。
(1)常用选项
a.“-I dir”:“-I dir”选项可以在头文件的搜索路径列表中添加 dir 目录。由于 Linux 中头文件都默认 放到了“/usr/include/”目录下,因此,当用户希望添加放置在其他位置的头文件时,就可以通过“-I dir” 选项来指定,这样,gcc 就会到相应的位置查找对应的目录。
比如在“/root/workplace/gcc”下有两个文件,可在 gcc 命令行中加入“-I”选项:
[root@localhost gcc]# gcc hello1.c –I /root/workplace/gcc/ -o hello1
在 Linux 中的每 个程序都会链接到一个或者多个库,函数库有态库和动态库两种,态库是一系列的目标文件(.o 文件)的归文件(文件名格式为 libname.a),如果在编某个程序时链接态库,则链接器将会搜索态库,从中取出它所需要的目标 文件并直接复制到该程序的可执行二进制文件(ELF 格式文件)之中;动态库(文件名格式为 libname.so[.主 版本号.次版本号.发行号])在程序编时并不会被链接到目标代码中,而是在程序运行时才被载入。
我们所编写的.c文件都可以制作一个函数库。过程如下:
a.静态库的创建和使用
使用归档工具ar将一些目标文件集成在一起。例如:
[root@localhost lib]# gcc -c haha.c
[root@localhost lib]# ar rcsv libpow.a haha.o
a - haha.o
下面编译主程序,它将会链接到刚生成的静态库haha.a:
[root@localhost lib]# gcc -o heihei heihei.c -L. -lpow
[root@localhost lib]# ./heihei 2 10
2^10=1024
其中,选项“-L dir”的功能与“-I dir”类似,能够在库文件的搜索路径列表中添加 dir 目录,而“-lname” 选项指示编译时链接到库文件 libname.a 或者 libname.so。本实例中,程序heihei.c 需要使用当前目录下的一个静态库 libpow.a。
b.动态库的创建和使用
数显使用gcc的-fPIC选项为动态库创造一个目标文件:
[root@localhost lib]# gcc -fPIC -Wall -c haha.c
接下来使用-shared选项和已创建的位置无关目标代码,生成一个动态库libpow.so
[root@localhost lib]# gcc -shared -o libpow.so haha.o
下面编译主程序,它将会链接到刚生成的动态库libpow.so
[root@localhost lib]# gcc -o heihei heihei.c -L. -lpow
在运行可执行程序之前,需要注册动态库的路径名。其方法有几种:修改/etc/ld.so.conf 文件,或者修改 LD_LIBRARY_PATH 环境变量,或者将库文件直接复制到/lib 或者/usr/lib 目录下(这两个目录为系统的默认的库路径名)。
[root@localhost lib]# op libpow.so /lib
[root@localhost lib]# ./heihei 2 10
2^10=1024
动态库只有当使用它的程序执行时才被链接使用,而不是将需要的部分直接编入可执行文件中,并且一 个动态库可以被多个程序使用故可称为共享库,而静态库将会整合到程序中,因此在程序执行时不用加载静态库。 从而可知,链接到静态库会使用户的程序,并且难以升级,但是可能会比较容易部署。而 链接到动态库会使用户的程序轻便,并且易于升级,但是会难以部署。
(3)告警和出错选项
a.“-ansi”
该选项强制 gcc 生成标准语法所要求的告警信息,尽管这还并不能保证所有没有警告的程序都是符合 ANSI C 标准的。无法发现无效数据类型的错误。如(long long)
b.“-pedantic”
打印ANSI C标准所列出的全部警告信息,同样也保证所有没有警告的程序都是符合ANSI C标准的。 能够查出无效数据类型的错误。
c.
“-Wall”
打印gcc 能够供的所有有用的报警信息。 能够找出未使用的变量,但不能找出无效数据类型的错误。
另外,gcc 还可以利用选项对单独的常见错误分别指定警告。
(4)优化选项
gcc 可以对代码进行优化,它通过编选项“-On”来制优化代码的生成,其中 n 是一个代表优化级别的整数。对于不同版本的 gcc 来讲,n 的取值范围及其对应的优化效果可能并不完全相同,比较典型的是从0变化到2或3。
不同的优化级别对应不同的优化处理工作。如使用优化选项“-O”主要进行线程跳转(Thread Jump)和 延迟退栈(Deferred Stack Pops)两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令度等。选项“-O3”则还包括循环展开和其他一些与处理器特性相关的优化工作。
(5)体系结构相关选项
8、gdb调试器
gdb 调试器是一款 GNU 开发组织并发布的 UNIX/Linux 下的程序调试工具。
它可以实现:
(1) $ gdb testgdb <---------- 启动gdb
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
(gdb) info break <-------------------- 查看断点信息。
(gdb) r <--------------------- 运行程序,run命令简写
(gdb) n <--------------------- 单条语句执行,next命令简写。
(gdb) c <--------------------- 继续运行程序,continue命令简写。 Continuing.
(gdb) p i<--------------------- 打印变量i的值,print命令简写。
(gdb) bt <--------------------- 查看函数堆栈。
(gdb) finish <--------------------- 退出函数。
(gdb) c <--------------------- 继续运行。
(gdb) q <--------------------- 退出gdb。
(2)gdb主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数即可。例如:
$ gcc -g hello.c -o hello
$ g++ -g hello.cpp -o hello
如果没有-g,将看不见程序的函数名和变量名,代替它们的全是运行时的内存地址。当用-g把调试信息加入,并成功编译目标代码以后,看看如何用gdb来调试。
启动gdb的方式如下:
9、makefile
$ vim Makefile
有了这个Makefile,不论什么时候修改源文件,只要执行一下make命令,所有必要的重新编译将自动执行。make程序利用Makefile中的数据,生成并遍历以test为根节点的树;
test(目标文件):prog.o code.o(依赖文件列表)
tab(至少一个tab的位置) gcc prog.o code.o -o test(命令)
.......
一个Makefile文件主要含有一系列的规则,每条规则包含一下内容:
一个目标,即make最终需要创建的文件,如可执行文件和目标文件;
目标也可以是要执行的动作,如‘clean’;
一个或多个依赖文件的列表,通常是编译目标文件所需要的其他文件。
之后的一系列命令,是make执行的动作,通常是把指定的相关文件编译成目标文件的编译命令,每个命令占一行,并以tab开头(初学者务必注意:是tab,而不是空格)
执行以上Makefile后就会自动化编译
如:
$ make
gcc -c prog.c -o prog.o
gcc -c code.c -o code.o
gcc prog.o code.o -o test
最后就会多产生: porg.o code.o test这三个文件,执行./test
查看结果
make clean
会执行rm -f *.o test
这条命令,完成 clean 操作。
Makefile还可以定义和使用宏(也称做变量),从而使其更加自动化,更加灵活,在Makefile中定义宏的格式为:
macroname = macrotext
使用宏的格式为:
$(macroname)
================================================================
实验楼操作笔记:
1、从教程vimtutor中退出只需输入":q!"
2、h:光标左移,l:光标右移,j:光标下行,k:光标上行
3、x:删除光标处字符
4、i:在光标处前一个位置插入字符
5、esc:恢复为正常状态
6、a:在光标位置输入文本
7、wq:保存文本后并退出
8、dw:删除以光标所在位置开头的单词
9、d$:删除从光标处直至末尾的部分文本
10、删除讲解:
11、dd:删除一整行;2dd:删除两整行
12:u:撤销上一次操作;U:撤销一整行全部操作;Ctrl-R:撤销撤销动作
13、dd删除后的句子会缓存至vim中,用p在欲插入粘贴的位置上方课将上一次剪切的句子贴入
14、r:用光标指向修改部位,r,然后输入欲改为的字符。完成替换
15、c$:更正至光标位置起到句末的部分
16、shift-g:跳转至指定行;Ctrl-g:查看文件信息和当前信息
17、/+字符:自动在文本中向下搜索关键词,?+字符:自动在文本中向上搜索关键词,n向后继续查找。Shift-n为向上查找。
18、%:括号匹配查看
19、:s/old/new:用new替换文本中第一个相符字符串;:s/old/new/g:替换整行中的相符字符串;#,#s/old/new/g:替换两行间的匹配串;:%s/old/new/g:替换整个文件中的匹配串;:%s/old/new/gc:进行全文替换时需询问用户确认
20、Shift+zz:
保存退出vim
21、行间转换:
22、行内转换:
23、~:将小写字母变为大写
24、复制粘贴和剪切:
25、ddp:交换上下行
26、替换和撤销:
27、使用命令进行快速调整缩进操作
28、shiftwidth命令
shiftwidth
命令是指>>
命令产生的缩进(可以简写成sw
) 普通模式下输入:
进入命令行模式下对shiftwidth
值进行设置可以控制缩进和回退的字符数获取目前的设定值
:set shiftwidth?查看缩进值默认值;:set shiftwidth=n设置缩进值
29、设置文本位置
命令行模式下输入:ce
(center)命令使本行内容居中
命令行模式下输入:ri
(right)命令使本行文本靠右
命令行模式下输入:le
(left)命令使本行内容靠左
30、高级查找
31、同时创建两个新文件并编辑:$ vim 1.txt 2.txt
默认进入1.txt
文件的编辑界面
恢复文件:vim -r
进入文档后,输入:ewcover 1.txt
31、可视模式选取
32、视窗操作
:new
打开一个新的vim视窗,并进入视窗编辑一个新文件
33、文档加密:$ vim -x file1
34、在vim执行外部命令:在命令行模式中输入!
可以执行外部的shell命令
35、vim中的查看帮助
36、vim的功能设定:可以在编辑文件的时候进行功能设定,如命令行模式下输入:set nu
(显示行数),设定值退出vim后不会保存。要永久保存配置需要修改vim配置文件。 vim的配置文件~/.vimrc
,可以打开文件进行修改,不过务必小心不要影响vim正常使用
37、获取目前的设定
38、set功能的说明
39、vim进入编译器后并不会打开任何文件,但是可以通过:e 文件路径进入某指定文件
40、I:在行首插入
41、A:在行末插入
42、o:在当前行后插入一个新行;O:在当前行前插入一个新行
43、X:删除游标所在前一个字符
44、d^:删除至行首
45、dG:删除到文档结尾处;dlG删除至文档首部
46、.表示重复上一次的命令操作
47、Ctrl+o:跳转回上次光标的位置
=============================================
遇到的问题:
1、在vim教程中,想练习:wq保存文件后退出时,总是将命令错误输入在如下图的小框内,然后后来使用:q!退出小框后再次输入:wq<Enter>后就成功了。
2、在Linux实验GCC的使用这一节中,最后让我们试编译hello.h、hello_print.c、hello_main.c三个文件时,输入$gcc hello_print.c hello_main.c -o hello会报错,如下图:
我尝试在hello_main.c中加入#include<stdio.h>,不再报错。但是却没有输出“Hello,Shi-Yan-Lou”。
后来我删掉了以上修改,重新编译,却又不报错了。我觉得很奇怪。而且也依旧没有输出。
3、makefile中按照实验指导操作却总是有报错,无法得出自动编译结果,频频出现“未定义调用myfun()"这样的提示。