李迟2011年4月知识积累

我从来不觉得写代码只是写代码的事,也不是整天拿数据结构和算法来研究,拿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可查看很多种格式的文件,如二进制:

1
$ hexdump -C a.out

显示结果有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的认识

其它未记录的还有一些。