C程序设计语言(部分参考答案)

观前提醒:该参考答案并不完全,只是我写到了哪些习题,就将答案记录了下来,我保证这些代码在我的电脑上是可以跑通的,但是这些答案有时并不是最优解。

第二章

P37 T 2-3

#include<stdio.h>

int htoi(const char arrh[]);

int main()
{
	char arrh[100];
	char c;
	a: printf("请输入一个16进制的数字(支持0x前缀):");
	/*for (int i = 0; i < 99 && (c = getchar()) != '\n'; i++)
		arrh[i] = c;*/
	scanf("%s", arrh);
	long index = htoi(arrh);
	if (index == -1)
	{
		printf("非法数字,请重新输入\n");
		goto a;
	}
	printf("十进制为:%ld\n", index);
	return 0;
}

int htoi(const char arrh[])
{
	int i = 0;
	int result = 0;
	// 判断字符数组的前两位是否是0x或0X
	if (arrh[i] == '0' && (arrh[i + 1] == 'x' || arrh[i + 1] == 'X'))
		i = 2;
	// 依次遍历转换数组元素
	while (arrh[i] != '\0')
	{
		int val;
		char c = arrh[i];
		// 有两种情况
		// 如果是数字
		if (c >= '0' && c <= '9')
			val = c - '0';
		// 如果是字母(分大小写)
		else if (c >= 'A' && c <= 'F')
			val = 10 + (c - 'A');
		else if (c >= 'a' && c <= 'f')
			val = 10 + (c - 'a');
		else
			return -1;
		// 关键点 把每次遍历转换后的值认为是最后一位,如果后面又遍历到了数字就把前面得到的值(总和)前进一位(值 * 16)
		result = result * 16 + val;
		i++;
	}
	return result;
}

P38 T 2-4

#include<stdio.h>

void squeeze(char s1[], char s2[]);

int main()
{
	char s1[100];
	char s2[100];
	printf("请输入s1字符串:");
	scanf("%s", s1);
	printf("请输入s2字符串:");
	scanf("%s", s2);
	squeeze(s1, s2);
	printf("修改后的s1字符串为:%s\n", s1);
	return 0;
}

void squeeze(char s1[], char s2[])
{
	int i, j, k;
	// 遍历s2数组
	for (i = 0; s2[i] != '\0'; i++)
	{	
		// 遍历s1数组,检查是否有与s2中元素匹配的情况
		for (j = 0, k = 0; s1[j] != '\0'; j++)
		{
			// 如果没有,就把该s1[j]放在s1[k]处
			if (s1[j] != s2[i])
				s1[k++] = s1[j];
		}
		s1[k] = '\0';
	}
}
  • Bug:
    • 输入空格将导致程序出错
    • 处理汉字有时会出现奇奇怪怪的问题

P38 T 2-5

#include<stdio.h>

int any(char s1[], char s2[], char position[]);

int main()
{
	char s1[100];
	char s2[100];
	char result[100];
	printf("输入s1:");
	scanf("%s", s1);
	// printf("输入的s1:%s\n", s1); // 测试节点
	printf("输入s2:");
	scanf("%s", s2);
	int index = any(s1, s2, result);
	printf("index的值:%d\n", index); // 测试节点
	if (index == -1)
	{
		printf("s2中字符均没有出现在s1中,代码:%d\n", index);
	}
	else
	{
		// printf("我执行了吗?\n"); // 测试节点
		for (int i = 0; i < index; i++) // 这里需要注意,result是int数组,不能用'\0'判断结束,需要记录实际存储的位置数量
		{
			printf("位置信息为:%d\n", result[i]);
		}
	}
	// printf("位置信息0为:%d\n", result[1]); // 测试
	return 0;
}

int any(char s1[], char s2[], char position[])
{
	// 1.对s2进行去重并按照原数组字母出现顺序排序
	int news2_len = 0;
	char news2[100]; // 存储去重后的数组
	for (int i = 0; s2[i] != '\0'; i++)
	{
		// 定义一个布尔量
		int is_exit = 0; // 0表示新数组中无原数组元素
		for (int j = 0; j < news2_len; j++)
		{
			if (news2[j] == s2[i])
			{
				// 修改布尔值
				is_exit = 1; // 1表示新数组中有原数组元素
			}
		}
		if (is_exit == 0) // 新数组中无原数组元素,存储该元素,并将新数组索引前进一位用于存储新元素
		{
			news2[news2_len++] = s2[i];
			// printf("news2_len现在为:%d\n", news2_len); // 测试节点
		}
	}
	news2[news2_len] = '\0';
	// printf("去重后的s2数组:%s\n", news2); // 测试节点
	// 2.检查s2元素在s1数组中第一次出现的位置
	int pos = 0;
	// 定义一个布尔值
	int is_com = 0; // 0表示s2中所有元素均未在s1数组中出现
	// 遍历去重后的s2数组
	for (int i = 0; news2[i] != '\0'; i++)
	{
		// 遍历s1数组
		for (int j = 0; s1[j] != '\0'; j++)
		{
			// 判断s2中数组是否出现在s1数组中
			if (s1[j] == news2[i])
			{
				// 修改布尔值
				is_com = 1; // 1表示s2中有元素出现在s1数组中
				// 如果出现就记录位置
				position[pos++] = j;
				// printf("现在的位置信息为:%d\n", position[pos - 1]); // 测试
				// (我们只要第一次出现的位置信息)该循环任务完成,结束内循环
				break;
			}
		}
		position[pos] = '\0';
	}
	if (is_com == 0)
		return -1;
	return pos;
}

P40 T 2-6

#include<stdio.h>

unsigned int setbits(unsigned int x, int p, int n, unsigned int y);

int main()
{
	unsigned int x, y;
	int p, n;
	x = 107, y = 204;
	p = 5, n = 3;
	unsigned int rs = setbits(x, p, n, y);
	printf("结果:%u (二进制:", rs);
	// 辅助打印二进制(便于验证)
	for (int i = 7; i >= 0; i--) {
		printf("%d", (rs >> i) & 1);
	}
	printf(")\n");
	return 0;
}

// 将x中从p位开始的n个(二进制)位设置为y中最右侧n位的值,x的其余各位保持不变
unsigned int setbits(unsigned int x, int p , int n, unsigned int y)
{
	// 1.清空x中从p位开始的n个位
	// 定义一个n位1的掩码
	unsigned int mask_n_1 = (1 << n) - 1;
	// 把n位1移动到x要清除的位上
	unsigned int mask_clear = mask_n_1 << (p - n + 1);
	// 清除:对0与运算
	// 保留:对1与运算
	unsigned int x_rs = x & ~mask_clear;
	// 2.获取y中最右侧n位的值
	// 保留:对1与运算
	unsigned int y_rs1 = y & mask_n_1;
	// 将取到的值移动到x清除的位上
	unsigned int y_rs2 = y_rs1 << (p - n + 1);
	// 3.合并x与y,并返回修改后的x
	// 合并:常用对0或运算
	return x_rs | y_rs2;
}

P40 T 2-7

#include<stdio.h>

unsigned int invert(unsigned int x, int p, int n);

int main()
{
	int n, p;
	n = 3;
	p = 5;
	unsigned int x = 240;
	unsigned int ob_x = invert(x, p, n);
	printf("指定位取反后的结果为:%u\n", ob_x);
	for (int i = 7; i >= 0; i--)
	{
		printf("%d", (x >> i) & 1);
		if (i % 4 == 0)
			printf(" ");
	}
	printf("\n");
	for (int i = 7; i >= 0; i--)
	{
		printf("%d", (ob_x >> i) & 1); 
		if (i % 4 == 0)
			printf(" ");
	}
	
	return 0;
}

// x中从第p位开始的n个(二进制)位求反,x的其余各位保持不变
unsigned int invert(unsigned int x, int p, int n)
{
	// 定义一个n位1的掩码
	unsigned int mask_n_1 = (1 << n) - 1;
	// 将掩码移位到x的p位
	unsigned int mask_shift = mask_n_1 << (p - n + 1);
	// 取反:与1按位异或运算取反,与0按位异或运算保留
	unsigned int ob_x = x ^ mask_shift;
	return ob_x;
}

P40 T 2-8

#include<stdio.h>

unsigned int rightrot(unsigned int x, int n);

int main()
{
	int n = 3;
	unsigned int x = 170;
	printf("%u二进制表示为:", x);
	for (int i = 7; i >= 0; i--)
	{
		printf("%d", (x >> i) & 1);
		if (i % 4 == 0) printf(" ");
	}
	printf("\n");
	unsigned int index = rightrot(x, n);
	printf("修改后的数字为:%u(二进制表示为:", index);
	for (int i = 7; i >= 0; i--)
	{
		printf("%d", (index >> i) & 1);
		if (i % 4 == 0) printf(" ");
	}
	printf(")\n");
	return 0;
}

unsigned int rightrot(unsigned int x, int n)
{
	// 1.得到x的最高位的索引
	int index = 0;
	unsigned int y = x;
	// 对x一直右移,直到x<1,说明x最高位移动到最低位
	if (x == 0) return 0;
	while (y > 1)
	{
		y >>= 1;
		index++;
	}
	// 如果循环次数大于有效位数的话,我们领循环次数等于有效次数,反应的结果是return原数
	if (n > index + 1) n = index + 1;
	// 2.得到x的最右侧n位数
	// 设置n位1掩码
	unsigned int mask_n_1 = (1 << n) - 1;
	// 与1与运算 得到x最右侧n位数 
	unsigned int right_n_x = x & mask_n_1;
	// 3.将x最右侧n位数置0,并右移n位
	unsigned int remove_right_x = (x & ~mask_n_1) >> n;
	// 4.将right_n_x左移 index-n+1 位,并与remove_right_x合并
	unsigned int result_x = (right_n_x << (index - n + 1)) | remove_right_x;
	return result_x;
}

P40 T 2-9

#include<stdio.h>

int main()
{
	int n = 64;
	printf("该数二进制表示为:");
	for (int i = 7; i >= 0; i--)
	{
		printf("%d", (n >> i) & 1);
		if (i % 4 == 0) printf(" ");
	}
	printf("\n");
	int count = bitcount(n);
	printf("该数的二进制中有几个1:%d\n", count);
	int rs = chest(n);
	printf("该数是否是2的幂:%d\n", rs);
	return 0;
}

// 统计一个数转换成二进制后,位值为1的个数
int bitcount(int x)
{
	int count = 0;
	while (x != 0)
	{
		x &= (x - 1);
		count++;
	}
	return count;
}

// 检查x是否是2的幂(1T 0F)
int chest(int x)
{
	if ((x &= (x - 1)) == 0) return 1;
	else return 0;
}

P42 T 2-10

#include<stdio.h>

int lower(char c);

int main()
{
	char c = lower('y');
	printf("%c\n", c);
	return 0;
}

// 大写字母转换成小写字母(如果传入的是小写字母,则return原字符)
int lower(char ch)
{
	return ch >= 'A' && ch <= 'Z' ? ch += 32 : ch;
}

第三章

P57 T 3-1

#include<stdio.h>

int main()
{
	int x = 5;
	int v[4 * 10] = { 0,1,2,3,4,5,6,7,8,9 };
	int index = binsearch(x, v, 10);
	if (index == -1)
		printf(" x 不在数组中!\n");
	else
		printf(" x 在数组的 %d 位置\n", index);
	return 0;
}

// 二分查找,找到数组v中x元素的所在位置,并返回,数组v必须是升序排列
int binsearch(int x, int v[], int len)
{
    // 1. 定义变量:low=查找范围左边界,high=右边界,mid=中间位置
    int low, high, mid;
    // 2. 初始化边界:low从数组第一个元素(索引0)开始,high到最后一个元素(索引len-1)
    low = 0;
    high = len - 1;

    // 3. 循环缩范围:只要左边界 < 右边界,就继续折半(直到low==high)
    while (low < high)
    {
        // 4. 计算中间位置:整数除法向下取整(比如(0+4)/2=2,(3+4)/2=3)
        mid = (low + high) / 2;
        // 5. 核心判断:利用数组升序特性缩范围
        if (x <= v[mid])
            // x <= 中间值 → x在左半区(包括mid),把右边界拉到mid
            high = mid;
        else
            // x > 中间值 → x在右半区(mid右侧),把左边界推到mid+1(mid已排除)
            low = mid + 1;
    }
    // 6. 循环结束后:low==high,验证该位置是否是目标值
    return (v[low] == x) ? low : -1;
}

P59 T 3-2

#include<stdio.h>

char* escape(char s[], char t[]);
char* unescape(char s[], char t[]);

int main()
{
	char s[1000], t[1000];
	int i, c;
	for (i = 0; i < 999; i++)
	{
		if ((c = getchar()) == EOF)
			break;
		t[i] = c;
	}
	t[i] = '\0';
	escape(s, t);
	printf("转换后的字符串为:\n");
	for (int j = 0; j < 999 && s[j] != '\0'; j++)
		printf("%c", s[j]);
	printf("\n");
	unescape(s, t);
	printf("再次还原后的字符串为:\n");
	for (int k = 0; k < 999 && s[k] != '\0'; k++)
		printf("%c", s[k]);
	printf("\n");
	return 0;
}

// 将字符串t复制到字符串s中,并在复制过程中将换行符、制表符等不可见字符分别转换为\n\t等相应的可见的转义字符序列,用switch结构
char* escape(char s[], char t[])
{
	int j = 0;
	for (int i = 0; t[i] != '\0'; i++)
	{
		int c;
		c = t[i];
		switch (c){
		case '\n': // 换行符→转义为\n
			s[j++] = '\\'; // 先存反斜杠
			s[j++] = 'n';  // 再存n
			break;
		case '\t': // 制表符→转义为\t
			s[j++] = '\\';
			s[j++] = 't';
			break;
		case ' ':  // 空格→替换为~(按你的需求)
			s[j++] = '\\';
			s[j++] = '~';
			break;
		default:   // 普通字符直接复制
			s[j++] = c;
			break;
		}
	}
	s[j] = '\0';
	return s;
}

// 还原
char* unescape(char s[], char t[])
{
	int i, j = 0;
	for (i = 0; t[i] != '\0'; i++)
	{
		int c;
		c = t[i];
		if (c == '\\')
		{
			c = t[i++];
			switch (c)
			{
			case 'n':
				s[j++] = '\n';
				break;
			case 't':
				s[j++] = '\t';
				break;
			case '~':
				s[j++] = ' ';
				break;
			default:
				s[j++] = '\\';
				break;
			}
		}
		else
			s[j++] = c;
	}
	s[j] = '\0';
	return s;
}

