C语言数组作为函数参数
指针传递:传递的指针只包含数组的起始地址,要使用数组还应传出数组长度信息; 数组名传递:数组名称依旧是一个指针,和方法1本质是一样的,只是书写形式不一样。
指针传递:传递的指针只包含数组的起始地址,要使用数组还应传出数组长度信息; 数组名传递:数组名称依旧是一个指针,和方法1本质是一样的,只是书写形式不一样。
1962 年,Corbato 首次提出多级反馈队列(Multi-level Feedback Queue, MLFQ),应用于兼容时分共享系统(CTSS)。
多级反馈队列需要解决两方面的问题:
操作系统曾经是一组函数(实际上是一个库),在内存中(在本例中,从物理地址0开始),然后有一个正在运行的程序(进程),在目前物理内存中(本例中,从物理地址 64KB 开始),并使用剩余内存。
比例份额(proportional-share)调度程序,也称公平份额(fair-share)调度程序。比例份额算法基于一个简单的想法:调度程序的最终目标是,确保每个工作获得一定比例的 CPU 时间,而不是优化周转时间和响应时间。
彩票调度是一个非常优秀的比例调度程序。
彩票数(ticket)代表了进程(或用户或其他)占用了某个资源的份额。例如:假设两个进程 A 和 B,A 拥有 75 张彩票,B 拥有 25 张。因此我们希望 A 占用 75% 的 CPU 时间,而 B 占用 25%。
文件打开模式 | 含义 |
---|---|
“r” | 以只读方式打开文件,该文件必须存在 |
”w“ | 只写方式创建并打开一个空文件,若存在同名文件,则丢弃全部数据,作为一个空文件 |
“a” | 附加模式(append),只写打开文件,在文件末尾写入数据,若文件不存在,则创建该文件。重定位操作(fseek,fsetpos,rewind)将被忽略。 |
“r+” | 读或修改:打开的文件可读可写,但该文件必须存在。 |
“w+” | 写或修改:创建一个空文件进行读取或写入。如果已经存在同名文件,则将其内容丢弃,作为一个空文件。 |
“a+“ | 附加或修改:打开文件进行读或写,所有输出操作均在文件末尾写入数据。重新定位操作(fseek,fsetpos,rewind)会影响下一次读取。如果文件不存在,则创建该文件。 |
周转时间
T周转时间=T完成时间-T到达时间
如果所有任务在同一时间到达,那么 T完成时间 = 0,T周转时间 = T完成时间
(1) 假设有 3 个工作 A,B和C在大致在同一时间到达,A 比 B 早一点,B 比 C 早一点,假设每个工作的运行 10s,计算周转时间?
假设当前目录下共有以下3个文件,接下来会利用以下代码来演示GDB的调试过程。
├── main.cpp
├── tool.h
└── tools.cpp
文件内容分别为:
tools.h:函数声明
1 | #ifndef _TOOLS_H_ |
tools.cpp:函数定义
1 | #include <iostream> |
main.cpp:主函数
1 | #include <iostream> |
以上代码只是为了演示之用,并无特别之处。
编译方式
1 | // -g 添加调试信息 -Wall 输出所有警告信息,例如定义了从未使用过的变量 |
调试可执行程序
1 | $gdb main |
调试 core 文件,core 文件是程序运行过程中出现Segmentation fault (core dumped)错误时,程序停止运行时产生的。core文件是程序运行状态的内存映象。使用gdb调试core文件,可以帮助我们快速定位程序出现段错误的位置。可执行程序编译时应加上-g编译选项,生成调试信息。
1 | $gdb <program> <core dump file> |
调试服务程序
1 | $gdb <program> <PID> |
有时需要在执行程序时输入额外的参数,例如:像下面这样执行程序test
1 | $ ./main 5 |
调试时,往往直接执行$ gdb main
那么怎么在gdb中输入该程序的参数呢?
1 | (gdb) set args 5 |
还可以查看程序的参数
1 | (gdb) show args |
GDB提供了两种查看源码的方式,分别是根据行号和函数名查看。除了可以查看本文件源码,还可以查看其他文件的源码。
本文件指的是该程序对应的main函数所在文件,即main.cpp
1 | (gdb) l # 每执行1次显示10行,再执行1次显示次10行 |
共同编译的所有文件中,除了main函数所在文件的其它文件。在这里,除了main.cpp即tools.cpp
1 | (gdb) l tools.cpp:15 # 在tools.cpp中,显示第15行附近的代码 |
1 | (gdb) show list # 显示行数 |
可以在一次调试中设置1个或多个断点,下一次只需让程序自动运行到设置断点位置,便可在上次设置断点的位置中断下来,极大的方便了操作,同时节省了时间。
可以根据行号,函数名设置断点,还可根据条件设置断点(一般用于循环中)
1 | (gdb) b 10 # 将第10行设置为断点 |
1 | b tools.cpp:12 # 将tools.cpp的第12行设置为断点 |
条件断点一般用于循环中
本文件
1 | # 设置i==2时,第18行为断点 |
其他文件
1 | # 设置tools.cpp文件内,i==5时,第22行为断点 |
1 | (gdb) i b # 显示所有断点 |
1 | clear n # 清除第n行的断点 |
有两种运行方式,一种是从主函数开始运行,一种是运行到第1个断点处
程序从main函数开始
1 | (gdb) run |
程序停在第一个断点处
1 | (gdb) start |
1 | c/continue # 向下运行到下一个断点处 |
1 | p 变量名 # 打印变量值,可以答应在当前作用域之内的变量名 |
可以在每次执行时都打印该变量的值,常用于循环体中
1 | display 变量名 # 自动打印指定变量的值 |
1 | where/bt # 当前运行的堆栈列表; |
1 | set var 变量名=变量值 # 可以临时改变该变量的值 |
使用 layout 可以同时在多个窗口调试代码
1 | layout src # 显示源代码窗口 |
GDB 默认调试的是父进程,使用以下命令切换调试的进程
1 | set follow-fork-mode child # 调试子进程 |
GDB 可以同时调试一个进程,也可以同时调试多个进程,使用以下命令设置:
1 | set detach-on-fork <mode> # 当 mode 为 on 时,表示程序只调试一个进程(可以是父进程、子进程)。当 mode 为 off 时,父子进程都在gdb的控制之下,其中一个进程正常的调试,另一个会被设置为暂停状态。 |
GDB 将每一个被调试程序的执行状态记录在一个名为 inferior 的结构中。一般情况下一个 inferior 对应一个进程,不同的 inferior 有不同的地址空间。inferior 有时候会在进程没有启动的时候就存在。
1 | info inferiors # 显示所有的 inferior,当前调试的进程前有 "*"。 |
切换进程使用命令 inferior
1 | inferior <num> # 切换到 num 号进程 |
设置捕获点中断,调用 fork 函数时将产生中断
1 | catch fork |
命令格式:例如 x/<n/f/u> <addr>
例如:
1 | (gdb) x/3cb 0x8049098 |
x(examine)为命令的简称,用于检查内存数据。
命令的意思是:查看从内存地址 0x8049098 开始的数据,每字节为单位(b),以ASCII字母形式显示©,显示 3 个单位的数据(3)。
下面对 3cb 三个字符对应的含义进行讲解:
3
表示显示的单位数据个数,可根据需要自定义,和第三个字符参数有关。
c
用什么格式来解析这些二进制位,因为内存本质上是二进制位,它可被解释为多种含义,例如:0111 0101,可表示十进制数 117,也可以表示十六进制数 75,也可以表示字符 K(ASCII 字母)
可用的格式字符如下:
c 按字符格式显示变量。
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示地址,并显示距离前继符号的偏移量(offset)。常用于定位未知地址(变量)。
f 按浮点数格式显示变量。
b
表示从当前地址往后请求的位宽大小。如果不指定的话,GDB默认是4个bytes。也可以指定以下字符
b表示单字节,h表示双字节,w表示四字 节,g表示八字节。
cgdb可以看作gdb的界面增强版,用来替代gdb的 gdb -tui。cgdb主要功能是在调试时进行代码的同步显示。
性质1: 在二叉树的第i层上至多有2i-1个节点(i>=1)
性质2: 深度为k的二叉树至多有2k-1个节点(k>=1)
性质3: 对任何一棵二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1
满二叉树:深度为k且含有2k-1个节点的二叉树