一个菜单项的回调函数
static void radio_menu_callback(GtkWidget *widget, gpointer data) {
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
char *str = (char *)data;
g_print("%s\n", str);
}
}我给回调函数直接传递了一个字符串,导致回调函数里打印出错误内容。
std::string any = "Hello";
g_signal_connect(
item771,
"toggled",
G_CALLBACK(radio_menu_callback),
any.c_str()
);原因就是std::string::c_str() 返回的是一个 临时指针,它仅在 any 字符串对象存活时有效。如果 any 被销毁(比如超出作用域或内容被修改),而回调函数仍在尝试访问该指针,会导致 悬垂指针(Dangling Pointer),引发未定义行为(崩溃或乱码)。
很明显函数结束后字符串被释放,指针指向了无效区域。
解决办法就是使用g_strdup拷贝字符串到堆,在回调函数里手动释放堆内存。
// 回调函数中释放内存
static void radio_menu_callback(GtkWidget *widget, gpointer data) {
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
char *str = (char *)data;
g_print("%s\n", str);
g_free(str); // 必须释放!
}
}
...
// 连接信号时复制字符串
std::string any = "Hello";
g_signal_connect(
item771,
"toggled",
G_CALLBACK(radio_menu_callback),
g_strdup(any.c_str()) // 深拷贝字符串到堆内存
);但是这样会不会造成回调函数连接时只拷贝一次,但是执行多次回调函数造成多次释放而崩溃的问题呢?我测试了没有这个问题,但是按理说是有这个问题的。所以最好的办法就是在回调函数里不进行释放。在程序退出时统一释放。
// 回调函数中释放内存
static void radio_menu_callback(GtkWidget *widget, gpointer data) {
if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
char *str = (char *)data;
g_print("%s\n", str);
//回调函数不释放
}
}
...
// 连接信号时复制字符串
std::string any = "Hello";
char *menu_data = g_strdup(any.c_str()); // 深拷贝一次
g_signal_connect(
item771,
"toggled",
G_CALLBACK(radio_menu_callback),
menu_data
);
// 在程序退出时统一释放(如窗口销毁时)
g_signal_connect(
window,
"destroy",
G_CALLBACK(g_free),
menu_data // 确保只释放一次
);
...
如果菜单项比较少,就不用写释放代码了,泄漏的内存可以忽略。