P61 3.5

#include<stdio.h>

void shellsort(int v[], int len);

int main()
{
	int v[11 * 4] = { 24,26,17,7,345,88,2,6,89,2,6 };
	shellsort(v, 11);
	for (int i = 0; i < 11; i++)
	{
		printf("%d ", v[i]);
	}
	return 0;
}

// 希尔排序
void shellsort(int v[], int len)
{
	int gap, i, j, temp;
	for ( gap = len / 2; gap >= 1 ; gap /= 2)
	{
		for ( i = gap; i < len; 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;
			}
		}
	}
}

P64 T 3-4

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<string.h>

void itoa(int n, char a[]);

int main()
{
	int n;
	char a[1000];
	scanf("%d", &n);
	itoa(n, a);
	printf("%s", a);
	return 0;
}

// 改进版itoa函数
void itoa(int n, char a[])
{
	int sign = 0;
	unsigned int un;
	if (n < 0)
	{
		sign = 1;
		un = (unsigned)(-n);
	}
	else
		un = (unsigned)n;
	int i = 0;
	// 将数字转换成字符(倒序)
	do
	{
		a[i++] = un % 10 + '0';
	} while ((un /= 10) > 0);
	if (sign)
		a[i++] = '-';
	a[i] = '\0';
	// 反转字符
	int len = strlen(a);
	for (int j = 0; j < len / 2 ; j++)
	{
		unsigned int temp = a[j];
		a[j] = a[len - 1 - j];
		a[len - 1 - j] = temp;
	}
}

P64 T 3-5

#include<stdio.h>
#include<string.h>

void itob(int n, char s[], int b);
void reverse(char s[]);

int main()
{
	int n = 703710;
	char s[1000];
	int b = 16;
	itob(n, s, b);
	printf("%s", s);
	return 0;
}

// 反转字符
void reverse(char s[])
{
	int len = strlen(s);
	for (int i = 0; i < len / 2; i++)
	{
		char c = s[i];
		s[i] = s[len - 1 - i];
		s[len - 1 - i] = c;
	}
}

// 将n转换为以b为底的字符串存储在s中
void itob(int n, char s[], int b)
{
	int sign = 0;
	unsigned int num;
	if (n < 0)
	{
		sign = 1;
		num = (unsigned)(-n);
	}
	else 
		num = (unsigned)n;
	int i = 0;
	// 特殊情况
	if (num == 0)
	{
		s[i++] = '0';
		s[i] = '\0';
		return 0;
	}
	// 转进制
	while (num != 0)
	{
		int re = num % b;
		if (re < 10)
			s[i++] = re + '0';
		else
			s[i++] = 'A' + (re - 10);
		num /= b;
	}
	if (sign)
		s[i++] = '-';
	s[i] = '\0';
	reverse(s);
}

P64 T 3-6

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<string.h>

void itoa(int n, char a[], int width);

int main()
{
	int n;
	char a[1000];
	scanf("%d", &n);
	itoa(n, a, 100);
	printf("%s", a);
	return 0;
}

// 改进版itoa函数
void itoa(int n, char a[], int width)
{
	int sign = 0;
	unsigned int un;
	if (n < 0)
	{
		sign = 1;
		un = (unsigned)(-n);
	}
	else
		un = (unsigned)n;
	int i = 0;
	// 将数字转换成字符(倒序)
	do
	{
		a[i++] = un % 10 + '0';
	} while ((un /= 10) > 0);
	if (sign)
		a[i++] = '-';
	a[i] = '\0';
	// 反转字符
	int len = strlen(a);
	for (int j = 0; j < len / 2; j++)
	{
		char temp = a[j];
		a[j] = a[len - 1 - j];
		a[len - 1 - j] = temp;
	}
	// 判断是否要添加空格
	if (len < width)
	{
		// 先将元素移动到最右端
		for (int k = len; k >= 0; k--)
			a[k + (width - len)] = a[k];
		// 左边添加空格
		for (int l = 0; l < width - len; l++)
			a[l] = ' ';
		// 避免极端情况(width == len)
		a[width] = '\0';
	}
}

第四章

P70 T 4-1

#include<stdio.h>
#define MAXLINE 1000

int getline(char s[]);
int strrindex(char s[], char t[]);
int streindex(char s[], char t[]);

char pattern[] = "ould";

int main()
{
	char s[MAXLINE];
	int rindex = 0;
	int eindex = 0;
	if (getline(s) > 0)
		rindex = strrindex(s, pattern);
		eindex = streindex(s, pattern);
	if (rindex == -1)
		printf("无,错误代码:%d\n", rindex);
	else
		printf("t在s中出现的最右侧起始位置:%d\n", rindex);
	if (eindex == -1)
		printf("无,错误代码:%d", eindex);
	else
		printf("t在s中出现的最右侧结尾位置:%d", eindex);
	return 0;
}

// 保存一个字符串并返回字符串的长度
int getline(char s[])
{
	int i;
	char c;
	for ( i = 0; (c = getchar()) != EOF && c != '\n'; i++)
	{
		s[i] = c;
	}
	s[i] = '\0';
	return i;
}

// 返回字符串t在s中最右边的起始位置,没有返回-1
int strrindex(char s[], char t[])
{
	int pos = -1;
	int i, j, k;
	// 遍历s字符串
	for (i = 0; s[i] != '\0'; i++)
	{
		for (j = i, k = 0; t[k] != '\0' && t[k] == s[j]; j++, k++)
			;
		// 匹配完全的必要条件
		if (k > 0 && t[k] == '\0')
			pos = i;
	}
	return pos;
}

// 返回字符串t在s中最右边的结尾位置,没有返回-1
int streindex(char s[], char t[])
{
	int pos = -1;
	int i, j, k;
	// 遍历s字符串
	for (i = 0; s[i] != '\0'; i++)
	{
		for (j = i, k = 0; t[k] != '\0' && t[k] == s[j]; j++, k++)
			;
		// 匹配完全的必要条件
		if (k > 0 && t[k] == '\0')
			pos = i + k - 1;
	}
	return pos;

}

P70 4.2

#include<stdio.h>
#include<ctype.h>

double atof(char s[]);

int main()
{
	char s[100] = "123.45";
	double n = atof(s);
	printf("%lf", n);
	return 0;
}

// 将字符串浮点数转换为数值浮点数
double atof(char s[])
{
	int i;
	int sign = 1;
	double val = 0.0;
	// 空格跳过
	for (i = 0; s[i] == ' '; i++)
		;
	// 判断符号
	if (s[i] == '+' || s[i] == '-')
	{
		sign = (s[i] == '+' ? 1 : -1);
		i++; // 跳过符号
	}
	// 处理数据(小数点前的数据)
	while (isdigit(s[i]))
	{
		val = val * 10 + (s[i] - '0');
		i++;
	}
	// 判断小数点
	int power = 1;
	if (s[i] == '.')
	{
		// 处理数据(小数点后的数据)
		i++; // 跳过小数点
		while (isdigit(s[i]))
		{
			val = val * 10 + (s[i] - '0');
			power *= 10;
			i++;
		}
	}
	return sign * val / power;
}

P72 T 4-2

#include<stdio.h>
#include<ctype.h>

