概述:

由于目前国产桌面操作系统都是以linux内核为基础开发的,因此国产桌面操作系统应用程序开发实际上就是在linux系统上进行应用开发。当前软件种类繁多,应用场景可以说无所不在,软件种类虽多,但目前我们软件开发环境主要还是windows系统和linux系统环境两类,除了Qt这种跨平台软件在windows和linux系统上都有相同的开发环境外,linux系统软件开发和windows系统的开发环境还是有一定的区别。

Windows 系统下一般是使用集成开发环境(IDE),例如vxworks软件开发工具:workbench,arm软件开发工具:keil。这类工具之所叫集成开发环境是因为它们将代码编辑器、编译器、调试器这些软件开发工具集成在了一起。

Linux系统下的软件开发工具也包括编辑器、编译器、调试器这些工具,不同于集成开发环境,这些工具都是独立的。

由于C语言软件设计、编码规范等内容和开发环境关联不大,因此本文档没有包含这些内容,只涉及linux开发环境相关内容。本文档主要介绍linux环境C语音编译器、调试器的相关功能和使用方法,不包含编辑器内容,因为软件代码可以在windows环境编辑完成后在上传到linux系统,也可以使用linux系统上的gedit工具进行编辑,使用方式和windows相似,另外linux上的vi工具也可以。

1编译器gcc

gcc是GNU(开源软件组织)推出的免费开元软件之一,gcc不光能编译C语言,也能编译C++,objective-C 等语言,在本文中只讲述编译C语言。

gcc将C语言源文件编译成可执行程序分别要执行以下几个步骤:预处理、编译、汇编、链接四个步骤。下面通过一个简单的C程序演示各个过程。程序树下:

#include "stdio.h"

#define DISP_STR "hello world"

int main()

{

printf("display str:%s\n",DISP_STR);

return 0;

}

1.1预处理

gcc对C源文件进行编译的第一步是通过预处理器(C preprocessor)对文件进行预处理,生成一个新文件,预处理的目的是展开源文件中的所有宏定义。

Linux终端运行命令gcc -E main.c > main.c.pre进行预处理,查看预处理后的文件如下:

int main()

{

printf("display str:%s\n","hello world");

return 0;

}

1.2 编译

预处理后接着生成的预处理文件被gcc调用编译工具(compiler)进行编译,编译的结果生成汇编程序,编译通俗的讲法就是翻译,这里将C语言翻译成汇编语言。运行命令:gcc -S main.c

将上面的main.c文件编译成main.s如下所示(演示平台为x86):

.file   "main.c"

.text

.section        .rodata

.LC0:

.string "hello world"

.LC1:

.string "display str:%s\n"

.text

.globl  main

.type   main, @function

main:

.LFB0:

.cfi_startproc

pushq   %rbp

.cfi_def_cfa_offset 16

.cfi_offset 6, -16

movq    %rsp, %rbp

.cfi_def_cfa_register 6

leaq    .LC0(%rip), %rsi

leaq    .LC1(%rip), %rdi

movl    $0, %eax

call    printf@PLT

movl    $0, %eax

popq    %rbp

.cfi_def_cfa 7, 8

ret

.cfi_endproc

.LFE0:

.size   main, .-main

.ident  "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"

.section        .note.GNU-stack,"",@progbits

1.2 汇编

编译之后,gcc使用汇编器(assembler)将汇编语言文件转换成目标文件(main.o),汇编之后的目标文件只包含符号信息。

运行命令:gcc -c main.c

生成目标文件main.o,main.o文件可以用工具objdump目标文件进行反汇编,例如终端输入命令:

objdump -d -s  main.o,反汇编信息如下:

main.o:     file format elf64-x86-64

Contents of section .text:

0000 554889e5 488d3500 00000048 8d3d0000  UH..H.5....H.=..

0010 0000b800 000000e8 00000000 b8000000  ................

0020 005dc3                               .].

Contents of section .rodata:

0000 68656c6c 6f20776f 726c6400 64697370  hello world.disp

0010 6c617920 7374723a 25730a00           lay str:%s..

Contents of section .comment:

0000 00474343 3a202855 62756e74 7520372e  .GCC: (Ubuntu 7.

0010 332e302d 31367562 756e7475 33292037  3.0-16ubuntu3) 7

0020 2e332e30 00                          .3.0.

Contents of section .eh_frame:

