3-1:
在上面有关折半查找的例子中,while循环语句内共执行了两次测试,其实只要一次就足够(代价是将更多的测试放在循环外执行)。重写该函数,使得在循环内部只执行一次测试。比较两种版本函数的运行时间
#include <stdio.h>
#define SIZE 100
int binsearch(int x, int v[], int n);
int main()
{
int search;
int result;
int number[SIZE];
for(int i = 0; i < SIZE; i++)
number[i] = i;
printf("请输入待查找数字\n");
while (scanf("%d", &search) == 1)
{
result = binsearch(search, number, SIZE);
if(result == -1)
printf("未查找到该数字\n");
else
printf("查找到数字%d\n", result);
printf("请继续输入,输入字母结束程序\n");
}
return 0;
}
int binsearch(int x, int v[], int n)
{
int mid, low, high;
low = 0;
high = n - 1;
mid = (low + high) / 2;
while(low <= high && x != v[mid])
{
if(x > v[mid])
low = mid + 1;
else
high = mid - 1;
mid = (low + high) / 2;
}
return (x == v[mid])? mid : -1;
}
3-2:
编写一个函数escape(s, t),将字符串t复制到字符串s中,并在复制过程中将换行符、制表符等不可见字符分别替换为\n、\t等相应的可见的转义字符序列。要求使用switch语句。再编写一个具有相反功能的函数,在复制过程中将转义字符序列替换为实际字符
#include <stdio.h>
#define MAXSIZE 1000
void escape(char s[], char t[]);
void unescape(char s[], char t[]);
int main(void)
{
char string1[MAXSIZE];
char string2[MAXSIZE];
char origin[MAXSIZE] = "good good study \a, day \b day \f up!\n good \r\t student\v \\ \' \" \? ";
printf("%s\n", origin);
escape(string1, origin);
printf("%s\n", string1);
unescape(string2, string1);
printf("%s\n", string2);
return 0;
}
void escape(char s[], char t[])
{
int j = 0;
for(int i = 0; t[i] != '\0'; i++)
{
switch(t[i])
{
case '\a': //响铃
s[j] = '\\';
s[++j] = 'a';
break;
case '\b': //退格
s[j] = '\\';
s[++j] = 'b';
break;
case '\f': //换页
s[j] = '\\';
s[++j] = 'f';
break;
case '\n': //换行
s[j] = '\\';
s[++j] = 'n';
break;
case '\r': //回车
s[j] = '\\';
s[++j] = 'r';
break;
case '\t': //水平制表符
s[j] = '\\';
s[++j] = 't';
break;
case '\v': //垂直制表符
s[j] = '\\';
s[++j] = 'v';
break;
case '\\': //反斜杠
s[j] = '\\';
s[++j] = '\\';
break;
case '\'': //单引号
s[j] = '\\';
s[++j] = '\'';
break;
case '\"': //双引号
s[j] = '\\';
s[++j] = '\"';
break;
case '\?': //问号
s[j] = '\\';
s[++j] = '?';
break;
default:
s[j] = t[i];
}
j++;
}
s[j] = t[i];
}
void unescape(char s[], char t[])
{
int j = 0;
for(int i = 0; t[i] != '\0'; i++)
{
switch(t[i])
{
case '\\':
switch(t[++i])
{
case 'a':
s[j] = '\a';
break;
case 'b':
s[j] = '\b';
break;
case 'f':
s[j] = '\f';
break;
case 'n':
s[j] = '\n';
break;
case 'r':
s[j] = '\r';
break;
case 't':
s[j] = '\t';
break;
case 'v':
s[j] = '\v';
break;
case '\\':
s[j] = '\\';
break;
case '\'':
s[j] = '\'';
break;
case '\"':
s[j] = '\"';
break;
case '?':
s[j] = '\?';
break;
default:
s[j] = '\\';
s[++j] = t[i];
break;
}
break;
default:
s[j] = t[i];
}
j++;
}
s[j] = t[i];
}
3-3:
编写函数expand(s1, s2),将字符串s1中类似a-z一类的速记符号在字符串s2中扩展为等价的完整列表abc…xyz。该函数可以处理大小写字母和数字,并可以处理a-b-c、a-z0-9与-a-z等类似的情况。作为前导和尾随的-字符原样打印
#include <stdio.h>
#define MAXSIZE 1000
void expand(char s1[], char s2[]);
int main(void)
{
char string[MAXSIZE];
char origin[MAXSIZE] = "printf a-z \nA-Z \n0-9 \nc-p \nF-K \n4-8 \na-b-c-f \n-b-f-k-, -c-f- \n";
printf("%s\n", origin);
expand(origin, string);
printf("%s\n", string);
return 0;
}
void expand(char s1[], char s2[])
{
int i, j;
int start, end;
j = 0;
for(i = 0; s1[i] != '\0'; i++)
{
if(s1[i] == '-')//不知道需不需要处理类似a-G这种情况,如果需要,改动一下条件即可
{
if((s1[i - 1] >= 'a' && s1[i - 1] <= 'z') && (s1[i + 1]) >= 'a' && s1[i + 1] <= 'z')
{
for(start = s1[i - 1] + 1, end = s1[i + 1]; start < end; start++, j++)
s2[j] = start;
}
else if((s1[i - 1] >= 'A' && s1[i - 1] <= 'Z') && (s1[i + 1]) >= 'A' && s1[i + 1] <= 'Z')
{
for(start = s1[i - 1] + 1, end = s1[i + 1]; start < end; start++, j++)
s2[j] = start;
}
else if((s1[i - 1] >= '0' && s1[i - 1] <= '9') && (s1[i + 1]) >= '0' && s1[i + 1] <= '9')
{
for(start = s1[i - 1] + 1, end = s1[i + 1]; start < end; start++, j++)
s2[j] = start;
}
else
{
s2[j] = s1[i];
j++;
}
}
else
{
s2[j] = s1[i];
j++;
}
}
s2[j] = s1[i];
}
3-4:
在数的对二的补码表示中,我们编写的itoa函数不能处理最大的负数,即n等于-2^(字长 - 1)的情况。请解释其原因。修改该函数,是它可以在任何机器上运行时都能打印出正确的值
/* 首先解释一下原因,在计算机中,表示一个正数的负数形式的方法是将该正数的
进制形式取反再加一,例如8位正数9,其二进制形式为0000 1001,取反得1111 0110
,再加一,得1111 0111,这就是8位的-9在计算机中的表示。
那么负数转换为正数也很简单,就是将上面的步骤颠倒过来,将8位的-9,即
1111 0111先减一,得1111 0110,再取反得0000 1001,就是8位的+9
假如一个计算机系统中int类型占用位数为8位,那么int类型的取值范围为
2^(8-1) ~ 2^(8-1) - 1,即-128 ~ 127,-128表示为1000 0000,127表示为
0111 1111,如果我们采用上面的方法求-128的绝对值+128,先减一,得0111 1111
,再取反得1000 0000,结果仍然是自身
那么do-while循环只会执行一次,然后条件判断((n /= 10) > 0)为假,终止循
环,从而无法打印正确的值
对于这种情况,我们有三种方法解决:
第一种方法是利用stdlib.h库中的abs函数,即绝对值函数,更改语句s[i++] =
(n % 10) + '0';为s[i++] = abs(n % 10) + '0';然后将循环判断条件更改为
while(n /= 10)
第二种方法是采用unsigned int类型,unsigned int类型的取值范围是 0 ~
2^(字长),当n为最大负数时,将n赋值给unsigned int类型变量,这样就能打印最大
负数
第三种方法是当n为负数时,将-(n + 1)赋值为n,然后在循环中将n还原,从而
可以打印最大负数,但是当输入的是-1000,这样的数时,返回的结果就会错误
*/
#include <stdio.h>
#define MAXSIZE 1000
void itoa(int n, char s[]);
void reverse(char s[]);
int main(void)
{
int number = -2147483648;
char string[MAXSIZE];
itoa(number, string);
printf("%s\n", string);
return 0;
}
void itoa(int n, char s[])
{
int i;
unsigned int u;
u = (n < 0) ? -n : n;
i = 0;
do{
s[i++] = (u % 10) + '0';
}while((u /= 10) > 0);
if(n < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
void reverse(char s[])
{
int temp;
for(int i = 0; s[i] != '\0'; i++)
;
i--;
for(int j = 0; i > j; j++, i--)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
3-5:
编写函数itob(n, s, b),将整数n转换为以b为底的数,并将转换结果以字符形式保存到字符串s中。例如itob(n, s, 16)把整数n格式化成十六进制整数保存到s中
#include <stdio.h>
#define MAXSIZE 1000
void itob(int n, char s[], int b);
void reverse(char s[]);
int main(void)
{
int number;
char string[MAXSIZE];
int base;
printf("请输入一个整数:");
while(scanf("%d", &number) == 1)
{
printf("请输入一个底数:");
while(scanf("%d", &base) == 1)
{
if(base <= 1 || base >= 30)
{
printf("请输入一个大于1小于30的底数\n");
continue;
}
else
itob(number, string, base);
printf("%d的%d进制表示形式为%s\n", number, base, string);
break;
}
printf("继续输入一个整数\n");
}
return 0;
}
void itob(int n, char s[], int b)
{
int i, sign;
if((sign = n) < 0)
n = -n;
i = 0;
do
{
if(n % b > 9)
s[i++] = (n % b) - 10 + 'A';
else
s[i++] = (n % b) + '0';
}while((n /= b) > 0);
if(sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
void reverse(char s[])
{
int temp;
for(int i = 0; s[i] != '\0'; i++)
;
i--;
for(int j = 0; i > j; j++, i--)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}
3-6:
修改itoa函数,使得该函数可以接受三个参数。其中第三个参数为最小字段宽度。为了保证转换后所得结果至少具有第三个参数指定的最小宽度,在必要时应在所得结果的左边填充一定的空格`
#include <stdio.h>
#define MAXSIZE 1000
void itoa(int n, char s[], int x);
void reverse(char s[]);
int main(void)
{
int number;
char string[MAXSIZE];
int space;
printf("请输入一个整数:");
while(scanf("%d", &number) == 1)
{
printf("请输入最小字段宽度:");
while(scanf("%d", &space) == 1)
{
itoa(number, string, space);
printf("%d的字符表示形式为%s\n", number, string);
break;
}
printf("继续输入一个整数\n");
}
return 0;
}
void itoa(int n, char s[], int x)
{
int i, sign;
if((sign = n) < 0)
n = -(n + 1);
i = 0;
do{
if(i == 0 && sign < 0)
s[i++] = (n % 10) + '0' + 1;
else
s[i++] = (n % 10) + '0';
}while((n /= 10) > 0);
if(sign < 0)
s[i++] = '-';
while(i < x)
s[i++] = ' ';
s[i] = '\0';
reverse(s);
}
void reverse(char s[])
{
int temp;
for(int i = 0; s[i] != '\0'; i++)
;
i--;
for(int j = 0; i > j; j++, i--)
{
temp = s[i];
s[i] = s[j];
s[j] = temp;
}
}