double atof(char s[]);

int main()
{
	char s[100] = "123.4e-6";
	double n = atof(s);
	printf("%.8lf", n);
	return 0;
}

// 优化版atof函数(能处理123.4e-6这种科学表示法)
double atof(char s[])
{
	int i;
	int sign = 1;
	double val = 0.0;
	// 空格跳过
	for (i = 0; s[i] == ' '; i++)
		;
	// 判断符号
	if (s[i] == '+' || s[i] == '-')
	{
		sign = (s[i] == '+' ? 1 : -1);
		i++; // 跳过符号
	}
	// 处理数据(小数点前的数据)
	while (isdigit(s[i]))
	{
		val = val * 10 + (s[i] - '0');
		i++;
	}
	// 判断小数点
	int power = 1;
	if (s[i] == '.')
	{
		// 处理数据(小数点后的数据)
		i++; // 跳过小数点
		while (isdigit(s[i]))
		{
			val = val * 10 + (s[i] - '0');
			power *= 10;
			i++;
		}
	}
	// 判断是否有e/E
	int exp_val = 0;
	int exp_sign = 1;
	int exp_power = 1;
	if (s[i] == 'e' || s[i] == 'E')
	{
		i++; // 跳过e/E
		// 判断e/E后的符号
		if (s[i] == '+' || s[i] == '-' || isdigit(s[i]))
		{
			exp_sign = ((s[i] == '+' || isdigit(s[i])) ? 1 : -1);
			i += isdigit(s[i]) ? 0 : 1; // 跳过符号如果有的话
			while (isdigit(s[i]))
			{
				exp_val = exp_val * 10 + (s[i++] - '0');
			}
		}
		for (int k = 0; k < exp_val; k++)
		{
			exp_power *= 10;
		}
	}
	return (exp_sign < 0) ? (sign * val / power / exp_power) : (sign * val / power * exp_power);
}

*:支持e后无数字的情况(123.4e)会输出(123.4),核心原因是因为exp_val = 0;

P75 4.3 逆波兰计算器

第一版(不支持小数和大于9的数)

#include<stdio.h>
#include<ctype.h>

#define MAXLINE 1000

double getop();
int push(double f);
double pop();

// 主入口,负责调用各个函数
int main()
{
	double n = getop();
	printf("%.2lf", n);
	return 0;
}

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{ 
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return -1; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

// 数据输入与处理函数
double getop()
{
	double op2;
	char c;
	while((c = getchar()) != '\n' && c != EOF)
	{
		if (isspace(c)) // 空格跳过
			continue;
		if (isdigit(c)) // 判断是否为数值
		{
			push(c - '0'); // 调用入栈函数入栈
		}
		else
		{
			switch (c)
			{
				// 从栈中弹出两个数,计算,结果压入栈中
				case '+':
					push(pop() + pop());
					break;
				case '-':
					op2 = pop();
					push(pop() - op2);
					break;
				case '*':
					push(pop() * pop());
					break;
				case '/':
					if ((op2 = pop()) == 0)
					{
						printf("错误:除零错误\n");
						return 0.0;
					}
					push(pop() / op2);
					break;
				default :
					printf("错误:非法字符 %c\n", c);
					return 0.0; // 非法符号

			}
		}
	}
	return pop();
	// printf("%lf", val[0]);
}

第二版(支持小数和大于9的数)

// 要求能处理输入的小数,以及大于9的数,将数据处理与数据逆波兰运算分开
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>

#define MAXLINE 1000
#define NUMBER '0'

char s[MAXLINE];

int getop();
int push(double f);
double pop();

// 逆波兰计算器
int main()
{
	int type;
	double op2;
	while ((type = getop()) != '\n' && type != EOF)
	{
		switch (type)
		{
			// 数字入栈
		case NUMBER:
			push(atof(s));
			break;
			// 从栈中弹出两个数,计算,结果压入栈中
		case '+':
			push(pop() + pop());
			break;
		case '-':
			op2 = pop();
			push(pop() - op2);
			break;
		case '*':
			push(pop() * pop());
			break;
		case '/':
			if ((op2 = pop()) == 0)
			{
				printf("错误:除零错误\n");
				return 1;
			}
			push(pop() / op2);
			break;
		default:
			printf("错误:非法字符 %c\n", type);
			return 1; // 非法符号
		}
	}
	// 打印计算结果
	printf("%.2lf", pop());
	return 0;
}

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return 0.0; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

// 数据输入 返回数据和运算符号
int getop()
{
	char c;
	int i = 0;
	int dot = 0;
	// 跳过空格
	while ((c = getchar()) == ' ' || c == '\t')
		;
	// 处理数字
	if (isdigit(c))
	{
		s[i++] = c;
		// 循环读取连续数字
		while (isdigit(c = getchar()) || (c == '.' && dot == 0))
		{
			if (c == '.')
			{
				dot = 1;
			}
			s[i++] = c;
		}
		s[i] = '\0';
		ungetc(c, stdin);
		return NUMBER;
	}
	if (c == '\n' || c == EOF)
	{
		return EOF;
	}
	return c;
}

P78 T 4-3

// 添加取模运算 支持负数运算
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>

#define MAXLINE 1000
#define NUMBER '0'

char s[MAXLINE];

int getop();
int push(double f);
double pop();

// 逆波兰计算器
int main()
{
	int type;
	double op2;
	while ((type = getop()) != EOF)
	{
		switch (type)
		{
			// 数字入栈
		case NUMBER:
			push(atof(s));
			break;
			// 从栈中弹出两个数,计算,结果压入栈中
		case '+':
			push(pop() + pop());
			break;
		case '-':
			op2 = pop();
			push(pop() - op2);
			break;
		case '*':
			push(pop() * pop());
			break;
		case '/':
			if ((op2 = pop()) == 0)
			{
				printf("错误:除零错误\n");
				return 1;
			}
			push(pop() / op2);
			break;
		case '%':
			if ((op2 = pop()) == 0)
			{
				printf("错误:模零错误\n");
				return 1;
			}
			push(fmod(pop(), op2));
			break;
		default:
			printf("错误:非法字符 %c\n", type);
			return 1; // 非法符号
		}
	}
	// 打印计算结果
	printf("%.2lf", pop());
	return 0;
}

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引
// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return 0.0; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
	char c;
	int i = 0;
	int dot = 0;
	// 跳过空格
	while ((c = getchar()) == ' ' || c == '\t')
		;
	// 判断负号还是减号
	if (c == '-')
	{
		char next = getchar();
		// 如果next是EOF,直接返回'-',不要ungetc
		if (next == EOF)
		{
			return '-';
		}
		// 下一个是数字/小数点 → 负号(负数)
		if (isdigit(next) || next == '.')
		{
			s[i++] = '-';
			c = next;
		}
		// 否则 → 减号运算符
		else
		{
			ungetc(next, stdin);
			return c;
		}
	}
	// 正数/小数/负数后续部分
	if (isdigit(c) || c == '.')
	{
		s[i++] = c;
		if (c == '.')
			dot = 1;
		while (isdigit(c = getchar()) || (c == '.' && dot == 0))
		{
			s[i++] = c;
			if (c == '.')
				dot = 1;
		}
		s[i] = '\0';       // 字符串结束符
		ungetc(c, stdin);  // 回退多读的字符
		return NUMBER;     // 返回数字标记
	}
	if (c == '\n' || c == EOF)
	{
		return EOF;
	}
	return c;
}