0000 14000000 00000000 017a5200 01781001  .........zR..x..

0010 1b0c0708 90010000 1c000000 1c000000  ................

0020 00000000 23000000 00410e10 8602430d  ....#....A....C.

0030 065e0c07 08000000                    .^......

Disassembly of section .text:

0000000000000000 <main>:

0:   55                      push   %rbp

1:   48 89 e5                mov    %rsp,%rbp

4:   48 8d 35 00 00 00 00    lea    0x0(%rip),%rsi        # b <main+0xb>

b:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 12 <main+0x12>

12:   b8 00 00 00 00          mov    $0x0,%eax

17:   e8 00 00 00 00          callq  1c <main+0x1c>

1c:   b8 00 00 00 00          mov    $0x0,%eax

21:   5d                      pop    %rbp

22:   c3                      retq

1.2 链接

汇编之后,gcc使用连接器将目标文件main.o和C语言的标准库链接在一起,生成一个可运行的执行程序。Linux系统下生成的可以执行文件并不需要像windows系统那样已.exe结尾,可以是任意名称。

输入命令:gcc -o test_gcc main.c

生成名称为“test_gcc”的执行程序,终端输入命令./test_gcc运行该程序,结果如下:

[jingjia@ubuntu1804/home/developer/samba/zhousonglin/gcc_test]$./test_gcc

display str:hello world

以上例子链接了C库,为了更好的说明gcc的链接功能,下面链接一个我们自定义的库文件,做如下修改:

1、在以上程序基础上增加一个文件:foo.c文件内容如下:

#include "stdio.h"

void foo()

{

printf("this is foo\n");

}

输入命令:ar crs libfoo.a foo.o,生成libfoo.a库文件

也可以生成动态库:

gcc -fPIC -shared -o libfoo.so foo.o

输入命令:cp libfoo.so /usr/local/lib将动态库拷贝到系统默认链接目录下,这样运行时才能找到对应库文件。运行命令sudo ldconfig始更改生效。

2、修改main.c增加对foo函数的调用:

#include "stdio.h"

#define DISP_STR "hello world"

extern void foo();

int main()

{

printf("display str:%s\n",DISP_STR);

foo();

return 0;

}

输入命令:gcc -o test_gcc main.c -L./ -lfoo,生成执行程序“test_gcc”,运行结果如下:

display str:hello world

this is foo

本章小结:本章通过一个简单的程序介绍了gcc工具编译C程序的整个过程,以上只介绍了gcc很小一部分功能,整个gcc工具非常强大,读者如果像进一步了解可以阅读《Using the GNU Compiler Collection》手册。

2编译管理工具make和Makefile

上一章中我们使用gcc编译程序都是通过手动输入命令的方式,虽然例子中的程序文件很简单,但每次编译都要重新输入命令确实很麻烦,而且我们的真实项目一般具有多个文件,而且还要链接各种库文件,在这种情况下就需要对项目文件进行编译管理,linux系统下make和Makefile能方便的对项目工程进行编译管理,make工具通过解析Makefile文件对项目程序进行编译管理。

2.1 Makefile文件结构

Makefile的规则由以下三部分组成:

目标:要生成的文件

依赖:生成目标依赖的其他目标

命令:告诉make如何生成目标(注意:命令必须TAB键开始)

规则格式如下:

target:prerequisites

command

Make解析Makefile文件时如果没有指定目标,默认从第一个目标开始执行,否则从指定目标开始执行。为了通过make生成最终目标,需要产生大量中间目标,makefile规则所描述的依赖关系将所有的目标关联在一起,最终生成库和创建可执行文件。

下面通过一个简单的Makefile,演示不同目标生成过程(命令前加‘@’符号作用不显示命令)Makefile文件的名称一般用“Makefile”(文件名称也可以是其他名字,但运行make时需要加-f参数,例如:make -f makefiletest):

终端输入make命令:

all:test

@echo "hello world"

test:

@echo "do test"

运行结果:

do test

hello world

由结果可知,默认先生成目标”all”,由于目标”all”依赖目标“test”,所以先生成目标“test”,然后生成目标“all”。

也可以指定生成某个目标,例如输入命令:make test:

运行结果:

do test

2.2 Makefile编写演示

用上章的例子代码演示简单Makefile的编写:
main.c代码:

#include "stdio.h"

#define DISP_STR "hello world"

int main()

