【问题标题】:How do you pass pointers/arrays as parameters to a GTK callback function?如何将指针/数组作为参数传递给 GTK 回调函数?
【发布时间】:2021-11-10 14:49:11
【问题描述】:

我想将整数列表numbers 传递给回调函数printer,它会在按下按钮时打印numbers 的内容。 this 论坛帖子中的最后一篇帖子建议我这样做:

#include <gtk/gtk.h>

static void printer(GtkButton *button, gpointer user_data)
{
    int *array = user_data;
    g_print("%d %d %d %d %d\n", array[0], array[1], array[2], array[3], array[4]);
}

static void activate(GtkApplication *app, gpointer user_data)
{
    GtkWidget *win;
    GtkWidget *box;
    GtkWidget *button;

    // initialize numbers
    int numbers[5] = {2, 3, 5, 7, 11};
   
    // window
    win = gtk_application_window_new(GTK_APPLICATION(app));
    gtk_window_set_title(GTK_WINDOW(win), "Printer");
    gtk_window_set_default_size(GTK_WINDOW(win), 80, 80);
    
    // box
    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child(GTK_WINDOW(win), box);  
    
    // button
    button = gtk_button_new_with_label("Test");
    g_signal_connect(button, "clicked", G_CALLBACK(printer), (gpointer) numbers);
    gtk_box_append(GTK_BOX(box), button);

    gtk_window_present(GTK_WINDOW(win));
}

int main (int argc, char **argv)
{
    GtkApplication *app;
    int status;
    
    app = gtk_application_new("com.example.printer", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);
    
    return status;
}

但是,当我尝试执行此操作时,程序输出:

0 0 0 32583 0

如果回调函数在打印之前修改了数组的内容,那么打印的结果就是正确的:

static void printer(GtkButton *button, gpointer user_data)
{
    int *array = user_data;
    array[0] = 3;
    array[1] = 2;
    array[2] = 1;
    array[3] = 0;
    array[4] = -1;
    g_print("%d %d %d %d %d\n", array[0], array[1], array[2], array[3], array[4]);
}

这会按预期输出3 2 1 0 -1

我不确定为什么array[x] 在这两种情况下都不同。这是将数组传递给回调函数的推荐方式吗?为什么array[x] 在两种情况下都返回不同的值?

【问题讨论】:

    标签: arrays c pointers callback gtk


    【解决方案1】:

    您传递给回调函数的数组的地址是activate 函数的本地。因此,当(稍后)实际执行该回调时,activate 函数已完成,指向 (numbers) 的数据缓冲区已超出范围。因此,您有未定义的行为。

    在您的情况下,回调例程仍然可以访问该内存(但不要依赖于此),但是您在初始化时放置在那里的数据已被覆盖(被哪个代码扇区和多少次覆盖,几乎是不可能的说,因为内存可能会在堆栈上的某个地方) - 因此你看到的垃圾值。但是,当您在回调函数 (printer) 中提供 new 值时,这些值会正确显示(但从技术上讲,您仍然有未定义的行为,因为 user_data 指向的内存 可能不再可用)。

    解决此问题的一种方法是创建numbers 数组static,以便在activate 函数完成时保留内存:

    static void activate(GtkApplication *app, gpointer user_data)
    {
        GtkWidget *win;
        GtkWidget *box;
        GtkWidget *button;
    
        // initialize numbers
        static int numbers[5] = {2, 3, 5, 7, 11}; // Make "static" to preserve after call
    
    //...   
    

    其他选项是:(a) 使numbers 数组成为全局变量(不太好,而且经常令人不悦); (b) 使用malloc 动态地(在堆上)为数组分配内存(但在完成后您需要释放该内存)。

    【讨论】:

    • 这个问题及其答案也可能会有所帮助。 What and where are the stack and heap?
    • @Chris 当然,这可能会使我在回答中使用的术语更加清晰。但是,实现不是必须以任何特定方式使用堆栈和堆 - 这里的重点是原始数组已超出范围。
    猜你喜欢
    • 2012-01-24
    • 2021-07-18
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-17
    • 2013-01-25
    相关资源
    最近更新 更多