gdb调试和makefile管理

一.gdb调试工具

命令

简写

作用

help

h

按模块列出命令类

help class

查看某一类型的具体命令

lsit

l

查看代码,可跟行号和函数名

quit

q

退出gdb

run

r

全速运行程序

start

单步执行,运行程序,停在第一行执行语句

next

n

逐过程执行

step

s

逐语句执行,遇到函数,跳到函数内执行

backtrace

bt

查看函数的调用的栈帧和层级关系

info

i

查看GDB内部局部变量的数值,info breakpoints

frame

f

切换函数的栈帧

finish

结束当前函数,返回到函数调用点

set

设置变量的值 set var n=100

run argv[1] argvp[2]

调试时命令行传参

print

p

打印变量和地址

break

b

设置断点,可根据行号和函数名

delete

d

删除渐点 d breakpoints NUM

display

设置观察变量

continue

c

取消观察变量

enable breakpoints

继续全速运行剩下的代码

disable breakpoints

启用断点禁用断点

x

查看内存x / 20xw 显示20个单元,16进制,4字节每单元

watch

被设置观察点的变量发生修改时,打印显示

i watch

显示观察点

core文件

ulimit -c 1024 开启core文件,调试时 gdb a.out core

前置工作:

mkdir -p ~/linux_1/gdb_test
cd ~/linux_1/gdb_test
touch gdbtest.c
vim gdbtest.c 往里面随意写入要调试的代码后退出
gcc gdbtest.c -o gdbtest
./gdbtst运行查看结果gcc gdbtest.c -o a.out -g使编译后的gdbtest.c产生的可执行为文件是包含调试信息的
ll  比较gdbtest和a.out的文件大小

1.基础指令

gdb  a.out进入调试模式当中,接下来的命令行前都会显示给(gdb)
//l 
//list
//    这两种能将要调试的源码列出,但有可能不是从第一行开始显示
list 1
l从第一行源码开始显示,然后依次点 l 会继续往下显示b 行号设置断点
r运行,也可以写成run,然后程序就会停止在设置断点的行号
nnext下一个,如果断点处是个函数,不会进去
sstep单步,如果遇到自定义函数调用会进去继续单步调试如果遇到系统函数,就要按 n ,否则会出错
util 行号如果遇到循环,调试的时候一个循环一个循环的进行,跳过循环内部util 加上循环结束的行号,然后接下来持续按 n 就可以一次n结束一个循环,直到整个for循环结束
p 变量名 查看变量所对应的值         
continue不想继续调试了,直接运行剩下的
quit退出当前调试

2.其它指令

1.进入gdb后,使用run去查找段错误出现位置char *p = “hello”;p[3] = ‘M’;对常量进行了赋值会出现段错误
2.strat进入gdb后,不想 b 设置断点然后 run 执行调试,直接从main函数开始调式可以用 start 替代:b main函数所在行号、run
3.finsh结束当前函数的调式
4.set args 参数设置main函数中形参的值,然后start,看代码块A
5.run 字符串1 字符串2 字符串3...和4一样设置main函数中的形参但是要先设置好断点
6.info b查看设置了哪些断点,断点是可以设多个的
7.b 行号 if 条件条件断点,比如fori循环中:b for所在行号 if i = 4在for循环执行到i=4的时候断点才会生效
8.ptype 变量名查看变量的类,ptype i;ptype arr[k]知识点扩展看下面(扩展1)
9.bt或者backtrace,查找函数的调用的栈帧和层级关系会显示出每个栈帧的信息以及编号
10.frame根据栈帧的编号切换栈帧看扩展1
11.display 变量名设置观察变量,一直跟踪该变量的情况,每n一下都会自动显示该变量值会自带编号
12.undisplay 编号取消设置跟踪变量,使用跟踪变量的编号

2.1 set命令行读取输入

--------代码块A

gdbtest.c中:int main(int argc, char *argv[]){printf("%s %s %s \n",argv[1],argv[2],argv[3]);   }保存退出[.....]# gcc gdbtest.c -o a.out
[.....]# ./a.out aa bb cc dd会输出aa bb cc    也就是int main函数中的参数会将命令行中的参数给读进去调式的时候,如何往里面添这aa bb cc dd?
[.....]# gcc gdbtest.c -o a.out -g
[.....]# gdb a.out
(gdb) set args aa bb cc dd
(gdb) start

