【学习笔记】
vi 文件名 #进入文件编辑模式
yy #复制当前行 y数字y #复制多少行内容 p #箭头移动到目的行粘贴 u #撤销上一步 dd #删除当前行 d数字d #删除光标后多少行 x #删除一个字母(相当于delete) X #删除一个字母(相当于backspace) yw #复制一个词 dw #删除一个词 shift+^ #移动到行头 shift+^ #移动到行尾 1+shift+g #移动到页头 shift+g #移动到页尾 N+shift+g #移动到目标行
i #当前光标前 a #当前光标后 o #当前光标行的下一行 delete #删除 esc #退出编辑模式
:w #保存 :q #退出 ! #强制执行 :wq! #强制保存退出
GCC 是一个交叉平台编译器,能够在当前CPU 平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。
.c,C 语言源代码
.h,程序所包含的头文件
.i,已经预处理过的C 源代码文件
.s,汇编语言源代码文件
.o,编译后的目标文件
写个hello.c源代码
gcc hello.c
#include<stdio.h> int main() { printf(“Hello world!\n”); return 0; }
保存、退出
该阶段,编译器将上述代码中的stdio.h编译起来,可用“-E”选项查看
gcc格式为
gcc [选项] 要编译的文件 [选项] [目标文件] #目标文件可缺省,默认生成.out文件
gcc -E hello.c -o hello.i #-o:指目标文件;-i:指 已经预处理过的源程序
预编译阶段将“stdio.h”中的内容插入到hello.i文件中
该阶段,gcc首先检查代码的规范性、语法错误;检查无误后,gcc把代码翻译成汇编语言。可用“-S”选项查看
gcc -S hello.i -o hello.s
汇编阶段是把编译阶段生成的“.s”文件转成目标文件。读者在此可使用选项“-C”看到汇编代码已转化为“.o”的二进制目标代码。
gcc –c file.s –o file.o
在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现。那么,是在哪里实现“printf”函数的呢?答案是:系统把这些函数实现都做到名为libc.so.6 的库文件中去了。
在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,从而链接到libc.so.6 库函数,这样就能实现函数printf 了,这也就是链接的作用。
gcc file.o –o file
gcc [选项] [文件]
-E:使用此选项表示仅作预处理,不进行编译、汇编和链接。 -S:编译到汇编语言不进行汇编和链接。 -c:编译到目标代码。 -o:文件输出到文件。 -static:此选项将禁止使用动态库,所以,编译出来的东西一般都很大,也不需要什么动态 链接库即可运行。 -share:此选项将尽量使用动态库,所以生成文件比较小,但是需要系统有动态库。 -I dir:在头文件的搜索路径列表中添加dir 目录。 -L dir:在库文件的搜索路径列表中添加dir 目录。 -llibrary:链接名为library 的库文件。
Linux 的大部分特色源自于Shell 的GNU(GNU symbolic debugger)调试器,也称作gdb
以一段代码为例
gcc gdb_sample.c
#冒泡排序 void bubble_sort(int arr[], int len) { int temp; for (int i = 0; i < len; i++) { for (int j = 0; j < len-1-i; j++) {//从第一个数开始,依次和后一个数比较 if (arr[j] > arr[j + 1]) { #把较大的数交换到后边 temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } #内层循环结束后,最大一个数排到最后,保持不动 #每结束一次内层循环,比较次数少一次,因此有-i } } } int main(){ int arr[] = { 22, 45, 67, 36, 93, 444, 26, 84, 3, 7 }; int len = (int)sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, len); for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } }
退出保存后使用gcc对代码进行编译,注意一定要加上选项“-g”,这样编译出的可
执行代码中才包含调试信息,否则之后gdb 无法载入该可执行文件。
启动gdb进行调试:
gcc -g gdb_sample.c -o gdb_sample
在gdb输入命令时,可用不用打全命令,在Linux下,可用敲击两次Tab键来补全命令。
输入l(list),查看所载入的文件(输入l即可,系统会自动识别)
break <function> #在进入指定函数时停住 break <linenum> #在指定行号停住 break +offset,break -offset #在当前行号的前面或后面的offset 行停住 break filename:linenum #在源文件filename 的linenum 行处停住 break filename:function #在源文件filename 的function 函数的入口处停住 break *address #在程序运行的内存地址处停住 break #break 命令没有参数时,表示在下一条指令处停住 break…if <condition> #…可以是上述的参数,condition 表示条件,在条件成立时停住
info breakpoints [n] info break [n]
输入r(run)即可,gdb 默认从首行开始运行代码,若想从
程序中指定行开始运行,可在r 后面加上行号:
r 10 #第十行开始执行
p [变量]
step <count> #单步跟踪,如果有函数调用,它会进入该函数 next <count> #同样单步跟踪,但如果有函数调用,不会进入该函数 set step-mode,set step-mode on #打开step-mode 模式 set step-mod off #关闭step-mode 模式 finish #运行程序,直到当前函数完成返回 until 或u #当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体
一个简单的Makefile:结构组成
all: #目标名字放在":"前面,名字由字母和下划线组成 #":"后面是需要链接的文件 echo "Hello World" #注意echo前必须有一个Tab键位,不能是空格 #echo 后面是生成目标的命令 test: echo "Test Target" #一个makefile可以定义多个目标
vi max.h
int max (int a, int b);
vi max.c
int max(int a,int b){ if(a>=b){ return a; } else{ return b; } }
vi min.h
int min(int a,int b);
vi min.c
int min(int a,int b){ if(a<b){ return a; } else{ return b; } }
vi main.c
#include <stdio.h> #include "max.h" #include "min.h" int main(int argc, char* argv[]){ int a = 5; int b = 3; int maxNum= max(a,b); int minNum = min(a,b); printf("the max value is %d\nthe min value is %d\n",maxNum,minNum); return 0; }
首先编译依赖库文件,然后编译main函数
编译max.c
gcc -c max.c -o max.o
编译min.c
gcc -c min.c -o min.o
编译main.c
gcc max.o min.o main.c -o main
./main
the max value is 5 the min value is 3
如果依赖函数过多,或者调用o库文件多,这样编译将非常麻烦
于是采用makefile方式来进行简化
vi Makefile #创建Makefile
#编辑Makefile(模板) simple:main.o max.o min.o gcc -o simple main.o max.o min.o #Tab间隔 main.o:main.c gcc -c main.c -o main.o max.o:max.c gcc -c max.c -o max.o min.o:min.c gcc -c min.c -o min.c
方法一:在Makefile所在的目录下运行命令
make
方法二:运行命令make all
make [目标名]
gcc -c max.c -o max.o gcc -c min.c -o min.o gcc max.o min.o main.c -o simple
./simple
the max value is 5 the min value is 3
在前面的simple 项目中,现在假设在程序所在的目录下面有一个clean 文件,这个文件也可以用过touch命令来创建。
假目标可以采用.PHONY关键字来定义,需要注意的是其必须是大写字母
.PHONY:clean #假目标 simple:main.o max.o min.o gcc -o simple main.o max.o min.o #Tab间隔 main.o:main.c gcc -c main.c -o main.o max.o:max.c gcc -c max.c -o max.o min.o:min.c gcc -c min.c -o min.c clean: rm main.o min.o max.o simple
采用.PHONY 关键字声明一个目标后,make 并不会将其当作一个文件来处理,而只是当作一个概念上的目标。对于假目标,我们可以想像的是由于并不与文件关联,所以每一次make 这个假目标时,其所在的规则中的命令都会被执行
在Makefile 中通过使用变量来使得它更简洁、更具可维护性
$@ #表示一个规则中的目标。当我们的一个规则中有多个目标时,$@所指的是其中任何造成命令被运行的目标。(表示的是目标的集合) $^ #表示的是规则中的所有先择条件。(表示的是依赖的集合) $< #表示的是规则中的第一个先决条件。(表示目标列表里最后一个依赖)
【注意】由于在Makefile 中“$”具有特殊含义,因此,如果想采用echo 输出“$”,则必需用两个连着的“$”。还有就是,“$@”对于Shell 也有特殊的意思,我们需要
在“$$@”之前再加一个脱字符“\”。
其他一些字符的表示:
%.o 表示所有的.o文件 %.c 表示所有的.c文件
.PHONY:clean CC = gcc #定义变量 RM = rm OBJ = simple OBJS = main.o max.o min.o $(OBJ):$(OBJS) $(CC) -o $@ $^ main.o:main.c $(CC) -c $^ -o $@ max.o:max.c $(CC) -c $^ -o $@ min.o:min.c $(CC) -c $^ -o $@ clean: @$(RM) $(OBJS) $(OBJ)
addprefix 函数是用来给字符串中的每个子串前加上一个前缀,其形式是:$(addprefix prefix, names…)
.PHONY:all SOURCE = main.c max.c min.c OBJS = /home/pyma/Makefile add source = $(addprefix $(OBJS)/. $(SOURCE)) all: @echo $(add_source)
filter 函数用于从一个字符串中,根据模式得到满足模式的字符串,其形式是:$(filter pattern..., text)
.PHONY:all SOURCE = main.c max.c min.c OBJS = /home/pyma/Makefile add source = $(addprefix $(OBJS)/. $(SOURCE)) sources = $(filter %.c %.s,$(add_source) all: @echo $(add_source)
从结果来看,经过filter 函数的调用以后,sources 变量中只存在.c 文件和.s 文件,而.h 文件则被过滤掉了
filter-out 函数用于从一个字符串中根据模式滤除一部分字符串,其形式是:$(filter-out pattern..., text)
.PHONY:all SOURCE = main.c max.c min.c OBJS = /home/pyma/Makefile add source = $(addprefix $(OBJS)/. $(SOURCE)) sources = $(filter-out %.h,$(add_source) all: @echo $(add_source)
从结果来看,filter-out 函数将以“.h”为后缀的文件从add_source 变量中给滤除了。可以看出,filter 与filter-out 是互补的
wildcard 是通配符函数,通过它可以得到我们所需的文件,这个函数相当于我们在Windows或是Linux 命令行中的“*”。其形式是:$(wildcard pattern)
.PHONY:all SOURCE = $(wildcard *.c) OBJS = /home/pyma/Makefile add source = $(addprefix $(OBJS)/. $(SOURCE)) all: @echo $(add_source)
运行结果:从当前Makefile 所在的目录下通过wildcard 函数得到所有的C 程序源文件
patsubst 函数是用来进行字符串替换的,其形式是:$(patsubst pattern, replacement, text)
.PHONY:clean CC = gcc #定义变量 RM = rm OBJ = simple SRCS = $(wildcard *.c) OBJS = $(patsubst %.c,%.o,$(SRCS)) $(OBJ):$(OBJS) $(CC) -O $@ $^ main.o:main.c $(CC) -c $^ -o $@ max.o:max.c $(CC) -c $^ -o $@ min.o:min.c $(CC) -c $^ -o $@ clean: @$(RM) $(OBJS) $(OBJ)
OBJS 变量中采用patsubst 函数进行字符串替换,将所有的.c 文件都替换成.o 文件。patsubst 函数可以使用模式,所以其也可以用于替换前缀等,功能更加强大
选自华清远见《嵌入式操作系统》