重温K&R(3)
最近要看的书好多,忙碌并快乐着,但是只要不乱了方寸就好,接着继续K&R的学习,这里是第三章,是关于流程控制的,我们可以用if-else啊,switch啊还有一些循环语句来实现,闲话少说,先把习题做上。
Exercise 3-1. Our binary search makes two tests inside the loop, when one would suffice (at the price of more tests outside.) Write a version with only one test inside the loop and measure the difference in runtime.
解:这里要说一下题目表达的是我们平时某些二分查找需要两个判断(if-else if-else),题目需要我们只用一个判断就实现,实际上这个很简单,用原先我关于二进制的专题里头的代码就是答案了,关键是原先判断的时候包含了一旦找到就直接退出的过程,我们可以将其省去,那么我们的程序就出来了。
int BinarySearch(int f, int t, int* a, int s) { int mid; while (f < t) { mid = f + ((t - f) >> 1); if (a[mid] >= s) t = mid; else f = mid + 1; } if (a[f] == s) return -1; else return f; }
Exercise 3-2. Write a function escape(s,t) that converts characters like newline and tab into visible escape sequences like \n and \t as it copies the string t to s. Use a switch. Write a function for the other direction as well, converting escape sequences into the real characters.
解:这里就让我们很开心啦,让我们用switch做escape操作,就是将将不可见的字符(如回车啊,制表符啊)换成可见形式(对应的为"\n"和"\t")。源串是s,目标串是t。这里的要求是用switch。同时题目又说了,让我们做一个反向的转换。我的程序作为例子处理回车和制表符,返回的函数是back(简单好用我就直接上了),请见谅。
#include <stdio.h> #include<string.h> void escape(char s[], char t[]) { int i, j; for (i = j = 0; s[i]; i++) switch (s[i]) { case '\t': t[j++] = '\\'; t[j++] = 't'; break; case '\n': t[j++] = '\\'; t[j++] = 'n'; break; default: t[j++] = s[i]; } t[j] = 0; } void back(char s[], char t[]) { int i, j; for (i = j = 0; s[i]; i++) if (s[i] == '\\') { switch (s[i + 1]) { case 'n': t[j++] = '\n'; break; case 't': t[j++] = '\t'; break; default: t[j++] = s[i + 1]; break; } i++; } else t[j++] = s[i]; t[j] = 0; } int main() { char a[500] = "fkjewk jf le\tkjfk\ndf\nfds",b[500]; escape(a, b); printf("a = %s\nb = %s\n", a, b); strcpy(a, b); back(a, b); printf("a = %s\nb = %s\n", a, b); return 0; }
Exercise 3-3. Write a function expand(s1,s2) that expands shorthand notations like a-z in the string s1 into the equivalent complete list abc...xyz in s2. Allow for letters of either case and digits, and be prepared to handle cases like a-b-c and a-z0-9 and -a-z. Arrange that a leading or trailing - is taken literally.
解:这个题目要我们展开所有的可能的区间表达式,比如0-5展开成012345这样的,我的函数操作了大小写和数字的序列,上升下降的都有,左右区间相同的我并没有给以处理。
void expand(char s1[], char s2[]) { int i, j, k, mov; for (i = j = 0; s1[i]; i++) if (s1[i] == '-' && i > 0 && s1[i - 1] != s1[i + 1] && ((s1[i - 1] >= '0' && s1[i - 1]<= '9' && s1[i + 1] >= '0' && s1[i + 1] <= '9') || (s1[i - 1] >= 'a' && s1[i - 1]<= 'z' && s1[i + 1] >= 'a' && s1[i + 1] <= 'z') || (s1[i - 1] >= 'A' && s1[i - 1]<= 'Z' && s1[i + 1] >= 'A' && s1[i + 1] <= 'Z'))) { if (s1[i + 1] > s1[i - 1]) mov = 1; else mov = -1; for (k = s1[i - 1] + mov; k != s1[i + 1]; k += mov) s2[j++] = k; } else s2[j++]=s1[i]; s2[j] = 0; }
Exercise 3-4. In a two's complement number representation, our version of itoa does not handle the largest negative number, that is, the value of n equal to . Explain why not. Modify it to print that value correctly, regardless of the machine on which it runs.
解:这里说当n在补码存储的条件下取值负最大的时候原函数无法表示,关键问题在于如果是负最大的话那么其补码表示为除了符号为是1其他都是0,而其相反数是无法用补码表示的(少了1),但是不管如何,我们只要稍微改改就可以让原来的程序继续使用,关键是如何对付负最大的情况,考虑到其实不管正负,如果我们保留原数进行计算,取余的时候加上绝对值那么就可以正常计算了,这就是主要的思想,我的程序也是如此的。
void itoa(int n, char s[]) { int i, sign = n; i = 0; do { s[i++] = abs(n % 10) + '0'; } while (n /= 10); if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); }
Exercise 3-5. Write the function itob(n,s,b) that converts the integer n into a base b character representation in the string s. In particular, itob(n,s,16) formats s as a hexadecimal integer in s
解:题目改来改去实际上没怎么变化,这里要我们重新写一个函数,它的作用是在原来的基础上加入进制的因素,也就是变成b进制表示,但是不管如何,只是我们在表示的时候对于大于10的数值进行A开始的替换,其他的只要将原来程序中具体数字10换成b就是我们要的程序了,非常之简单(PS:我只弄到62进制,再高我想不到可以用来表示的东西了)。
void itoa(int n, char s[], int b) { int i, t, sign = n; i = 0; do { t = abs(n % b); if (t <= 9) s[i++] = t + '0'; else if (t <= 35) s[i++] = t - 10 + 'A'; else s[i++] = t - 36 + 'a'; } while (n /= b); if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); }
Exercise 3-6. Write a version of itoa that accepts three arguments instead of two. The third argument is a minimum field width; the converted number must be padded with blanks on the left if necessary to make it wide enough.
解:题目难度不如上一个,要求只有限制宽度,就是不够的时候用空格补齐就可以了,这个很容易办到,只要在3-4的基础上往后添加空格就行,代码改动不大。
void itoa(int n, char s[], int w) { int i, t, sign = n; i = 0; do { s[i++] = abs(n % 10) + '0'; } while (n /= 10); if (sign < 0) s[i++] = '-'; for (; i < w; i++) s[i] = ' '; s[i] = '\0'; reverse(s); }
小结:这一章题目不多,单纯认识知识的话内容相对简单,有些细节还是需要注意的。
1.关于switch最后的default,其break最好还是加上,特别是当你增减新功能的时候防止由于遗漏break而增加这个特殊的bug
2.正如原文所说do-while语句中do后的花括号最好还是加上,不然鬼知道你以后会不会再加东西进去
3.对于goto语句我不发表任何意见,用就用,用好就行