2.2 栈帧:

-----代码块B

void init_arr(int *arr,int len){int i;......
}
void print_arr(....){.....
}
int main(void){char *p = "hello";int len;int arr[k];init_arr(arr,len);  -- (A)print_arr(arr,N);   -- (B)
}---以上为hello.c中的文件
gcc hello.c -o hello -g
gdb hello
b 1
n
n
s//进入到init_arr函数当中
ptype p
:No symbol "p" int current context//涉及到作用域的问题,p在main栈帧里,不在init_arr栈帧中//那么调式到init_arr函数时如何查看main栈帧中p的类型呢//不采用向init_arr函数中传入p的方式
bt 查看函数的调用的栈帧和层级关系,会显示出每个栈帧的信息栈帧前有对应的数字n,可以通过frame切换栈帧
frame n 切换到main函数的栈帧
ptype p这时候就能查到p的类型

栈帧:随着函数调用而在stack上开辟的一片内存空间,用户存放函数调用时产生的局部变量临时值

局部变量---函数被调用的时候,会在该函数所在的栈帧上存放局部变量以及传入函数的形参,当函数调用结束后,该栈帧就会被释放掉

临时值---函数的栈帧从哪个位置开始又从哪个位置消失再开始产生栈帧就和临时值有关,比如函数内fori循环,i达到什么条件循环结束,函数结束,i此时就是所谓临时值

如何在栈帧init_arr(调式到了函数init_arr)中去访问到main栈帧中的p呢?

这时候就可以用到指令 backtrace / bt 以及 frame

这就解决了没有向栈帧B(init_arr)中传入所想要查看的函数print_arr中的变量i的时候,在函数B(init_arr)中无法查看函数栈帧A(print_arr)中变量i的值的问题

二.Makefile项目管理

专门管理项目

1.规则

基本原则:

 若想生成目标,检查规则中的依赖条件是否存在,如不存在,则寻找是否有规则用来

检查规则中的目标是否需要更新,必须先检查它的所有依赖,依赖中有任一个被更新,则目标必须更新

  • 分析各个目标和依赖之间的关系
  • 根据依赖关系自底向上执行命令
  • 根据修改时间比目标新,确定更新
  • 如果目标不依赖任何条件,则执行对应命令,以示更新 
命名:makefile/Makefile只能命名这两个,否则无法去使用一些默认的命令目标:依赖条件(一个tab缩进)命令如:
add.o:add.cgcc -Wall -g -c add.c -o add.o    
目标:要生成的目标文件
依赖:目标文件由哪些文件生成
命令:通过执行该命令由依赖文件生成目录

1.1 分步骤

[....]# mkdir ~/linux_1/make_test_1
[....]# cd ~/linux_1/make_test_1
[....]# vim ./hello.c写入一部分代码
[....]# touch ./makefile命名只能用makefile或者Makefile------1-----
[....]# vim ./makefile打开makefile向里面去写入所谓的命名规则                           hello:hello.cgcc hello.c -o hello:wq
[....]# makemakefile中的依赖条件存在,运行规则
>>gcc hello.c -o hello
[....]# ls
>>hello.c  hello  makefile------2------
[....]# vim ./makefile打开makefile修改规则,想要让hello.c生成可执行文件需要先-c生成.o,在gcc生成.out文件,hello是最终目标hello:hello.ogcc -c hello.o -o hellohello.o:hello.cgcc -c hello.c -o hello.o:wq
[....]# rm ./hello
[....]# make
>>gcc -c hello.c -o hello.o
>>gcc -c hello.o -o hello
[....]# ls
>>hello hello.o hello.c makefile

1.2 修改编译、最终目标

