📄 book.html
字号:
{ *list_end = g_slist_append(*list_end, data)->next; }} </pre><p> To use this function, you would store the list and its end somewhere, and pass their address to <tt>efficient_append()</tt>:</p> <pre> GSList* list = NULL; GSList* list_end = NULL; efficient_append(&list, &list_end, g_strdup("Foo")); efficient_append(&list, &list_end, g_strdup("Bar")); efficient_append(&list, &list_end, g_strdup("Baz"));</pre><p> Of course you have to be careful not to use any list functions that might change the end of the list without updating <tt>list_end</tt>.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.8:</b> Changing linked list contents</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_append(GSList* list, gpointer data)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_prepend(GSList* list, gpointer data)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_insert(GSList* list, gpointer data, gint position)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_remove(GSList* list, gpointer data)</pre></font> </td></tr></table></p><p> For accessing list elements, the functions in Function Listing 2.9 are provided. None of these change the list's structure. <tt>g_slist_foreach()</tt> applies a <tt>GFunc</tt> to each element of the list. A <tt>GFunc</tt> is defined as follows:</p> <pre>typedef void (*GFunc)(gpointer data, gpointer user_data);</pre><p> Used in <tt>g_slist_foreach()</tt>, your <tt>GFunc</tt> will be called on each <tt>list->data</tt> in <tt>list</tt>, passing the <tt>user_data</tt> you provided to <tt>g_slist_foreach()</tt>. <tt>g_slist_foreach()</tt> is comparable to Scheme's "map" function.</p> <p> For example, you might have a list of strings, and you might want to be able to create a parallel list with some transformation applied to the strings. Here is some code, using the <tt>efficient_append()</tt> function from an earlier example:</p> <pre>typedef struct _AppendContext AppendContext;struct _AppendContext { GSList* list; GSList* list_end; const gchar* append;};static void append_foreach(gpointer data, gpointer user_data){ AppendContext* ac = (AppendContext*) user_data; gchar* oldstring = (gchar*) data; efficient_append(&ac->list, &ac->list_end, g_strconcat(oldstring, ac->append, NULL));}GSList*copy_with_append(GSList* list_of_strings, const gchar* append){ AppendContext ac; ac.list = NULL; ac.list_end = NULL; ac.append = append; g_slist_foreach(list_of_strings, append_foreach, &ac); return ac.list;}</pre><p> glib and Gtk+ use the "function pointer and user data" idiom heavily. If you have functional programming experience, this is much like using lambda expressions to create a <i>closure</i>. (A closure combines a function with an <i>environment</i>---a set of name-value bindings. In this case the "environment" is the user data you pass to <tt>append_foreach()</tt>, and the "closure" is the combination of the function pointer and the user data.)</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.9:</b> Accessing data in a linked list</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_find(GSList* list, gpointer data)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_nth(GSList* list, guint n)</pre></font> </td></tr><tr><td> <font color="black"><pre>gpointerg_slist_nth_data(GSList* list, guint n)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_last(GSList* list)</pre></font> </td></tr><tr><td> <font color="black"><pre>gintg_slist_index(GSList* list, gpointer data)</pre></font> </td></tr><tr><td> <font color="black"><pre>voidg_slist_foreach(GSList* list, GFunc func, gpointer user_data)</pre></font> </td></tr></table></p><p> There are some handy list-manipulation routines, listed in Function Listing 2.10. With the exception of <tt>g_slist_copy()</tt>, all of these affect the lists in-place. Which means you must assign the return value and forget about the passed-in pointer, just as you do when adding or removing list elements. <tt>g_slist_copy()</tt> returns a newly-allocated list, so you can continue to use both lists and must free both lists eventually.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.10:</b> Manipulating a linked list</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>guintg_slist_length(GSList* list)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_concat(GSList* list1, GSList* list2)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_reverse(GSList* list)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_copy(GSList* list)</pre></font> </td></tr></table></p><p> Finally, there are some provisions for sorted lists, shown in Function Listing 2.11. To use these, you must write a <tt>GCompareFunc</tt>, which is just like the comparison function in the standard C <tt>qsort()</tt>. Using glib types, this becomes:</p> <pre>typedef gint (*GCompareFunc) (gconstpointer a, gconstpointer b);</pre><p> If <tt>a < b</tt>, the function should return a negative value; if <tt>a > b</tt> a positive value; if <tt>a == b</tt> it should return 0.</p> <p> Once you have a comparison function, you can insert an element into an already-sorted list, or sort an entire list. Lists are sorted in ascending order. You can even recycle your <tt>GCompareFunc</tt> to find list elements, using <tt>g_slist_find_custom()</tt>. (A word of caution: <tt>GCompareFunc</tt> is used inconsistently in glib; sometimes it glib expects an equality predicate instead of a <tt>qsort()</tt>-style function. However, the usage is consistent within the list API.)</p> <p> Be careful with sorted lists; misusing them can rapidly become very inefficient. For example, <tt>g_slist_insert_sorted()</tt> is an O(n) operation, but if you use it in a loop to insert multiple elements the loop runs in exponential time. It's better to simply prepend all your elements, then call <tt>g_slist_sort()</tt>.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.11:</b> Sorted lists</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_insert_sorted(GSList* list, gpointer data, GCompareFunc func)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_sort(GSList* list, GCompareFunc func)</pre></font> </td></tr><tr><td> <font color="black"><pre>GSList*g_slist_find_custom(GSList* list, gpointer data, GCompareFunc func)</pre></font> </td></tr></table></p><h3>2.2.2: Trees</h3><p> There are two different kinds of tree in glib; <tt>GTree</tt> is your basic balanced binary tree, useful to store key-value pairs sorted by key; <tt>GNode</tt> stores arbitrary tree-structured data, such as a parse tree or taxonomy.</p> <h4>GTree</h4><p> To create and destroy a <tt>GTree</tt>, use the constructor-destructor pair displayed in Function Listing 2.12. <tt>GCompareFunc</tt> is the same <tt>qsort()</tt>-style comparison function described for <tt>GSList</tt>; in this case it's used to compare keys in the tree.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.12:</b> Creating and destroying balanced binary trees</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>GTree*g_tree_new(GCompareFunc key_compare_func)</pre></font> </td></tr><tr><td> <font color="black"><pre>voidg_tree_destroy(GTree* tree)</pre></font> </td></tr></table></p><p> Functions for manipulating the contents of the tree are shown in Function Listing 2.13. All very straightforward; <tt>g_tree_insert()</tt> overwrites any existing value, so be careful if the existing value is your only pointer to a chunk of allocated memory. If <tt>g_tree_lookup()</tt> fails to find the key, it returns <tt>NULL</tt>, otherwise it returns the associated value. Both keys and values have type <tt>gpointer</tt>, but the <tt>GPOINTER_TO_INT()</tt> and <tt>GPOINTER_TO_UINT()</tt> macros allow you to use integers instead.</p> <p><table bgcolor="#EEEEFF" width="100%"><caption><b>Function Listing 2.13:</b> Manipulating <tt>GTree</tt> contents</caption><tr align="right"><td> <font color="black"> <tt>#include <glib.h></tt> </font> </td></tr><tr><td> <font color="black"><pre>voidg_tree_insert(GTree* tree, gpointer key, gpointer value)</pre></font> </td></tr><tr><td> <font color="black"><pre>voidg_tree_remove(GTree* tree, gpointer key)</pre></font> </td></tr><tr><td> <font color="black"><pre>gpointerg_tree_lookup(GTree* tree, gpointer key)</pre></font> </td></tr>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -