当前位置: 首页 > news >正文

Linux环境基础开发工具使用

目录

1. Linux软件包管理器yum

1.1 什么是软件包

1.2 Linux软件生态

1.3 关于rzsz 

1.4 注意事项 

1.5 查看软件包

2. Linux编辑器-vim使用 

2.1 vim的基本概念 

2.2 vim的基本操作 

2.3 简单vim配置 

3. 编译器gcc/g++

3.1 背景知识

3.2 gcc编译选项 

3.2.1 预处理(进行宏替换) 

3.2.2 编译(生成汇编)

3.2.3 汇编(生成机器可识别的代码)

3.2.4 连接(生成可执行文件或库文件) 

3.2.5 补充(sudo无法执行问题) 

 3.3 动态链接和静态链接

3.4 静态库和动态库

3.5 gcc其他常用选项 

4. 自动化构建-make/makefile 

 4.1 背景

4.2 基本使用 

4.3 推导过程

4.4 适度语法扩展

5. Linux第一个系统程序 — 进度条

5.1 补充 - 回车与换行 

5.2 行缓冲区 

5.3 倒计时程序

5.4 进度条代码

6. 版本控制器Git

6.1 版本控制器

6.2 git简史

6.3 安装git 

6.4 Git的使用

7.  调试器-gdb.cgdb使用

7.1 gbd的下载 

7.2 样例代码

7.3 预备

7.4 常见使用

7.5 常见技巧

7.4.1 watch

7.4.2 set var确定问题原因

7.4.3 条件断点


1. Linux软件包管理器yum

1.1 什么是软件包

  • 在Linux下安装软件,一个通常的办法是下载到程序的源代码,并进行编译,得到可执行程序。
  • 但是这样太麻烦了,于是有些人把一些常用的软件提前边缘好,做成软件包(可以理解成windows上的安装程序)放在一个服务器上,通过包管理器可以很方便的回去到这个编译好的软件包,直接进行安装。
  • 软件包和软件包管理器,就好比"App"和"应用商店"这样的关系。
  • yum(Yellow aog Updater,Modified)是Linux下非常常用的一种包管理器,主要应用在Fedora,RedHat,Centos等发行版上。
  • Ubuntu:主要使用apt(Advanced Package Tool)作为其包管理器,apt同样提供了自动解决依赖关系,下载和安装软件包的功能。

1.2 Linux软件生态

  • Linux下载软件的过程(Ubuntu,Centos,other)

  • 操作系统的好坏评估---生态问题 

  • 为什么会有人免费特定社区提供软件,还发布,还提供云服务器下载?

  • 软件包依赖的问题

  • 国内镜像源

以下是⼀些国内Linux软件安装源的官方链接:

1. 阿⾥云官方镜像站

◦ 官方链接:https://developer.aliyun.com/mirror/

◦ 阿里云提供了丰富的Linux发行版镜像,包括CentOS、Ubuntu、Debian等,用户可 以通过该镜像站快速下载和更新软件包。

2. 清华⼤学开源软件镜像站

◦ 官⽅链接:https://mirrors.tuna.tsinghua.edu.cn/

◦ 清华⼤学镜像站提供了多种Linux发⾏版的镜像,以及Python、Perl、Ruby等编程语 ⾔的扩展包。该镜像站还提供了丰富的⽂档和教程,帮助⽤⼾更好地使⽤这些软件 包。

3. 中国科学技术⼤学开源镜像站

◦ 官方链接:http://mirrors.ustc.edu.cn/

◦ 中科⼤镜像站提供了多种Linux发⾏版的镜像,以及常⽤的编程语⾔和开发⼯具。⽤⼾ 可以通过该镜像站⽅便地获取所需的软件包和⼯具。 

4. 北京交通⼤学⾃由与开源软件镜像站

◦ 官⽅链接:https://mirror.bjtu.edu.cn/

◦ 北交⼤镜像站提供了多种Linux发⾏版的镜像,以及相关的软件仓库和⼯具。该镜像站 还提供了详细的⽂档和指南,帮助⽤⼾配置和使⽤这些软件源。

5. 中国科学院软件研究所镜像站(ISCAS)

◦ 官⽅链接:http://mirror.iscas.ac.cn/

◦ ISCAS镜像站提供了多种Linux发⾏版、编程语⾔和开发⼯具的镜像。⽤⼾可以通过该 镜像站快速获取所需的软件包和更新。

6. 上海交通⼤学开源镜像站

◦ 官⽅链接:https://ftp.sjtu.edu.cn/

◦ 上海交⼤镜像站提供了丰富的Linux软件资源,包括多种发⾏版的镜像和软件仓库。⽤ ⼾可以通过该镜像站⽅便地下载和安装所需的软件包。

7. ⽹易开源镜像站

◦ 官⽅链接:http://mirrors.163.com/

◦ ⽹易镜像站提供了多种Linux发⾏版的镜像,以及相关的软件仓库和⼯具。该镜像站还 提供了便捷的搜索功能,帮助⽤⼾快速找到所需的软件包。 此外,还有⼀些其他的国内镜像源,如搜狐开源镜像站等,但可能由于时间变化或政策调 整,部分镜像站的链接或状态可能有所变动。因此,建议⽤⼾在使⽤前访问官⽅⽹站或咨询 相关社区以获取最新的信息和帮助。

1.3 关于rzsz 

这个工具用于windows机器和远端的Linux机器通过xshell传输文件。 安装完毕之后可以通过拖拽的方式将文件上传上去。

1.4 注意事项 

关于yum的所有操作必须保证主机(虚拟机)网络畅通!!!

可以通过ping指令验证。 

ping www.baidu.com

 1.5 查看软件包

 通过yum list命令可以罗列出当前一共有那些软件包,由于包的数目可能非常之多,这里我们需要使用grep命令只筛选出我们关注的包:例如:

yum list | grep lrzsz

结果如下:

lrzsz.x86_64                             0.12.20-36.el7                @ba

