GTK3向回调函数传递自定义结构体触发segmentation error段错误求助
问题根源
你遇到的段错误本质是栈内存失效导致的野指针访问,核心原因如下:
你定义的MyLabelPointers labelpointers是创建在调用g_signal_connect的函数的栈空间上的,当这个函数执行完成返回后,这块栈内存会被系统自动回收。后续信号触发调用回调函数时,你传入的&labelpointers已经指向无效的内存区域,里面存储的成员值都是被释放后的垃圾数据,你调试输出里的lpointers->readstringslabel = 0x4就是典型的已释放内存的垃圾值,对这个地址解引用操作自然触发段错误。
修复方案
有两种可行的修复方式:
方案1:将结构体分配到堆上
修改创建结构体的代码,用g_new或者malloc在堆上分配内存,堆内存不会随函数返回自动释放,只要你不主动free就一直有效:
// 把原来栈上的定义改成堆分配 MyLabelPointers *labelpointers = g_new(MyLabelPointers, 1); labelpointers->readstringslabel = gtk_label_new("0"); labelpointers->snoteslabel = gtk_label_new("0"); labelpointers->fnoteslabel = gtk_label_new("0"); labelpointers->genericnoteslabel = gtk_label_new("0"); labelpointers->tabslabel = gtk_label_new("0"); labelpointers->chordslabel = gtk_label_new("0");
对应的g_signal_connect传参直接传堆指针即可:
g_signal_connect(G_OBJECT (window), "key_press_event",G_CALLBACK(pressspacebar), labelpointers);
后续不需要用到这个结构体的时候,手动调用g_free(labelpointers)释放内存即可,避免内存泄漏。
方案2:将结构体定义为静态变量/全局变量
如果你的程序生命周期里这个结构体只需要一份实例,可以直接把MyLabelPointers labelpointers;定义为静态变量或者全局变量,这两类变量的内存分配在全局数据区,不会随函数返回失效。
额外检查项
如果修改内存分配方式后还有问题,可以额外检查回调函数的签名是否符合GTK信号的要求:key-press-event的回调签名标准格式是gboolean callback(GtkWidget *widget, GdkEvent *event, gpointer user_data),你需要在回调函数开头显式把user_data强转为MyLabelPointers*类型,避免类型不匹配导致的参数解析错误:
gboolean pressspacebar (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { MyLabelPointers *lpointers = (MyLabelPointers*)user_data; // 后续逻辑不变 }
内容的提问来源于stack exchange,提问作者Jim Stokes

