代码驱动的程序设计学习
这学期的课程已经到了第三周了,不少同学在博客中连代码托管还没弄好。出现的问题有:
- Ubuntu上git安装配置出现问题;
- 使用git基本的流程不对,连最基本的
git add; git commit; git push
流程都不会,还直接到git@OSC上通过代码片断把电脑上的代码又贴一遍,浪费时间; - 有的同学输了一周的代码,最后一起git commit 一下,而不是完成一段代码就
git add;git commit
一下; - 由于上一条的问题,代码的注释都一样,不能精细解释代码的功能;
- ...
上学期的《Java程序设计》中git和git@OSC已经用了一学期了,现在的情况说明不少同学要么上学期学的东西不扎实,学过的知识忘了,要么上学期就没学会。我感觉没学会的可能性很大,这么简单的东西不大会忘。虽然有「使用开源中国托管代码」和卢肖明同学的「Ubuntu下git的安装与使用」 这些博客指导,好好看实践相关内容的同学还是太少,还是有必要结合课程写个step by step 的教程,虽然下面的内容课堂上都演示过,不少同学还是没有学会。
更大的问题是完成学习任务时缺少思考,累却收获不大,从而失去学习的激情。
学习时问题驱动是个很好的方式,「本学期第一周学习任务」要求根据「别出心裁的Linux命令学习法」的思想学习「Linux 基础入门(新版)」,目前这门课有13节,不少人要么没学完,要么反映学完了没搞明白。这时候基于自己的实践,通过问题驱动
来找最常用的命令,你会发现3/4/5/6节是学习重点。编程的学习,代码驱动是个很好的方式,我们的教材《深入理解计算机系统V2》是那么厚的一本书,通过使用git和gdb来积极主动的读写代码是一个很好的方式。积极主动的前提是自己有思考,不是仅仅完成老师的任务,那么按照下面的方法学习,这学期收获的不是一门功课的好成绩,而是为学习信息安全专业打下的坚实的计算机基础:程序设计、操作系统、计算机组成原理、汇编语言、编译原理、计算机网络、软件工程...
工具参考
下面的内容涉及到git和gdb的使用,这儿给出这两个工具的cheatsheet,方便大家参考,当然打印一份夹在教材中随用随查更好。
- git cheatsheet
- gdb cheatsheet
git的安装测试
Ubuntu中在bash中输入git
, 如果没有安装git
,会有如下提示:
git的安装非常简单,根据上图中的提示,我们在bash中输入sudo apt-get install git
就可以了,可以输入git --version
来测试是否安装成功。如下图所示:
不管是使用自己电脑上的虚拟机还是使用实验楼上的虚拟机,新建一个体现自己学号姓名
的文件夹很重要,让老师一眼能看出来这是谁做的。如下图,输入mkdir 20145300rocedu
新建一个20145300rocedu
文件夹。通过输入cd 20145300rocedu
进入20145300rocedu
文件夹:
有的同学没有项目的概念,这学期的代码和上学期的Java课程中的代码混在一起,非常不好。
我们的教材《深入理解计算机系统V2》的英文版名为《Computer Systems :A Programmer's Perspective》,和那本C语言名著《C程序设计语言 (The C Programming Language)》有个专有的缩写K&R
一样,《深入理解计算机系统V2》的专用缩写为CSAPP
,我们使用的是第二版,所以加上学号信息,通过在bash中输入mkdir 20145300CSAPP2E
建立教材的项目文件夹。根据C项目的规范,我们在20145300CSAPP2E
文件夹下建立相应的文件夹:
- src:存放源代码文件
- include: 存放头文件
- bin:存放编译后的目标文件、可执行文件等
- lib:存放项目所需的静态库、动态(共享)库
- res: 存放项目所需的图标、声音、图片等资源
- docs: 存放项目相关的参考资料、帮助文档,比如大家的学习博客就可以放在这里
- ...
此外还可以建立dist文件夹存放打包,发布以后的代码;建立examples文件夹存放示例代码等。
项目文件夹下可以通过touch README Makefile
新建README和Makefile两个文件:
- README 是对项目的简要介绍:licence、功能、编译环境等,详细的要在docs下有帮助文档
- Makefile 实现项目编译自动化
- 其他相关文件
我们的教材有12章,在src
文件夹下为每章建立了一个文件夹。
建好的项目结构如下图所示:
我们的工作目录一般选择项目目录下,其他的使用相对路径。比如我们在src/01intro
目录下输入教材第一章第一页的hello world
程序hello.c, 我们使用vim src/01intro/hello.c
命令:
代码如下图所示:
在Vim中通过:wq
保存hello.c并退出Vim后,我们使用gcc src/01intro/hello.c -o bin/hello
来编译程序,注意命令中相对路径的使用。可执行程序hello生成在bin目录下,我们可以使用./bin/hello
来执行hello程序。此时目录结构如下图所示:
我们通过git config
对git作一下简单配置,user.name
尽量体现自己的学号信息。
git cheatsheet中有常用的配置项:
我们通过git init
把项目纳入git管理。以后就是最常用的git add; git commit
了,使用git commit时一定要注意解释一下刚才做了什么。比如我们在这用git commit -m "initiate csapp2e project with hello world program"
说明‘我们用hello world程序初始化了CSAPP2E这个项目’。
如下图,git cheatsheet中有常用的git 查看命令,git log
会列出所有历史记录,最近的排在最上方,显示提交对象的哈希值,作者、提交日期、和提交说明。如果记录过多,则按Page Up、Page Down、↓、↑来控制显示;按q退出历史记录列表。如上图,我们此时只有一条git commit记录。
我们建立了项目目录结构后,编译命令中要用到相对路径,命令就长且难以记忆了,比如上面我们使用gcc src/01intro/hello.c -o bin/hello
来编译hello world程序。我们可以通过shell脚本来简化编译运行程序。注意我们要用chmod +x compile.sh
给脚本添加可执行权限,我们就可以用./compile.sh
来执行脚本了。如下图所示:
脚本中是编译、运行程序的命令,shell脚本中第一行是固定写法, #
后面是注释:
我们想体会教材上gcc编译程序的四个阶段:预处理、编译、汇编、链接。
可以在脚本中增加相关命令:
大家要注意,源代码产生的中间文件是不需要纳入git的管理的,也就是说我们上面建立的项目目录结构中的bin是不需要git管理的,否则项目所占空间可能会很大,往代码托管网站使用git push
推送时,会浪费不少流量,也浪费代码托管网站的存储空间。我们可以使用.gitignore
文件把不想纳入git管理的文件、文件夹排除掉。
我们完成一个小任务,就git add; git commit
一下,注意在注释中说明刚才做了什么:
大家注意不要到周末了写了几百行代码了才git add; git commit
一下,这样导致所有代码的注释都一样了,很多同学的注释都是“Week2”、"Week3"、“第三周代码”。
积极主动敲代码
有的同学说实践了没有收获,主要问题是思考不够,更多的是动手不够,导致基本知识都不明白。比如看到教材p21 关于溢出的讲解:
如果你基础好,一下子就明白了,就可以继续后面的学习了。
如果你没有见过这种情况,或者对书上四个式子都会得到-88490188
的结论有怀疑的话,就可以把代码敲一下,C程序我们命名为testoverflow.c
代码如下图所示,像要补充个main函数这件事竟然不少人不知道:
在compile.sh中添加相应的编译、运行命令:
然后编译运行程序,结果验证了教材上讲的没有问题。
完成了这个小程序,我们可以git add; git commit
一下,这样如git log
的结果所示,git就记录了你的学习轨迹:
一周怎么也要git add; git commit
十几次、几十次,如果上学期这么做了,我不大相信会忘记这两条命令。
需要不需要写代码要根据自己的情况决定,这样的学习过程每个人是不一样的,遇到的问题也是不一样的,有了不一样的过程,不同的问题老师才可以因材施教,大部分人都有人问题就是老师的教学重点。
使用gdb辅助学习
gdb是个调试工具,也是一个很好的学习工具,像「积极主动敲代码,使用JUnit学习Java」中的JUnit一样,虽然是单元测试工具,都可以用来作为我们积极思考、主动学习的工具。
gdb有两个替代工具cgdb,ddd,在Ubuntu中可以使用sudo apt-get install cgdb
, sudo apt-get install ddd
来安装。
我们以教材上第44页有符号数和无符号数的转换为例。我们为代码命令为testcast.c
:
我们补充main函数,输入相关代码:
在compile.sh中添加编译运行命令,注意为了使用gdb调试,我们用了gcc的-g
选项:
运行结果和教材中的一样:
完成一段代码,测试没有问题了,我们就git add;git commit
一下,注意在注释中解释一下做了什么。
如果到这,你可能还不能理解教材中说的「在相同长度的无符号和有符号整数之间进行强制类型转换时,大多数C语言实现遵循的原则是底层的位模式不变」。什么是「数值可以改变,位模式不变」?书上给出了一系列公式,相信有人是看不懂的。
我们可以使用gdb加深理解。
gdb cheatsheet中对print
命令(print
命令可以缩写为p
)有细解,我们可以用/t
选项查看变量的二进制形式,也就是教材中说的「位模式」:
我们用cgdb 对testcast进行调试:
我们通过b p44_t2u
、b p44_u2t
设置两个函数断点,使用r
运行testcast,程序在第22行停止,我们运行两个n(next)
命令让程序停在第24行,这时我们使用 p /t v
和p /t uv
查看v和uv的位模式,你会发现都是1100 1111 1100 0111
,也就是说有符号整数v被强制转换成无符号整数uv,从位模式上看都 是1100 1111 1100 0111
,不同的是对于v,1100 1111 1100 0111
最高位1解释成符号位,表示负数,v的值是-12345,对于uv,1100 1111 1100 0111
最高位1就表示2的15次方了,也就是32768,这就是「数值可以改变,位模式不变」。
继续往下调试:
你可以看到有符号整数转换成无符号整数也是一样:数值变了,位模式没变。
如果这时候你能想到第一章的一个公式「信息=位+上下文」就更好了。同样的位模式1100 1111 1100 0111
,不同的上下文中可以表示-12345
,也可表示53191
;同样的位模式1111 1111 1111 1111
,不同的上下文中可以表示4294967295
,也可以表示-1
。
用这种方式积极主动的学习,我不大相信冰雪聪明的你们会学不明白。当然,这需要极大的时间投入的,像张雪纯同学在「第三周学习总结」中说的那样:
『原来较为轻视课本的学习,总想走捷径,这次一页页读过课本以后发现自己对这些基础知识有了更深刻的了解。其实阅读课本也是很重要的。有时候看上去很笨的方法可能是最有效的。』
周筠老师(知乎,微博,豆瓣)评论总结:「课本的内容是经过作者反复斟酌,去粗取精并做了系统思考而写成的,所以阅读课本反而是真正的捷径。有这样的认识,是上课的大收获啊,祝贺!请继续加油!」,我深表同意!
思考题
这两个题目认真做的视情况会有1-5分的加分:
- 参考卢肖明同学的「Ubuntu下git的安装与使用」,把自己的学习内容新建一个项目,按这篇博客的方式重新整理一下,通过
git remote add
,git push
推送到git@OSC类似的代码托管网站。 - 使用compile.sh有什么问题?如何使用Makefile改进?要求添加C代码Makefile不需改动。第一个完成者加5分。
参考资烤料
欢迎关注“rocedu”微信公众号(手机上长按二维码)
做中教,做中学,实践中共同进步!
-
版权声明:自由转载-非商用-非衍生-保持署名| Creative Commons BY-NC-ND 3.0
如果你觉得本文对你有帮助,请点一下左下角的“好文要顶”和“收藏该文”