《C程序设计语言》(《The C Programming Language》)第二版第三章练习题

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;
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值