{

printf("display str:%s\n",DISP_STR);

return 0;

}

Makefile内容:

test.exe:main.o

gcc -o test.exe main.o

main.o:main.c

gcc -c -o main.o main.c

终端运行make命令,当前目录下生成目标文件

通过熟悉上面简单的Makefile文件我们已经了解Makefile的结构和运行机制。以上makefile形式虽然也可以编译、产生执行程序,但是可维护性不好,当我们项目中增加,减少文件时都需要修改Makefile文件,例如需要增加一个前面例子的foo.c源文件,main.c文件增加对foo.c中函数的引用:

foo.c文件内容:

#include "stdio.h"

void foo()

{

printf("this is foo\n");

}

main.c文件内容:

#include "stdio.h"

#define DISP_STR "hello world"

extern void foo();

int main()

{

printf("display str:%s\n",DISP_STR);

foo();

return 0;

}

Makefile文件:

test.exe:main.o foo.o

gcc -o test.exe main.o foo.o

main.o:main.c

gcc -c -o main.o main.c

foo.o:foo.c

gcc -c -o foo.o foo.c

从上面例子可以看到增加一个源文件会导致Makefile需要修改多处。实际项目中源文件多,而且改动频繁,这样结构的Makefile可维护性太差。下面通过介绍Makefile的其他一些语法特性增加Makefile的可维护性。

2.3 Makefile常用语法点

2.3.1 假目标

项目中通常需要对某些已s生成的目标文件进行清理,这种情况下就需要用到假目标,假目标没有依赖文件,只有命令,运行make时只要指定对应目标就会执行目标命令,假目标采用”.PHONY”来定义(字母大写)。在以上Makefile例子中增加clean假目标来清除生成的文件,Makefile文件内容如下:

.PHONY: clean

test.exe:main.o foo.o

gcc -o test.exe main.o foo.o

main.o:main.c

gcc -c -o main.o main.c

foo.o:foo.c

gcc -c -o foo.o foo.c

clean:

rm foo.o main.o test.exe

2.3.2 自定义变量

Makefile中的变量可提供Makefile 的可维护性,引用变量采用$(变量名)或${变量名}的形式,以上例子中增加以下变量:

.PHONY: clean

CC = gcc

RM = rm

EXE = test.exe

OBJS = main.o foo.o

$(EXE):$(OBJS)

$(CC) -o $(EXE) $(OBJS)

main.o:main.c

$(CC) -c -o main.o main.c

foo.o:foo.c

$(CC) -c -o foo.o foo.c

clean:

$(RM) -rf $(OBJS) $(EXE)

引入变量的好处是当需要修改变量相关内容时不需要进行多处修改,只需要修改变量定义处。

2.3.3 自动变量

上面增加自定义变量后虽然一定程度上增加了Makefile的维护性,但是依赖文件还是需要多次修改,自动变量可以进一步增加Makefile的维护性,自动变量是make工具默认的,不需要我们重新定义,Makefile有以下常用的自动变量:

$@:表示一个规则中的目标,当规则中有多个目标“@”表示任何导致规则中命令被执行的目标。

$^:表示规则中所有依赖条件

$<:表示规则中第一个依赖条件

用自动变量替换后的makefile文件如下:

.PHONY: clean

CC = gcc

RM = rm

EXE = test.exe

OBJS = main.o foo.o

$(EXE):$(OBJS)

$(CC) -o $@ $^

main.o:main.c

$(CC) -c -o $@  $^

foo.o:foo.c

$(CC) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(EXE)

修改后的Makefile虽然看上去简单了一些,但每个源文件依然都要单独写一个规则,我们可以通过模式进一步简化。

2.3.4 模式

模式类似于正则匹配符功能,上面的例子使用模式后可以修改成如下形式:

.PHONY: clean

CC = gcc

RM = rm

EXE = test.exe

OBJS = main.o foo.o

$(EXE):$(OBJS)

$(CC) -o $@ $^

%.o:%.c

$(CC) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(EXE)

运用模式的Makfile进一步简化了,增减源文件时只需要修改“OBJS”变量增删对应的目标文件名。下面通过Makefile的函数进一步简化。

2.3.5 函数

函数是makefile的重要功能,通过使用函数能极大的简化Makefile编写,下面介绍两个常用的函数作为示例来了解函数的用法。

$(wildcard pattern):通配符函数,通过该函数可以得到当前目录下所有满足pattern模式的文件名或目录名列表。

