cs61c-lab01
Lab1: Number Rep, C and CGDB
cs61c - Great Ideas in Computer Architecture (Machine Structures) lab 笔记第一篇
课程主页:https://inst.eecs.berkeley.edu/~cs61c/fa20/#by-week
个人仓库:https://github.com/2333monster/my-cs61c-lab
参考
gdb调试手册:https://blog.nowcoder.net/n/1957d46e0ee34168aee2289faf36ee5c#gdb_20
Setup
获得skeleton code
1 |
|
Compiling and Running a C Program
使用gcc 编译c程序,生成执行文件
1 |
|
使用执行文件a.out
的名称运行c程序
1 |
|
或者使用-o
命令,它能指定执行文件的名称
1 |
|
Exercise 1: See what you can C
本练习中,将看到宏定义处理器的示例
更改V0 ~ V3 的宏定义,使其输出以下内容
1 |
|
更改后(好像不要求完全不同,完全不同确实写不出来)
1 |
|
Exercise 2: Catch those bugs!
使用GDB进行Debug,GDB reference card
GDB stands for “GNU De-Bugger.”
使用-g
调用 (c)gdb
1 |
|
这会生成一个可被gdb
识别的可执行文件
1 |
|
gdb常用调试指令
调试指令 | 作用 |
---|---|
b(break) 行号 | 在源代码指定的某一行设置断点,其中行号用于指定具体打断点的位置。 |
r(run) | 执行被调试的程序,其会自动在第一个断点处暂停执行。 |
n(next)(int) | 令程序一行代码一行代码的执行。执行n次 |
p(print) 变量名 | 打印指定变量的值 |
l(list) | 显示源程序代码的内容,包括各行代码所在的行号。 |
q(quit) | 终止调试。 |
断点设置格式
1 |
|
- location
location 的值 | 含 义 |
---|---|
linenum | linenum 是一个整数,表示要打断点处代码的行号。 |
filename:linenum | filename 表示源程序文件名;linenum 为整数,表示具体行数。整体的意思是在指令文件 filename 中的第 linenum 行打断点。 |
+ offset | offset 为整数,+offset 表示以当前程序暂停位置为准,向后数 offset 行处打断点 |
- offset | offset 为整数,-offset 表示以当前程序暂停位置为准,向前数 offset 行处打断点 |
function | function 表示程序中包含的函数的函数名,即 break 命令会在该函数内部的开头位置打断点,程序会执行到该函数第一行代码处暂停。 |
filename:function | filename 表示远程文件名;function 表示程序中函数的函数名。整体的意思是在指定文件 filename 中 function 函数的开头位置打断点。 |
- cond 表达式
1 |
|
Action Item
- setting a breakpoint at main
1 |
|
- using gdb’s run command
1 |
|
- using gdb’s single-step command
1 |
|
gdb questions
While you’re in a gdb session, how do you set the arguments that will be passed to the program when it’s run?
1
set args 5
How do you create a breakpoint?
1
b filename: linenum
How do you execute the next line of C code in the program after stopping at a breakpoint?
1
n(next) / s(step)
If the next line of code is a function call, you’ll execute the whole function call at once if you use your answer to #3. (If not, consider a different command for #3!) How do you tell GDB that you want to debug the code inside the function (i.e. step into the function) instead? (If you changed your answer to #3, then that answer is most likely now applicable here.)
1
step
How do you continue the program after stopping at a breakpoint?
1
run
How can you print the value of a variable (or even an expression like 1+2) in gdb?
1
2p 1+2
p variable_nameHow do you configure gdb so it displays the value of a variable after every step?
1
display variable
How do you show a list of all variables and their values in the current function?
1
info args
How do you quit out of gdb?
1
q
Exercise 3: Debugging w/ YOU(ser input)
好像是关于redirection ,没懂
Exercise 4: Valgrind’ing away
有两种bug类型
- bohrbugs:在一致的稳定的情况下会重复出现的错误
- heisenbugs:在相同情况下不一定会重复出现,一般由于内存管理不当
使用Valgrind 找出”heisenbugs”
To help catch these “heisenbugs” we will use a tool called Valgrind. Valgrind is a program which emulates your CPU and tracks your memory accesses. This slows down the process you’re running (which is why we don’t, for example, always run all executables inside Valgrind) but also can expose bugs that may only display visible incorrect behavior under a unique set of circumstances.
安装valgrind (在ubuntu环境下)
1 |
|
segfault_ex
出现错误的原因
1 |
|
访问数组外未定义的内存,典型的heisenbug。
使用valgrind
1 |
|
输出结果:
1 |
|
no_segfalut
的问题
1 |
|
for (int j = 0; j < sizeof(a); j++)
使用sizeof(a) a 是一个包含五个元素的整型数组,sizeof的结果是5*4=20,而不是5,导致访问未定义的内存,从而产生未知的结果。这是一个典型的Heisenbug问题,因为其结果取决于内存中未定义的值,可能会导致程序崩溃或输出错误的结果。
使用valgrind
1 |
|
结果
1 |
|
关键词:uninitialised value
分析上面两个程序
Why didn’t the
no_segfault_ex
program segfault?
首先c以这种形式分配数组内存是stack中给了一个指针,随着j的增大,会访问a[5]~a[19]这实际是a+5 ~ a+19 的内存,这些内存是存在的,由于这不是分配给a 的内存,且没有初始化,其中会存储着脏数据,导致不可确定的。不过由于没有超出stack的容量,不会造成segfault(程序崩溃)Why does the
no_segfault_ex
produce inconsistent outputs?
同上,不确定的数据导致不确定的输出Why is
sizeof
incorrect? How could you still usesizeof
but make the code correct?
同上,更改1
2
3
4...
arraylength = sizeof(a)/sizeof(a[0]);
for(int j=0;j<arraylength;j++)
...
Exercise 5: Pointers and Structures in C
在ll_cycle.c中,完成函数ll_has_cycle()来实现以下检查单向链表是否有环的算法。
- Start with two pointers at the head of the list. We’ll call the first one tortoise and the second one hare.
- Advance hare by two nodes. If this is not possible because of a null pointer, we have found the end of the list, and therefore the list is acyclic.
- Advance tortoise by one node.
- If tortoise and hare point to the same node, the list is cyclic. Otherwise, go back to step 2.
ll_cycle.c
1 |
|
解释算法原理Wikipedia article