C语言中如何将数组适配fprintf和fgets以实现文本存储与加载功能
C语言中如何将数组适配fprintf和fgets以实现文本存储与加载功能
嘿,刚学C遇到这种问题太正常了,我来帮你一步步拆解问题出在哪,以及怎么修改代码来实现你的便签保存/加载功能~
先看你遇到的核心错误原因
你收到的警告和错误主要是参数类型不匹配和对C语言中指针/数组的理解偏差导致的:
- fprintf参数错误:
messages_array是char*类型的数组,messages_array[i]本身就是指向字符串的指针,你传&messages_array[i]相当于传了char**类型,而fprintf的第二个参数需要的是const char*格式字符串,正确的做法是用格式符%s来输出字符串。 - fgets参数错误:同理,
fgets的第一个参数需要char*类型的缓冲区,你传&messages_array[i]是char**,类型完全不兼容,直接传messages_array[i]即可,但要确保这个指针指向的是可写的、足够大的内存空间。 - 除此之外,代码里还有几个容易踩的C语言坑,我会一起帮你修正。
关键问题的具体解决步骤
1. 修正字符串长度判断的错误
你在change_messages里用sizeof(new_message)判断字符串长度是错误的——因为new_message是指针,sizeof(new_message)得到的是指针本身的大小(32位系统是4字节,64位是8字节),永远不会超过100。正确的做法是用strlen(new_message)来获取字符串的实际长度。
2. 修正数组越界检查
你的messages_array有9个元素,索引范围是0~8,所以array_number的合法范围应该是0<=array_number<9(如果用户输入的是19,那你要转成08)。你当前的检查array_number>10完全起不到作用,反而会允许越界。
3. 修正内存管理问题
你初始化messages_array时用的是字符串字面量("this message..."),这类字符串存在只读内存段,不能用free释放。所以初始化的时候要给每个元素分配可写的内存空间,后续修改时才能安全地free和重新分配。
4. 修正文件读写的逻辑
- 保存时:循环遍历所有9个元素,用
fprintf(myFile, "%s\n", messages_array[i])把每个字符串写入文件,每行一个消息,方便读取。 - 读取时:用
fgets(messages_array[i], 100, myFile)读取每行内容,注意fgets会把换行符读进去,需要用strcspn去掉它。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdbool.h> #include <stdint.h> #include <string.h> #include <readline/readline.h> #include <readline/history.h> #include <signal.h> #include <libnotify/notify.h> #include <glib.h> // 适配g_object_unref的头文件 #define EXIT_SUCCESS 0 #define EXIT_FAILURE_READING_FILE 1 #define EXIT_FAILURE_LIBNOTIFY 2 #define MAX_MESSAGE_LEN 100 #define MESSAGE_COUNT 9 char *ProgramTitle = "styckynotes"; char *messages_array[MESSAGE_COUNT]; // 先声明数组,后续统一初始化 // 初始化消息数组:分配可写内存并设置默认值 void init_messages() { for (int i = 0; i < MESSAGE_COUNT; i++) { messages_array[i] = malloc(MAX_MESSAGE_LEN + 1); // +1存储字符串结束符 if (messages_array[i] == NULL) { fprintf(stderr, "内存分配失败,程序退出\n"); exit(EXIT_FAILURE); } strncpy(messages_array[i], "this message has not yet been set", MAX_MESSAGE_LEN); messages_array[i][MAX_MESSAGE_LEN] = '\0'; // 确保字符串安全终止 } } int change_messages(int array_number, char* new_message) { // 检查数组索引合法性 if (array_number < 0 || array_number >= MESSAGE_COUNT) { fprintf(stderr, "错误:索引超出范围,数组仅包含%d个元素\n", MESSAGE_COUNT); return 1; } // 检查消息长度是否超限 if (strlen(new_message) > MAX_MESSAGE_LEN) { fprintf(stderr, "错误:消息过长,最多允许%d个字符\n", MAX_MESSAGE_LEN); return 1; } // 释放旧内存,重新分配并复制新消息 free(messages_array[array_number]); messages_array[array_number] = malloc(MAX_MESSAGE_LEN + 1); if (messages_array[array_number] == NULL) { fprintf(stderr, "内存分配失败\n"); return 1; } strncpy(messages_array[array_number], new_message, MAX_MESSAGE_LEN); messages_array[array_number][MAX_MESSAGE_LEN] = '\0'; // 将所有消息写入文件 FILE *myFile = fopen("messages.txt", "w"); if (myFile == NULL) { fprintf(stderr, "打开保存文件失败,无法保存消息\n"); return EXIT_FAILURE_READING_FILE; } for (int i = 0; i < MESSAGE_COUNT; i++) { fprintf(myFile, "%s\n", messages_array[i]); } fclose(myFile); return 0; } int note_pls(int count, int key) { // 从按键映射到数组索引(假设key对应字符'1'-'9') int pull_from = key - '1'; if (pull_from < 0 || pull_from >= MESSAGE_COUNT) { fprintf(stderr, "无效的消息索引\n"); return 1; } NotifyNotification *notification = notify_notification_new("reminder", messages_array[pull_from], "dialog-information"); if (notification == NULL) { fprintf(stderr, "创建通知失败!\n"); return EXIT_FAILURE_LIBNOTIFY; } notify_notification_show(notification, NULL); g_object_unref(G_OBJECT(notification)); notify_uninit(); return 0; } int exitrl(int count, int key) { // 退出前释放所有内存 for (int i = 0; i < MESSAGE_COUNT; i++) { free(messages_array[i]); } exit(EXIT_SUCCESS); } int signal_handler(void) { // 绑定快捷键到对应函数,无需重新声明函数 rl_bind_keyseq("\\C-e-1", note_pls); rl_bind_keyseq("\\C-e-2", note_pls); rl_bind_keyseq("\\C-e-3", note_pls); rl_bind_keyseq("\\C-e-4", note_pls); rl_bind_keyseq("\\C-e-5", note_pls); rl_bind_keyseq("\\C-e-6", note_pls); rl_bind_keyseq("\\C-e-7", note_pls); rl_bind_keyseq("\\C-e-8", note_pls); rl_bind_keyseq("\\C-e-9", note_pls); rl_bind_keyseq("\\C-e-0", exitrl); return 0; } int main(void) { // 初始化消息数组内存 init_messages(); // 初始化libnotify bool notifyInitStatus = notify_init(ProgramTitle); if (!notifyInitStatus) { fprintf(stderr, "初始化libnotify失败!\n"); // 释放已分配的内存后退出 for (int i = 0; i < MESSAGE_COUNT; i++) { free(messages_array[i]); } exit(EXIT_FAILURE_LIBNOTIFY); } // 加载保存的消息(如果文件存在) FILE *myFile = fopen("messages.txt", "r"); if (myFile != NULL) { for (int i = 0; i < MESSAGE_COUNT; i++) { if (fgets(messages_array[i], MAX_MESSAGE_LEN + 1, myFile) != NULL) { // 去掉fgets读取到的换行符 messages_array[i][strcspn(messages_array[i], "\n")] = '\0'; } } fclose(myFile); } else { printf("未找到保存文件,使用默认消息\n"); } signal_handler(); // 保持程序运行的readline循环 char *input; while ((input = readline(">> ")) != NULL) { add_history(input); // 可在此扩展用户命令逻辑,比如处理修改消息的指令 free(input); } // 程序退出前清理资源 for (int i = 0; i < MESSAGE_COUNT; i++) { free(messages_array[i]); } notify_uninit(); return EXIT_SUCCESS; }
额外说明
- 新增
init_messages函数统一初始化数组,确保每个元素都分配了可写内存,避免了free字符串字面量的错误。 - 用
strncpy替代strcpy并结合长度限制,防止缓冲区溢出。 - 修正了main函数的分支逻辑:之前你把文件读取代码写在了libnotify初始化失败的分支里,现在调整到了正确的执行路径。
- 完善了内存释放逻辑,避免内存泄漏。
- 保存文件时每个消息单独占一行,加载时自动去除换行符,保证消息格式正确。
备注:内容来源于stack exchange,提问作者Ibolya Vojtek
相关产品推荐
相关产品推荐

