GTK3 C语言中如何正确将控件数组作为参数传递给回调函数?
问题根源
你的问题核心是栈内存失效:entries是create_Enterprise_form函数内的栈局部数组,当函数执行完毕返回窗口对象后,栈上的entries数组会被系统回收。点击Save按钮触发回调时,访问的是已经失效的内存地址,此时entries指向的内容已经不是原来的GtkEntry对象,所以会出现Element 0 is not a GtkEntry的错误,甚至导致程序崩溃无响应。
而你注释的循环在create_Enterprise_form函数内部执行,此时栈数组还处于有效作用域内,所以能正确识别GtkEntry并打印文本。
解决方案
要解决这个问题,需要把存储GtkEntry的数组放到堆内存或者用GTK推荐的容器(比如GArray)来管理,确保回调触发时数组仍然有效。下面是两种可行的实现方式:
方法1:使用GArray(推荐,符合GLib/GTK编程习惯)
GArray是GLib提供的动态数组,会在堆上分配内存,生命周期可以手动控制,适合存储GTK控件对象。
回调函数修改
// Callback function for the "Save" button void save_enterprise_website(GtkWidget *button, GArray *entries_array) { for (guint i = 0; i < entries_array->len; i++) { GtkWidget *entry = g_array_index(entries_array, GtkWidget*, i); if (GTK_IS_ENTRY(entry)) { const char *text = gtk_entry_get_text(GTK_ENTRY(entry)); g_print("%s\n", text); } else { g_print("Element %u is not a GtkEntry\n", i); } } }
创建表单函数修改
// Function to create the customization form for the company website template GtkWidget *create_Enterprise_form(GtkApplication *app) { GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Enterprise_Website"); gtk_window_set_default_size(GTK_WINDOW(window), 1280, 720); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); GtkWidget *grid = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(window), grid); const char *parameters[] = { "Name", "About Us", "Slogan", "Contact", "Body Color", "Body Font Family", "Header BG Color", "Header Text Color", "A Text Color", "Footer BG Color", "Footer Text Color", "Hero BG Color" }; // 使用GArray存储GtkEntry,元素大小为GtkWidget*的字节数 GArray *entries_array = g_array_new(FALSE, FALSE, sizeof(GtkWidget*)); const char *default_texts[] = { "Company Name","A brief description of the company and its history", "A slogan for the company", "Address, phone number, contact form, etc.", "#f4f4f4", "Arial, sans-serif", "#333", "#fff", "#fff", "#333", "#fff", "#f4f4f4" }; for (int i = 0; i < G_N_ELEMENTS(parameters); i++) { GtkWidget *label = gtk_label_new(parameters[i]); GtkWidget *entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), default_texts[i]); PangoFontDescription *font_desc = pango_font_description_from_string("Sans Bold 14"); gtk_widget_override_font(label, font_desc); gtk_widget_override_font(entry, font_desc); pango_font_description_free(font_desc); int vertical_spacing = 10; gtk_widget_set_margin_bottom(label, vertical_spacing); gtk_widget_set_margin_bottom(entry, vertical_spacing); // 控件未显示时get_allocated_width返回0,直接设置固定宽度 gtk_widget_set_size_request(entry, 600, -1); gtk_grid_attach(GTK_GRID(grid), label, 0, i, 1, 1); gtk_grid_attach(GTK_GRID(grid), entry, 1, i, 1, 1); // 将entry添加到GArray g_array_append_val(entries_array, entry); } GtkWidget *submit_button = gtk_button_new_with_label("Save"); // 将GArray作为user_data传给回调 g_signal_connect(submit_button, "clicked", G_CALLBACK(save_enterprise_website), entries_array); gtk_grid_attach(GTK_GRID(grid), submit_button, 1, G_N_ELEMENTS(parameters), 1, 1); gtk_widget_set_halign(grid, GTK_ALIGN_CENTER); gtk_widget_set_valign(grid, GTK_ALIGN_CENTER); // 将GArray绑定到window对象,窗口销毁时自动释放数组 g_object_set_data_full(G_OBJECT(window), "entries_array", entries_array, (GDestroyNotify)g_array_unref); return window; }
方法2:使用堆分配数组
如果不想用GArray,可以用g_malloc在堆上分配数组,同样能避免栈内存失效问题:
回调函数修改
void save_enterprise_website(GtkWidget *button, GtkWidget **entries) { for (int i = 0; i < 12; i++) { if (GTK_IS_ENTRY(entries[i])) { const char *text = gtk_entry_get_text(GTK_ENTRY(entries[i])); g_print("%s\n", text); } else { g_print("Element %d is not a GtkEntry\n", i); } } }
创建表单函数修改
GtkWidget *create_Enterprise_form(GtkApplication *app) { // ... 其他代码不变 ... // 用g_malloc在堆上分配数组 GtkWidget **entries = g_malloc(sizeof(GtkWidget*) * G_N_ELEMENTS(parameters)); for (int i = 0; i < G_N_ELEMENTS(parameters); i++) { // ... 创建label和entry的代码不变 ... entries[i] = entry; } GtkWidget *submit_button = gtk_button_new_with_label("Save"); g_signal_connect(submit_button, "clicked", G_CALLBACK(save_enterprise_website), entries); // ... 其他代码不变 ... // 将数组绑定到window,窗口销毁时释放内存 g_object_set_data_full(G_OBJECT(window), "entries", entries, g_free); return window; }
额外注意点
- 原代码中
gtk_widget_set_size_request(entry, 3 * gtk_widget_get_allocated_width(entry), -1)存在问题:控件未显示时gtk_widget_get_allocated_width返回0,导致设置宽度为0。建议直接设置固定宽度(比如600),或者在窗口realize信号触发后再调整大小。 - 使用
g_object_set_data_full可以确保窗口销毁时自动释放我们分配的内存,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Shedar
相关产品推荐
相关产品推荐