注意事项:

  • 软件包名称:主版本号.次版本号,源程序发行号-软件包的发行号.主机平台.cpu架构
  • "x86_64"后缀表示64位系统的安装包,"i686"后缀表示32位系统安装包.选择包时要和系统匹配
  • "el7"表示操作系统发行版的版本,"el7"表示ide是centos7/redhat7."el6"表示centos6/redhat6.
  • 最后一列.base表示的是"软件源"的名称,类似于"小米应用商店","华为应用商店"这样的概念

Linux下软件安装一次,所有人都能用。

yum只能同时安装一个软件,不能既安装软件A,又安装软件B。

安装: (-y:不需要问,直接安装)

卸载: (-y:不需要问,直接卸载)

2. Linux编辑器-vim使用 

 vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于xwindows、mac os、windows。

2.1 vim的基本概念 

其实vim有好多种模式,目前掌握三种即可,分别是命令模式(command mode)、插入模式(insert mode)和底行模式(last line mode)、各模式的功能区别如下: 

  • 正常/普通/命令模式(Normal mode)

控制屏幕光标的移动、字符、字或行的删除,移动复制某区段及进入insert mode下,或者到last line mode

  • 插入模式(insert mode)

只有在insert mode下,才可以做文字输入,按[ESC]键可回到命令行模式,该模式是我们后面用的最频繁的编辑模式。

  • 末行模式(last line mode)

文件保存或退出,也可以进行文本替换,找字符串,列出行号等操作,在命令模式下,shift+;即可进入该模式。要查看你所有的模:打开vim,底行模式直接输入。

:help vim-modes

这里一共有12种模式:six BASIC modes和six ADDITIONAL modes

2.2 vim的基本操作 

1. 进入vim,在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面

  • $ vim test.c
  •  不过有一点要特别注意,就是你进入vim之后,是处于[正常模式],你要切换到[插入模式]才能够输入文字。

2. [正常模式]切换[插入模式]

  • 输入a
  • 输入i
  • 输入o

3. [插入模式]切换至[正常模式]

  • 目前处于[插入模式],就只能一直输入文字,如果发现输错了字,想用光标键往回挪动,将该字删除,可以先按一下[ESC]键转到[正常模式]再删除文字,当然,也可以直接删除。

4. [正常模式]切换至[末行模式]

  • [shift+;],其实就是输入[:]

5. 退出vim及保存文件,在[正常模式]下,按一下[:]冒号键进入[Last line mode],例如:

  • :w(保存当前文件)
  • :wq(输入 [wq]),存盘并退出vim)
  • :q!(输入q!,不存盘强制退出vim)

在命令模式下提供那么多的命令是为了提高编辑效率。

命令模式下set nu调出行号,set nonu去掉行号。

底行到插入,插入到底行是不支持的。

 更加实用的操作:

2.3 简单vim配置 

 配置文件的位置

  • 在目录/etc/下面,有个名为vimrc的文件,这是系统种公共的vim配置文件,对所有用户都有效。
  • 而在每个用户的主目录下,都有字节建立私有的配置文件,命名为:".vimrc"。例如,/root目录下,通常已经存在一个.vimrc文件,如果不存在,则创建之。
  • 切换用户称为字节执行su,进入自己的主工作目录,执行cd ~
  • 打开自己目录下的.vimrc文件,执行vim .vimrc

常用配置选项,用来测试 

  • 设置语法高亮:syntax on
  • 显示行号:set nu
  • 设置缩进的空格数为4:set shiftwidth=4

本质最简单的就是去修改它当前家目录下的.vimrc的文件,往里面加配置项就可以,而且自己配置的只能自己实用,别人实用不了。

一键化配置

  • 参考链接:gitee搜索vimforcpp

VimForCpp: 快速将vim打造成c++ IDEicon-default.png?t=O83Ahttps://gitee.com/HGtz2222/VimForCpp

  • 配置命令:
    curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh
  • 卸载命令:bash ~/.VimForCpp/uninstall.sh

注意:

  • 首先不能是root
  • 必须在自己当前的工作路径下

要配置Ubuntu的话 可以在gitee上搜vimplus

链接:

vimplus: vim/Neovim configure for C/C++ on(MAC/Ubuntu/Deepin/Raspbian/UOS/LinuxMint/elementaryOS/Debian/Kali/Parrot/CentOS/fedora/openSUSE/ArchLinux/ManjaroLinux/Gentoo)icon-default.png?t=O83Ahttps://gitee.com/keeferwu/vimplus

使用插件:

 要配置好看的vim,原生的配置可能功能不全,可以选择安装插件来完成配置,保证用户是你要配置的用户,接下来:

  • 安装TagList插件,下载taglist_xx.zip,解压完成,将解压出来的doc的内容放到~/.vim/doc,将解压出来的plugin下的内容拷贝到~/.vim.plugin。
  • 在~/.vimrc中添加:let Tlist_Show_One_File=1 let Tlist_Exit_OnlyWindow=1 let Tlist_Use_Right_Window=1
  • 安装文件浏览器和窗口管理器插件:WinManager
  • 下载Winmanager.zip,2.X版本以上的
  • 解压winmanager.zip,将解压出来的doc的内容放到~/.vim/doc,将加压出来的plugin下的内容拷贝到~/.vim/plugin
  • 在〜/.vimrc中添加 let g:winManagerWindowLayout=‘FileExplorer|TagList nmap wm :WMToggle
  • 然后重启vim,打开~/XXX.c或〜/XXX.cpp,在normal状态下输⼊"wm",你将看到上图的效果。更具 体移步:点我,其他⼿册,请执⾏ vimtutor 命令。

参考资料:

GitHub - wsdjeg/vim-galore-zh_cn: Vim 从入门到精通icon-default.png?t=O83Ahttps://github.com/wsdjeg/vim-galore-zh_cn

3. 编译器gcc/g++