[....]# cd ~/linux_1/make_test_2/
[....]# touch add.c div1.c hello.c Makefile sub.cadd div1 sub中分别写入函数定义,能够返回各自的值
[....]# vim hello.c写入:int add(int a,int b);......int main(int argc, char *argv[]){int a = 10;int b = 5;printg("%d+%d=%d",a,b,add(a,b));......return 0;    }
[....]# gcc hello.c  add.c sub.c div1.c -o a.out
[....]# ./a.out
>>10+5=15
>>.....*****使用Makefile*******
[....]# rm a.out
[....]# vim Makefilegcc hello.c add.c sub.c div1.c -o a.out:wq
[....]# make
>>gcc hello.c  add.c sub.c div1.c -o a.out//比如对add.c做出修改,但另外两个不用做出修改,这时候要//重新make一下得到修改后得到的可执行文件a.out//但是,sub和div1明明没修改却也还要跟着重新编译一次//就像一个团队中因为一人的原因导致另外的人也要跟着一起加班//因为gcc四步骤中编译最耗时,那么就可以先让.c编程成.o文件
[....]# rm Makefile
[....]# vim Makefilea.out:hello.o add.o sub.o div1.ogcc hello.o add.o sub.o div1.o -o a.outhello.o:hello.cgcc -c hello.c -o hello.oadd.o:add.cgcc -c add.c -o add.o........:wq
[....]# rm ./*.o
[....]# make
>>gcc -c hello.c -o hello.o
>>gcc -c add.c -o add.o
>>.....
>>gcc hello.o add.o sub.o div1.o -o a.out
[....]# ./a.out
>>10+5=10
>>.....
[....]# vim add.c 做出修改return a+b+1:wq
[....]# make
>>gcc -c add.c -o add.o
>>gcc hello.o add.o sub.o div1.o -o a.out//可以发现,这时候只对add.c做出修改,make后//只有add.c重新编译成add.o//然后gcc链接生成可执行文件,div1和sub都没有重新编译//见原理1*****注意******
[....]# rm ./*.o
[....]# rm Makefile
[....]# vim Makefilehello.o:hello.cgcc -c hello.c -o hello.oadd.o:add.cgcc -c add.c -o add.o........a.out:hello.o add.o sub.o div1.ogcc hello.o add.o sub.o div1.o -o a.out:wq
[....]# make
>>gcc -c hello.c -o hello.o//makefile会查看你写的makefile文件,把它碰到的第一组规则作为//最终目标,一旦最终目标执行了,规则都不在执行//指定终极目标见原理1
[....]# make -f m
# 如果创建的makefile不在当前目录,而在m目录中,用-f指明

原理:

因为add.c的时间是比add.o的时间早的,也就是现有add.c后又add.o,而这时候add.c一旦做出修改,那么其时间就比add.o的时间晚了,此时makefile辨别到,就会对add.c重新编译生成新的add.o

也就是依赖条件 add.c 时间比生成目标 add.o 时间要早

可以通过ALL来指定终极目标

2.函数

Makefile函数

src = $(wildcard *.c)
# 找到当前目录下所有后缀为.c的文件,将文件名提取出来作为一个列表赋值给src
# 此时src就是所谓的依赖列表obj = $(patsubst %.c,%.o,$(src))
把src变量里所有后缀位.c的文件替换成.o,同时作为新列表存储到obj
%是匹配等价的意思,比如从src取出来的是./src/add.c,那么%.c中的%就是./src/add
然后会等价给%.o,是通配符,有锁定的意思

运用》》》

clean函数

vim ./makefileclean:-rm -rf $(obj) a.out# –: “-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”:wqmake clean -n# 检查要执行的命令是否正确,只打印要执行的命令
make clean # 确认没问题后再去执行

如果目录下已经被创建了ALL或者是clean文件,那么可能就不会去执行clean命令,可以通过:

.PHONY:clean ALL

生成伪目标,不管条件满足与否,它这个目标都会被执行

Makefile函数new

a.  $(foreach var, list, text)        # 对于list中的每个值var都执行text,然后用=赋值存到起来
b.  $(filter pattern..., text)        # 在text中取出符合pattern格式的值$(filter-out pattern..., text)    # 在text中取出不符合pattern格式的值
c.  $(wildcard pattern)               # 在patter定义了文件名的格式,wildcard取出其中存在的文件
d.  $(pastubst pattern, replacement, text)# 在test列表中符合pattern的将其替换成replacement,然后将替换后的列表传出
# a
A = a b c
B = $(foreach f, &(A), &(f).o)  # b
C = a b c d/
D = $(filter %/, $(C)) 
E = $(filter-out %/, $(C))  # c
F =  $(wildcard *.c) 
F2 = a.c b.c c.c d.c
F3 =  $(wildcard $(F2))  # 看F2中哪些文件是真实存在makefile所在目录下的# d
G = $(pastubst %.c, %.o, $(F)) # 将F中的.c都换成.o文件,然后将列表赋值到GALL:echo B = $(B)  # echo B = a.o b.o c.oecho D = $(D)  # echo D = d/echo E = $(E)  # echo E = a b c echo F = $(F)  # 输出的是和makefile同个目录下的.c文件echo F3 = $(F3)# 输出的makefile目录下和F2同时拥有的.c文件         echo G = $(G)  # a.o b.o c.o d.o 