$(patsubst pattern, replacement, text):模式替换函数,该函数将text列表中满足pattern模式的名称替换成replacement模式名称。

以上Makefile用上面两个函数替换后如下:

.PHONY: clean

CC = gcc

RM = rm

EXE = test.exe

SRC = $(wildcard *.c)

OBJS = $(patsubst %.c, %.o, $(SRC))

$(EXE):$(OBJS)

$(CC) -o $@ $^

%.o:%.c

$(CC) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(EXE)

引入函数后进一步简化了Makefile,目前增加、删除源文件不需要对Makefile进行修改,可以直接运行make编译。例如增加一个bar.c文件,代码如下:
#include "stdio.h"

void bar()

{

printf("this is bar\n");

}

修改main.c增加对bar()函数调用:
#include "stdio.h"

#define DISP_STR "hello world"

extern void foo();

extern void bar();

int main()

{

printf("display str:%s\n",DISP_STR);

foo();

bar();

return 0;

}

终端输入:make clean、make进行编译,生成test.exe执行文件,输入命令./test.exe运行结果如下:

display str:hello world

this is foo

this is bar

整个过程Makefile无需任何修改。

2.3.6 makefile示例

2.3.6.1 静态库编译

将以上例子中的bar.c和foo.c文件编译成静态库文件,步骤如下:

  1. 创建lib目录,将bar.c和foo.c拷贝到目录。
  2. 为了方便外部引用,在lib目录中创建一个头文件libtest.h如下:

#ifndef __TEST__H_

#define __TEST__H_

extern void foo();

extern void bar();

#endif

3、创建makefile文件如下所示:

.PHONY: clean

CC = gcc

RM = rm

LIB = libtest.a

SRC = $(wildcard *.c)

OBJS = $(patsubst %.c, %.o, $(SRC))

ARFLAGS = crs

AR = ar

$(LIB):$(OBJS)

$(AR) $(ARFLAGS) $@ $^

%.o:%.c

$(CC) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(LIB)

输入make命令后在lib目录下生成libtest.a库文件。

2.3.6.2 动态库编译

如果要生成动态库文件,makefile修改如下:

.PHONY: clean

CC = gcc

RM = rm

SLIB = libtest.a

DLIB = libtest.so

SRC = $(wildcard *.c)

OBJS = $(patsubst %.c, %.o, $(SRC))

ARFLAGS = crs

AR = ar

CFLAGS = -fPIC -shared

all:$(SLIB) $(DLIB)

$(SLIB):$(OBJS)

$(AR) $(ARFLAGS) $@ $^

$(DLIB):$(OBJS)

$(CC) $(CFLAGS) -o $@ $^

%.o:%.c

$(CC) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(SLIB) $(DLIB)

输入make命令后在lib目录下同时生成libtest.a和libtest.so库文件。

2.3.6.3 库文件链接

1、main.c调用库文件接口,代码如下:

#include "stdio.h"

#include "libtest.h"

#define DISP_STR "hello world"

int main()

{

printf("display str:%s\n",DISP_STR);

foo();

bar();

return 0;

}

2、修改Makefile链接上面生成的库,生成执行文件,修改后如下所示:

.PHONY: clean

CC = gcc

RM = rm

EXE = test.exe

SRC = $(wildcard *.c)

OBJS = $(patsubst %.c, %.o, $(SRC))

LDLIBS = -ltest

LDPATH = ./lib

INCPATH = ./lib

$(EXE):$(OBJS)

$(CC) -o $@ $^ -L$(LDPATH) $(LDLIBS)

%.o:%.c

$(CC) -I$(INCPATH) -c -o $@  $^

clean:

$(RM) -rf $(OBJS) $(EXE)

3.输入make命令在当前目录下生成test.exe执行文件。

本章小结:本章通过一个简单的程序例子介绍了makefile的基本组成和编写方式,以上Makefile例子可以满足一般的程序编译,但离规范化还有一定差异,如果想进一步了解Makefile编写,可以通过阅读《GNU Make》手册深入学习。

3软件调试器gdb

通过集成开发环境(IDE)调试软件非常方便,步骤通常是开始调试->打断点->运行程序到断点位置->单步运行->查看对应寄存器或变量值->停止调试。在windows环境以上步骤通常都可以通过图形界面进行操作,在linux环境通过gdb调试器也可以对程序进行调试。