P78 T 4-4

// 添加取模运算 支持负数运算 添加了一些栈操作命令 程序将在输入’$‘时结束运算,并打印结果(默认栈顶)
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>

#define MAXLINE 1000
#define NUMBER '0'

char s[MAXLINE];
int START = 1;

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引

int getop();
int push(double f);
double pop();

// 逆波兰计算器
int main()
{
	int type;
	double op2;
	while (START)
	{
		while ((type = getop()) != EOF)
		{
			switch (type)
			{
				// 数字入栈
			case NUMBER:
				push(atof(s));
				break;
				// 从栈中弹出两个数,计算,结果压入栈中
			case '+':
				push(pop() + pop());
				break;
			case '-':
				op2 = pop();
				push(pop() - op2);
				break;
			case '*':
				push(pop() * pop());
				break;
			case '/':
				if ((op2 = pop()) == 0)
				{
					printf("错误:除零错误\n");
					return 1;
				}
				push(pop() / op2);
				break;
			case '%':
				if ((op2 = pop()) == 0)
				{
					printf("错误:模零错误\n");
					return 1;
				}
				push(fmod(pop(), op2));
				break;
				// 新增命令
			case 'p': // 打印栈顶元素
				if (sp > 0)
				{
					printf("栈顶元素:%.2f\n", val[sp - 1]);
				}
				else
				{
					printf("错误:栈空\n");
				}
				break;

			case 'd': // 复制栈顶元素
				if (sp > 0)
				{
					push(val[sp - 1]);
				}
				else
				{
					printf("错误:栈空\n");
				}
				break;
			
			case 's': // 交换栈顶两个元素
				if (sp > 1)
				{
					double temp = val[sp - 1];
					val[sp - 1] = val[sp - 2];
					val[sp - 2] = temp;
				}
				else
				{
					printf("错误:栈不足两个元素\n");
				}
				break;

			case 'c': // 清空栈
				sp = 0; // 重置栈
				printf("栈已清空\n");
				break;

			case '$': // 结束
				START = 0;
				break;

			default:
				printf("错误:非法字符 %c\n", type);
				return 1; // 非法符号
			}
		}
	}
	// 打印计算结果
	printf("结果:%.2lf", pop());
	return 0;
}

// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return 0.0; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
	char c;
	int i = 0;
	int dot = 0;
	// 跳过空格
	while ((c = getchar()) == ' ' || c == '\t')
		;
	// 判断负号还是减号
	if (c == '-')
	{
		char next = getchar();
		// 如果next是EOF,直接返回'-',不要ungetc
		if (next == EOF)
		{
			return '-';
		}
		// 下一个是数字/小数点 → 负号(负数)
		if (isdigit(next) || next == '.')
		{
			s[i++] = '-';
			c = next;
		}
		// 否则 → 减号运算符
		else
		{
			ungetc(next, stdin);
			return c;
		}
	}
	// 正数/小数/负数后续部分
	if (isdigit(c) || c == '.')
	{
		s[i++] = c;
		if (c == '.')
			dot = 1;
		while (isdigit(c = getchar()) || (c == '.' && dot == 0))
		{
			s[i++] = c;
			if (c == '.')
				dot = 1;
		}
		s[i] = '\0';       // 字符串结束符
		ungetc(c, stdin);  // 回退多读的字符
		return NUMBER;     // 返回数字标记
	}
	if (c == '\n' || c == EOF)
	{
		return EOF;
	}
	return c;
}

P78 T 4-5

// 添加取模运算 支持负数运算 添加了一些栈操作命令 程序将在输入’$‘时结束运算,并打印结果(默认栈顶)可以访问sin exp pow函数
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<math.h>

#define MAXLINE 1000
#define NUMBER '0'

char s[MAXLINE];
int START = 1;

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
int sp = 0; // 栈的索引

int getop();
int push(double f);
double pop();

// 逆波兰计算器
int main()
{
	int type;
	double op2;
	double op3;
	while (START)
	{
		while ((type = getop()) != EOF)
		{
			switch (type)
			{
				// 数字入栈
			case NUMBER:
				push(atof(s));
				break;
				// 从栈中弹出两个数,计算,结果压入栈中
			case '+':
				push(pop() + pop());
				break;
			case '-':
				op2 = pop();
				push(pop() - op2);
				break;
			case '*':
				push(pop() * pop());
				break;
			case '/':
				if ((op2 = pop()) == 0)
				{
					printf("错误:除零错误\n");
					return 1;
				}
				push(pop() / op2);
				break;
			case '%':
				if ((op2 = pop()) == 0)
				{
					printf("错误:模零错误\n");
					return 1;
				}
				push(fmod(pop(), op2));
				break;
				// 新增命令
			case 'p': // 打印栈顶元素
				if (sp > 0)
				{
					printf("栈顶元素:%.2f\n", val[sp - 1]);
				}
				else
				{
					printf("错误:栈空\n");
				}
				break;

			case 'd': // 复制栈顶元素
				if (sp > 0)
				{
					push(val[sp - 1]);
				}
				else
				{
					printf("错误:栈空\n");
				}
				break;
			
			case 's': // 交换栈顶两个元素
				if (sp > 1)
				{
					double temp = val[sp - 1];
					val[sp - 1] = val[sp - 2];
					val[sp - 2] = temp;
				}
				else
				{
					printf("错误:栈不足两个元素\n");
				}
				break;

			case 'c': // 清空栈
				sp = 0; // 重置栈
				printf("栈已清空\n");
				break;

			case 'i':
				push(sin(pop())); // x的正弦值
				break;

			case 'e':
				push(exp(pop())); // 幂函数e^x
				break;

			case '^':
				op2 = (int)pop(); // y
				op3 = pop(); // x
				if (op3 == 0 && op2 <= 0)
				{
					printf("错误:未定义行为!\n");
				}
				else
				{
					push(pow(op3, op2));
				}
				break;

			case '$': // 结束
				START = 0;
				break;

			default:
				printf("错误:非法字符 %c\n", type);
				return 1; // 非法符号
			}
		}
	}
	// 打印计算结果
	printf("结果:%.2lf", pop());
	return 0;
}

// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return 0.0; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

// 数据输入 返回数据和运算符号 (支持负数入栈)
int getop()
{
	char c;
	int i = 0;
	int dot = 0;
	// 跳过空格
	while ((c = getchar()) == ' ' || c == '\t')
		;
	// 判断负号还是减号
	if (c == '-')
	{
		char next = getchar();
		// 如果next是EOF,直接返回'-',不要ungetc
		if (next == EOF)
		{
			return '-';
		}
		// 下一个是数字/小数点 → 负号(负数)
		if (isdigit(next) || next == '.')
		{
			s[i++] = '-';
			c = next;
		}
		// 否则 → 减号运算符
		else
		{
			ungetc(next, stdin);
			return c;
		}
	}
	// 正数/小数/负数后续部分
	if (isdigit(c) || c == '.')
	{
		s[i++] = c;
		if (c == '.')
			dot = 1;
		while (isdigit(c = getchar()) || (c == '.' && dot == 0))
		{
			s[i++] = c;
			if (c == '.')
				dot = 1;
		}
		s[i] = '\0';       // 字符串结束符
		ungetc(c, stdin);  // 回退多读的字符
		return NUMBER;     // 返回数字标记
	}
	if (c == '\n' || c == EOF)
	{
		return EOF;
	}
	return c;
}