3.自动变量

$@:在规则的命令中,表示规则中的目标

$<:在规则的命令中,表示规则中的第一个条件,如果将该变量

应用在模式规则当中,它可将依赖条件列表中的依赖依次取

出,套用模式规则

$^:在规则的命令中,表示规则中的所有条件,组成一个列表,

以空格隔开,如果这个列表中有重复的项则消除重复项。

》》》》

add.o依赖于add.c,也就是add.c($<,条件)决定add.o($@,目标)

模式规则

.o 依赖于 .c

静态模式规则

$(obj):%.o:%.c

gcc -c $< -o %@

# 表示对哪一个依赖条件 $(obj) 去套用这个规则

4.赋值方法

:= # 即使变量 A := XXX -> A的值即可确定,在定义时即确定

= # 延时变量 B = XXX -> B的值使用到时才确定

?= # 延时变量,如果是第1次定义才起效,如果在前面该变量已定义,则忽略这句

+= # 附加,它是即时变量还是延时变量却决于前面的定义

********eg:
A = $(C)
B := $(c)
D = 100ask
D ?= weiALL:echo A = $(C) # 空echo B = $(C) # abcecho D = $(D) # 100ask
C = abc# A的值在定义的时候就已经确定了,也就是为空(C为空),即使后面给C赋值了abc,A也还是为空
# 在echo B,B的值才被确认,ALL是最终目标,C先被赋值了abc,echo B 使用了B,B的值确定为abc
# D第一次已经被定义,D ?= wei不会赋值到D上,D的值还是第一次赋值的100ask

5.合并

经过上面合并可以得出最终的模板:

1、2:src存放的是make后面的多个.c文件的列表,obj则是将src中的.c文件列表变成.o列表赋值到obj

11、12:挨个去生产.o文件(E预处理.i、S编译.s、c预处理.o)

8、9:差不多是链接,collect2将obj中对应的所有.o文件都链接成a.out目标文件

17:将clean ALL判断为假想目标,这样就不会去判断其是否存在

other:

objs := main.o sub.o

test : $(objs)
gcc -o test $^# 需要判断是否存在依赖文件
# .main.o.d .sub.o.d
dep_files := $(foreach f, $(objs), .$(f).d)
dep_files := $(wildcard $(dep_files))# 把依赖文件包含进来
ifneq ($(dep_files),)include $(dep_files)
endif%.o : %.c
gcc -Wp,-MD,.$@.d  -c -o $@  $<clean:
rm *.o test -fdistclean:
rm  $(dep_files) *.o test -f

6.运用

head.h文件:

hello.c:


# obj目录中存放.o文件,src目录中存放.c文件,makefile放规则
# inc目录存放.h文件
# 对makefile中的规则重新设置,指定路径
[....]# cd /StudyTest/maketest
[....]# touch ./inc
[....]# cd ./inc
[....]# vim head.h[....]# cd ..  # 返回上一级目录,也就是maketest[....]# ls ./src
>>add.c div1.c hello.c mul.c sub.c[....]# ls 
>>inc obj src makefile[....]# gcc -c ./src/add.c -o ./obj/add.o -I ./inc# 不使用make规则来实现。。。。。
[....]# gcc add.o sub.o ... hello.o -o a.out# 链接生成可执行文件# 使用make规则来写:
[....]# vim makefilesrc=$(wildcard ./src/*.c)obj=$(patsubst ./src/%.c ./obj/%*.o, $(src))# 用%代替add、sub等名字inc_path=./inc # 指定头文件所在目录myArgs= -Wall -gALL:a.outa.out:$(obj)gcc $^ -o $@ $(myArgs)$(obj):./obj/%.o:./src/%.cgcc -c $< -o $@ $(myArgs) -I $(inc_path)# 头文件的处理是在预处理阶段的,不是在链接阶段# 因此 -I 得加在这里clean:-rm -rf $(obj) a.out.PHONY:clean ALL

