GTK 3程序在Windows正常运行,Linux调用gtk_combo_box_text_remove时崩溃
问题描述
我正在为大学项目开发一款C语言程序,用SQLite3处理数据库,GTK3构建GUI。使用GtkComboBoxText控件,通过gtk_combo_box_text_append()和gtk_combo_box_text_remove()添加、移除下拉选项。
调用gtk_combo_box_text_remove()时程序崩溃,但仅在Linux系统出现此问题,Windows下完全正常。该函数只接收GtkComboBoxText指针和待移除选项的索引值作为参数。
用Valgrind检测到以下内存错误:
==30432== Invalid read of size 1 ==30432== at 0x7D08730: __strcpy_sse2_unaligned (in /lib64/libc-2.26.so) ==30432== by 0x405A70: on_combo_changed (main.c:981) ==30432== by 0x72D7E95: ??? (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x72F447B: g_signal_emit_valist (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x72F49CE: g_signal_emit (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x52DAF1B: ??? (in /usr/lib64/libgtk-3.so.0.2404.16) ==30432== by 0x72D7C5C: g_closure_invoke (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x72EB84D: ??? (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x72F404B: g_signal_emit_valist (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x72F49CE: g_signal_emit (in /usr/lib64/libgobject-2.0.so.0.6200.6) ==30432== by 0x53B0758: gtk_list_store_remove (in /usr/lib64/libgtk-3.so.0.2404.16) ==30432== by 0x52DEEB8: gtk_combo_box_text_remove (in /usr/lib64/libgtk-3.so.0.2404.16) ==30432== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==30432== ==30432== ==30432== Process terminating with default action of signal 11 (SIGSEGV): dumping core
尝试增大栈空间等方法均无效。
补充:可复现的精简代码
已用精简代码复现问题,Valgrind报错相同,同样Windows正常、Linux崩溃。
测试代码
#include <stdio.h> #include <string.h> #include <gtk/gtk.h> GtkBuilder *builder; GtkWidget *window; GtkWidget *fixed; GtkWidget *button; GtkWidget *label; GtkComboBoxText *combo; char active[100]; int foo = 0; int main(int argc, char *argv[]){ gtk_init(&argc, &argv); builder = gtk_builder_new_from_file("test.glade"); window = GTK_WIDGET(gtk_builder_get_object(builder, "window")); fixed = GTK_WIDGET(gtk_builder_get_object(builder, "fixed")); combo = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(builder, "combo")); button = GTK_WIDGET(gtk_builder_get_object(builder, "button")); label = GTK_WIDGET(gtk_builder_get_object(builder, "label")); gtk_builder_connect_signals(builder, NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "something"); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), "something 2"); gtk_widget_show_all(window); gtk_main(); return EXIT_SUCCESS; } void on_button_clicked () { gtk_combo_box_text_remove(combo, foo); } void on_combo_changed () { sprintf(active, "%s", gtk_combo_box_text_get_active_text(combo)); foo = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); }
Glade文件
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.40.0 --> <interface> <requires lib="gtk+" version="3.24"/> <object class="GtkWindow" id="window"> <property name="width-request">400</property> <property name="height-request">200</property> <property name="can-focus">False</property> <property name="default-width">400</property> <property name="default-height">200</property> <child> <!-- n-columns=1 n-rows=3 --> <object class="GtkGrid" id="grid"> <property name="width-request">400</property> <property name="height-request">200</property> <property name="visible">True</property> <property name="can-focus">False</property> <child> <object class="GtkButton" id="button"> <property name="label" translatable="yes">button</property> <property name="width-request">400</property> <property name="height-request">60</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> <signal name="clicked" handler="on_button_clicked" swapped="no"/> </object> <packing> <property name="left-attach">0</property> <property name="top-attach">2</property> </packing> </child> <child> <object class="GtkLabel" id="label"> <property name="width-request">400</property> <property name="height-request">60</property> <property name="visible">True</property> <property name="can-focus">False</property> <property name="label" translatable="yes">example</property> </object> <packing> <property name="left-attach">0</property> <property name="top-attach">0</property> </packing> </child> <child> <object class="GtkComboBoxText" id="combo"> <property name="width-request">400</property> <property name="height-request">80</property> <property name="visible">True</property> <property name="can-focus">False</property> <signal name="changed" handler="on_combo_changed" swapped="no"/> </object> <packing> <property name="left-attach">0</property> <property name="top-attach">1</property> </packing> </child> </object> </child> </object> </interface>
编译命令
gcc -g -Wall -Wextra -o main main.c `pkg-config --cflags --libs gtk+-3.0 gmodule-2.0`
请问这是代码问题还是GTK的bug?
问题分析与解决方案
这是代码逻辑问题,并非GTK的bug。问题出在on_combo_changed信号处理函数中:
- 调用
gtk_combo_box_text_remove()移除选项时,GTK会触发changed信号。如果被移除的是当前选中的选项,下拉框会自动将选中状态设为无选中项(此时gtk_combo_box_get_active()返回-1)。 - 此时
on_combo_changed被调用,gtk_combo_box_text_get_active_text(combo)会返回NULL(因为没有选中项),但代码直接把NULL传给sprintf的%s格式符,导致访问空指针,触发段错误。 - Windows下可能因内存布局或GTK行为差异(比如移除选中项后默认选中第一个剩余选项)未触发该场景,但Linux下GTK的行为符合规范:移除选中项后无选中状态,返回
NULL。
修复方法
在on_combo_changed中先检查gtk_combo_box_text_get_active_text()的返回值是否为NULL,再进行字符串拷贝:
void on_combo_changed () { const gchar *text = gtk_combo_box_text_get_active_text(combo); if (text != NULL) { g_strlcpy(active, text, sizeof(active)); // 用g_strlcpy更安全,避免缓冲区溢出 } else { active[0] = '\0'; // 清空字符串或做其他处理 } foo = gtk_combo_box_get_active(GTK_COMBO_BOX(combo)); }
另外,on_button_clicked中移除选项时,需确保foo是有效的索引(比如移除最后一个选项后,foo可能变成无效值),可添加索引有效性检查:
void on_button_clicked () { gint count = gtk_combo_box_text_get_length(combo); if (foo >= 0 && foo < count) { gtk_combo_box_text_remove(combo, foo); } }
这样即可避免Linux下的崩溃问题,同时让代码更健壮,符合GTK的API规范。
内容的提问来源于stack exchange,提问作者Edward Teller
相关产品推荐
相关产品推荐