3.1 背景知识

  1. 预处理(进行宏替换.去注释/条件编译/头文件展开等)
  2. 编译(生成汇编)
  3. 汇编(生成机器可识别代码)
  4. 链接(生成可执行文件或库文件)

3.2 gcc编译选项 

 格式 gcc [选项] 要编译的文件 [选项] [目标文件]

3.2.1 预处理(进行宏替换) 

  • 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
  • 预处理指令是以#号开头的代码行
  • 实例:gcc -E main.c -o main.i
  • 选项"-E",该选项的作用是让gcc在预处理结果后停止编译过程。
  • 选项"-o" 是指目标文件,".i"文件为已经过预处理的C原始程序。

 3.2.2 编译(生成汇编)

  • 在这个阶段中,gcc首选要检查代码的规范性,是否有语法错误等,以为确定代码的实际要左做的工作,在检查无误后,gcc把代码翻译成汇编语言。
  • 用户可以使用"-S"选项来进行检查,该选项只进行编译而不进行汇编,生成汇编代码。
  • 实例: gcc -S main.c -o main.s 

 3.2.3 汇编(生成机器可识别的代码)

  • 汇编阶段是把编译阶段生成的".s"文件转成目标文件
  • 读者在此可使用选项"-c"就可以看到汇编代码已转化为".o"的二进制目标代码了
  • 实例: gcc -c -o main.c 

3.2.4 连接(生成可执行文件或库文件) 

  • 在成功编译之后,就进入了链接阶段。
  • 实例: gcc main.c -o main 

3.2.5 补充(sudo无法执行问题) 

ls -l /etc/sudoers //要修改这个文件只能超级管理员修改,别的用户打开时看不到的,
这个与用户有关,与用什么工具打开没有关系

xshell下查看是Centos还是Ubuntu

  • Centos: cat /etc/redhat-release
  • Ubuntu: cat /etc/lsb-release 

 3.3 动态链接和静态链接

在我们的实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的,即每个*.c文件会形成一个*.o文件,为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接,静态链接的缺点很明显。

  • 浪费空间:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本。
  • 更新比较困难:因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的是运行速度快。

动态链接的出现解决了静态链接中提到的问题。动态链接的基本思想是把程序按照模板拆分成各个相对独立部分,在程序运行时才将他们链接在以前形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

动态链接起始远比静态链接要常用的多。比如我们查看下main这个可执行程序依赖的动态库,会发现它就用到了一个c动态链接库:

ldd mainlinux-vdso.so.1 =>  (0x00007fffac6ed000)libc.so.6 => /lib64/libc.so.6 (0x00007f4d19ae5000)/lib64/ld-linux-x86-64.so.2 (0x00007f4d19eb3000)
//ldd命令用于打印程序或者库文件所依赖的共享库列表

在这里涉及到一个重要的概念:库

  • 我们的C程序中,并没有定义"printf"的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现"printf"函数的呢?
  • 最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径"/usr/lib"下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数"printf"了,而这也就是链接的作用。

3.4 静态库和动态库

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但是在运行时就不再需要库文件了。其后缀名一般为".a"。
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为".so",如前面所述的libc.so.6就是动态库,gcc在编译时默认使用动态库。完成了链接后.gcc就可以生成可执行文件,如下所示。gcc main.o -o hello
  • gcc默认生成的二进制程序,就是动态链接,这点可以通过file命令验证。

注意1: 

  • Linux下,动态库XXX.so,静态库XXX.a
  • Windows下,动态库XXX.dll,静态库XXX.lib

一般我们的云服务器,C/C++的静态库并没有安装,可以采用如下方法安装。

//Centos
yum install glibc-static libstdc++-static -y

 

3.5 gcc其他常用选项 

  • -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面。
  • -S 编译到汇编语言不进行汇编和链接
  • -c 编译到目标文件
  • -o 文件输出到文件
  • -static 此选项对生成的文件采用静态链接
  • -g 生成调试信息。GNU调式器可利用该信息
  • -shared此选项讲尽量使用动态库,所以生成文件比较小,但是需要系统由动态库
  • -O0
  • -O1
  • -O2
  • -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优先级别最高
  • -w 不生成任何告警信息。
  • -Wall 生成所有告警信息。 

4. 自动化构建-make/makefile 

 4.1 背景

  • 会不会写makefile,从一个侧面说明了一个人是否基本完成大型工程的能力
  • 一个工程的源文件不计数,其按类型,功能,模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译, 哪些文件需要重新编译,甚至于进行更复杂的功能操作。
  • makefile带来的好吃就是 —— "自动化构建",一旦写好,只需要一个make命令,整个工程完成自动编译,极大提高了软件开发的效率。
  • make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi和make,Visual C++的nmake,Linux下的GUN和make。可见,makefile都成为了一种在工程方面的编译方法。
  • make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。

makefile/Makefile首字母可以大写,也可以小写。

4.2 基本使用 

实例代码

include <stdio.h>
int main()
{printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");printf("hello world!!\n");return 0;
}

Makefile文件

main:myproc.cgcc -o main main.c || gcc main.c -o main.PHONY:clean
clean:rm -f main

依赖关系

  • 上面的文件main,它依赖main.c

依赖方法

  • gcc -o main main.c就是与之对应的依赖关系

项目清理

  • 工程是需要被清理的
  • 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要make执行。即命令——"make clean",以此来清除所有的目标文件,以便重编译。
  • 但是一般我们这种clean的目标文件,我们将它设置为伪目标,用.PHONY修饰,伪目标的特性是,总是被执行的。
  • 可以将我们的main目标文件声明成伪目标,测试一下。

什么叫做总是被执行? 

$ stat XXXFile: ‘XXX’Size: 987               Blocks: 8         IO Block: 4096   regular file
Device: fd01h/64769d         Inode: 1321125    Links: 1
Access: (0666/-rw-rw-rw-)  Uid: ( 1000/ wanghao)   Gid: ( 1000/ wanghao)
Access: 2024-11-15 00:41:45.915034571 +0800
Modify: 2024-11-15 00:41:45.915034571 +0800
Change: 2024-11-15 00:41:45.915034571 +0800
⽂件= 内容+ 属性
Modify: 内容变更,时间更新
Change:属性变更,时间更新
Access:常指的是⽂件最近⼀次被访问的时间。在
Linux的早期版本中,每当⽂件被访问时,其atime都会更新。但这种机制会导致⼤量的
IO操作。