7.选项参数

-n模拟执行make,make clean命令我们可以使用“-f”选项指定文件,不再使用名为“Makefile”的文件,比
如:make -f Makefile.build 
我们可以使用“-C” and “-f”选项指定目录,切换到其他目录里去执行指定的make文件,比如:make -C a/ -f Makefile.build 
我们可以指定目标,不再默认生成第一个目标:make -C a/ -f Makefile.build other_target

8.通用Makefile

顶层目录Makefile.build:

 PHONY := __build
# 第一个目标
__build:
# obj-y是Makegile中定义过的,expert导入Makefile后有Makegile中已经被赋值上的列表
obj-y :=subdir-y :=
EXTRA_CFLAGS :=# 将目录下的Makefile文件包含进来 -- 类似于合并
# 在顶层目录去执行Makefile去执行就包含进顶层的Makefile
# -C指定了去子目录执行Makefile就包含进子目录下的Makefile
include Makefile# obj-y := main.o sub.o a/ c/ d/
# $(filter %/, $(obj-y))   : a/ c/ d/
# __subdir-y  : a c d
# subdir-y    : a c d,存放的是子目录的录名
# 在顶层去执行Makefilebuild:将子目录的(例如a/ c/ d/)的a b c目录名取出来,附加到即时变量subdir-y
# 如果是在子目录去执行Makefilebuild,则下面两个列表都是为空(子目录无子目录)
__subdir-y:= $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y+= $(__subdir-y)# 各子目录中的.c文件编译后打包起来的.o列表:a/built-in.o c/built-in.o d/built-in.o
# 这个是在子目录中执行完Makefilebuild后的假想打包起来的.o列表,在子目录未提前存好打包的列表
subdir_objs := $(foreach f,$(subdir-y),$(f)/built-in.o)# 顶层目录中的.c文件的编译后的想要取名的.o列表:main.o sub.o
# 如果是去子目录中执行的Makefilebuild,挑选出的就是a下的sub2.o sub3.o
cur_objs := $(filter-out %/, $(obj-y))# 加上.d后缀挑出编译后目录中存在的.d存起来,用于distclean清除文件
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
# 包含进Makefilebuild文件中
ifneq ($(dep_files),)include $(dep_files)
endifPHONY += $(subdir-y)# 第一个目标,__build,在第3行指明过,依赖未形成,去执行依赖
__build : $(subdir-y) built-in.o# 编译子目录,进入子目录编译,TOPDIR是Makefile中的,是该Makefilebuild的目录位置
# 就是$@指定了make要去子目录下make,执行的makefile文件是顶层目录的Makefile.build
# 而include则是包含了$@子目录下的Makefile -- a/中也有自己的Makefile文件
$(subdir-y):
make -C $@ -f $(TOPDIR)/Makefile.build# 当前目录下的.o 和子目录下的 built-in.o() 要打包起来,全都打包进built-in.o
built-in.o : $(cur_objs) $(subdir_objs)
$(LD) -r -o $@ $^# 存放依赖文件
dep_file = .$@.d# 假想的各自的.o
# 编译当前目录中的文件
# 编译参数 
# 额外的编译选项(可以不设置,是设置了makefile的文件的所属目录才有该参数的功能)-- 子目录中makefile设置了,子目录下的.c有该参数功能
# 额外给xxx.c设置它自己的编译选项(是设置了的makefile文件所属目录下的指定.c才有该参数的功能) -- 子目录中的makefile设置了,子目录下指定的.c有该参数功能
%.o : %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -Wp,-MD,$(dep_file) -c -o $@ $<.PHONY : $(PHONY)

顶层Makefile:

