GTK回调函数的多参数传递和一个关于内存分配的细节问题

GTK中绑定一个回调函数到相应构件中基本上都是采用GObject中所提供的g_signal_connect()等函数,而回调函数的定义形式又是void function(GtkWidget *widget, gpointer data);
这两者的形式很明显就是只允许传一个参数。但一个参数就只能包含一个内容吗?显然C语言中的结构体此处就是一个必不可少的角儿了,所以呢,我们应该恰当的去定义一个结构体,包含我们需要传递的变量,然后进行传参。

 

下面是一个代码示例,其中创建的结构体包含了两个变量:

struct EntryStruct
{
    GtkEntry *usernameEntry;
    GtkEntry *passwordEntry;
};

 

代码如下:

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

/*因为要传参给回调函数,故此处设直一个结构体以将usernameEntry和idEntry包装起来*/
struct EntryStruct
{
    GtkEntry *usernameEntry;
    GtkEntry *passwordEntry;
};

void approveButton_clicked(GtkWidget* button, gpointer data);
void backToMainButton_clicked(GtkWidget* button, gpointer data);

int main(int argc, char *argv[])
{
    gtk_init(&argc, &argv);
    struct EntryStruct entries;
    GtkLabel *usernameLabel, *passwordLabel;
    GtkButton *approvetButton, *cancelButton;
    GtkTable *tableView;
    GtkWindow *window;
    
    window = (GtkWindow*) gtk_window_new(GTK_WINDOW_TOPLEVEL);

    /*初始化label*/
    usernameLabel = (GtkLabel*) gtk_label_new(NULL);
    gtk_label_set_markup(usernameLabel, "<b>账号</b>");
    passwordLabel = (GtkLabel*) gtk_label_new(NULL);
    gtk_label_set_markup(passwordLabel, "<b>密码</b>");

    /*初始化entry*/
    entries.usernameEntry = (GtkEntry*) gtk_entry_new();
    entries.passwordEntry = (GtkEntry*) gtk_entry_new();

    /*初始化button,并设置相应事件*/
    approvetButton = (GtkButton*) gtk_button_new_with_label("确认");
    g_signal_connect(GTK_OBJECT(approvetButton), "clicked", GTK_SIGNAL_FUNC(approveButton_clicked), (gpointer) & entries);
    cancelButton = (GtkButton*) gtk_button_new_with_label("取消");
    g_signal_connect(GTK_OBJECT(cancelButton), "clicked", GTK_SIGNAL_FUNC(backToMainButton_clicked), NULL);

    /*创建新布局*/
    tableView = (GtkTable*) gtk_table_new(4, 4, FALSE);
    gtk_table_attach(tableView, GTK_WIDGET(usernameLabel), 2, 3, 1, 2, GTK_EXPAND
                     , GTK_FILL, 30, 15);
    gtk_table_attach(tableView, GTK_WIDGET(entries.usernameEntry), 3, 4, 1, 2, GTK_EXPAND
                     , GTK_FILL, 30, 15);
    gtk_table_attach(tableView, GTK_WIDGET(passwordLabel), 2, 3, 2, 3, GTK_EXPAND
                     , GTK_FILL, 30, 15);
    gtk_table_attach(tableView, GTK_WIDGET(entries.passwordEntry), 3, 4, 2, 3, GTK_EXPAND
                     , GTK_FILL, 30, 15);
    gtk_table_attach(tableView, GTK_WIDGET(approvetButton), 2, 3, 3, 4, GTK_FILL | GTK_EXPAND
                     , GTK_FILL, 30, 15);
    gtk_table_attach(tableView, GTK_WIDGET(cancelButton), 3, 4, 3, 4, GTK_FILL | GTK_EXPAND
                     , GTK_FILL, 30, 15);

    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(tableView));
    gtk_widget_show_all(GTK_WIDGET(window));
    gtk_window_set_title(GTK_WINDOW(window), "用户登录");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
    gtk_main();
    return EXIT_SUCCESS;
}

/*确认事件的回调函数*/
void approveButton_clicked(GtkWidget* button, gpointer data)
{
    struct EntryStruct *entry = (struct EntryStruct*) data;
    GtkEntry *usernameEntry = (GtkEntry*) entry->usernameEntry;
    GtkEntry *passwordEntry = (GtkEntry*) entry->passwordEntry;
    char username[20];
    char password[20];
    strcpy(username, gtk_entry_get_text((GtkEntry*) usernameEntry));
    strcpy(password, gtk_entry_get_text((GtkEntry*) passwordEntry));
    //下面就是你自己的操作了
}

/*取消事件的回调函数*/
void backToMainButton_clicked(GtkWidget* button, gpointer data)
{
}

 

 当然,如果这篇博客就是这么一段代码也就

这段代码中有一个缺陷,那就是参数虽然传递给了回调函数,但回调函数却有可能无法获取那两个Entry实例

 

先说下缺陷在哪里吧,就是在定义EntryStruct时(代码的第17行),我并没有添加一个static,结果就...当然在这个程序中还是没有错误的。

static struct EntryStruct entries;
 

这个问题得说到C语言内存分配的三种形式:

1.在静态存储区域分配。这个在程序编译的时候就已经分配好了,在整个程序运行其间都存在,如全局变量,static变量。

2.在栈上中分配。在执行函数时,函数内局部变量的存储单元都可以在栈上创建。函数执行结束时这些存储单元自动被释放(即栈的pop)。

3.从堆上分配,亦即动态内存分配。程序在运行时由程序员手动去分配,通常采用malloc和free(C++中还多了一个new和delete)。

 

程序的问题就出在这里,定义了一个局部的非静态变量,C语言自动的将其压入栈中,而函数运行完之后,结构体就会被释放,虽然传递了一个指向结构体的指针过去,但这块内存上的内容已经不是原来那样的了。而加了static之后,就很理解了。(当然也可以将其定义为全局变量,不过这个可是很容易污染程序的哦。)

这个程序的缺陷就在于此,虽然main是最后一个结束的函数(这也意味着结构体在程序运行期间是一直存在的)。但若出现在其他函数中,问题就显而易见了,感觉这个问题还是让人挺容易忽视的。

 

 

 

你可能感兴趣的:(C++,c,C#)