结论:

.PHONY:让make忽略源文件和可执行目标文件的M时间对比。

4.3 推导过程

myproc:myproc.o                                       gcc myproc.o -o myproc
myproc.o:myproc.s gcc -c myproc.s -o myproc.o
myproc.s:myproc.i gcc -S myproc.i -o myproc.s
myproc.i:myproc.cgcc -E myproc.c -o myproc.i
.PHONY:clean 
clean: rm -f *.i *.s *.o myproc

编译: 

 $ makegcc -E myproc.c -o myproc.igcc -S myproc.i -o myproc.sgcc -c myproc.s -o myproc.ogcc myproc.o -o myproc

  • make是如何工作的,在默认的方式下,也就是我们只输入make命令。那么:
  1. make会在当前目录下找名字叫"Makefile"或"makefile"的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到myproc这个文件,并把这个文件作为最终的目标文件。
  3. 如果myproc文件不存在,或是myproc所依赖的后面的myproc.o文件的文件修改时间要比myproc这个文件新(可以用touch测试) ,那么,他就会执行后面所定义的命令来生成myproc这个文件。
  4. 如果myproc所依赖的myproc.o文件不存在,那么make会在当前文件中找目标为myproc.o文件的依赖性,如果找到则再根据那一个规则生成myproc.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的,于是make会生成myproc.o文件,然后再用myproc.o文件声明make的终极任务,也就是执行文件hello了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作了。

4.4 适度语法扩展

BIN=proc.exe            #定义变量
CC=gcc
#SRC=$(shell ls *.c)    #采⽤shell命令⾏⽅式,获取当前所有.c⽂件名
SRC=$(wildcard *.c)     #或者使⽤wildcard 函数,获取当前所有.c⽂件名
OBJ=$(SRC:.c=.o)        #将SRC的所有同名.c 替换成为.o形成⽬标⽂件列表
LFLAGS=-o               #链接选项
FLAGS=-c                #编译选项
RM=rm -f                #引⼊命令$(BIN):$(OBJ)@$(CC) $(LFLAGS) $@ $^        # $@:代表⽬标⽂件名。$^: 代表依赖⽂件列表@echo "linking ... $^ to $@"
%.o:%.c                           # %.c 展开当前⽬录下所有的.c。%.o: 同时展开同名.o@$(CC) $(FLAGS) $<            # %<: 对展开的依赖.c⽂件,⼀个⼀个的交给gcc。@echo "compling ... $< to $@"  # @:不回显命令
.PHONY:clean
clean:$(RM) $(OBJ) $(BIN)           # $(RM): 替换,⽤变量内容替换它.PHONY:test
test:@echo $(SRC)@echo $(OBJ)#makefile的注释是用"#"注释

5. Linux第一个系统程序 — 进度条

5.1 补充 - 回车与换行 

  •  回车:将光标回到行首 \r
  • 换行:换到下一行 \n
  • 回车换行是两个动作,C语言中的\n表示的就是\r\n,回车换行,先是回到当前行的行首,然后换到下一行。

5.2 行缓冲区 

#include <stdio.h>
int main()
{printf("hello hello world!!\n");sleep(3);return 0;
}

#include <stdio.h>
int main()
{printf("hello world!!");sleep(3);return 0;
}

第二种情况,当我没有加'\n'的时候现现象就是它先执行睡眠三秒,然后打印出hello world,那么在睡眠期间它打印的hello wrold是在输入缓冲区的,但是第一种情况,带'\n'的先是一定在缓冲区的,只不过这个缓冲区是给显示器提供的,所以只要有缓冲区就必要要存在刷新策略,其中显示器的刷新策略叫做行刷新,也就是说碰到了打印的字符串包换了'\n',那么该消息就会立即显示到显示器上,如果不包含'\n',该字符串不做刷新,要么程序结束自动刷新,要么强制刷新,所以这就是为什么带'\n'的字符串立即现在,不带'\n'的消息不立即显示的原因。

如果想让不带'\n'的立即刷新应该怎么办?而且不想让他行刷新。

除了程序退出,我们还可以强制刷新。

  • man fflush

#include <stdio.h>
int main()
{printf("hello world!!");fflush(stdout);//刷新                                                                                                       sleep(3);                                                                                                               return 0;                                                                                                               
}

5.3 倒计时程序