CROSS_COMPILE = 
AS= $(CROSS_COMPILE)as
LD= $(CROSS_COMPILE)ld
CC= $(CROSS_COMPILE)gcc
CPP= $(CC) -E
AR= $(CROSS_COMPILE)ar
NM= $(CROSS_COMPILE)nmSTRIP= $(CROSS_COMPILE)strip
OBJCOPY= $(CROSS_COMPILE)objcopy
OBJDUMP= $(CROSS_COMPILE)objdump# 该变量的值在所有目录中都可以见
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP# 编译参数
CFLAGS := -Wall -O2 -g
# 库的目录,头文件h的路径
CFLAGS += -I $(shell pwd)/include LDFLAGS := export CFLAGS LDFLAGS# 当前目录,该变量的值在所有目录中都可见(export)
TOPDIR := $(shell pwd)
export TOPDIR# 链接后要产生的指定的可执行文件的名
TARGET := test# obj-y存放的是顶层目录的.o以及子目录a/等
# 添加本目录下的main.c(main.o)和sub.c(sub.o)编进进obj-y
obj-y += main.o 
obj-y += sub.o
# 指定子目录(存放其它.c文件的子目录) -- 添加进列表
# 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir日录下的Makefile决定
obj-y += a/# 目标是all,依赖是start_recursive_build和$(TARGET),还有,产生
all : start_recursive_build $(TARGET)
@echo $(TARGET) has been built!  # 可执行文件已经创建# 执行本本目录下的Makefile.build处理顶层目录(Makefilebuild本目录)
# $(TOPDIR)存放的是前面shell pwd获得的Makefilebuild的路径
# -C后面跟的是目录,表示make要执行哪个文件的makefile
# 用-f来指定make执行的是Makefilebuild,不添加该参数make还是执行本Makefile
start_recursive_build:
make -C ./ -f $(TOPDIR)/Makefile.build# 顶层 Makefile 中把顶层目录的 built-in.o 链接成 APP,生成可执行文件
$(TARGET) : built-in.o
$(CC) -o $(TARGET) built-in.o $(LDFLAGS)clean:
rm -f $(shell find -name "*.o")
rm -f $(TARGET)distclean:
rm -f $(shell find -name "*.o")
rm -f $(shell find -name "*.d")
rm -f $(TARGET)# 顶层目录的Makefile:
# 它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,
# 主要是定义工具链前缀CROSS COMPILE,定义编译参数CFLAGS,
# 定义链接参数LDFLAGS,这些参数就是文件中用export导出的各变量。
子目录Makefile:EXTRA_CFLAGS := -D DEBUG
CFLAGS_sub3.o := -D DEBUG_SUB3obj-y += sub2.o 
obj-y += sub3.o 

说明:

本程序的Makefile分为3类:
1. 顶层目录的Makefile
2. 顶层目录的Makefile.build
3. 各级子目录的Makefile一、各级子目录的Makefile:它最简单,形式如下:EXTRA_CFLAGS  := 
CFLAGS_file.o := 
obj-y += file.o
obj-y += subdir/"obj-y += file.o"  表示把当前目录下的file.c编进程序里,"obj-y += subdir/" 表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。"EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置"CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置注意: 
1. "subdir/"中的斜杠"/"不可省略
2. 顶层Makefile中的CFLAGS在编译任意一个.c文件时都会使用
3. CFLAGS  EXTRA_CFLAGS  CFLAGS_xxx.o 三者组成xxx.c的编译选项二、顶层目录的Makefile:它除了定义obj-y来指定根目录下要编进程序去的文件、子目录外,主要是定义工具链前缀CROSS_COMPILE,定义编译参数CFLAGS,定义链接参数LDFLAGS,这些参数就是文件中用export导出的各变量。三、顶层目录的Makefile.build:这是最复杂的部分,它的功能就是把某个目录及它的所有子目录中、需要编进程序去的文件都编译出来,打包为built-in.o详细的讲解请看视频。
四、怎么使用这套Makefile:
1.把顶层Makefile, Makefile.build放入程序的顶层目录在各自子目录创建一个空白的Makefile2.确定编译哪些源文件修改顶层目录和各自子目录Makefile的obj-y : obj-y += xxx.o
obj-y += yyy/
这表示要编译当前目录下的xxx.c, 要编译当前目录下的yyy子目录3. 确定编译选项、链接选项修改顶层目录Makefile的CFLAGS,这是编译所有.c文件时都要用的编译选项;修改顶层目录Makefile的LDFLAGS,这是链接最后的应用程序时的链接选项;修改各自子目录下的Makefile:"EXTRA_CFLAGS",    它给当前目录下的所有文件(不含其下的子目录)设置额外的编译选项, 可以不设置"CFLAGS_xxx.o",    它给当前目录下的xxx.c设置它自己的编译选项, 可以不设置4. 使用哪个编译器?修改顶层目录Makefile的CROSS_COMPILE, 用来指定工具链的前缀(比如arm-linux-)5. 确定应用程序的名字:修改顶层目录Makefile的TARGET, 这是用来指定编译出来的程序的名字6. 执行"make"来编译,执行"make clean"来清除,执行"make distclean"来彻底清除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1544078.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 窗口类的继承关系和作用

