You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中如何将数组适配fprintf和fgets以实现文本存储与加载功能

C语言中如何将数组适配fprintf和fgets以实现文本存储与加载功能

嘿,刚学C遇到这种问题太正常了,我来帮你一步步拆解问题出在哪,以及怎么修改代码来实现你的便签保存/加载功能~

先看你遇到的核心错误原因

你收到的警告和错误主要是参数类型不匹配和对C语言中指针/数组的理解偏差导致的:

  1. fprintf参数错误:messages_array是char*类型的数组,messages_array[i]本身就是指向字符串的指针,你传&messages_array[i]相当于传了char**类型,而fprintf的第二个参数需要的是const char*格式字符串,正确的做法是用格式符%s来输出字符串。
  2. fgets参数错误:同理,fgets的第一个参数需要char*类型的缓冲区,你传&messages_array[i]是char**,类型完全不兼容,直接传messages_array[i]即可,但要确保这个指针指向的是可写的、足够大的内存空间。
  3. 除此之外,代码里还有几个容易踩的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:15:28