这一章讲控制流,基本就是if-else、while、do-while、switch-case之类的。基本的东西大家都懂。需要强调的是在默认状态下,if-else语句中的else是与之前最近的if匹配的,所以如果这不是你的本意,那么最好加上大括号括起来。其实我几乎不会犯这个错误,因为我写程序时,习惯于即便if后面只有一条语句,我也会打上括号(这个习惯好像是看C++primer上养成的)。
但是在习题中,告诉你这个程序是不能转化最小的负数的:即-2^(n-1),这是因为通过程序,可以看出,这段代码先提取正负号,然后把剩下的数字转化为对应的字符。但是当遇到最小的负数时,因为n位能表达的最大的数字是2^(n-1)-1(有一位用作符号位了),所以不能简单地采用对于负数,先求出他的相反数,然后利用这个相反数化成字符串,最后在前面补上负号来实现。对于这种特殊的情况,可以采用对于每次求模运算时计算绝对值,最后补上负号来绕过这个问题:
这个例子也提醒我们,写程序时,有时候还是需要注意体层和一些边界值情况,否则有可能出错的。
书中给出了一个shell排序算法的程序
void shellsort(int V[], int n)
{
int gap, i, j, temp;
for (gap = n/2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j = i-gap; j>=0 && V[j]>V[j+gap]; j -= gap) {
temp = V[j];
V[j] = V[j+gap];
V[j+gap] = temp;
}
}
恕我愚钝,我至今还没有完全弄明白它是怎么搞的。一般人的shell排序算法写不了这么简洁。
这个程序也让我感想颇多,大牛有时候是不一定会考虑小白们的感受的,就好比有时候数学书上的一句“显然……”,就可能难倒一大片学生。不光是中国人喜欢简洁、漂亮的东西(比如同济的《高等数学》,其实那本书写的很不错),国外的人也一样。但是国外的入门书籍一般都会煞费苦心的萝莉啰嗦的讲很多东西,国内的很多书可没有这个耐心。
书里面有个习题很有意思,就是将数字转化为对应的字符串:void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) /* record sign */
n = -n; /* make n positive */
i = 0;
do { /* generate digits in reverse order */
s[i++] = n % 10 + '0'; /* get next digit */
} while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
但是在习题中,告诉你这个程序是不能转化最小的负数的:即-2^(n-1),这是因为通过程序,可以看出,这段代码先提取正负号,然后把剩下的数字转化为对应的字符。但是当遇到最小的负数时,因为n位能表达的最大的数字是2^(n-1)-1(有一位用作符号位了),所以不能简单地采用对于负数,先求出他的相反数,然后利用这个相反数化成字符串,最后在前面补上负号来实现。对于这种特殊的情况,可以采用对于每次求模运算时计算绝对值,最后补上负号来绕过这个问题:
#define abs(x) ((x)<0 ? -(x):(x))
void reverse(char s[])
{
int c, i , j;
for(i = 0, j = strlen(s)-1;i < j;i++,j--)
{
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
void itoa(int n, char s[])
{
int i ,sign;
sign = n;
i = 0;
do
{
s[i++] = abs(n % 10) + '0';
}while((n /= 10) != 0);
if(sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
这个例子也提醒我们,写程序时,有时候还是需要注意体层和一些边界值情况,否则有可能出错的。