通过 GTK 的 g_signal_connect() 传递回调参数会导致 C 语言中出现意外值

Passing callback arguments through GTK's g_signal_connect() leads to unexpected values in C

提问人:heroes-of-balkan 提问时间:9/21/2023 更新时间:9/21/2023 访问量:74

问:

我正在使用 GTK4 和 Miniaudio 在 C 中做一个小项目。我需要通过 g_signal_connect() 将一些参数传递给几个小部件。由于我是 GTK 的新手,我尝试在 GTK 的入门中尝试一个基本的 GTK 示例(hello-world-gtk 示例)。但是,当我通过 g_signal_connect() 传递任何回调参数时,我会得到意想不到的值。

以下是修改后的 hello-world-gtk 代码示例:

#include <stdio.h>
#include <gtk/gtk.h>

void hello(GtkWidget *widget, void *data) {
  printf("pointer in hello(): %p\n", data);
  printf("data value: %d\n", *(int*)data);
}

void activate(GtkApplication *app, gpointer user_data) {
  GtkWidget *window;
  GtkWidget *button;

  int abc = 5;
  void *ptr = &abc;

  window = gtk_application_window_new(app);
  gtk_window_set_title(GTK_WINDOW(window), "Window");
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

  button = gtk_button_new_with_label("Hello World");

  printf("pointer in activate(): %p\n", ptr);
  printf("abc value: %d\n", *(int*)ptr);
  // Problem occurs here.
  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(hello), ptr);
  gtk_window_set_child(GTK_WINDOW(window), button);

  gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char **argv) {
  GtkApplication *app;
  int status;

  app = gtk_application_new("org.gtk.example", G_APPLICATION_REPLACE);
  g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  status = g_application_run(G_APPLICATION(app), argc, argv);
  g_object_unref(app);

  return status;
}

它是用命令编译的。gcc $(pkg-config --cflags gtk4) -o hello-world-gtk hello-world-gtk.c $(pkg-config --libs gtk4)

终端输出为:

pointer in activate(): 0x7fff5f4c8124
abc value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 0
pointer in hello(): 0x7fff5f4c8124
data value: 0

在初始化时,将打印终端中的前两行,并在每次单击按钮时打印出新的两行(“hello():...”和“数据值:...”)。

正如可能猜到的那样,我希望传递要打印的指针值,而不是每次都为 0,或者:

pointer in activate(): 0x7fff5f4c8124
abc value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 5

我尝试过在谷歌上搜索其他人如何使用该函数,语法非常相似,但它们获得了预期值(就像这里一样,创建了我的小代码演示,看看我是否在 C 中理解 void* 和回调,但我在该演示中获得了预期值。g_signal_connect()

演示:

#include <stdio.h>

void doing_things(int (*fx)(int), void *param) {
  int x = *(int*)param;
  printf("Applied function: %d\n", fx(x));
}


int square(int value) {
  return value * value;
}

int main() {
  int my_val = 5;
  int (*my_square)(int) = square;
  void *my_pointer = &my_val;
  doing_things(my_square, my_pointer);
  return 0;
}

Demo 的输出:

Applied function: 25

我尝试将类型和类型转换为不同的类型(基本上是),但没有成功。datagpointervoid*

我有一个想法来制作回调参数,但它被认为是我知道多少的回练习。extern

如果您知道如何解决这个问题,我将不胜感激!

提前致谢。

c gcc 回调 gtk gtk4

评论


答:

1赞 Ted Lyngmo 9/21/2023 #1
int abc = 5;

此变量是函数的本地变量,也是您提供给 ...activate()g_signal_connect

g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(hello), ptr);

...一回来就会晃来晃去。因此,取消引用内部指针具有未定义的行为activate()hello()

一个简单的解决方法是使变量存活到程序执行结束:static

static int abc = 5;

评论

0赞 heroes-of-balkan 9/23/2023
谢谢。我完全忘记了变量的范围以及它的位置。它持续的时间不够长,无法使功能正常工作。我发现了另一个具有相同问题和答案的问题abchello()
1赞 Ted Lyngmo 9/23/2023
巴尔干@heroes 可爱!别客气!
0赞 heroes-of-balkan 9/23/2023
除了 或 之外,变量范围还有其他“修复”:using or declare in 并将其地址传递给 。老实说,我很惊讶这个程序没有段错误。staticexternmalloc()abcmain()activate()
1赞 Ted Lyngmo 9/23/2023
巴尔干@heroes 当然,有很多选择。重要的是,当指针被取消引用时,变量仍然有效。理论上可能会发生段错误,但指针很可能会指向程序分配的内存段。