P90 T 4-14

#include<stdio.h>

#define swap(t, x, y) { t temp = x; x = y; y = temp;}

int main()
{
	int x = 5;
	int y = 10;
	printf("Before swap: x = %d, y = %d\n", x, y);
	swap(int, x, y);
	printf("After swap: x = %d, y = %d\n", x, y);
	return 0;
}

第五章

P97 T 5-1

#include<stdio.h>
#include<ctype.h>

int getint(int* p);

int main()
{
	int arr[5];
	for (int i = 0; i < 5 && getint(&arr[i]) != EOF; i++)
		;
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

int getint(int* p)
{
	int c, sign;
	while (isspace(c = getchar()))
		;
	if (!isdigit(c) && c != EOF && c != '+' && c != '-')
	{ // 不是数
		ungetc(c, stdin);
		return 0;
	}
	sign = (c == '-') ? -1 : 1;
	if (c == '-' || c == '+')
		c = getchar(); // 获取下一个字符
	if (!isdigit(c))
	{
		ungetc(sign < 0 ? '-' : '+', stdin);
		return 0;
	}
	for (*p = 0 ; isdigit(c); c = getchar())
	{
		*p = *p * 10 + (c - '0');
	}
	*p *= sign;
	if (c != EOF)
	{
		ungetc(c, stdin);
	}
	return c;
}

P97 T 5-2

#include<stdio.h>
#include<ctype.h>

float getfloat(float* p);

int main()
{
	float arr[5];
	for (int i = 0; i < 5 && getfloat(&arr[i]) != EOF; i++)
		;
	for (int i = 0; i < 5; i++)
	{
		printf("%.2f ", arr[i]);
	}
	return 0;
}

float getfloat(float* p)
{
	int c, sign;
	float pow = 1.0;
	while (isspace(c = getchar()))
		;
	if (!isdigit(c) && c != EOF && c != '+' && c != '-')
	{ // 不是数
		ungetc(c, stdin);
		return 0.0;
	}
	sign = (c == '-') ? -1 : 1;
	if (c == '-' || c == '+')
		c = getchar(); // 获取下一个字符
	if (!isdigit(c))
	{
		ungetc(c, stdin);
		return 0.0;
	}
	// 读取整数部分
	for (*p = 0; isdigit(c); c = getchar())
	{
			*p = *p * 10 + (c - '0');
	}
	// 处理小数部分
	if (c == '.')
	{
		c = getchar();
		for (; isdigit(c); c = getchar())
		{
			*p = *p * 10 + (c - '0');
			pow *= 10;
		}
	}
	*p /= pow;
	*p *= sign;
	if (c != EOF)
	{
		ungetc(c, stdin);
	}
	return c;
}

P106 T 5-3

#include<stdio.h>

void strcat(char* s, char* t);

int main()
{
	char s[14] = "Hello";
	char t[8] = "_World!";
	printf("修改前:%s\n", s);
	strcat(s, t);
	printf("修改后:%s\n", s);
	return 0;
}

// 将t指向的字符串复制到s字符串的尾部
void strcat(char* s, char* t)
{
	// 得到s字符串的末尾指针(指向‘\0’)
	while (*s != '\0')
		s++;
	// 复制(包括“\0”)
	while (*s++ = *t++)
		;
}

P106 T 5-4

#include<stdio.h>

int strend(char* s, char* t);

int main()
{
	char s[] = "would";
	char t[] = "ld";
	printf("%d", strend(s, t));
	return 0;
}

// 如果字符串t出现在字符串s的尾部,函数返回1,否则返回0
int strend(char* s, char* t)
{
	// 注意点,要求完全符合
	// 可以从后往前比较
	// 得到字符串s的尾部‘\0’位置处指针
	char* end_s = s;
	for ( ; *end_s != '\0'; end_s++)
		;
	// 得到字符串t的尾部‘\0’位置处指针
	char* end_t = t;
	for (; *end_t != '\0'; end_t++)
		;
	// 比较
	while (end_t > t)
	{
		if (*--end_s != *--end_t)
			return 0;
	}

	return 1;
}

P118 T 5-10

// 计算从命令行中输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示 例如 5 3 2 - * 输出结果为4即:(5-2)* 2 = 4
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>

#define MAXLINE 1000
int sp = 0; // 栈的索引

int push(double f);
double pop();

// 逆波兰计算器
int main(int argc, char* argv[])
{
	int c;
	double op2;
	while (--argc > 0)
	{
		++argv;
		c = (*argv)[0];
		if (isdigit(c) || c == '.' || 
			(c == '-' && (isdigit((*argv)[1]) || ((*argv)[1]) == '.')))
		{
			push(atof(*argv));
		}
		else
		{
			if (sp < 2) 
			{
				printf("错误:操作数不足\n");
				return 1;
			}
			switch (c)
			{
				// 从栈中弹出两个数,计算,结果压入栈中
			case '+':
				push(pop() + pop());
				break;
			case '-':
				op2 = pop();
				push(pop() - op2);
				break;
			case '*':
				push(pop() * pop());
				break;
			case '/':
				if ((op2 = pop()) == 0)
				{
					printf("错误:除零错误\n");
					return 1;
				}
				push(pop() / op2);
				break;
			default:
				printf("错误:非法字符 %s\n", *argv);
				return 1; // 非法符号
			}
		}
	}

	if (sp != 1) 
	{
		printf("错误:表达式不合法\n");
		return 1;
	}
	// 打印计算结果
	printf("%.2lf", pop());
	return 0;
}

// 入栈与弹出函数
// 两个函数共用一个栈
double val[MAXLINE];
// push入栈函数
int push(double f)
{
	if (sp < MAXLINE)
	{
		val[sp++] = f; // 入栈
		return 0; // 正常结束,表示入栈成功
	}
	else
	{
		printf("错误:栈满,入栈失败\n");
		return -1; // 入栈失败
	}
}
// pop弹出函数
double pop() // 弹出一个数(先入后弹)
{
	if (sp <= 0)
	{
		printf("错误:栈空,无数据弹出\n");
		return 0.0; // 栈中无数据可供弹出
	}
	else // 说明栈中至少有一个数据可供弹出
	{
		return val[--sp];
	}
}

P118 T 5-13

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define MAXLINES 5000
#define MAXLINE 1000

int getline(char s[], int lim);
int readline(char* arr[], int maxlines);
void writetail(char* arr[], int nline, int n);

int main(int argc, char* argv[])
{
	int n = 10; // 默认参数 n = 10
	int nline = 0; // 行数
	// 定义一个二维数组,存储输入的每行文字
	char* arr[MAXLINES];
	// 获取参数n
	if (argc > 1 && argv[1][0] == '-')
	{
		n = atoi(argv[1] + 1);
	}
	// 将输入存储到指针数组中
	nline = readline(arr, MAXLINES);
	// 输出最后n行(检查特殊情况)
	writetail(arr, nline, n);
	return 0;
}

int getline(char s[], int lim)
{
	int c, i;

	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;

	if (c == '\n')
	{
		s[i] = c;
		i++;
	}

	s[i] = '\0';

	return i;
}

int readline(char* arr[], int maxlines)
{
	int nline = 0; // 行数初始化为0
	int len = 0;
	char s[MAXLINE]; // 定义一个缓存字符数组
	while ((len = getline(s, MAXLINE)) > 0)
	{
		// 去掉换行符
		s[len - 1] = '\0';
		// 申请内存空间
		char* p = malloc(len);
		strcpy(p, s);
		arr[nline++] = p; // 存储到指针数组中
	}
	return nline; // 返回行数
}

void writetail(char* arr[], int nline, int n)
{
	int start = 0;
	/* 先排查特殊情况 1)n > nline 2) nline == 0 */
	if (nline == 0)
	{
		printf("无输入\n");
		return;
	}
	if (n > nline)
	{
		n = nline;
	}
	start = nline - n;
	printf("最后%d行为:\n", n);
	for (int i = start;  i < nline; i++)
	{
		printf("%s\n", arr[i]);
	}
}

第六章

P138 6.5

从这里开始,代码风格({}位置改为和Java风格一致)

#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<string.h>

#define MAXLINE 50

// 定义一个结构体存储word count 以及left right
typedef struct tnote {
	char* word;
	int count;
	struct tnote* left;
	struct tnote* right;
} Tnote;

int getword(char* word, int lim);
Tnote* addtree(Tnote* p, char* w);
void treeprint(Tnote* p);

int main() {
	char w[MAXLINE];
	Tnote* root = NULL;
	while (getword(w, MAXLINE) != EOF)
		if (isalpha(w[0]))
			root = addtree(root, w);
	treeprint(root);
	return 0;
}

// 创建二叉树 
Tnote* addtree(Tnote* p, char* w) {
	int cond;
	/* 第一次添加的数据默认为根,之后每次比较后出现NULL的情况,就创建新的节点 */
	if (p == NULL) {
		p = malloc(sizeof(Tnote)); /* 新节点 */
		char* w1 = malloc(strlen(w) + 1); /* 给得到的字符串申请一块内存空间 */
		strcpy(w1, w); /* 将得到的字符串复制到新的内存地址 */
		p -> word = w1; /* 给节点赋值 */
		p -> count = 1;
		p -> left = p -> right = NULL; /* 左右孩子默认为空 */
	}
	else if ((cond = strcmp(w, p->word)) == 0)
		p -> count++;
	else if (cond < 0) {
		p -> left = addtree(p -> left, w); /* 递归调用函数 */
	}
	else {
		p -> right = addtree(p -> right, w);
	}
	return p;
}

// 中序遍历
void treeprint(Tnote* p) {
	/* 左 根 右 */
	if (p != NULL) {
		treeprint(p -> left);
		printf("%d %s\n", p -> count, p -> word);
		treeprint(p -> right);
	}
}

// 获取单词
int getword(char* word, int lim) {
	char* w = word;
	int c;
	while (isspace(c = getchar()))
		;
	if (c != EOF)
		*w++ = c;
	if (!isalpha(c)) {
		*w = '\0';
		return c;
	}
	for (; --lim > 0; w++) {
		if (!isalnum(*w = getchar())) {
			ungetc(*w, stdin);
			break;
		}
	}
	*w = '\0';
	return word[0];
}

P143 T 6-4(第一版 单词树-频率树)

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX 20

typedef struct wordnode {
    char* word;
    int count;
    struct wordnode* left;
    struct wordnode* right;
} Wnode;

/* 单词树 */
Wnode* insertw(Wnode* root, char* word) {
    if (root == NULL) {
        root = malloc(sizeof(Wnode));
        root -> word = malloc(strlen(word) + 1);
        strcpy(root -> word, word);
        root -> count = 1;
        root -> left = root -> right = NULL;
        return root;
    }
    int n = strcmp(word, root -> word);
    if (n == 0)
        root -> count++;
    else if (n < 0)
        root -> left = insertw(root -> left, word);
    else
        root -> right = insertw(root -> right, word);
    return root;
}

typedef struct countnode {
    int count;
    char* word;
    struct countnode* left;
    struct countnode* right;
} Cnode;

/* 频率树 */
Cnode* insertc(Cnode* root, int value, char* word) {
    if (root == NULL) {
        root = malloc(sizeof(Cnode));
        root -> count = value;
        root -> word = malloc(strlen(word) + 1);
        strcpy(root -> word, word);
        root -> left = root -> right = NULL;
        return root;
    }
    int n = value - root -> count;
    if (n == 0) {
        if (strcmp(root -> word, word) < 0)
            root -> left = insertc(root -> left, value, word);
        else
            root -> right = insertc(root -> right, value, word);
    }
    else if (n < 0)
        root -> left = insertc(root -> left, value, word);
    else
        root -> right = insertc(root -> right, value, word);
    return root;
}

/* 遍历单词树得到频率和单词,并创建频率树 */
Cnode* wordorder_creatcounttree(Wnode* tree, Cnode* counttree) {
    if (tree == NULL)
        return counttree;
    counttree = wordorder_creatcounttree(tree->left, counttree);
    counttree = insertc(counttree, tree->count, tree->word);
    counttree = wordorder_creatcounttree(tree->right, counttree);
    return counttree;
}

/* 右 根 左 遍历频率树 */
void countorder(Cnode* tree) {
    if (tree == NULL)
        return;
    countorder(tree -> right);
    printf("%d %s\n", tree -> count, tree -> word);
    countorder(tree -> left);
}

/* 获取输入的单词 */
int getword(char* word) {
    int c;
    while (isspace((c = getchar())))
        ;
    if (c == EOF)
        return EOF;
    if (!isalpha(c)) {
        word[0] = c;
        word[1] = '\0';
        return c;
    }
    int i = 0;
    word[i++] = c;
    while (i < MAX - 1 && isalnum(c = getchar()))
        word[i++] = c;
    word[i] = '\0';
    if (c != EOF)
        ungetc(c, stdin);
    return word[0];
}

int main() {
    char word[MAX];
    int c;
    Wnode* wordtree = NULL;
    Cnode* counttree = NULL;
    while ((c = getword(word)) != EOF) {
        if (isalnum(word[0])) {
            wordtree = insertw(wordtree, word);
        }
    }
    counttree = wordorder_creatcounttree(wordtree, counttree);
    countorder(counttree);
    return 0;
}

P143 T 6-4(第二版 单词树-数组)

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#define MAXLINE 1000

typedef struct wordnode {
    char* word;
    int count;
    struct wordnode* left;
    struct wordnode* right;
} wnode;

wnode* insert(wnode* root, char* word) {
    if (root == NULL) {
        root = malloc(sizeof(wnode));
        root -> word = malloc(strlen(word) + 1);
        strcpy(root -> word, word);
        root -> count = 1;
        root -> left = root -> right = NULL;
        return root;
    }
    int n = strcmp(word, root -> word);
    if (n == 0)
        root -> count++;
    else if (n < 0)
        root -> left = insert(root -> left, word);
    else
        root -> right = insert(root -> right, word);
    return root;
}

int getword(char* w) {
    int c, i;
    while (isspace(c = getchar()))
        ;
    if (c == EOF)
        return c;
    if (!isalpha(c)) {
        w[0] = c;
        w[1] = '\0';
        return c;
    }
    w[0] = c;
    for (i = 1; isalnum(c = getchar()); i++)
        w[i] = c;
    w[i] = '\0';
    if (c != EOF)
        ungetc(c, stdin);
    return w[0];
}

/* 中序遍历得到wnode类型的数组(写法一) */
wnode arr[MAXLINE];
int n = 0;
void inorder(wnode* tree) {
    if (tree == NULL)
        return;
    inorder(tree -> left);
    arr[n].word = malloc(strlen(tree->word) + 1);
    strcpy(arr[n].word, tree -> word);
    arr[n].count = tree -> count;
    n++;
    inorder(tree -> right);
}

/* 中序遍历得到wnode类型的数组(写法二) */
void inorder2(wnode* tree, wnode arr[], int *n) {
    if (tree == NULL)
        return;
    inorder2(tree -> left, arr, n);
    arr[*n].word = tree -> word;
    arr[*n].count = tree -> count;
    (*n)++;
    inorder2(tree -> right, arr, n);
}

/* inorder2的干净接口 */
int _inorder2(wnode* tree, wnode arr[]) {
    int n = 0;
    inorder2(tree, arr, &n);
    return n;
}

/* cmp函数 */
int cmp(const void* a, const void* b) {
    wnode* x = (wnode*)a;
    wnode* y = (wnode*)b;
    if (x -> count != y -> count)
        return x -> count < y -> count ? 1 : -1;
    return strcmp(y -> word, x -> word);
}

/*释放*/
void freetree(wnode* tree) {
    if (tree == NULL)
        return;
    freetree(tree->left);
    freetree(tree->right);
    free(tree->word);
    free(tree);
}

int main() {
    char word[100];
    wnode* tree = NULL;
    wnode arr1[MAXLINE];
    while ( getword(word) != EOF) {
        if (isalpha(word[0]))
            tree = insert(tree, word);
    }
    int n = _inorder2(tree, arr1);
    /* 对数组排序,降序打印 */
    qsort(arr1, n, sizeof(wnode), cmp);
    for (int i = 0; i < n; i++)
        printf("%d %s\n", arr1[i].count, arr1[i].word);
    freetree(tree);
    return 0;
}

P145 T 6-5

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct nlist {
    struct nlist* next;
    char* name;
    char* defn;
};

#define HASHSIZE 101
static struct nlist* hashtab[HASHSIZE];

unsigned hash(char* s) {
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++) {
        hashval = *s + 31 * hashval;
    }
    return hashval % HASHSIZE;
}

