2012-6-27日下午,去了一个软件公司笔试面试,3道题目,都是 C 语言的编程题,题意简单明了,写起来好麻烦,而且是在纸上写的,平常习惯了写写改改,后来发现整个卷面乱的真不是给人看的。
第一题:将n个文件合并到一个文件中,要求保存每个文件的文件名,文件长度,文件数据。函数签名 int fpack(const char *flist[], const char *dstfile); flist的形式 {"1.doc", "2.txt", "3.exe", NULL} 。第一题就难倒我了,首先是要读写二进制文件,fread, fwrite本身就用的不多,又有4个参数,参数 File * 在第一个还是在第四个常搞不清楚,且第二个参数和第三个参数容易混淆;文件长度要通过读文件数据才知道,那么文件头信息中的文件长度还不能一开始就写入,需要 fseek 来回定位,当时就直接看第2题和第3题了,最后回过头了,硬着头皮写完了这个函数,现在回来仔细想想当时写得真是漏洞百出啊,于是又重新写了一遍:
1 #include <stdio.h>
2 #include <string.h>
3
4 struct SFile { // 注意边界对齐,f_name[6] 与 f_name[8] 时,sizeof(SFile) 都等于12
5 int f_len; // 感觉 f_len 应该写在 f_name 前面比较好
6 char f_name[8];
7 };
8
9 void print_buf(const char *buf, int len) {
10 for (int i = 0; i < len; ++i)
11 printf("%X", buf[i]);
12 puts("");
13 }
14
15 int fpack(const char *flist[], const char *dstfile) {
16 int n = 0;
17 const char **p = flist;
18 char buf[1024];
19 int len = 0;
20 SFile file;
21
22 for (; *p != NULL; ++p, ++n) ; // 统计需要 pack 的文件个数
23
24 FILE *pDst = fopen(dstfile, "wb");
25 if (pDst == NULL) {
26 puts("pDst fopen error!");
27 return -1;
28 }
29
30 printf("n = [%d]\n", n); //
31 fwrite(&n, sizeof(n), 1, pDst); // 文件前siezof(int)个字节保存文件的个数
32
33 for (p = flist; *p != NULL; ++p) { // 遍历每个文件
34 printf("f_name = [%s]\n", *p);
35 strcpy(file.f_name, *p); // 构造结构体
36 file.f_len = 0;
37
38 FILE *pf = fopen(*p, "rb"); // 打开文件
39 if (pf == NULL) {
40 puts("pf fopen error!");
41 fclose(pDst);
42 return -1;
43 }
44
45 fseek(pDst, sizeof(SFile), SEEK_CUR); // 跨越文件头信息,先写文件数据
46 while ((len = fread(buf, 1, 1024, pf)) > 0) { // 注意不能写成 fread(buf, 1024, 1, pf),否则 len 为 0
47 printf("len = [%d]\n", len); //
48 print_buf(buf, len); //
49 file.f_len += len; // 更新文件长度数据
50 fwrite(buf, 1, len, pDst);
51 }
52
53 int sz1 = sizeof(SFile);
54 int sz2 = file.f_len;
55 printf("sz1 = [%d], sz2 = [%d]\n", sz1, sz2); //
56 printf("fn = [%d], fl = [%d]\n", sizeof(file.f_name), sizeof(file.f_len));
57
58 fseek(pDst, -(sz1 + sz2), SEEK_CUR); // 回到写文件头信息的位置
59 fwrite(&file, sz1, 1, pDst); // 写入头信息
60 fseek(pDst, 0, SEEK_END); // 定位到文件尾,为下一个文件做准备
61
62 fclose(pf); // 关闭当前文件
63 }
64
65 fclose(pDst); // 关闭目标文件
66 return 0;
67 }
68
69 int main(int argc, char *argv[])
70 {
71 const char *s[8] = {"111.txt", "222.txt", "333.txt"};
72 const char *d = "d.txt";
73
74 printf("iRet = %d\n", fpack(s, d));
75
76 return 0;
77 }
第三题:将一个带头结点的单链表转换成一个带头结点的双向循环链表,这个题目不仅要满足是双向链表,还要满足循环链表,从单链表直接跳了两步,但是和第一题、第二题比起来,对我来说或许还能折腾出来,于是就先写了此题,回来在机器来又找原来写了遍,测试了下基本没有出错,如下:
1 #include <malloc.h>
2 #include <stdio.h>
3
4 struct Node {
5 int data;
6 Node *next;
7 };
8
9 struct BiNode {
10 int data;
11 BiNode *next;
12 BiNode *prev;
13 };
14
15 BiNode *convert(const Node *head) { // 假定内存足够,malloc不会失败
16 BiNode *pBh = (BiNode *)malloc(sizeof(BiNode)); // 双向循环链表头结点
17 pBh->data = 0;
18 pBh->next = pBh->prev = NULL; // 默认置空
19
20 BiNode *q = NULL;
21 Node *p = head->next;
22
23 if (p != NULL) { // 单链表不为空
24 q = (BiNode *)malloc(sizeof(BiNode));
25 q->data = p->data;
26 q->next = q->prev = q; // 双向循环链表只有一个结点的时候,next 和 prev 均指向自己
27 pBh->next = pBh->prev = q; // 头结点两个指针均指向第一个结点
28
29 p = p->next;
30 for (; p != NULL; p = p->next) { // 遍历单链表余下的结点
31 BiNode *t = (BiNode *)malloc(sizeof(BiNode));
32 t->data = p->data;
33 t->next = q->next;
34 t->prev = q;
35
36 q->next = t; // 原来的尾结点next指向新的尾结点
37 pBh->next->prev = t; // 第一个结点prev指向新的尾结点
38
39 q = t; // q 保持指向尾结点
40 }
41 }
42
43 return pBh;
44 }
45
46 void print_clockwise(const BiNode *h) {
47 const BiNode *p = h->next; // 第一个结点
48 printf("data = [%d]\n", p->data);
49
50 p = p->next;
51 for (; p != h->next; p = p->next)
52 printf("data = [%d]\n", p->data);
53
54 puts("----");
55 }
56
57 void print_anticlockwise(const BiNode *h) {
58 const BiNode *p = h->next; // 第一个结点
59 printf("data = [%d]\n", p->data);
60
61 p = p->prev;
62 for (; p != h->prev; p = p->prev)
63 printf("data = [%d]\n", p->data);
64
65 puts("----");
66 }
67
68 int main(void)
69 {
70 // 构造单链表
71 Node n[5];
72 Node *head = &n[0];
73 head->data = 0;
74 head->next = &n[1];
75 n[1].data = 1;
76 n[1].next = &n[2];
77 n[2].data = 2;
78 n[2].next = &n[3];
79 n[3].data = 3;
80 n[3].next = &n[4];
81 n[4].data = 4;
82 n[4].next = NULL;
83
84 BiNode *pBh = convert(head);
85
86 // 顺时针打印
87 print_clockwise(pBh);
88
89 // 逆时针打印
90 print_anticlockwise(pBh);
91
92 return 0;
93 }