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是最後一個結束的函數(這也意味着結構體在程序運行期間是一直存在的)。但若出現在其他函數中,問題就顯而易見了,感覺這個問題還是讓人挺容易忽視的。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章