3.1 启动调试器

使用gdb调试程序前需要程序编译时带上调试信息,修改上一章例子中断Makefile:编译目标文件时通过增加“-g”参数在生成的目标文件中添加调试信息,修改后s如下:

.PHONY: clean

CC = gcc

CFLAGS = -g -c

RM = rm

EXE = test.exe

SRC = $(wildcard *.c)

OBJS = $(patsubst %.c, %.o, $(SRC))

$(EXE):$(OBJS)

$(CC) -o $@ $^

%.o:%.c

$(CC) $(CFLAGS) -o $@  $^

clean:

$(RM) -rf $(OBJS) $(EXE)

运行make命令生成test.exe执行程序。终端输入命令:gdb +程序名的方式启动gdb调试器,例如运行命令gdb ./test.exe:

3.2 在线运行调试程序

gdb提供了两种方式在线运行程序:

  1. 输入start: 程序跳到main入口处。
  1. 输入r(run):运行程序,如无断点则程序运行完直至结束,否则运行到指定断点处。

3.3 断点功能

3.3.1 断点设置

gdb设置断点有以下2种常用方式设置断点:

  1. b(break) + 函数名:在函数名第一次调用处打断点,例如输入:b foo打断点:
  1. b(break) + 文件名:行号:在指定文件和行号处打断点,例如输入:b main.c:10

3.3.2断点位置查看

输入:info b(break)命令可以查看当前调试程序所有设置断点位置,例如在函数foo和bar调用处打上断点后查看断点信息如下:

3.3.3删除断点

输入 :d(delete) + 断点号命令可以删除对应断点,以上例子中输入d  1后,查看断点信息如下所示:

3.4 单步调试程序

在需调试位置打上断点、运行程序,调试程序运行到断点处后通常需要进行单步调试来进一步定位具体问题。gdb单步调试相关命令有以下几个:

  1. n(next):单步调试,不进入调用函数体,例如:
  1. s(step):进入调用函数体,例如:
  1. finish:跳出当前函数体,例如:
  1. c(continue):继续运行,例如:
  1. p(print) + 变量名:查看指定变量当前值,修改上面例子main.c增加变量如下:

#include "stdio.h"

#define DISP_STR "hello world"

extern void foo();

extern void bar();

static int i = 0;

int main()

{

printf("display str:%s\n",DISP_STR);

i++;

foo();

i++;

bar();

return 0;

}

在foo函数调用处打上断点,然后单步运行,查看i变量值:

  1. 查看调试源码:

输入l(list) 可以查看断点附近源代码,依次输入命令可以连续查看断点之后的代码,例如:

3.5 退出调试

输入q(quit)可以退出调试,Quit anyway? (y or n) 处输入’y’,s例如:

本章小结:本章介绍了gdb调试器的基本功能和用法,通过本章内容我们基本可以对开发的程序进行在线调试,对于一些复杂工程我们可能还有用到gdb的其他功能,如果想深入了解gdb可以阅读《Debugging with gdb》手册。

总结:本文档分三章内容主要介绍了linux系统环境下C语言程序开发所使用的常用工具:编译器、编译管理工具、调试器。为了方便新手快速上手,文档对于各工具只是进行了初步讲解,希望对linux应用开发入门有所帮助,想深入熟悉和理解还需要在项目中不断练习。

