我从来不觉得写代码只是写代码的事,也不是整天拿数据结构和算法来研究,拿C、C++大头书来看。我对自己的要求是:坚持融会贯通,坚持多层次联系,坚持学习,坚持积累。
非代码知识 主要关于linux日常使用的。
1、将man转换成pdf,如
1 $ man -t open | ps2pdf - open.pdf
(看似有空格的地方,实际上就是有空格) 注意,像write这个函数,需要在-t 后加2,以表示它是系统函数。 2、objdump反汇编,如果编译使用-g选项,可以用-dS选项:
1 $ objdump -dS a.out > aa.txt
3、hexdump可查看很多种格式的文件,如二进制:
显示结果有3列,依次为偏移、十六进制数据,可读(可打印)字符。这与用UE打开二进制文件效果一样。 4、在代码前面添加行号:
1 $ awk '{printf("%02d: %sn", FNR, $0)}' < foo.c > bar.c
5、shell操作 近来经常用SecureCRT操作linux,有时不太喜欢用光标那几个键移动,经常使用组合键,由于花了一定时间学习emacs(约一年前开始接触,断断续续用不少时间)。下面总结一下自己在shell(bash)中常用的快捷键,对emacs熟的人应该很清楚了,最英文比较敏感的也应该意会到每个单词约表示什么意思。
1 2 3 4 5 6 7 8 9 10 11 12 13 C+a:移到命令开始处 C+e:移到命令结尾 C+f C+b:向前、向后移一个字符 M+f M+b:向前、向后移一个单词 可用比较长的命令行感受一下这些命令的便捷,最经典的,假如配置qtopia2时有前面某个地方写错了,除了使用光标移到前面外,可以使用M+b向后移,更可以使用C+a直接移到开头。 C+p C+n:前一历史命令、后一历史命令 C+k:删除光标至行尾所有字符。——称之为剪切更恰当 C+w:似乎与C+k一样 C+y:粘贴 C+l:清屏,与clear命令作用相同 C+u:删除整行,可用C+y粘贴 ……其它的
除了emacs、bash外,在u-boot命令行中也有类似的。可研究一下readline这个库。
代码点滴 1、获取系统信息:
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdlib.h> #include <stdio.h> #include <sys/utsname.h> /* uname */ int main(void) { struct utsname name; uname(&name); printf("%s/n%s/n%s/n%s/n%s/n", name.sysname, name.nodename,name.release, name.version, name.machine); return 0; }
运行结果:
1 2 3 4 5 6 $ ./a.out Linux FightNow 2.6.27.25-78.2.56.fc9.i686 #1 SMP Thu Jun 18 12:47:50 EDT 2009 i686
它与uname -a作用一样。
2、函数指针测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 /***************************************************************** 函数指针测试 $ ./a.out in func:sig1, num:100 in func:foobar 100 200 in func:sig2, num:250 in func:foobar 300 400 in ts_input_read() read me 2011-4 *****************************************************************/ #include <stdio.h> /*函数指针*/ /* 这两种有何区别? */ typedef int boot_fn(int a, int b); typedef int (*boot_fn_p)(int a, int b); int foobar(int a, int b) { printf("in func:%s ", "foobar"); printf("%d %d/n", a, b); return 0; } void sig1(int num, boot_fn handle) { printf("in func:%s, num:%d/n", "sig1", num); handle(100,200); //printf("%p %p/n", handle, &handle); } void sig2(int num, boot_fn_p handle) { printf("in func:%s, num:%d/n", "sig2", num); handle(300,400); //printf("%p %p/n", handle, &handle); } /*结构体中的函数指针*/ struct tslib_ops { int (*read)(char *inf, int nr); int (*fini)(char *inf); }; struct tslib_module { void *handle; const struct tslib_ops *ops; }; static int ts_input_read(char *inf, int nr) { printf("in ts_input_read()/n"); printf("%s/n", inf); return 0; } static int ts_input_fini(char *inf) { printf("int ts_input_fini()/n"); return 0; } static const struct tslib_ops input_ops = { .read = ts_input_read, .fini = ts_input_fini, }; struct tslib_module module; int main(void) { sig1(100, foobar); sig2(250, foobar); module.ops = &input_ops; module.ops->read("read me", 11); return 0; }
上面代码中使用了稍微修改后的tslib中的一些代码。
3、论“断言” 网上很多笔试题都喜欢用断言,经典者莫如strcpy等等函数的实现,如无断言,则说明应试者没有具备基本的出错处理能力云云。见那么多人喜欢断言,于是用gcc和vc测试了一下。 在gcc下故意造一错误,用断言判断之:
1 2 3 4 5 6 7 8 9 #include <assert.h> int main(void) { int f = 250; assert(f!=250); return 0; }
编译无警告,运行出错,如下:
1 2 a.out: test.c:7: main: Assertion `f!=250' failed. 已放弃
说明断言生效了。 同样代码,在vc6.0下测试,除了弹出Debug Error!对话框外,dos窗口还出现:
1 Assertion failed: c!=250, file E:Late Leec-testtestmain.c, line 367
后果非常严重:程序退出运行,而其它信息与操作系统有关。 不过,说实话,我很少用到断言。出错信息的处理每个人都不相同,但是原则都是尽可能地友好、人性化。比如,调用malloc需要判断返回值,不过我没有使用assert,只是简单打印出错信息,返回负数。 有人问我为什么要判断malloc的返回值,我回答不上来。在调用系统调用时我也判断返回值,不为什么,只是对自己写的程序负责,为了方便调试、日后管理。 但是,跟人讨论技术时,时刻将技术名词挂载嘴边,会显得自己更有水平(xx设计模式、xx算法、xx架构,等等)。至于如何交流、如何忽悠人,就仁者见仁了。 说到笔试,很多资料都有这么一题: 写一strcpy函数,让面试者说。 说什么?说参数有没有const,使用了assert没有,返回值有没有?注意不注意结束符。并写一个标准的strcpy。 其实我也想出一笔试题: 求sizeof(int)。 如果面试者直接说4,说明它在32位平台上写过程序。 如果他问面试官这题目的环境、平台,说明他注意到了不同平台会有不同结果。 如果他继续讨论单片机、ARM、x86甚至MIPS平台,说明他有一定的硬件功底; 如果他再讨论了计算机的组成及结构,冯·诺依曼体系,讲述了当初计算机的发展,说明他在计算机领域已经有一定水平。 如果再讨论到计算机科学的方方面面,并展示自己写的操作系统、编译器及科学研究成果,那么他不用面试了,可以去拿计算机领域里的大奖了。 4、char ch = "Fuck the world!"[2];
实际上ch为字符“c”,即字符串第2个字符(从0算起)。与那个得过奖的unix程序本质一致。参见:linux下CPP的认识
其它未记录的还有一些。