#include <stdio.h>
#include <unistd.h>
int main()
{int cnt = 9;while(cnt >= 0){printf("%d\r",cnt);fflush(stdout);//刷新刷入缓冲区cnt--;sleep(1);}printf("\n");                                                                                                                 return 0;
}

 这样的代码确实可以现实的倒计时程序,但是如果我将cnt的初始值9改成10的话,该程序就会出问题,当我在向显示器上打印1234的时候显示器上的是字符1,字符2,字符3,字符4,其实显示器只认字符,所以显示器才叫做字符设备,只不过显示器把字符1234写在了一起,看起来是1234,实则是1234这四个字符,所以c语言中的%d格式化是把整数转字符。

#include <stdio.h>
#include <unistd.h>
int main()
{int cnt = 15;while(cnt >= 0){printf("%-2d\r",cnt);//格式控制,默认是右对齐,想让左对齐就用-2d                                                            fflush(stdout);//刷新刷入缓冲区cnt--;sleep(1);}printf("\n");return 0;
}

5.4 进度条代码

//MakeFile
BIN=process
#动态获取当前目录下所有的.c文件有两种做法
#SRC=$(shell ls *.c) #1.动态获取当前目录下所有的.c文件
SRC=$(wildcard *.c)  #2.makefile自己的语法,把当前目录下的.c罗列出来
OBJ=$(SRC:.c=.o) #把所有的.c换成.o形成形成OBJ
CC=gcc     #用到的编译器是gcc
RM=rm -f   #删除命令$(BIN):$(OBJ)#@$(CC) $(OBJ) -o $(BIN)$(CC) $^ -o $@echo "链接 $^ 成 $@"
%.o:%.c    #%是通配符的意思,匹配任意内容,.c是任意以.c结尾的文件@$(CC) -c $<    #$<是把一批文件一个一个的拿过来,通过gcc加工成.oecho "编译 ... $< 成 $@".PHONY:clean
clean:@$(RM) $(OBJ) $(BIN).PHONY:mymain
mymain:@echo $(BIN)    #不想让echo命令回显,那么前面就加上@符号,不显示命令执行的过程,只显示结果@echo $(SRC)@echo $(OBJ)
//process.h
#pragma once
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define SIZE 101
#define STYLE '='
//v1
void process();
//v2
void FlushProcess(const char* tips,double total,double current);
//process.c
#include "process.h"//#define SIZE 100
//v2:根据进度,动态刷新一次进度条
void FlushProcess(const char* tips ,double total,double current)
{const char* table = "|/-\\";int len = strlen(table);static int index = 0;int i = 0;char buffer[SIZE];memset(buffer,0,sizeof(buffer));double rate = current*100/total;int num = (int)rate;for(;i < num;i++){buffer[i] = STYLE;}printf("%s...[%-100s][%.1lf%%][%c]\r",tips,buffer,rate,table[index++]);fflush(stdout);index %= len;if(num >= 100) printf("\n");
}
//v1版本:展示进度条基本功能
void process()
{int rate = 0;char buffer[SIZE];memset(buffer,0,sizeof(buffer));const char* lable = "|/-\\";int len = strlen(lable);while(rate <= 100){printf("[%-100s][%d%%][%c]\r",buffer,rate,lable[rate%len]);//%100s默认是右对齐,前面加上-就是左对齐buffer[rate] = STYLE;fflush(stdout);//刷新输出缓冲器rate++;usleep(50000);//usleep可以在man 3 usleep中查看,1000000表示1s}printf("\n");
}
//main.c
#include "process.h"
//函数指针
typedef void (*call_t)(const char*,double,double);
double total = 1024.0;//目标的总量
//double speed = 1.0;//网速
double speed[] = {20.0,0.05,0.03,0.02,0.01,0.001};
//回调函数
void download(int total,call_t cb)
{srand(time(NULL));double current = 0;//下载量,刚开始我为0while(current <= total){//更新进度cb("下载中",total,current);//进行回调if(current >= total) break;int random = rand() % 6;//下载代码usleep(50000);current += speed[random];if(current >= total) current = total;}
}void upload(int total,call_t cb)
{srand(time(NULL));double current = 0;//下载量,刚开始我为0while(current <= total){cb("上传中",total,current);//进行回调if(current >= total) break;int random = rand() % 6;usleep(50000);current += speed[random];if(current >= total) current = total;}
}
int main()
{download(1024.0,FlushProcess);printf("dowmload 1024.0MB done\n");download(10240.0,FlushProcess);printf("dowmload 10240.0MB done\n");download(512.0,FlushProcess);printf("dowmload 512.0MB done\n");download(1024.0,FlushProcess);printf("dowmload 1024.0MB done\n");download(770.0,FlushProcess);printf("dowmload 777.0MB done\n");download(111.0,FlushProcess);printf("dowmload 111.0MB done\n");upload(1024.0,FlushProcess);printf("upload 111.0MB upload\n");//process();return 0;
}

6. 版本控制器Git

不知道你工作或者学习的时候,有没有遇到这样的情况:我们再编写各种文档时,为了仿真文档丢失,失误,失误后能恢复到原来的版本,不得不复制出一个副本,比如:

"报告-v1"

"报告-v2"

“报告-v3”

"报告-确定版"

"报告-最终版"

"报告-究极进化版"

.....

每个版本有各自的内容,但最终只会有一份报告需要被我们使用。

但在此之前的工作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的文件就越来越多,文件多不是问题,问题是:随着版本数量的不断增多,你还记得这些版本各自都是修改了什么吗?

文档如此,我们写的项目代码,也是存在这个问题的!!

6.1 版本控制器

  • 为了能够更方便我们管理这些不同版本的文件,便有了版本控制器。所谓的版本控制器,就是能让你了解到一个文件的历史,以及它的发展过程的系统。通俗的讲就是一个可以记录工程的每一次改动和版本迭代的一个管理系统,同时也方便多人协同作业。
  • 目前最主流的版本控制就是Git,Git可以控制电脑上所有格式的文件,例如doc,excel,dwg,dgn,rvt等等。对于我们开发人员来说,Git最重要的就是可以帮助我们管理开发项目中的源代码文件。

6.2 git简史

 同生活中的许多伟大事物,Git诞生于一个极富纷争大举创新的时代。

Linux内核开源项目有着为数众多的参与者。绝大多数的Linux内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991 - 2002年间)。到2002年,整个项目组开始启用一个专有的分布式版本控制系统BitKeeper来管理和维护代码。

到了2005年,开发BitKeeper的商业公司同Linux内核开源社区的合作关系结束,他们回收了Linux内核社区免费使用BitKeeper的权力。这就迫使Linux开源社区(特别是Linux的缔造者Linus Torvalds)基于使用BitKeeper时的经验教训,开发出自己的版本系统。他们对新的系统制定了若干目标:

  • 速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似Linux内核一样的超大规模项目(速度和数据量)

自诞生于2005年以来,Git日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统。

6.3 安装git 

//Centos
sudo yum install git
//Ubuntu
sudo apt install -y git
//查看版本信息
git --version
//同步远端的数据
git pull

6.4 Git的使用

Git的安装和使用-CSDN博客文章浏览阅读824次,点赞12次,收藏8次。第一次上传需要填写用户名和密码,如果密码填错,需要在凭证管理器中删除,如果第二次再次上传的话还弹出这个框的话就不用去凭证管理器中删除,不弹的话就得去凭证管理器中删除。注意:这个软件是依赖前一个软件的,在安装的过程中需要选择第一个软件的路径,这里我就不做更改了,默认就行。右击鼠标,选择Git Clone,然后讲自己复制的仓库链接粘贴进去,然后点击ok。首先文件夹中要放我们要上传的文件或者文件夹,我这里就放了一个C语言的代码。点进仓库里面,我们发现里面的文件和我们云端上的文件是一样的。https://blog.csdn.net/m0_74271757/article/details/144035465?spm=1001.2014.3001.5502

7.  调试器-gdb.cgdb使用

7.1 gbd的下载 

//CentOscurl -sLf https://gitee.com/lpsdz-ybhdsg-jk/yum-source-update/raw/master/install.sh -o ./install.sh && bash ./install.shsudo yum install gdb -y

7.2 样例代码

//main.c
#include <stdio.h>
int sum(int begin,int end)
{int index = 0;                                                                                                                for(int i = begin;i <= end;i++){index += i;}return index;
}int main()
{int start = 1;int end = 100;printf("I will begin\n");int n = sum(start,end);printf("running done, result is: [%d-%d]=%d\n", start, end, n);return 0;
}

7.3 预备

  • 程序的发布方式有两种,Debug模式和release模式,Linux gcc/g++出来的二进制程序,默认是release模式。
  • 要使用gbd调式,必须在源代码生成二进制程序的时候,加上-g选项,如果没有添加,程序无法被编译。
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux
3.2.0, not stripped$ gcc mycmd.c -o mycmd -g # debug模式
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically
linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux
3.2.0, with debug_info, not stripped

7.4 常见使用

  • 开始:gdb binFile
  • 退出:ctrl +d 或quit调试命令 
命令作用样例
list/l显示源代码,从上次位置开始,每次列出10行list/l 10
list/l 函数名列出指定函数的源代码list/l main
list/l 文件名:行号列出指定文件的源代码list/l mycmd.c:1
r/run从程序开始连续执行run
n/next单步执行,不进入函数内部next
s/step单步执行,进入函数内部step
break/b [文件名:]行号在指定行号设置断点

break 10

break test.c:10

break/b 函数名在函数开头设置断点break main
info break/b查看当前所有断点信息info break
finish执行到当前函数返回,然后停止finish
print/p打印表达式的值print start+end
p 变量打印指定变量的值p x
set var 变量 = 值修改变量的值set var i = 10
continue/c从当前位置开始连续执行程序continue

deletc/d

breakpoints

删除所有断点delete breakpoints

delete/d

breakpoints n

删除序号为n的断点delete breakpoints 1
disable breakpoints禁用所有断点disable breakpoints
enable breakpoints启用所有断点enable breakpoints
info/i breakpoints查看当前设置的断点列表info breakpoints
display 变量名跟踪显示指定变量的值(每次停止时)display x
undisplay 编号取消对指定编号的变量的跟踪显示undisplay 1
until x行号执行到指定行号until 20
backtrace/bt查看当前执行栈的各级函数调用及参数backtrace
info/i locals查看当前栈帧的局部变量值info locals
quit退出GDB调试器quit

 

7.5 常见技巧

安装cgdb:

  • 上面的基本调试还是麻烦,虽然是黑屏,但是还是想看到代码调试
  • 推荐安装cgdb:
  • Ubuntu:sudo apt-get install -y cgdb
  • Centos:sudo yum install -y cgdb

7.4.1 watch

执行时监视一个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB会暂停程序的执行,并通知使用者。

注意:

  • 如果你有一些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你。

7.4.2 set var确定问题原因

更改一下标志位,假设我们想得到+-index

 

7.4.3 条件断点

添加条件断点

注意:

  • 条件断点添加常见两种方式:1.新增 2.给已有断点追加
  • 注意两者的语法区别,不要写错了。
  • 新增:b/行号/文件名:行号/函数名 if i == 20(条件)
  • 给已有断点追加:condition 2 i == 20,其中2是已有断点编号,没有if
  • cgdb分屏操作,Esc进入代码屏,i回到gdb屏

相关文章:

Linux环境基础开发工具使用

目录 1. Linux软件包管理器yum 1.1 什么是软件包 1.2 Linux软件生态 1.3 关于rzsz 1.4 注意事项 1.5 查看软件包 2. Linux编辑器-vim使用 2.1 vim的基本概念 2.2 vim的基本操作 2.3 简单vim配置 3. 编译器gcc/g 3.1 背景知识 3.2 gcc编译选项 3.2.1 预处理…...

AI生成的一个.netcore 经典后端架构

下面是一个完整的 .NET Core 后端项目示例&#xff0c;使用 Dapper 作为轻量级 ORM 访问 Oracle 数据库&#xff0c;并实现高性能架构。我们将实现学生表、课程表、成绩表和班级表的基本增删改查功能&#xff0c;以及查询某个班级学生成绩的功能&#xff0c;并使用自定义缓存来…...

深度学习-48-AI应用实战之基于face_recognition的人脸识别

文章目录 1 人脸识别1.1 识别原理1.2 应用场景2 python实现人脸识别2.1 windows安装face_recognition2.2 安装问题及解决3 使用示例3.1 人脸区域检测3.2 对齐与编码3.3 人脸匹配3.4 信息录入4 附录4.1 函数cv2.rectangle4.2 参考附录1 人脸识别 通过图片或者摄像头的方式,将识…...

【Rabbitmq篇】高级特性----事务,消息分发

目录 事务 消息分发 应用场景 1. 限流 2.负载均衡 事务 RabbitMQ是基于AMQP协议实现的,该协议实现了事务机制,因此RabbitMQ也支持事务机制.SpringAMQP也提供了对事务相关的操作.RabbitMQ事务允许开发者确保消息的发送和接收是原子性的,要么全部成功,要么全部失败. 何为原…...

Python进程和线程适用场景

在选择使用 进程&#xff08;Process&#xff09;和 线程&#xff08;Thread&#xff09;时&#xff0c;通常取决于任务的类型、程序的需求以及硬件资源的限制。进程和线程各自有不同的特点&#xff0c;适用于不同的场景。下面是关于进程和线程的一些常见应用场景和选择指导&am…...

flutter开发环境—Windows

一、简介 我们使用最新版的flutter版本安装。 参考链接 名称地址官方网站https://flutter.dev/官方中文网站文档 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter软件下载路径https://docs.flutter.dev/release/archive?tabwindows 二、操作流程 2.1 下载软件 点…...

展示和添加篮球队信息--laravel与elementplus

之前使用laravel与inertia来做过一样的功能,感觉不满意,因此再结合elementplus重做一遍,先展示下重做后的效果。重写后的代码相比之下比较优雅。 球队首页 球队添加页 球员首页 很明显的改变,我新增了侧栏菜单来控制局部模块(这里是指NBABasketba…...

写一份客服网络安全意识培训PPT

一、为什么要对客服人员定期进行网络安全培训呢&#xff1f; 人员组成复杂&#xff1a;企业既有自由人员又有采购的外包公司客服&#xff0c;为了节约成本可能外包占大多数&#xff0c;这必然加强了人群的流动性所以往往得不到系统的培训。人员素质参差不齐&#xff1a;因为工…...

具体的技术和工具在县级融媒体建设3.0中有哪些应用?

以下是结合数据来看县级融媒体建设3.0的一些情况&#xff1a; 技术应用方面 大数据&#xff1a;人民网舆情数据中心执行主任董盟君提到&#xff0c;通过大数据分析可让融媒体单位快速关注聚焦点&#xff0c;实现智能策划、智能推送、智能传播&#xff0c;推动媒体传播影响力提…...

【uniapp】轮播图

前言 Uniapp的swiper组件是一个滑块视图容器组件&#xff0c;可以在其中放置多个轮播图或滑动卡片。它是基于微信小程序的swiper组件进行封装&#xff0c;可以在不同的平台上使用&#xff0c;如微信小程序、H5、App等。 效果图 前端代码 swiper组件 <template><vi…...

Rust编程语言代码详细运行、编译方法

以下是针对不同类型的 Rust 代码&#xff08;以常见的命令行程序为例&#xff09;详细的运行方法&#xff1a; 前提条件 在运行 Rust 代码之前&#xff0c;确保你已经在系统上安装了 Rust 编程语言环境。如果尚未安装&#xff0c;可以通过以下步骤进行安装&#xff1a; 访问…...

node.js基础学习-http模块-JSONP跨域传值(四)

前言 JSONP&#xff08;JSON with Padding&#xff09;是一种用于跨域数据传输的技术。在浏览器的同源策略限制下&#xff0c;一般情况下&#xff0c;JavaScript 不能直接从不同域的服务器获取数据。JSONP 通过利用 <script> 标签的跨域特性来绕过这个限制。 它本质上是一…...

Unity高效编程经验50条分享

1.避免频繁创建临时对象 错误写法&#xff1a;obj.transform.position pos;这种写法会在Lua中频繁返回transform对象导致gc正确写法&#xff1a;创建一个静态方法来设置位置&#xff0c;例如 class LuaUtil { static void SetPos(GameObject obj, float x, float y, float z)…...

TypeScript 泛型

在 TypeScript 中&#xff0c;泛型是一种强大的工具&#xff0c;它允许你在定义函数、类、接口或类型别名时不指定具体的类型。这意味着你可以为这些实体创建可重用的组件&#xff0c;这些组件可以在不同的类型上以一致的方式工作。今天&#xff0c;我们将深入探讨 TypeScript …...

【Java从入门到放弃 之 条件判断与循环】

条件判断与循环 条件判断if 语句if-else 语句if-else 嵌套语句switch 语句 循环for 循环while 循环do-while 循环break 和 continuebreak 关键字continue 关键字总结 条件判断 条件判断用于根据不同的条件执行不同的代码块。Java 中常用的条件判断语句有 if、if-else 和 switc…...

Ubuntu20.04安装kalibr

文章目录 环境配置安装wxPython下载编译测试报错1问题描述问题分析问题解决 参考 环境配置 Ubuntu20.04&#xff0c;python3.8.10&#xff0c;boost自带的1.71 sudo apt update sudo apt-get install python3-setuptools python3-rosinstall ipython3 libeigen3-dev libboost…...

Flink 任务启动脚本-V2(包括ck启动)

#!/bin/bash#crontab时设置&#xff0c;如果依赖其他环境变量配置&#xff0c;可以在脚本执行一下环境变量脚本 source /etc/profile# 进入脚本目录 curdirdirname "$0" curdircd "$curdir"; pwd echo "进入启动脚本目录 $curdir"# 定义应用程序…...

扫雷-完整源码(C语言实现)

云边有个稻草人-CSDN博客 在学完C语言函数之后&#xff0c;我们就有能力去实现简易版扫雷游戏了&#xff08;成就感满满&#xff09;&#xff0c;下面是扫雷游戏的源码&#xff0c;快试一试效果如何吧&#xff01; 在test.c里面进行扫雷游戏的测试&#xff0c;game.h和game.c…...

python -从文件夹批量提取pdf文章的第n页,并存储起来

python -从文件夹批量提取pdf文章的第n页&#xff0c;并存储起来 废话不多说&#xff0c;看下面代码 讲解一下下面代码 reader PyPDF2.PdfReader (file) 将文件转化为PdfReader 对象&#xff0c;方便使用内置方法。 first_page reader.pages[0] 提取第一页 writer PyPDF…...

R Excel 文件操作指南

R Excel 文件操作指南 概述 R 语言是一种强大的统计分析工具&#xff0c;广泛用于数据分析和可视化。在实际应用中&#xff0c;经常需要将 R 语言与 Excel 文件结合使用&#xff0c;以便处理和分析数据。本指南将介绍如何在 R 中读取、写入和操作 Excel 文件。 准备工作 在…...

RabbitMQ 安装延迟队列插件 rabbitmq_delayed_message_exchange

前言&#xff1a; RabbitMQ 延迟队列插件&#xff08;rabbitmq_delayed_message_exchange&#xff09;是一个社区开发的插件&#xff0c;它为 RabbitMQ 添加了支持延迟消息的功能。通过这个插件&#xff0c;用户可以创建一种特殊的交换机类型 x-delayed-message&#xff0c;该…...

fatal error in include chain (rtthread.h):rtconfig.h file not found

项目搜索这个文件 rtconfig 找到后将其复制粘贴到 你的目录\Keil\ARM\ARMCC\include 应该还有cJSON&#xff0c;rtthread.h和 等也复制粘贴下...

Java 反射(Reflection)

Java 反射&#xff08;Reflection&#xff09; Java 反射&#xff08;Reflection&#xff09;是一个强大的特性&#xff0c;它允许程序在运行时查询、访问和修改类、接口、字段和方法的信息。反射提供了一种动态地操作类的能力&#xff0c;这在很多框架和库中被广泛使用&#…...

Python爬取机车网车型数据并存入Mysql数据库

结果展示&#xff08;文末附完整代码&#xff09;&#xff1a; 一、引言 在当今数字化时代&#xff0c;数据对于各个领域的重要性不言而喻。对于机车行业而言&#xff0c;获取丰富的机车品牌、车型及详细信息数据&#xff0c;能够为市场分析、消费者研究等提供有力支持。本文将…...

fpga 时序分析基础

目录 触发器的动态参数 同步时序电路分析 1. 时钟脉冲的特性 2. 同步时序电路分析 Timing Analyzer的应用 异步时序与亚稳态问题 时序分析就是对时序电路进行时序检查&#xff0c;通过分析电路中所有寄存器之间的路径延迟以检查电路的传输延迟是否会导致触发器的建立时间…...

python学习——二维列表的列表生成式

二维列表的列表生成式允许你生成一个列表&#xff0c;其中每个元素本身也是一个列表。这在处理矩阵或表格数据时非常有用。 以下是如何使用列表生成式来创建二维列表的示例&#xff1a; 文章目录 基本语法示例1. 创建一个 3x3 的单位矩阵2. 创建一个 4x4 的乘法表3. 创建一个 …...

【错误❌】——槽函数定义好但未初始化

public slots:void onClose(); 初始化即可成功&#xff1a;...

OpenCV相机标定与3D重建(6)将3D物体点投影到2D图像平面上函数projectPoints()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::fisheye::projectPoints 是 OpenCV 库中用于鱼眼镜头模型的函数&#xff0c;它将3D物体点投影到2D图像平面上。这个函数对于模拟或者理解鱼眼…...

【Linux】剧幕中的灵魂更迭:探索Shell下的程序替换

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出&#xff0c;万山无阻 目录 &#x1f4d6;一、进程程序替换 1.替换的演示 ❓替换与执行流 ❓程序替换≠进程替换 2.替换的原理 …...

38 基于单片机的宠物喂食(ESP8266、红外、电机)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用L298N驱动连接P2.3和P2.4口进行电机驱动&#xff0c; 然后串口连接P3.0和P3.1模拟ESP8266&#xff0c; 红外传感器连接ADC0832数模转换器连接单片机的P1.0~P1.…...

ps网页版在线制作/家庭优化大师

引用&#xff1a;http://www.williamlong.info/archives/3181.html CC攻击&#xff08;Challenge Collapsar&#xff09;是DDOS&#xff08;分布式拒绝服务&#xff09;的一种&#xff0c;也是一种常见的网站攻击方法&#xff0c;攻击者通过代理服务器或者肉鸡向向受害主机不停…...

贺州招聘网站建设/淘宝产品关键词排名查询

由Matt Heusser和Markus Grtner合著的“Save our Scrum”一书为实施Scrum的团队提供了建议。书中探讨了对于实施Scrum有困难的团队&#xff0c;他们可以做什么才能摆脱困境&#xff0c;并找到更好的方法来使用Scrum。\InfoQ读者可以下载本书的一份样本&#xff0c;并使用该优惠…...

网站开发filter/深圳营销型网站定制

Jdevloper资料&#xff0c;绝对经典&#xff01;&#xff01;&#xff01;链接:http://www.itpub.net/854062,1.html来自 “ ITPUB博客 ” &#xff0c;链接&#xff1a;http://blog.itpub.net/39335/viewspace-350967/&#xff0c;如需转载&#xff0c;请注明出处&#xff0c;…...

pc开奖网站开发/网址缩短在线生成器

来源是&#xff1a;Text at LEFT如何更改CSS以使两个DIV看起来像&#xff1f;----------------------|Text at LEFT | ####|| | ####|| | ####|| | || | |----------------------即。文本在左侧DIV左上角&#xff0c;图像在右侧DIV右上方对齐。更新&#xff1a;我使用的是&…...

网站构建/营销型网站的分类不包含

作为开发人员&#xff0c;每个人都会遇到有关在生产服务器上启用GC日志的问题。 建议在生产服务器上启用GC登录吗&#xff1f; 是的&#xff0c;建议在生产服务器上启用GC登录 。 通过在JVM上启用GC登录的开销很小。 根据标准性能评估公司&#xff08;SPEC&#xff09; &#x…...

c 网站开发/免费技能培训在哪里报名

/*** * A:案例演示* 集合嵌套之ArrayList嵌套ArrayList* 案例:* 我们学科,学科又分为若个班级* 整个学科一个大集合* 若干个班级分为每一个小集合*/Testpublic void twoArrary() {ArrayList<ArrayList<Person>> list new ArrayList<>();ArrayList<Person…...