diff --git a/src/huffman/huffman.c b/src/huffman/huffman.c index c2abd0f..fd2fd49 100644 --- a/src/huffman/huffman.c +++ b/src/huffman/huffman.c @@ -4,6 +4,12 @@ #include #include +// 前向声明所有静态函数 +static void free_huffman_tree(MinHeapNode* node); +static void on_encode_clicked(GtkWidget *widget, gpointer user_data); +static void on_decode_clicked(GtkWidget *widget, gpointer data); + +// 全局变量 static GtkWidget *text_view_input; static GtkWidget *text_view_output; static GtkWidget *text_view_codes; @@ -12,10 +18,18 @@ static MinHeapNode* huffman_tree = NULL; static HuffmanCode huffman_codes[MAX_CHAR]; static int code_count = 0; +// 释放哈夫曼树的内存 +static void free_huffman_tree(MinHeapNode* node) { + if (node == NULL) return; + free_huffman_tree(node->left); + free_huffman_tree(node->right); + free(node); +} + // 存储编码 void store_code(char data, char* code) { huffman_codes[code_count].character = data; - huffman_codes[code_count].code = strdup(code); + huffman_codes[code_count].code = g_strdup(code); code_count++; } @@ -32,7 +46,7 @@ char* get_code(char c) { // 清理编码表 void clear_huffman_codes() { for (int i = 0; i < code_count; i++) { - free(huffman_codes[i].code); + g_free(huffman_codes[i].code); } code_count = 0; } @@ -190,98 +204,118 @@ void count_frequency(const char* text, char* data, int* freq, int* size) { } // 编码回调函数 -static void on_encode_clicked(GtkWidget *widget, gpointer data) { +static void on_encode_clicked(GtkWidget *widget, gpointer user_data) { + (void)user_data; + + // 清理之前的状态 + clear_huffman_codes(); + if (huffman_tree) { + free_huffman_tree(huffman_tree); + huffman_tree = NULL; + } + + // 获取输入文本 GtkTextBuffer *input_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input)); - GtkTextBuffer *output_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); - GtkTextBuffer *codes_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_codes)); - GtkTextIter start, end; gtk_text_buffer_get_bounds(input_buffer, &start, &end); - char *text = gtk_text_buffer_get_text(input_buffer, &start, &end, FALSE); - - // 检查输入 - if (!text || strlen(text) == 0) { - handle_error(gtk_widget_get_toplevel(widget), - ERROR_INVALID_INPUT, - "请输入要编码的文本"); - g_free(text); - return; - } - - // 内存分配检查 - char *encoded = malloc(strlen(text) * 8 + 1); - if (!encoded) { - handle_error(gtk_widget_get_toplevel(widget), - ERROR_MEMORY_ALLOCATION, - "内存分配失败"); - g_free(text); + char *input_text = gtk_text_buffer_get_text(input_buffer, &start, &end, FALSE); + + if (!input_text || strlen(input_text) == 0) { + handle_error(gtk_widget_get_toplevel(widget), ERROR_INVALID_INPUT, "请输入要编码的文本"); + g_free(input_text); return; } - - // 清理之前的编码表 - clear_huffman_codes(); - // 清空输出 - gtk_text_buffer_set_text(output_buffer, "", -1); - gtk_text_buffer_set_text(codes_buffer, "", -1); - - // 统计频率 - char char_data[MAX_CHAR]; + // 统计字符频率 + char char_array[MAX_CHAR]; int freq[MAX_CHAR]; - int size; - count_frequency(text, char_data, freq, &size); - + int size = 0; + count_frequency(input_text, char_array, freq, &size); + // 构建哈夫曼树 - huffman_tree = build_huffman_tree(char_data, freq, size); - - // 生成并显示编码表 + huffman_tree = build_huffman_tree(char_array, freq, size); + if (!huffman_tree) { + handle_error(gtk_widget_get_toplevel(widget), ERROR_MEMORY_ALLOCATION, "构建哈夫曼树失败"); + g_free(input_text); + return; + } + + // 清空编码表显示 + gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_codes)), "", -1); + + // 生成编码表 char code_str[MAX_TREE_HT]; - print_codes(huffman_tree, code_str, 0, codes_buffer); - + print_codes(huffman_tree, code_str, 0, gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_codes))); + // 编码文本 - encode_text(text, output_buffer); - - g_free(text); - free(encoded); + GString *encoded = g_string_new(""); + for (char *p = input_text; *p; p++) { + char *code = get_code(*p); + if (code) { + g_string_append(encoded, code); + g_string_append(encoded, " "); // 添加空格分隔 + } + } + + // 显示结果 + GtkTextBuffer *output_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); + gtk_text_buffer_set_text(output_buffer, encoded->str, -1); + + // 清理资源 + g_string_free(encoded, TRUE); + g_free(input_text); } // 解码回调函数 static void on_decode_clicked(GtkWidget *widget, gpointer data) { + (void)data; + + // 获取输入的编码 GtkTextBuffer *input_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input)); - GtkTextBuffer *output_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); - GtkTextIter start, end; gtk_text_buffer_get_bounds(input_buffer, &start, &end); char *encoded_text = gtk_text_buffer_get_text(input_buffer, &start, &end, FALSE); - - // 检查输入是否为空 + if (!encoded_text || strlen(encoded_text) == 0) { - gtk_text_buffer_set_text(output_buffer, "请输入要解码的二进制串", -1); + handle_error(gtk_widget_get_toplevel(widget), ERROR_INVALID_INPUT, "请输入要解码的二进制串"); g_free(encoded_text); return; } - - // 检查是否已经构建了哈夫曼树 + + // 检查输入是否为有效的二进制串 + for (char *p = encoded_text; *p; p++) { + if (*p != '0' && *p != '1' && *p != ' ' && *p != '\n') { + handle_error(gtk_widget_get_toplevel(widget), ERROR_INVALID_INPUT, + "输入必须是二进制串(只包含0和1,可以包含空格和换行)"); + g_free(encoded_text); + return; + } + } + + // 检查是否有哈夫曼树 if (!huffman_tree) { - gtk_text_buffer_set_text(output_buffer, "请先进行编码操作", -1); + handle_error(gtk_widget_get_toplevel(widget), ERROR_INVALID_OPERATION, + "请先进行编码操作以生成哈夫曼树"); g_free(encoded_text); return; } - - // 检查输入是否只包含0和1 - for (int i = 0; encoded_text[i]; i++) { - if (encoded_text[i] != '0' && encoded_text[i] != '1' && encoded_text[i] != ' ') { - gtk_text_buffer_set_text(output_buffer, "输入必须是由0和1组成的二进制串", -1); - g_free(encoded_text); - return; - } + + // 解码 + char *decoded = decode_text(encoded_text, huffman_tree); + if (!decoded) { + handle_error(gtk_widget_get_toplevel(widget), ERROR_INVALID_OPERATION, + "解码失败:无效的编码或内存不足"); + g_free(encoded_text); + return; } - - char *decoded_text = decode_text(encoded_text, huffman_tree); - gtk_text_buffer_set_text(output_buffer, decoded_text, -1); - + + // 显示结果 + GtkTextBuffer *output_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); + gtk_text_buffer_set_text(output_buffer, decoded, -1); + + // 清理资源 g_free(encoded_text); - free(decoded_text); + free(decoded); } // 编码文本 @@ -300,22 +334,57 @@ void encode_text(const char* text, GtkTextBuffer* output_buffer) { // 解码文本 char* decode_text(const char* encoded_text, MinHeapNode* root) { - char* result = malloc(strlen(encoded_text) + 1); + if (!root || !encoded_text) return NULL; + + // 预分配足够的空间 + size_t max_len = strlen(encoded_text) + 1; + char* result = malloc(max_len); + if (!result) return NULL; + int result_index = 0; MinHeapNode* current = root; - for (int i = 0; encoded_text[i] != '\0'; i++) { - if (encoded_text[i] == ' ') continue; // 跳过分隔空格 + // 添加安全检查 + size_t max_iterations = strlen(encoded_text) * 2; + size_t iteration_count = 0; + + for (const char* p = encoded_text; *p && result_index < max_len - 1; p++) { + if (*p == ' ' || *p == '\n') continue; // 跳过空格和换行 + + if (*p != '0' && *p != '1') { + free(result); + return NULL; + } - if (encoded_text[i] == '0') - current = current->left; - else if (encoded_text[i] == '1') - current = current->right; - - if (current->left == NULL && current->right == NULL) { + if (!current) { // 添加空指针检查 + free(result); + return NULL; + } + + current = (*p == '0') ? current->left : current->right; + + if (!current) { // 添加空指针检查 + free(result); + return NULL; + } + + // 到达叶子节点 + if (!current->left && !current->right) { result[result_index++] = current->data; current = root; } + + // 安全检查 + if (++iteration_count > max_iterations) { + free(result); + return NULL; + } + } + + // 确保解码完成 + if (current != root) { + free(result); + return NULL; } result[result_index] = '\0'; @@ -360,7 +429,7 @@ GtkWidget* create_huffman_page(void) { g_signal_connect(encode_button, "clicked", G_CALLBACK(on_encode_clicked), NULL); g_signal_connect(decode_button, "clicked", G_CALLBACK(on_decode_clicked), NULL); - // 创��编码表显示区域 + // 创建编码表显示区域 GtkWidget *codes_frame = gtk_frame_new("编码表"); gtk_box_pack_start(GTK_BOX(page), codes_frame, TRUE, TRUE, 5); diff --git a/src/main.c b/src/main.c index 12b2111..f2e34d8 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,8 @@ #include "utils/error_handler.h" #define BACKGROUND_CHANGE_INTERVAL 300000 +#define DEFAULT_WINDOW_WIDTH 1366 +#define DEFAULT_WINDOW_HEIGHT 768 typedef struct { GtkWidget *window; @@ -173,7 +175,7 @@ static gboolean on_window_resize(GtkWidget *widget, // 根据窗口宽度调整字体大小 if (width != last_width) { - app_ctx.font_scale_factor = (double)width / 1920.0; // 以1920像素为基准 + app_ctx.font_scale_factor = (double)width / 1920.0; gtk_range_set_value(GTK_RANGE(app_ctx.font_scale), app_ctx.font_scale_factor); update_font_size(); last_width = width; @@ -182,7 +184,7 @@ static gboolean on_window_resize(GtkWidget *widget, if (resize_timer > 0) { g_source_remove(resize_timer); } - resize_timer = g_timeout_add(150, update_background, NULL); + resize_timer = g_timeout_add(500, (GSourceFunc)update_background, NULL); return FALSE; } @@ -227,6 +229,18 @@ static void load_backgrounds(void) { closedir(dir); } +// 窗口大小变化的回调函数 +static void on_window_size_changed(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) { + // 获取新的窗口尺寸 + int width, height; + gtk_window_get_size(GTK_WINDOW(widget), &width, &height); + + // 更新UI元素大小或进行其他必要的调整 + // ... 根据需要添加代码 ... +} + int main(int argc, char *argv[]) { gtk_init(&argc, &argv); srand(time(NULL)); @@ -237,17 +251,22 @@ int main(int argc, char *argv[]) { // 创建主窗口 app_ctx.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(app_ctx.window), "算法与数据结构课程设计"); - - // 设置窗口默认大小 - GdkDisplay *display = gdk_display_get_default(); - GdkMonitor *monitor = gdk_display_get_primary_monitor(display); - GdkRectangle workarea = {0}; - gdk_monitor_get_workarea(monitor, &workarea); + gtk_window_set_title(GTK_WINDOW(app_ctx.window), "算法与数据结构课程设计展示系统 by 210052202019 龙正"); + + // 设置默认窗口大小 + gtk_window_set_default_size(GTK_WINDOW(app_ctx.window), + DEFAULT_WINDOW_WIDTH, + DEFAULT_WINDOW_HEIGHT); + + // 允许调整窗口大小 + gtk_window_set_resizable(GTK_WINDOW(app_ctx.window), TRUE); + + // 设置窗口最小尺寸(可选) + gtk_widget_set_size_request(app_ctx.window, 800, 600); - int width = (workarea.width * 80) / 100; - int height = (workarea.height * 80) / 100; - gtk_window_set_default_size(GTK_WINDOW(app_ctx.window), width, height); + // 添加窗口大小变化的回调(如果需要响应窗口大小变化) + g_signal_connect(G_OBJECT(app_ctx.window), "size-allocate", + G_CALLBACK(on_window_size_changed), NULL); // 创建叠加容器 GtkWidget *overlay = gtk_overlay_new(); @@ -315,7 +334,7 @@ int main(int argc, char *argv[]) { g_signal_connect(app_ctx.window, "configure-event", G_CALLBACK(on_window_resize), NULL); // 设置背景更新定时器 - g_timeout_add(100, update_background, NULL); + g_timeout_add(BACKGROUND_CHANGE_INTERVAL, update_background, NULL); // 初始化字体相关变量 app_ctx.base_font_size = 14; // 基础字体大小 diff --git a/src/recursion/recursion.c b/src/recursion/recursion.c index 1791ee8..947310c 100644 --- a/src/recursion/recursion.c +++ b/src/recursion/recursion.c @@ -16,27 +16,53 @@ static GtkWidget *text_view_output; +// 前向声明 +static void replace_chinese_punctuation(char* str); + // 处理单个文件的内容 void process_file(const char* content, GtkTextBuffer *output_buffer) { GtkTextIter end; gtk_text_buffer_get_end_iter(output_buffer, &end); // 格式化处理 - char* formatted = strdup(content); + char* formatted = g_strdup(content); if (formatted) { // 替换中文标点为英文标点 - for (char* p = formatted; *p; p++) { - if (*p == ':') *p = ':'; - if (*p == '(') *p = '('; - if (*p == ')') *p = ')'; - } + replace_chinese_punctuation(formatted); gtk_text_buffer_insert(output_buffer, &end, formatted, -1); - free(formatted); + g_free(formatted); } else { gtk_text_buffer_insert(output_buffer, &end, content, -1); } } +// 修改中文字符处理 +static void replace_chinese_punctuation(char* str) { + unsigned char* p = (unsigned char*)str; + while (*p) { + if (p[0] == 0xEF && p[1] == 0xBC) { + if (p[2] == 0x9A) { // ':' + *str = ':'; + p += 3; + } else if (p[2] == 0x88) { // '(' + *str = '('; + p += 3; + } else if (p[2] == 0x89) { // ')' + *str = ')'; + p += 3; + } else { + *str = *p; + p++; + } + } else { + *str = *p; + p++; + } + str++; + } + *str = '\0'; +} + // 递归处理include指令 void replace_includes(const char* filename, GtkTextBuffer *output_buffer, int depth) { GtkTextIter end; @@ -115,15 +141,15 @@ void replace_includes(const char* filename, GtkTextBuffer *output_buffer, int de } snprintf(full_path, PATH_MAX, "%s/%s", dirname, included_file); g_free(dirname); - + // 输出包含文件的开始标记 gtk_text_buffer_get_end_iter(output_buffer, &end); gtk_text_buffer_insert(output_buffer, &end, "\n// ----- Begin included file: ", -1); gtk_text_buffer_insert(output_buffer, &end, included_file, -1); gtk_text_buffer_insert(output_buffer, &end, " -----\n\n", -1); - + // 递归处理包含文件 replace_includes(full_path, output_buffer, depth + 1); - + // 输出包含文件的结束标记 gtk_text_buffer_get_end_iter(output_buffer, &end); gtk_text_buffer_insert(output_buffer, &end, "\n// ----- End included file: ", -1); diff --git a/src/sorting/sorting.c b/src/sorting/sorting.c index 0540597..92c6bb8 100644 --- a/src/sorting/sorting.c +++ b/src/sorting/sorting.c @@ -3,10 +3,21 @@ #include #include #include +#include +#define _GNU_SOURCE static GtkWidget *text_view_input; static GtkWidget *text_view_output; +// 函数声明 +static int compare_ints(const void* a, const void* b); +static char* array_to_string(const int arr[], int size); + +// 比较函数用于qsort +static int compare_ints(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + // 从A构造D void construct_D_from_A(const int* A, int N, int* D, int* D_size) { if (!A || !D || !D_size || N <= 0) { @@ -14,59 +25,80 @@ void construct_D_from_A(const int* A, int N, int* D, int* D_size) { return; } - if (N > 100) { // 假设最大允许100个元素 + // 检查A[0]是否为0 + if (A[0] != 0) { + handle_error(NULL, ERROR_INVALID_INPUT, "A序列的第一个数必须为0"); + return; + } + + if (N > MAX_ARRAY_SIZE) { handle_error(NULL, ERROR_BUFFER_OVERFLOW, "输入数组过大"); return; } + // 计算D序列的大小 + int expected_size = (N * (N - 1)) / 2; + if (expected_size > MAX_ARRAY_SIZE) { + handle_error(NULL, ERROR_BUFFER_OVERFLOW, "结果数组将超出最大限制"); + return; + } + int k = 0; - for (int i = 1; i < N; i++) { - for (int j = 0; j < i; j++) { + for (int i = 1; i < N && k < MAX_ARRAY_SIZE; i++) { + for (int j = 0; j < i && k < MAX_ARRAY_SIZE; j++) { D[k++] = A[i] - A[j]; } } *D_size = k; - sort_array(D, k); -} -// 排序数组 -void sort_array(int* arr, int size) { - for (int i = 0; i < size - 1; i++) { - for (int j = 0; j < size - i - 1; j++) { - if (arr[j] > arr[j + 1]) { - int temp = arr[j]; - arr[j] = arr[j + 1]; - arr[j + 1] = temp; - } - } - } + // 使用快速排序 + sort_array(D, k); } // 从D构造A void construct_A_from_D(const int* D, int D_size, int* A, int* A_size) { + if (!D || !A || !A_size || D_size <= 0) { + handle_error(NULL, ERROR_INVALID_INPUT, "无效的输入参数"); + return; + } + + if (D_size > MAX_ARRAY_SIZE) { + handle_error(NULL, ERROR_BUFFER_OVERFLOW, "输入数组过大"); + return; + } + + // 创建查找表 + int min_val = D[0], max_val = D[0]; + for (int i = 1; i < D_size; i++) { + if (D[i] < min_val) min_val = D[i]; + if (D[i] > max_val) max_val = D[i]; + } + + int range = max_val - min_val + 1; + char* exists = calloc(range, sizeof(char)); + if (!exists) { + handle_error(NULL, ERROR_MEMORY_ALLOCATION, "内存分配失败"); + return; + } + + // 标记所有存在的差值 + for (int i = 0; i < D_size; i++) { + exists[D[i] - min_val] = 1; + } + // A[0] = 0 A[0] = 0; *A_size = 1; // 从D中选择合适的数构造A - for (int i = 0; i < D_size && *A_size < 100; i++) { + for (int i = 0; i < D_size && *A_size < MAX_ARRAY_SIZE; i++) { int candidate = D[i]; int valid = 1; // 检查是否可以将candidate添加到A中 - for (int j = 0; j < *A_size; j++) { + for (int j = 0; j < *A_size && valid; j++) { int diff = abs(candidate - A[j]); - int found = 0; - - // 检查差值是否在D中 - for (int k = 0; k < D_size; k++) { - if (D[k] == diff) { - found = 1; - break; - } - } - - if (!found) { + if (diff < range && !exists[diff - min_val]) { valid = 0; break; } @@ -75,13 +107,24 @@ void construct_A_from_D(const int* D, int D_size, int* A, int* A_size) { if (valid) { A[*A_size] = candidate; (*A_size)++; + + if (*A_size >= MAX_ARRAY_SIZE) { + break; + } } } + + free(exists); + + // 检查是否成功构造了A序列 + if (*A_size == 1) { + handle_error(NULL, ERROR_INVALID_INPUT, "无法构造有效的A序列:输入的D序列不是有效的差分序列"); + } } // 重命名函数并确保它是静态的 static int parse_array_numbers(const char* input, int* numbers, int* count) { - char* input_copy = strdup(input); + char* input_copy = g_strdup(input); char* token = strtok(input_copy, " ,\n"); *count = 0; @@ -104,6 +147,8 @@ static int parse_array_numbers(const char* input, int* numbers, int* count) { // 构造D的回调函数 static void on_construct_D_clicked(GtkWidget *widget, gpointer data) { + (void)data; + ErrorContext error_ctx; init_error_context(&error_ctx); @@ -122,8 +167,8 @@ static void on_construct_D_clicked(GtkWidget *widget, gpointer data) { gtk_text_buffer_get_bounds(buffer, &start, &end); input_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); - if (!input_text) { - THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法获取输入文本"); + if (!input_text || strlen(input_text) == 0) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "请输入要处理的数列"); } numbers = malloc(MAX_NUMBERS * sizeof(int)); @@ -135,14 +180,24 @@ static void on_construct_D_clicked(GtkWidget *widget, gpointer data) { int count; if (!parse_array_numbers(input_text, numbers, &count)) { - THROW(&error_ctx, ERROR_INVALID_INPUT, "无效的输入格式"); + THROW(&error_ctx, ERROR_INVALID_INPUT, "无效的输入格式 - 请输入用空格或逗号分隔的数字"); } - // 构造D序列 - construct_D_sequence(numbers, count, result); + if (count < 2) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "请至少输入两个数字"); + } + + // 检查第一个数是否为0 + if (numbers[0] != 0) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "A序列的第一个数必须为0"); + } + + // 计算D序列 + int D_size; + construct_D_from_A(numbers, count, result, &D_size); // 显示结果 - result_str = array_to_string(result, count); + result_str = array_to_string(result, D_size); if (!result_str) { THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法生成结果字符串"); } @@ -163,6 +218,8 @@ static void on_construct_D_clicked(GtkWidget *widget, gpointer data) { // 构造A的回调函数 static void on_construct_A_clicked(GtkWidget *widget, gpointer data) { + (void)data; + ErrorContext error_ctx; init_error_context(&error_ctx); @@ -181,8 +238,8 @@ static void on_construct_A_clicked(GtkWidget *widget, gpointer data) { gtk_text_buffer_get_bounds(buffer, &start, &end); input_text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); - if (!input_text) { - THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法获取输入文本"); + if (!input_text || strlen(input_text) == 0) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "请输入要处理的数列"); } numbers = malloc(MAX_NUMBERS * sizeof(int)); @@ -194,14 +251,25 @@ static void on_construct_A_clicked(GtkWidget *widget, gpointer data) { int count; if (!parse_array_numbers(input_text, numbers, &count)) { - THROW(&error_ctx, ERROR_INVALID_INPUT, "无效的输入格式"); + THROW(&error_ctx, ERROR_INVALID_INPUT, "无效的输入格式 - 请输入用空格或逗号分隔的数字"); + } + + if (count < 1) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "请至少输入一个数字"); } - // 构造A序列 - construct_A_sequence(numbers, count, result); + // 计算A序列 + int A_size; + construct_A_from_D(numbers, count, result, &A_size); + + // 检查结果的第一个数是否为0 + if (A_size > 0 && result[0] != 0) { + // 如果第一个数不是0,说明这个D序列不是有效的差分序列 + THROW(&error_ctx, ERROR_INVALID_INPUT, "无法构造有效的A序列:输入的D序列不是有效的差分序列(A序列必须从0开始)"); + } // 显示结果 - result_str = array_to_string(result, count); + result_str = array_to_string(result, A_size); if (!result_str) { THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法生成结果字符串"); } @@ -278,8 +346,16 @@ GtkWidget* create_sorting_page(void) { return page; } +// 排序数组 - 使用快速排序 +void sort_array(int* arr, int size) { + if (!arr || size <= 0) return; + qsort(arr, size, sizeof(int), compare_ints); +} + // 将数组转换为字符串 static char* array_to_string(const int arr[], int size) { + if (!arr || size <= 0) return NULL; + char* result = malloc(size * 12 + 1); // 每个数字最多10位,加上逗号和空格 if (!result) return NULL; @@ -294,15 +370,3 @@ static char* array_to_string(const int arr[], int size) { } return result; } - -// 构造D序列 -static void construct_D_sequence(const int* numbers, int count, int* result) { - int D_size; - construct_D_from_A(numbers, count, result, &D_size); -} - -// 构造A序列 -static void construct_A_sequence(const int* numbers, int count, int* result) { - int A_size; - construct_A_from_D(numbers, count, result, &A_size); -} diff --git a/src/sorting/sorting.h b/src/sorting/sorting.h index d89df80..4b1529c 100644 --- a/src/sorting/sorting.h +++ b/src/sorting/sorting.h @@ -9,11 +9,6 @@ GtkWidget* create_sorting_page(void); void construct_D_from_A(const int* A, int N, int* D, int* D_size); void construct_A_from_D(const int* D, int D_size, int* A, int* A_size); -void sort_array(int* arr, int size); - -// 内部函数声明 -static char* array_to_string(const int arr[], int size); -static void construct_D_sequence(const int* numbers, int count, int* result); -static void construct_A_sequence(const int* numbers, int count, int* result); +void sort_array(int* arr, int size); // 这个需要实现 #endif diff --git a/src/union/union.c b/src/union/union.c index 07f08fe..99829b3 100644 --- a/src/union/union.c +++ b/src/union/union.c @@ -6,34 +6,52 @@ #include #include -// 定义全局变量 -GtkWidget *text_view_input1; -GtkWidget *text_view_input2; -GtkWidget *text_view_output; +// 定义全局GUI组件变量 +// 这些变量在整个模块中共享,用于用户交互 +GtkWidget *text_view_input1; // 第一个集合的输入文本框 +GtkWidget *text_view_input2; // 第二个集合的输入文本框 +GtkWidget *text_view_output; // 结果输出文本框 // 定义静态变量 +// result数组用于存储集合运算的结果 static int result[MAX_SET_SIZE]; -// 函数声明(移到使用之前) +// 函数声明(静态函数) static bool contains(const int arr[], int size, int value); static char* array_to_string(const int arr[], int size); +/** + * 解析输入字符串为集合 + * 参数: + * input: 输入字符串,格式为以空格或逗号分隔的整数 + * set: 输出数组,用于存储解析后的整数 + * 返回值: + * 成功返回解析得到的整数个数,失败返回-1 + * 算法原理: + * 1. 跳过所有空白字符和逗号 + * 2. 使用strtol函数将字符串转换为整数 + * 3. 检查转换是否成功 + * 4. 将成功转换的整数存入数组 + */ static int parse_union_set(const char* input, int* set) { int count = 0; const unsigned char* p = (const unsigned char*)input; while (*p) { + // 跳过空白字符和逗号 while (*p && (isspace(*p) || *p == ',')) { p++; } if (!*p) break; + // 转换字符串为整数 char* end; int num = strtol((const char*)p, &end, 10); + // 检查转换是否成功 if ((const char*)p == end) { - return -1; + return -1; // 转换失败 } set[count++] = num; @@ -43,7 +61,18 @@ static int parse_union_set(const char* input, int* set) { return count; } -// 检查数组中是否包含某个元素 +/** + * 检查数组中是否包含某个元素 + * 参数: + * arr: 要搜索的数组 + * size: 数组大小 + * value: 要查找的值 + * 返回值: + * 找到返回true,否则返回false + * 算法原理: + * 使用线性搜索,遍历数组查找指定值 + * 时间复杂度:O(n) + */ static bool contains(const int arr[], int size, int value) { for (int i = 0; i < size; i++) { if (arr[i] == value) { @@ -53,9 +82,20 @@ static bool contains(const int arr[], int size, int value) { return false; } -// 将数组转换为字符串 +/** + * 将整数数组转换为字符串 + * 参数: + * arr: 要转换的数组 + * size: 数组大小 + * 返回值: + * 转换后的字符串,调用者负责释放内存 + * 算法原理: + * 1. 为每个数字分配足够的空间(最多12字符,包括符号、数字和分隔符) + * 2. 使用snprintf安全地转换每个数字 + * 3. 用逗号和空格分隔每个数字 + */ static char* array_to_string(const int arr[], int size) { - char* result = malloc(size * 12 + 1); + char* result = malloc(size * 12 + 1); // 每个数字最多10位,加上逗号和空格 if (!result) return NULL; result[0] = '\0'; @@ -70,6 +110,18 @@ static char* array_to_string(const int arr[], int size) { return result; } +/** + * 执行集合并集运算 + * 参数: + * widget: GTK部件指针 + * data: 用户数据(未使用) + * 算法原理: + * 1. 获取两个输入集合 + * 2. 将第一个集合的所有元素添加到结果中 + * 3. 检查第二个集合的每个元素,如果不在结果中则添加 + * 4. 对结果进行排序 + * 时间复杂度:O(n log n),主要来自排序步骤 + */ void perform_set_union(GtkWidget *widget, gpointer data) { (void)widget; (void)data; @@ -241,51 +293,103 @@ void perform_set_intersection(GtkWidget *widget, gpointer data) { }); } -void perform_set_difference(GtkWidget *widget, gpointer data) { +void perform_set_difference_both(GtkWidget *widget, gpointer data) { (void)widget; (void)data; - GtkTextBuffer *buffer1 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input1)); - GtkTextBuffer *buffer2 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input2)); - - GtkTextIter start, end; - gtk_text_buffer_get_bounds(buffer1, &start, &end); - char *set1_text = gtk_text_buffer_get_text(buffer1, &start, &end, FALSE); - - gtk_text_buffer_get_bounds(buffer2, &start, &end); - char *set2_text = gtk_text_buffer_get_text(buffer2, &start, &end, FALSE); - - int set1[MAX_SET_SIZE], set2[MAX_SET_SIZE]; - int size1 = parse_union_set(set1_text, set1); - int size2 = parse_union_set(set2_text, set2); - - g_free(set1_text); - g_free(set2_text); + ErrorContext error_ctx; + init_error_context(&error_ctx); - if (size1 < 0 || size2 < 0) { - log_error(2, "无效输入 - 请输入两个集合"); - return; - } + GtkTextBuffer *buffer1 = NULL, *buffer2 = NULL; + char *set1_text = NULL, *set2_text = NULL; + int *set1 = NULL, *set2 = NULL; + char *result_str_ab = NULL, *result_str_ba = NULL; + gchar *display = NULL; + int result_ab[MAX_SET_SIZE], result_ba[MAX_SET_SIZE]; - // 计算差集 (set1 - set2) - int result_size = 0; - for (int i = 0; i < size1; i++) { - if (!contains(set2, size2, set1[i])) { - result[result_size++] = set1[i]; + TRY(&error_ctx) { + // 获取输入 + buffer1 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input1)); + buffer2 = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_input2)); + + if (!buffer1 || !buffer2) { + THROW(&error_ctx, ERROR_INVALID_OPERATION, "无法获取输入缓冲区"); } + + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer1, &start, &end); + set1_text = gtk_text_buffer_get_text(buffer1, &start, &end, FALSE); + + gtk_text_buffer_get_bounds(buffer2, &start, &end); + set2_text = gtk_text_buffer_get_text(buffer2, &start, &end, FALSE); + + if (!set1_text || !set2_text) { + THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "内存分配失败"); + } + + set1 = malloc(MAX_SET_SIZE * sizeof(int)); + set2 = malloc(MAX_SET_SIZE * sizeof(int)); + + if (!set1 || !set2) { + THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "内存分配失败"); + } + + int size1 = parse_union_set(set1_text, set1); + int size2 = parse_union_set(set2_text, set2); + + if (size1 < 0 || size2 < 0) { + THROW(&error_ctx, ERROR_INVALID_INPUT, "无效输入 - 请输入有效的集合"); + } + + // 计算 A-B + int result_size_ab = 0; + for (int i = 0; i < size1; i++) { + if (!contains(set2, size2, set1[i])) { + result_ab[result_size_ab++] = set1[i]; + } + } + + // 计算 B-A + int result_size_ba = 0; + for (int i = 0; i < size2; i++) { + if (!contains(set1, size1, set2[i])) { + result_ba[result_size_ba++] = set2[i]; + } + } + + // 对结果排序 + sort_array(result_ab, result_size_ab); + sort_array(result_ba, result_size_ba); + + // 生成结果字符串 + result_str_ab = array_to_string(result_ab, result_size_ab); + result_str_ba = array_to_string(result_ba, result_size_ba); + + if (!result_str_ab || !result_str_ba) { + THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法生成结果字符串"); + } + + display = g_strdup_printf("差集(A-B)结果:{%s}\n差集(B-A)结果:{%s}", + result_str_ab, result_str_ba); + if (!display) { + THROW(&error_ctx, ERROR_MEMORY_ALLOCATION, "无法生成显示字符串"); + } + + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); + gtk_text_buffer_set_text(buffer, display, -1); } - - // 对结果排序 - sort_array(result, result_size); - - // 显示结果 - char *result_str = array_to_string(result, result_size); - gchar *display = g_strdup_printf("差集结果:{%s}", result_str); - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_output)); - gtk_text_buffer_set_text(buffer, display, -1); - - free(result_str); - g_free(display); + CATCH(&error_ctx) { + handle_error(gtk_widget_get_toplevel(widget), error_ctx.code, error_ctx.message); + } + FINALLY({ + g_free(set1_text); + g_free(set2_text); + free(set1); + free(set2); + free(result_str_ab); + free(result_str_ba); + g_free(display); + }); } GtkWidget* create_union_page(void) { @@ -326,7 +430,7 @@ GtkWidget* create_union_page(void) { GtkWidget *union_button = gtk_button_new_with_label("并集"); GtkWidget *intersection_button = gtk_button_new_with_label("交集"); - GtkWidget *difference_button = gtk_button_new_with_label("差集"); + GtkWidget *difference_button = gtk_button_new_with_label("差集(A-B和B-A)"); gtk_box_pack_start(GTK_BOX(button_box), union_button, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(button_box), intersection_button, TRUE, TRUE, 5); @@ -334,7 +438,7 @@ GtkWidget* create_union_page(void) { g_signal_connect(union_button, "clicked", G_CALLBACK(perform_set_union), NULL); g_signal_connect(intersection_button, "clicked", G_CALLBACK(perform_set_intersection), NULL); - g_signal_connect(difference_button, "clicked", G_CALLBACK(perform_set_difference), NULL); + g_signal_connect(difference_button, "clicked", G_CALLBACK(perform_set_difference_both), NULL); // 创建输出区域 GtkWidget *output_frame = gtk_frame_new("运算结果"); diff --git a/src/union/union.h b/src/union/union.h index bf728e9..d46bebc 100644 --- a/src/union/union.h +++ b/src/union/union.h @@ -13,7 +13,7 @@ extern GtkWidget *text_view_output; // 函数声明 void perform_set_union(GtkWidget *widget, gpointer data); void perform_set_intersection(GtkWidget *widget, gpointer data); -void perform_set_difference(GtkWidget *widget, gpointer data); +void perform_set_difference_both(GtkWidget *widget, gpointer data); GtkWidget* create_union_page(void); #endif diff --git a/src/utils/error_handler.c b/src/utils/error_handler.c index 27e2bb5..98c49aa 100644 --- a/src/utils/error_handler.c +++ b/src/utils/error_handler.c @@ -6,21 +6,9 @@ void init_error_context(ErrorContext *ctx) { ctx->code = ERROR_NONE; ctx->message[0] = '\0'; - ctx->details[0] = '\0'; ctx->has_error = FALSE; } -// 清理错误上下文 -void clear_error_context(ErrorContext *ctx) { - init_error_context(ctx); -} - -// 添加全局错误处理器 -static void global_error_handler(const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) __attribute__((unused)); - // 获取错误代码对应的描述 const char* get_error_string(ErrorCode code) { switch (code) { @@ -44,7 +32,7 @@ const char* get_error_string(ErrorCode code) { } // 显示错误对话框 -void show_error_dialog(GtkWidget *parent, const char *title, const char *message) { +static void show_error_dialog(GtkWidget *parent, const char *title, const char *message) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, @@ -55,12 +43,6 @@ void show_error_dialog(GtkWidget *parent, const char *title, const char *message gtk_widget_destroy(dialog); } -// 在本地视图中显示错误信息 -void show_error_message(GtkWidget *text_view, const char *message) { - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); - gtk_text_buffer_set_text(buffer, message, -1); -} - // 记录错误到日志文件 void log_error(ErrorCode code, const char *message) { time_t now; diff --git a/src/utils/error_handler.h b/src/utils/error_handler.h index bed4065..ee94799 100644 --- a/src/utils/error_handler.h +++ b/src/utils/error_handler.h @@ -22,7 +22,6 @@ typedef enum { typedef struct { ErrorCode code; char message[256]; - char details[1024]; jmp_buf jump_buffer; gboolean has_error; } ErrorContext; @@ -33,16 +32,14 @@ typedef struct { #define THROW(ctx, error_code, msg) do { \ (ctx)->code = (error_code); \ strncpy((ctx)->message, (msg), sizeof((ctx)->message) - 1); \ + (ctx)->message[sizeof((ctx)->message) - 1] = '\0'; \ (ctx)->has_error = TRUE; \ longjmp((ctx)->jump_buffer, 1); \ } while(0) #define FINALLY(code) code -// 错误处理函数 +// 错误处理函数声明 void init_error_context(ErrorContext *ctx); -void clear_error_context(ErrorContext *ctx); -void show_error_dialog(GtkWidget *parent, const char *title, const char *message); -void show_error_message(GtkWidget *text_view, const char *message); void handle_error(GtkWidget *parent, ErrorCode code, const char *message); const char* get_error_string(ErrorCode code); void log_error(ErrorCode code, const char *message);