C 程序设计语言——第三章练习题
- 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 run-time.
- 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.
- 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.
- 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 -(2wordsize-1). Explain why not. Modify it to print that value correctly, regardless of the machine on which it runs.
- 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.
- 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.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 run-time.
分析:一个二分查找值的函数,本书中的程序如下:
int binsearch(int x, int v[], int n)
{
int low, high, mid;
low = 0;
high = n - 1;
while(low <= high)
{
mid = (low + high) / 2;
if (x < v[mid])
high = mid - 1;
else if (x > v[mid])
low = mid + 1;
else
return mid;
}
return -1;
}
题目需要更改函数,循环内部只有一个测试条件,并比较两种方法的运行时间。
#include<stdio.h>
#include<time.h>
#include<string.h>
#define LEN 100
int *i_gets(int *v, int n);
int Print(int *v);
int input(void);
int binsearch1(int x, int *v, int n);
int binsearch2(int x, int *v, int n);
int main(void)
{
int v[LEN];
int x;
int size;
clock_t start, finish;
double total;
int find;
printf("Please enter integers (no more than %d), nonumeric to stop "
"input:\n",LEN);
i_gets(v, LEN);
printf("\nThe input numbers:\n");
size = Print(v);
printf("Please enter the number you want to search: ");
x = input();
printf("Binary search with two methods:\n");
start = clock();
find = binsearch1(x, v, size);
finish = clock();
total = (double) (finish - start) / CLOCKS_PER_SEC;
printf("Method 1 takes %f seconds\n",total);
start = clock();
find = binsearch2(x, v, size);
finish = clock();
total = (double) (finish - start) / CLOCKS_PER_SEC;
printf("Method 2 takes %f seconds\n",total);
if(find != -1)
printf("Find %d.\n",x);
else
printf("Can't find %d.\n",x);
return 0;
}
int *i_gets(int *v, int len)
{
int i = 0;
int t;
while(i < len)
{
if(scanf("%d", &t) == 1 && getchar() == '\n')
v[i++] = t;
else
break;
}
while(getchar() != '\n')
continue;
return v;
}
int Print(int *v)
{
int i;
for(i = 0; v[i] != '\0'; i++)
printf("%d%c",v[i], (i + 1) % 5 == 0 ? '\n' : ' ');
if(i % 5 != 0)
putchar('\n');
return i;
}
int input(void)
{
int n, status;
int ok = 1;
while(ok)
{
status = scanf("%d",&n);
if (status != 1 || getchar() != '\n')
{
printf("Enter again: ");
while(getchar() != '\n')
continue;
}
else
return n;
}
}
int binsearch1(int x, int *v, int len)
{
int low, high, mid;
low = 0;
high = len - 1;
while (low <= high)
{
mid = (low + high) / 2;
if (x < v[mid])
high = mid - 1;
else if (x > v[mid])
low = mid + 1;
else
return mid;
}
return -1;
}
int binsearch2(int x, int *v, int len)
{
int low, high, mid;
low = 0;
high = len - 1;
mid = (low + high) / 2;
while (low <= high && v[mid] != x)
{
if (x < v[mid])
high = mid - 1;
else
low = mid + 1;
mid = (low + high) / 2;
}
return (v[mid] == x) ? mid : -1;
}
测试结果:
Please enter integers (no more than 100), nonumeric to stop input:
32
14
5
6
2
3
6
5
7
8
9
4
3
f
The input numbers:
32 14 5 6 2
3 6 5 7 8
9 4 3
Please enter the number you want to search: 0
Binary search with two methods:
Method 1 takes 0.000002 seconds
Method 2 takes 0.000001 seconds
Can't find 0.
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.
#include<stdio.h>
#include<string.h>
#define LEN 40
void escape(char *s, char *t);
void unescape(char *s, char *t);
int main(void)
{
char s[LEN], t[LEN], u[LEN];
printf("Please enter a string:\n");
fgets(t, LEN, stdin);
escape(s, t);
printf("\nString displays visible escape sequence:\n");
puts(s);
printf("\nString converts escape sequences into the real characters:\n");
unescape(u, s);
puts(u);
return 0;
}
void escape(char *s, char *t)
{
int i, j;
for(i = 0, j = 0; t[i] != '\0'; i++)
switch(t[i])
{
case '\n': s[j++] = '\\'; s[j++] = 'n'; break;
case '\t': s[j++] = '\\'; s[j++] = 't'; break;
default: s[j++] = t[i];
}
s[j] = '\0';
}
void unescape(char *s, char *t)
{
int i, j;
for(i = 0, j = 0; t[i] != '\0'; i++)
switch(t[i])
{
case '\\':
switch (t[++i])
{
case 'n': s[j++] = '\n'; break;
case 't': s[j++] = '\t'; break;
default: s[j++] = '\\'; s[j++] = t[i]; break;
}
break;
default: s[j++] = t[i]; break;
}
s[j] = '\0';
}
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.
分析:
- 将字符串扩展后的长度可能增加,增加多少不确定,如果一开始分配的两个字符串的长度一样,那么扩展后 s2 的长度可能超过,因此考虑对题中的函数做修改,将扩展后的字符串作为返回值,该字符串不事先定义,而是在函数内识别出需要扩展的未后定义。
- 识别的分两种:字母和数字,数字只识别单个数字,范围 0 ~ 9。
- 注意一个字符前后都有 - 时要区分两种情况考虑:
a-b-c 和 -a-z
注意第一种不能变成abbc,解决方法是填充时只填充中间未显示的值。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
char *s_gets(char *s, int n);
char *expand(char *s1, char *s2);
int check(char c);
#define LEN 100
int main(void)
{
char s1[LEN], s2[LEN];
char *t = s2;
printf("Please enter a string (no more than %d characters:\n",LEN-1);
s_gets(s1, LEN);
printf("Original string: %s\n",s1);
t = expand(s1, s2);
printf("Expanded string: %s\n", t);
return 0;
}
char *s_gets(char *s, int n)
{
char *ret, *find;
ret = fgets(s, n, stdin);
if(ret)
{
find = strchr(s, '\n');
if(find)
*find = '\0';
else
while(getchar() != '\n')
continue;
}
return ret;
}
int check(char c)
{
int n;
if(isdigit(c))
n = 0;
else if(c >= 'a' && c <= 'z')
n = 1;
else if(c >= 'A' && c <= 'Z')
n = 2;
else
n = -1;
return n;
}
char *expand(char *s1, char *s2)
{
char *t = s2;
int i = 0, k = 0;
int c1, c2, j;
if(s1[i] != '\0')
t[k++] = s1[i++];
while(s1[i] != '\0')
{
if(s1[i] == '-' && s1[i+1] != '\0')
{
c1 = check(s1[i-1]);
c2 = check(s1[i+1]);
if(c1 == c2 && c1 != -1 && c2 != -1 && s1[i-1] < s1[i+1])
{
for(j = s1[i-1] + 1; j < s1[i+1]; j++)
t[k++] = j;
t[k++] = s1[i+1];
i += 2;
}
else
t[k++] = s1[i++];
}
else
t[k++] = s1[i++];
}
t[k] = '\0';
return t;
}
结果:
Please enter a string (no more than 99 characters:
s-f-ab-c-d-5-8-2-
Original string: s-f-ab-c-d-5-8-2-
Expanded string: s-f-abcd-5678-2-
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 -(2wordsize-1). Explain why not. Modify it to print that value correctly, regardless of the machine on which it runs.
书中的程序如下:
/* itoa: convert n to characters in s */
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);
}
- 该程序输入最大的负数时,得到的结果是错的,原因如下:
(该部分知识点见:深入理解计算机系统英文版第二版p121)
以本系统 int 为4字节为例,int 的范围为 -2^31 ~ 2^31 - 1,因此,当 n 为 -2^31 时,-n 本应该是 231,但该值超出了范围,将该值减去模(232),得到其值为 -2^31。 - 修改程序:
if((sign = n) < 0)
n = (unsigned) (-n);
改过后,当 n 为 -2^31 时,就变成 2^31,而不超过范围。
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.
#include<stdio.h>
#include<string.h>
#include<limits.h>
#define LEN 34
void itob(int n, char *s, int b);
int input(int min, int max);
char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int main(void)
{
int n, b;
char s[LEN];
printf("Please enter a interger: ");
n = input(INT_MIN, INT_MAX);
printf("Please enter the base ( 2 ~ 36): ");
b = input(2, 36);
itob(n, s, b);
printf("%d base %d is %s.\n", n, b, s);
return 0;
}
int input(int min, int max)
{
int status;
long n;
int ok = 1;
while(ok == 1)
{
status = scanf("%ld",&n);
//判断语句中getcha() != '\n' 必须放最后
if(status != 1 || n < min || n > max || getchar() != '\n' )
{
printf("Enter again (between %d to %d): ",min, max);
while(getchar() != '\n')
continue;
continue;
}
else
return (int)n;
}
}
void swap(char *s1, char *s2)
{
char t;
t = *s1; *s1 = *s2; *s2 = t;
}
void reverse(char *s)
{
int len, i, j;
len = strlen(s);
for(i = 0, j = len-1; i < j; i++, j--)
swap(&s[i], &s[j]);
}
void itob(int n, char *s, int b)
{
int sign, i;
if ((sign = n) < 0)
n = (unsigned) (-n);
i = 0;
do
{
s[i++] = digits[n % b]; //直接将转化的数字与字符对应,无需再转化
} while((n /= b) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
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.
#include<stdio.h>
#include<string.h>
#include<limits.h>
#define LEN 47
void itoa(int n, char *s, int w);
int input(int min, int max);
int main(void)
{
int n, w;
char s[LEN];
printf("Please enter a integet (%d ~ %d): ",INT_MIN, INT_MAX);
n = input(INT_MIN, INT_MAX);
printf("Please enter the minimum width (0 ~ 40): ");
w = input(0, 40);
itoa(n, s, w);
printf("%d corresopnding to %s.\n",n, s);
return 0;
}
int input(int min, int max)
{
int status;
long n;
int ok = 1;
while(ok == 1)
{
status = scanf("%ld",&n);
//判断语句中getcha() != '\n' 必须放最后
if(status != 1 || n < min || n > max || getchar() != '\n' )
{
printf("Enter again (between %d to %d): ",min, max);
while(getchar() != '\n')
continue;
continue;
}
else
return (int)n;
}
}
void swap(char *s1, char *s2)
{
int t;
t = *s1; *s1 = *s2; *s2 = t;
}
void reverse(char *s)
{
int len, i, j;
len = strlen(s);
for (i = 0, j = len-1; i < j; i++, j--)
swap(&s[i], &s[j]);
}
void itoa(int n, char *s, int w)
{
int sign, i;
if ((sign = n) < 0)
n = (unsigned) (-n);
i = 0;
do
{
s[i++] = n % 10 + '0';
} while ((n /= 10) > 0);
if (sign < 0)
s[i++] = '-';
while (i < w)
s[i++] = ' ';
s[i] = '\0';
reverse(s);
}