linux应用程序开发指南-开发工具介绍相关推荐

  1. 基于bboss开发平台eclipse开发工程生成工具介绍

    为什么80%的码农都做不了架构师?>>>    基于bboss开发平台eclipse开发工程生成工具介绍 一.工具简介   bboss开发平台发布出两个资源包:  war部署包  数 ...

  2. linux驱动程序开发指南-字符驱动介绍

    概述: 在linux系统中设备驱动程序通常是作为应用层和设备层的中间层软件,驱动程序的主要功能是实现应用层访问硬件设备的具体操作接口,通过调用驱动程序,上层应用程序可以采用统一的接口访问各种硬件设备. ...

  3. 32款iOS开发插件和工具介绍[效率]

    插件和工具介绍内容均收集于网络,太多了就不一一注明了,在此谢过! 1.Charles 为了调试与server端的网络通讯协议.经常须要截取网络封包来分析. Charles通过将自己设置成系统的网络訪问 ...

  4. 一款好用的Linux系统服务器性能监控分析工具介绍

    软件性能测试过程中经常要对服务器性能指标(比如CPU.内存.磁盘IO及网络IO等等)进行监控以分析出软件在此服务器上的性能瓶颈以便进行后续的服务器调优及软件性能优化.下面为大家介绍一款小编认为比较好用 ...

  5. 代码统计工具有哪几种_跟我学“Linux”小程序Web版开发(四):引入统计及Crash收集...

    在完成了产品的基础开发以后,接下来需要进行一些周边的工作,这些周边工具将会帮助下一步优化产品. 在完成了产品的基础开发以后,接下来需要进行一些周边的工作,这些周边工具将会帮助下一步优化产品. 为什么要 ...

  6. Dynamics CRM 客户端程序开发:常用工具介绍之Dynamics XRM Tools

    声明:本博客参照以下网址,在此标记以便自己以后查询方便. http://luoyong0201.blog.163.com/blog/static/112930520142782014329/

  7. js页面初始化方法只调用一次_跟我学 “Linux” 小程序 Web 版开发(三):云开发相关数据调用

    介绍 在完成了界面的实现后,接下来可以开始进行和云开发相关的数据对接.完成数据对接后,应用基础就打好了,接下来的就是发布上线以及一些小的功能的加入. 配置 在进行相关的配置调用的时候,你需要先登录腾讯 ...

  8. 音视频开发常用分析工具介绍

    综述 工欲善其事,必先利其器:兵马未到,粮草先行. 在音视频开发过程中,利用工具可以更方便.更直观.更快捷的分析音视频的数据,便于开发过程中分析.调试和解决问题. 现总结一些音视频开发过程中常用的分析 ...

  9. 软件开发工程师常用工具介绍

    本文主要记录软件开发工程师在工作及学习中常用的工具,后面有时间把每个工具的基本用法都总结下. 工具合集 序号 工具名称 简述 使用指南 1 GitHub 适合团队开发人员之间共同开发时使用 GitHu ...

  10. 游戏开发常用引擎工具介绍对比区别(UE4,Unity,Cocos,LayaAir,[egret白鹭])

    UE4(即虚幻4) 是一套为开发实时技术而存在的引擎工具.目前广泛应用于3D建模渲染.游戏开发中.它完善的工具套件以及简易的工作流程能够使开发者快速修改或查看成果,对于代码的依赖性很低.而完整公开的源 ...

最新文章

  1. 基于双目事件相机的视觉里程计
  2. Docker 运行的 应用程序无法连接Oracle数据库的解决办法
  3. this和self区别
  4. IOS开发数据库篇—SQLite模糊查询
  5. NYOJ 801 Haffman编码
  6. 阿里云数据库2020技术年报新鲜出炉,全力开启牛年新征程!
  7. PyTorch基础(part1)
  8. 以个人身份加入.NET基金会
  9. 编写干净的测试–验证或不验证
  10. 互联网日报 | 小米11取消随机附送充电器;苏宁30周年发庆生红包;2021年全国两会召开时间确定...
  11. Linux下的系统排错以及无图形修改root密码
  12. kill -9 和 kill -15 的区别
  13. Facebook推出高速光网络技术将共享
  14. 苹果屏幕镜像_苹果屏幕镜像一直在转,秒懂投屏帮你解决
  15. Linux基金会:开源技术不受制于《美国出口管制条例》EAR 限制,可自由使用
  16. 计算机硬件功能作用,cpu的作用和主要功能是什么
  17. FFmpeg将mp4转成flv
  18. Linux 学习 第六单元
  19. 强化学习:Actor-Critic、SPG、DDPG、MADDPG
  20. 论文+答辩时PPTword的使用注意点

热门文章

  1. 模拟电子技术基础概念
  2. 自考 《计算机网络原理04741》真题解析
  3. revit插件有哪些常用的?介绍几个常用的revit插件操作简单
  4. 数据结构与算法经典书籍——大话数据结构(带配套源码)
  5. 如何利用RFM模型对客户进行精细化管理
  6. foobar2000的使用
  7. ExtremeComponents源码解析(一)
  8. 搜索系统硬盘中包含指定字符串的文件的工具和方法——全文搜索、搜索文件内容(持续更新中)
  9. 安徽2022农民丰收节 国稻种芯:郑栅洁启动舒城主场活动仪式
  10. 献给青春的歌 · 致「 腾讯QQ 18 岁」