显示一个月的提醒列表
使用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类型的指针指向数组,用来分配内存用。
如果指针指向动态分配的内存块,就可以忽略它是指针的湿式,直接当做数组名字用,但是本例是需要数组先进行赋值,然后根据运行结果再分配内存,所以定义时要保留指针数组。
[看书+软件操作+博客总结]的方式学习很有收获。