核心基类 [1] QObject&#xff1a;Qt中许多类的基类&#xff0c;支持Qt对象模型&#xff0c;包括信号和槽机制、对象树和事件系统等。虽然它本身不是直接用于创建窗口的&#xff0c;但它是许多窗口和控件类继承链中的重要一环。 注&#xff1a;如果你创建了一个自定义类&…

杰发科技——Eclipse环境安装

文件已传到网盘&#xff1a; 1. 安装文件准备 2. 安装Make 默认路径&#xff1a;C:\Program Files (x86)\GnuWin32\bin\ 不复制的话会报错 Error: Program "make" not found in PATH 3. 安装工具链 默认路径&#xff1a;C:\Program Files (x86)\Arm GNU Toolchain…

OpenAI converting API code from GPT-3 to chatGPT-3.5

题意&#xff1a;将OpenAI API代码从GPT-3转换为ChatGPT-3.5 问题背景&#xff1a; Below is my working code for the GPT-3 API. I am having trouble converting it to work with chatGPT-3.5. 以下是我用于GPT-3 API的工作代码。我在将其转换为适用于ChatGPT-3.5时遇到了…

Android Studio 真机USB调试运行频繁掉线问题

一、遇到问题 Android Studio使用手机运行项目时&#xff0c;总是频繁掉线&#xff0c;连接很不稳定&#xff0c;动不动就消失&#xff0c;基本上无法使用 二、问题出现原因 1、硬件问题&#xff1a;数据线 换条数据线试试&#xff0c;如果可以&#xff0c;那就是数据线的…

15年408-数据结构

第一题 解析&#xff1a; 栈第一次应该存main的信息。 然后进入到main里面&#xff0c;要输出S(1)&#xff0c;将S(1)存入栈内&#xff0c; 进入到S(1)中&#xff0c;1>0,所以还要调用S(0) S(0)进入栈中&#xff0c;此时栈内从下至上依次是main(),S(1),S(0) 答案选A 第二题&…

昇腾AI异构计算架构CANN——高效使能AI原生创新

异构计算与人工智能的关系是什么&#xff1f;昇腾AI异构计算架构CANN是什么&#xff1f;有哪些主要特点&#xff1f;开发者如何利用CANN的原生能力进行大模型创新&#xff0c;构筑差异化竞争力&#xff1f;带着这些问题&#xff0c;我们来认识昇腾AI异构计算架构——CANN。 1 …

随机验证码验证【JavaScript】

这段 JavaScript 代码实现了随机验证码的生成和验证功能。 实现效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-…

Vue3教程 - 2 开发环境搭建

更好的阅读体验&#xff1a;点这里 &#xff08; www.foooor.com &#xff09; 2 开发环境搭建 要进行 Vue 开发&#xff0c;需要安装 Node.js&#xff0c;因为构建 Vue 项目的工具&#xff0c;例如 Webpack、Vite等&#xff0c;这些工具依赖于Node.js环境来运行。 Node.js…

OpenAI GPT-3 API: What is the difference between davinci and text-davinci-003?

题意&#xff1a;OpenAI GPT-3 API&#xff1a;davinci 和 text-davinci-003 有什么区别 问题背景&#xff1a; Im testing the different models for OpenAI, and I noticed that not all of them are developed or trained enough to give a reliable response. 我正在测试…

玩手机数据集 8201张玩手机的照片,有对应的xml和txt文件,可以用于yolo训练

玩手机数据集 8201张玩手机的照片&#xff0c;有对应的xml和txt文件&#xff0c;可以用于yolo训练 玩手机数据集&#xff08;Phone Usage Detection Dataset&#xff09; 数据集概述 该数据集专为检测人们使用手机的行为设计&#xff0c;旨在帮助研究人员和工程师开发高效的…