struct nlist* lookup(char* s) {
    struct nlist* np;
    for (np = hashtab[hash(s)]; np != NULL; np = np -> next )
        if (strcmp(np -> name, s) == 0)
            return np;
    return NULL;
}

void undef(char* s) {
    struct nlist* np,* prev;
    unsigned hashval = hash(s);
    np = hashtab[hashval];
    prev = NULL;
    while (np != NULL) {
        if (strcmp(s, np -> name) == 0) {
            if (prev == NULL) {
                hashtab[hashval] = np -> next;
            }
            else {
                prev -> next = np -> next;
            }
            free(np -> name);
            free(np -> defn);
            free(np);
            return;
        }
        prev = np;
        np = np -> next;
    }
}

P145 T 6-6

从这里开始*的位置将沿用K&R代码风格(即解引用符号紧跟变量名)

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct nline {
    struct nline *next;
    char *name;
    char *defn;
};

#define HASHSIZE 101
static struct nline *hashtab[HASHSIZE];

/* 实现字符串转数字 */
unsigned hash(char *s) {
    unsigned hashval = 0;
    while (*s != '\0') {
        hashval = *s + 31 * hashval;
        s++;
    }
    return hashval % HASHSIZE;
}

/* 查找 */
struct nline *lookup(char *s) {
    struct nline *np;
    for (np = hashtab[hash(s)]; np != NULL; np = np -> next)
        if (strcmp(s, np -> name) == 0)
            return np;
    return NULL;
}

