C语言学习显示一个月提醒列表(字符串库+动态分配数组)

显示一个月的提醒列表

使用C语言的字符串库

这一周学习了指针的高级应用,并通过《C语言程序设计现代方法》中例题“显示一个月的提醒列表”程序编写学习字符串库函数等。
题目:用户需要输入一系列提醒,每条提醒都要有一个前缀来说明是一个月中的哪一天。当用户输入的是0而不是有效的日期是,程序会显示出录入的全部提醒列表,按日期排序。下面是代码加个人理解注释:

/*Prints a one-month reminder list */
#include <stdio.h>
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS
#define MAX_REMIND 50 /*maximum number of reminders*/
#define MSG_LEN 60   /*max length of reminder message*/

int read_line(char str[], int n);

int main(void)//形式参数任意类型
{
	char reminders[MAX_REMIND][MSG_LEN + 3];
	char day_str[3], msg_str[MSG_LEN + 1];
	int day, i, j, num_remind = 0;

for (;;) {                             //强制执行for循环
	if (num_remind == MAX_REMIND) {
		printf("--No space left --\n");
		break;
	}
	printf("Enter day and reminder: ");
	scanf("%2d", &day);//1个月只需要输入某一天就行
	if (day == 0)
		break;
	sprintf(day_str, "%2d", day);//打印到字符串,所以day_str[3]多留一位给空字符,起到强制转换格式作用
	read_line(msg_str, MSG_LEN);
	for (i = 0; i < num_remind; i++)
		if (strcmp(day_str, reminders[i]) < 0)//比较输入的日期和提醒数组中第一列是否有对应,不同为-1
			break;
	for (j = num_remind; j > i; j--)
		strcpy(reminders[j], reminders[j-1]);//复制字符串--把找到的位置后面的字符串全都往后移动一个位置

	strcpy(reminders[i], day_str);//把找到的日期放到day_str数组最后一个空位
	strcat(reminders[i], msg_str);//字符串拼接--把提醒事项字符串放到日期后面
	//**即使i=0或者没有匹配提醒事项的日期,也要将输入的日期赋给数组,此时没有提醒事项可以用回车跳出msg_str的输入
	num_remind++;
}

printf("\nDay Reminder\n");
for (i = 0; i < num_remind; i++)
	printf(" %s\n", reminders[i]);
return 0;
}

int read_line(char str[], int n)
{
	int ch, i = 0;//ch i是函数内部变量,结束后不保存值

	while ((ch = getchar()) != '\n') //条件语句之前先从缓存中每次读取1个字符给ch
		if (i < n)
			str[i++] = ch;   //相当于将提醒事项一个个敲进str[]数组,最后按回车(转义字符)结束
	str[i] = '\0';//空字符NULL
	return i;
}

程序理解

把字符串存储在二维的字符数组中,数组的每一行包含一个字符串。在程序读入日期以及相关提醒后,通过使用strcmp函数进行比较来查找数组从而确定这一天所在的位置。然后,程序会使用strcpy函数把此位置之后的所有字符串往后移动一位。最后,程序会把这一天复制到数组中。并且调用strcat函数来把提醒附加到这一天后面(日期和提醒在此之前是分开存放的。

VS2017报错的地方

1、关闭SDL检查,启用控制台程序;
2、#define _CRT_SECURE_NO_WARNINGS:用于避免检查scanf、strcpy等字符串函数,因为可能会内存泄漏,应使用scanf_s、strcpy_s等;
3、main函数不过定义,应该在项目属性中c/c+±–预编译头—关闭;不能同时存在.c和.cpp文件。

程序运行

Enter day and reminder: 24 Susan's birthday
Enter day and reminder: 5 6:00 - Dinner with Marge and Russ
Enter day and reminder: 26 Movie - "Chinatown"
Enter day and reminder: 7 10:30 - Dental appointment
Enter day and reminder: 12 Movie - "Dazed and Confused"
Enter day and reminder: 5 Saturday class
Enter day and reminder: 12 Saturday class
Enter day and reminder: 0

Day Reminder
  5 Saturday class
  5 6:00 - Dinner with Marge and Russ
  7 10:30 - Dental appointment
 12 Saturday class
 12 Movie - "Dazed and Confused"
 24 Susan's birthday
 26 Movie - "Chinatown"

F:\VS2017\Tixingliebiao\Debug\Tixingliebiao.exe (进程 15540)已退出,返回代码为: 0。
若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口...

动态分配字符串的数组

优点:字符串存储在字符数组中,可能很难预测这些数组需要的长度,通过动态地分配字符串,可以推迟到程序运行时才做决定。
如上文,我们定义了50行60列的数组用来存放提醒列表的字符串,内存空间浪费了。使用malloc函数动态分配内存空间。

/*Prints a one-month reminder list */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define _CRT_SECURE_NO_WARNINGS
#define MAX_REMIND 50 /*maximum number of reminders*/
#define MSG_LEN 60   /*max length of reminder message*/

int read_line(char str[], int n);

int main(void)//形式参数任意类型
{
	char *reminders[MAX_REMIND];//定义指针指向一维的数组
	char day_str[3], msg_str[MSG_LEN + 1];
	int day, i, j, num_remind = 0;

	for (;;) {                             //强制执行for循环
		if (num_remind == MAX_REMIND) {
			printf("--No space left --\n");
			break;
		}
		printf("Enter day and reminder: ");
		scanf("%2d", &day);//1个月只需要输入某一天就行
		if (day == 0)
			break;
		sprintf(day_str, "%2d", day);//打印到字符串,所以day_str[3]多留一位给空字符,起到强制转换格式作用
		read_line(msg_str, MSG_LEN);
		for (i = 0; i < num_remind; i++)
			if (strcmp(day_str, reminders[i]) < 0)//比较输入的日期和提醒数组中第一列是否有对应,不同为-1
				break;
		for (j = num_remind; j > i; j--)
			reminders[j]=reminders[j - 1];

		reminders[i] = malloc(2 + strlen(msg_str)+1);
		if (reminders[i] == NULL) {
			printf("--No space left --\n");
			break;
		}

		strcpy(reminders[i], day_str);//把找到的日期放到day_str数组最后一个空位
		strcat(reminders[i], msg_str);//字符串拼接--把提醒事项字符串放到日期后面
		//**即使i=0或者没有匹配提醒事项的日期,也要将输入的日期赋给数组,此时没有提醒事项可以用回车跳出msg_str的输入
		num_remind++;
	}

	printf("\nDay Reminder\n");
	for (i = 0; i < num_remind; i++)
		printf(" %s\n", reminders[i]);
	return 0;
}

int read_line(char str[], int n)
{
	int ch, i = 0;//ch i是函数内部变量,结束后不保存值

	while ((ch = getchar()) != '\n') //条件语句之前先从缓存中每次读取1个字符给ch
		if (i < n)
			str[i++] = ch;   //相当于将提醒事项一个个敲进str[]数组,最后按回车(转义字符)结束
	str[i] = '\0';//空字符NULL
	return i;
}

定义了char *reminders[MAX_REMIND],char类型的指针指向数组,用来分配内存用。
如果指针指向动态分配的内存块,就可以忽略它是指针的湿式,直接当做数组名字用,但是本例是需要数组先进行赋值,然后根据运行结果再分配内存,所以定义时要保留指针数组。
[看书+软件操作+博客总结]的方式学习很有收获。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值