Sentinel-1 数据处理时如何手动下载高程数据

在Sentinel-1 数据数据预处理时&#xff0c;会使用高程数据进行地形校正。但选择自动下载高程时&#xff0c;由于网络原因经常会卡死&#xff0c;造成预处理过程不能正常进行&#xff01; 这个问题经过我的反复实践&#xff0c;可以通过手动下载高程数据来解决。下面是具体方法…

MySQL之基本查询(一)(insert || select)

目录 一、表的增删查改 二、表的增加insert 三、表的读取select where 条件子句 结果排序 筛选分页结果 一、表的增删查改 我们平时在使用数据库的时候&#xff0c;最重要的就是需要对数据库进行各种操作。而我们对数据库的操作一般来说也就是四个操作&#xff0c;CRUD :…

【学术会议征稿】第七届电力电子与控制工程国际学术会议(ICPECE 2024)

高录用|快检索|JPCS独立出版ISSN:1742-6596 连续6年EI检索&#xff0c;稳定且快速 见刊后均1个月检索 第七届电力电子与控制工程国际学术会议&#xff08;ICPECE 2024&#xff09; 2024 7th International Conference on Power Electronics and Control Engineering 2024年…

认知作战壳吉桔:未来认知战战略全案发展趋势

认知作战壳吉桔&#xff1a;未来认知战战略全案发展趋势 认知作战壳吉桔&#xff1a;未来认知战战略全案发展趋势 关键词&#xff1a;认知战战术融合, 数据驱动决策, 生成式人工智能AIGC, 影响力认知, 个性化定制, 沉浸式体验, 视频直播认知, 受众体验优化, 社交媒体互动, 可…

[PICO VR]Unity如何往PICO VR眼镜里写持久化数据txt/json文本

前言 最近在用PICO VR做用户实验&#xff0c;需要将用户实验的数据记录到PICO头盔的存储空间里&#xff0c;记录一下整个过程 流程 1.开启写入权限 首先开启写入权限&#xff1a;Unity->Edit->Player->安卓小机器人->Other Settings->Configuration->Wri…

如何用LightningChart Python实现地震强度数据可视化应用程序?

LightningChart Python是知名图表控件公司LightningChart Ltd正在研发的 Python 图表&#xff0c;目前还未正式推出&#xff0c;感兴趣的朋友可以戳文末链接申请试用&#xff01; 什么是地面震动强度 Python 应用程序&#xff1f; 地面震动是地震的基本特征&#xff0c;会对建…

【Linux】fork入门级使用

目录 一、前置准备 1、进程的基本概念 2、进程标识符PID、PPID 1&#xff09;pid介绍 2&#xff09;获取pid和ppid 二、fork函数 1、fork的基本介绍 1&#xff09;fork&#xff08;&#xff09;&#xff1a; 创建子进程 2&#xff09;对于函数具体的描述 3&#xff09…

yuque-dl-语雀知识库下载为本地markdown

之前有下载语雀的需求&#xff0c;发现了一个开源项目&#xff0c;用起来还是很顺手的 需要环境 需要前端的node环境 https://nodejs.cn/ Node.js 18.4 or later 插入 npm i -g yuque-dl 使用 $ yuque-dl --helpUsage:$ yuque-dl <url>Commands:<url> …

Python 如何读取和写入文件

Python 如何读取和写入文件 文件操作是编程中常见的任务&#xff0c;几乎所有编程语言都支持文件的读写功能。在 Python 中&#xff0c;文件操作简单直观&#xff0c;适用于从初学者到经验丰富的开发者。无论是处理小型文本文件&#xff0c;还是需要处理大型数据文件&#xff…

fo-dicom是如何实现DICOM 的网络通信功能

一、前言 前面的文章&#xff0c;我们介绍了fo-dicom是一个怎样的开源库等一些内容&#xff1a; fo-dicom&#xff0c;第一个基于.NET Standard 2.0 开发的DICOM开源库fo-dicom开源库是如何满足 DICOM标准的基本要求fo-dicom开发之DICOM数据解析&#xff1a;常见数据类型及处…