/* 插入哈希表 */
struct nline *insert(char *name, char *defn) {
    unsigned hashval;
    struct nline *np;
    if ((np = lookup(name)) == NULL) { /* 查找为空 */
        np = (struct nline *)malloc(sizeof(*np));
        if (np == NULL) /* 防止malloc函数没有申请到空间的情况,下方同理 */
            return NULL;
        np -> name = strdup(name);
        if (np -> name == NULL)
            return NULL;
        hashval = hash(name);
        np -> next = hashtab[hashval]; /* 新插入的元素的next要指向旧元素 */
        hashtab[hashval] = np; /* 将新插入的元素设为对头 */
    }
    else /* 已经存在,更新defn */
        free(np -> defn);
    np -> defn = strdup(defn); /* 更新defn(无论是否查到name,我们都要更新defn,索性就将defn的更新放在末尾) */
    if (np -> defn == NULL)
        return NULL;
    return np;
}

/* 获取输入单词 */
int getword(char *s, int lim) {
    /* 我们要加入对#定义的获取 */
    int c, i;
    while (isspace(c = getchar())) /* 跳过空格 */
        ;
    if (c == EOF)
        return c;
    if (isalpha(c) || c == '_') { /* 处理字母和下划线 */
        s[0] = c;
        i = 1;
        while (i < lim - 1 && isalnum(c = getchar())) {
            s[i++] = c;
        }
        if (c != EOF)
            ungetc(c, stdin);
        s[i] = '\0';
        return s[0];
    }
    if (isdigit(c)) { /* 处理数字 */
        s[0] = c;
        i = 1;
        while (i < lim - 1 && isalnum(c = getchar())) {
            s[i++] = c;
        }
        if (c != EOF)
            ungetc(c, stdin);
        s[i] = '\0';
        return s[0];
    }
    /* 其他字符(这里负责捕捉#) */
    s[0] = c;
    s[1] = '\0';
    return c;
}

#define MAXWORD 100

int main() {
    char word[MAXWORD];
    char name[MAXWORD];
    char defn[MAXWORD];
    struct nline *np;
    while (getword(word, MAXWORD) != EOF) {
        if (strcmp("#", word) == 0) {
            getword(word, MAXWORD);
            if (strcmp("define", word) == 0) {
                getword(name, MAXWORD); /* 下一个单词是宏名 */
                getword(defn, MAXWORD); /* 下一个单词是替换内容 */
                insert(name, defn);
            }
        }
        else if (isalpha(word[0])) {
            if ((np = lookup(word)) != NULL)
                printf("%s ", np -> defn);
            else
                printf("%s ", word);
        }
        else
            printf("%s ", word);
    }
    return 0;
}

至此,我的第二次C语言学习就此结束,书中后的两章内容对我之后的发展作用不大,因此略过。

对于我自己编写的这套所谓的参考答案,参考价值并不高,我在写这些习题的时候,很大一部的纠错是得益于当今AI的迅速发展,在这里非常感谢豆包和GPT两位老师的帮助,没有这两位老师,我将不可能完成这对我来说近乎不可能的任务。

因此希望有提前看到这里的读者,善用AI,但如果真的是因为某些不可抗拒因素导致无法使用AI,再将我所编写的所谓的参考答案作为参考。

该参考答案我不能保证是最优解,但是我有九成的把握保证这些答案是正确的,能跑通的。

啊?居然还没有达到一万字吗?可恶~~~

“莫道桑榆晚,未霞尚满天。”

posted @ 2026-04-04 21:51  Mone10086  阅读(2)  评论(0)    收藏  举报