4_04_GLib库入门与实践_指针数组

GLib提供了一种称为指针数组的数据结构GPtrArray,用于存储长度不固定的字符串或其他数据。本文详细介绍了GPtrArray的创建、大小调整、元素插入和删除、遍历、查找以及排序等功能。通过示例代码展示了如何使用这些函数进行操作,并探讨了指针数组的内存分布和管理。
摘要由CSDN通过智能技术生成

简介

GLib提供了三种类型的数组,普通数组,指针数组和字节数组,本节讨论指针数组。
在上一节介绍GArray动态数组时,数组元素可以是int类型,也可以是一个结构体类型,甚至结构体中的成员变量还可以是一个指针,但根据其内存模型一节,每个数组元素的长度都是固定的,如果我们想存长度不固定的字符串怎么办?一种方法是利用带指针的结构体,将字符串存在结构体的指针成员变量中,另一种方法就是指针数组了。

数据结构

普通数组和指针数组的结构体定义如下:
普通数组

struct GArray {
  gchar *data;
  guint len;
};

指针数组

struct GPtrArray {
  gpointer *pdata;
  guint	    len;
};

指针数组存储非固定长度的字符串具有天然优势,并且,指针数组比普通GArray数组多了遍历及查找两个操作方法。

函数列表

GPtrArray * 	g_ptr_array_new ()
GPtrArray * 	g_ptr_array_sized_new ()
GPtrArray * 	g_ptr_array_new_with_free_func ()
GPtrArray * 	g_ptr_array_new_full ()
void 	g_ptr_array_set_free_func ()
GPtrArray * 	g_ptr_array_ref ()
void 	g_ptr_array_unref ()
void 	g_ptr_array_add ()
void 	g_ptr_array_insert ()
gboolean 	g_ptr_array_remove ()
gpointer 	g_ptr_array_remove_index ()
gboolean 	g_ptr_array_remove_fast ()
gpointer 	g_ptr_array_remove_index_fast ()
GPtrArray * 	g_ptr_array_remove_range ()
void 	g_ptr_array_sort ()
void 	g_ptr_array_sort_with_data ()
void 	g_ptr_array_set_size ()
#define 	g_ptr_array_index()
gpointer * 	g_ptr_array_free ()
void 	g_ptr_array_foreach ()
gboolean 	g_ptr_array_find ()
gboolean 	g_ptr_array_find_with_equal_func ()

函数功能分类

创建
GPtrArray * g_ptr_array_new ()
GPtrArray * g_ptr_array_sized_new ()
GPtrArray * g_ptr_array_new_with_free_func ()
GPtrArray * g_ptr_array_new_full ()
void g_ptr_array_set_size ()

测长
GPtrArray->len

释放
gpointer * g_ptr_array_free ()
void g_ptr_array_set_free_func ()

插入
void g_ptr_array_add ()
void g_ptr_array_insert ()

访问
#define g_ptr_array_index()

遍历
void g_ptr_array_foreach ()

移除
gboolean g_ptr_array_remove ()
gpointer g_ptr_array_remove_index ()
gboolean g_ptr_array_remove_fast ()
gpointer g_ptr_array_remove_index_fast ()
GPtrArray * g_ptr_array_remove_range ()

引用和解引用
GPtrArray * g_ptr_array_ref ()
void g_ptr_array_unref ()

查找
gboolean g_ptr_array_find ()
gboolean g_ptr_array_find_with_equal_func ()

排序
void g_ptr_array_sort ()
void g_ptr_array_sort_with_data ()

函数功能说明及综合演示

创建
// 创建指针数组
GPtrArray *	g_ptr_array_new ()

// 创建指针数组,但先设置最大长度,此举可以避免在添加元素时频繁的内存申请。
GPtrArray *	g_ptr_array_sized_new ()

// 创建带有释放函数的指针数组,当g_ptr_array_free释放数组时,释放函数会被调到。
GPtrArray *	g_ptr_array_new_with_free_func ()

// 相当于g_ptr_array_sized_new+g_ptr_array_new_with_free_func
GPtrArray *	g_ptr_array_new_full ()

// 设置指针数组的最大长度,g_ptr_array_new和g_ptr_array_set_size等价于g_ptr_array_sized_new
void	g_ptr_array_set_size ()
测长

指针数组没有提供数组测长的具体方法,我们仍可以采用和GArray数组相同的方式来获取数组长度。

GPtrArray->len
释放
// 数组释放,本函数不会自动释放数组元素中的数据。
gpointer *	g_ptr_array_free ()

函数原型为:

gpointer *
g_ptr_array_free (GPtrArray *array,
                  gboolean free_seg);

如果第二个参数设置为FALSE,则需要用户自行释放数组元素,
如果第二个参数设置为TRUE,则需要先调用g_array_set_clear_func函数设置数组元素的释放函数。
void g_ptr_array_set_free_func ()
如果g_ptr_array_free设置了free_segment但没有通过g_ptr_array_new_with_free_func设置过释放函数,则需要调用本函数设置释放函数。

插入
// 将数据data追加到数组尾。数组会自动扩容。
void
g_ptr_array_add (GPtrArray *array,
                 gpointer data);

// 将数据data插入到指定下标位置。数组会自动扩容。
void
g_ptr_array_insert (GPtrArray *array,
                    gint index_,
                    gpointer data);
访问
// 根据数组元素的下标得到数组元素的值
#define	g_ptr_array_index()
遍历
// 对指针数组按照给定的遍历函数进行遍历
void
g_ptr_array_foreach (GPtrArray *array,
                     GFunc func,
                     gpointer user_data);

针对以上函数的示例程序如下:
源码见glib_examples\glib_ptr_array\glib_ptr_array_basic

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s \n", (gchar *)user_data, (gchar *)data);
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gint i = 0;
    gchar *str1 = "one";
    gchar *str2 = "two";
    gchar *str3 = "three";

    parr = g_ptr_array_sized_new(10);

    g_ptr_array_add(parr, (gpointer)str1);
    g_ptr_array_add(parr, (gpointer)str3);
    g_ptr_array_insert(parr, 1, (gpointer)str2);

    for(i=0; i<parr->len; i++) {
        g_print("parr[%d]:%s \n", i, (gchar *)g_ptr_array_index(parr, i));
    }

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "foreach");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

parr[0]:one 
parr[1]:two 
parr[2]:three 
foreach: one 
foreach: two 
foreach: three
移除

将数组元素从数组中移除

// 移除第一次出现的数组元素,后面的元素依次补齐。如果GDestroyNotify释放函数不为空,则该释放函数会被调用。
gboolean
g_ptr_array_remove (GPtrArray *array,
                    gpointer data);

// 按数组下标移除元素,后面的元素依次补齐。如果GDestroyNotify释放函数不为空,则该释放函数会被调用。
gpointer
g_ptr_array_remove_index (GPtrArray *array,
                          guint index_);

// 快速移除第一次出现的数组元素,后面的元素不会依次补齐,而是最后一个元素直接补此空缺。如果GDestroyNotify释放函数不为空,则该释放函数会被调用。
gboolean
g_ptr_array_remove_fast (GPtrArray *array,
                         gpointer data);

// 按数组下标快速移除第一次出现的数组元素,后面的元素不会依次补齐,而是最后一个元素直接补此空缺。如果GDestroyNotify释放函数不为空,则该释放函数会被调用。
gpointer
g_ptr_array_remove_index_fast (GPtrArray *array,
                               guint index_);

// 按数组下标移除多个数组元素,后面的元素会补齐被移除后的空缺位置。如果GDestroyNotify释放函数不为空,则该释放函数会被调用。
GPtrArray *
g_ptr_array_remove_range (GPtrArray *array,
                          guint index_,
                          guint length);

上述移除函数的示例代码如下:
源码见glib_examples\glib_ptr_array\glib_ptr_array_remove

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

static void _ptr_array_free_func(gpointer data)
{
    if(NULL != data) {
        g_free(data);
    }
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gboolean ret = FALSE;
    gpointer p = NULL;
    gchar str0[8] = {0};
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};
    gchar str4[8] = {0};
    gchar str5[8] = {0};
    gchar str6[8] = {0};
    
    g_strlcpy(str0, "one", 8);
    g_strlcpy(str1, "two", 8);
    g_strlcpy(str2, "two", 8);
    g_strlcpy(str3, "three", 8);
    g_strlcpy(str4, "four", 8);
    g_strlcpy(str5, "five", 8);
    g_strlcpy(str6, "six", 8);

    parr = g_ptr_array_sized_new(10);
    
    g_ptr_array_add(parr, (gpointer)g_strdup(str0));
    g_ptr_array_add(parr, (gpointer)g_strdup(str1));
    g_ptr_array_add(parr, (gpointer)g_strdup(str2));
    g_ptr_array_add(parr, (gpointer)g_strdup(str3));
    g_ptr_array_add(parr, (gpointer)g_strdup(str4));
    g_ptr_array_add(parr, (gpointer)g_strdup(str5));
    g_ptr_array_add(parr, (gpointer)g_strdup(str6));

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "ori");

    g_ptr_array_set_free_func(parr, _ptr_array_free_func);

    ret = g_ptr_array_remove(parr, "two");
    g_print("remove(two) ret: %s \n", ret ? "TRUE" : "FALSE");
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove(two)");

    p = g_ptr_array_remove_index(parr, 1);
    g_print("remove_index(1) p:%p \n", p);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_index(1)");

    ret = g_ptr_array_remove_fast(parr, "three");
    g_print("remove_fast(three) ret: %s \n", ret ? "TRUE" : "FALSE");
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_fast(three)");

    p = g_ptr_array_remove_index_fast(parr, 2);
    g_print("remove_index_fast(2) p:%p \n", p);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_index_fast(2)");

    g_ptr_array_remove_range(parr, 0, 2);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_range(0,2)");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

ori: one(addr:0x693dd0) 
ori: two(addr:0x694690) 
ori: two(addr:0x6946b0) 
ori: three(addr:0x6946d0) 
ori: four(addr:0x6946f0) 
ori: five(addr:0x694710) 
ori: six(addr:0x694730) 
remove(two) ret: FALSE 
remove(two): one(addr:0x693dd0) 
remove(two): two(addr:0x694690) 
remove(two): two(addr:0x6946b0) 
remove(two): three(addr:0x6946d0) 
remove(two): four(addr:0x6946f0) 
remove(two): five(addr:0x694710) 
remove(two): six(addr:0x694730) 
remove_index(1) p:0x694690 
remove_index(1): one(addr:0x693dd0) 
remove_index(1): two(addr:0x6946b0) 
remove_index(1): three(addr:0x6946d0) 
remove_index(1): four(addr:0x6946f0) 
remove_index(1): five(addr:0x694710) 
remove_index(1): six(addr:0x694730) 
remove_fast(three) ret: FALSE 
remove_fast(three): one(addr:0x693dd0) 
remove_fast(three): two(addr:0x6946b0) 
remove_fast(three): three(addr:0x6946d0) 
remove_fast(three): four(addr:0x6946f0) 
remove_fast(three): five(addr:0x694710) 
remove_fast(three): six(addr:0x694730) 
remove_index_fast(2) p:0x6946d0 
remove_index_fast(2): one(addr:0x693dd0) 
remove_index_fast(2): two(addr:0x6946b0) 
remove_index_fast(2): six(addr:0x694730) 
remove_index_fast(2): four(addr:0x6946f0) 
remove_index_fast(2): five(addr:0x694710) 
remove_range(0,2): six(addr:0x694730) 
remove_range(0,2): four(addr:0x6946f0) 
remove_range(0,2): five(addr:0x694710)

上面g_ptr_array_remove和g_ptr_array_remove_fast返回值为FALSE,且实际上数组元素也并没有被移除成功,原因是什么呢?
官方文档有下面一句话:
Removes the first occurrence of the given pointer from the pointer array.
上述程序中传入的"two"字符串指针指向的内存地址是静态存储区中的字符串"two",而指针数组中指向"two"的指针(有两个)指向的是通过g_strdup在堆上申请的内存,这两个指针是不相等的。
可以修改成以下方式。
源码见glib_examples\glib_ptr_array\glib_ptr_array_remove2

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

static void _ptr_array_free_func(gpointer data)
{
    if(NULL != data) {
        g_free(data);
    }
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gboolean ret = FALSE;
    gpointer p = NULL;
    gchar str0[8] = {0};
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};
    gchar str4[8] = {0};
    gchar str5[8] = {0};
    gchar str6[8] = {0};
    gchar *str[8] = {NULL};
    
    g_strlcpy(str0, "one", 8);
    g_strlcpy(str1, "two", 8);
    g_strlcpy(str2, "two", 8);
    g_strlcpy(str3, "three", 8);
    g_strlcpy(str4, "four", 8);
    g_strlcpy(str5, "five", 8);
    g_strlcpy(str6, "six", 8);

    str[0] = g_strdup(str0);
    str[1] = g_strdup(str1);
    str[2] = g_strdup(str2);
    str[3] = g_strdup(str3);
    str[4] = g_strdup(str4);
    str[5] = g_strdup(str5);
    str[6] = g_strdup(str6);

    g_print("str1:%s(addr:%p) | str[1]:%s(addr:%p) \n", str1, str1, str[1], str[1]);
    g_print("str3:%s(addr:%p) | str[3]:%s(addr:%p) \n", str3, str3, str[3], str[3]);

    parr = g_ptr_array_sized_new(10);
    
    g_ptr_array_add(parr, (gpointer)str[0]);
    g_ptr_array_add(parr, (gpointer)str[1]);
    g_ptr_array_add(parr, (gpointer)str[2]);
    g_ptr_array_add(parr, (gpointer)str[3]);
    g_ptr_array_add(parr, (gpointer)str[4]);
    g_ptr_array_add(parr, (gpointer)str[5]);
    g_ptr_array_add(parr, (gpointer)str[6]);

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "ori");

    g_ptr_array_set_free_func(parr, _ptr_array_free_func);

    ret = g_ptr_array_remove(parr, str[1]); // Caution: Not str1, Not "two"!
    g_print("remove(two) ret: %s \n", ret ? "TRUE" : "FALSE");
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove(two)");

    p = g_ptr_array_remove_index(parr, 1);
    g_print("remove_index(1) p:%p \n", p);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_index(1)");

    ret = g_ptr_array_remove_fast(parr, str[3]);  // Caution: Not str3, Not "three"!
    g_print("remove_fast(three) ret: %s \n", ret ? "TRUE" : "FALSE");
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_fast(three)");

    p = g_ptr_array_remove_index_fast(parr, 2);
    g_print("remove_index_fast(2) p:%p \n", p);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_index_fast(2)");

    g_ptr_array_remove_range(parr, 0, 2);
    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "remove_range(0,2)");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

str1:two(addr:0x7fffebfd6930) | str[1]:two(addr:0x1120030) 
str3:three(addr:0x7fffebfd6950) | str[3]:three(addr:0x1120070) 
ori: one(addr:0x1120010) 
ori: two(addr:0x1120030) 
ori: two(addr:0x1120050) 
ori: three(addr:0x1120070) 
ori: four(addr:0x1120090) 
ori: five(addr:0x11200b0) 
ori: six(addr:0x11200d0) 
remove(two) ret: TRUE 
remove(two): one(addr:0x1120010) 
remove(two): two(addr:0x1120050) 
remove(two): three(addr:0x1120070) 
remove(two): four(addr:0x1120090) 
remove(two): five(addr:0x11200b0) 
remove(two): six(addr:0x11200d0) 
remove_index(1) p:0x1120050 
remove_index(1): one(addr:0x1120010) 
remove_index(1): three(addr:0x1120070) 
remove_index(1): four(addr:0x1120090) 
remove_index(1): five(addr:0x11200b0) 
remove_index(1): six(addr:0x11200d0) 
remove_fast(three) ret: TRUE 
remove_fast(three): one(addr:0x1120010) 
remove_fast(three): six(addr:0x11200d0) 
remove_fast(three): four(addr:0x1120090) 
remove_fast(three): five(addr:0x11200b0) 
remove_index_fast(2) p:0x1120090 
remove_index_fast(2): one(addr:0x1120010) 
remove_index_fast(2): six(addr:0x11200d0) 
remove_index_fast(2): five(addr:0x11200b0) 
remove_range(0,2): five(addr:0x11200b0)

修改为以上方式,由于str[1]和str[3]均为指针数组中的元素,因此可以移除成功。另外,也可以通过查找的方式,找到对应的数组元素,再将该数组元素移除。

查找

gboolean g_ptr_array_find ()
gboolean g_ptr_array_find_with_equal_func ()
举例,字符串查找。
源码见glib_examples\glib_ptr_array\glib_ptr_array_find

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

static void _ptr_array_free_func(gpointer data)
{
    if(NULL != data) {
        g_free(data);
    }
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    guint idx = 0;
    gboolean ret = FALSE;
    gchar str0[8] = {0};
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};
    gchar str4[8] = {0};
    gchar str5[8] = {0};
    gchar str6[8] = {0};
    gchar *str[8] = {NULL};
    
    g_strlcpy(str0, "one", 8);
    g_strlcpy(str1, "two", 8);
    g_strlcpy(str2, "two", 8);
    g_strlcpy(str3, "three", 8);
    g_strlcpy(str4, "four", 8);
    g_strlcpy(str5, "five", 8);
    g_strlcpy(str6, "six", 8);

    str[0] = g_strdup(str0);
    str[1] = g_strdup(str1);
    str[2] = g_strdup(str2);
    str[3] = g_strdup(str3);
    str[4] = g_strdup(str4);
    str[5] = g_strdup(str5);
    str[6] = g_strdup(str6);

    parr = g_ptr_array_sized_new(10);
    
    g_ptr_array_add(parr, (gpointer)str[0]);
    g_ptr_array_add(parr, (gpointer)str[1]);
    g_ptr_array_add(parr, (gpointer)str[2]);
    g_ptr_array_add(parr, (gpointer)str[3]);
    g_ptr_array_add(parr, (gpointer)str[4]);
    g_ptr_array_add(parr, (gpointer)str[5]);
    g_ptr_array_add(parr, (gpointer)str[6]);

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "ori");

    g_ptr_array_set_free_func(parr, _ptr_array_free_func);

    ret = g_ptr_array_find(parr, str[3], NULL);
    g_print("find three? %s \n", ret ? "TRUE" : "FALSE");

    ret = g_ptr_array_find(parr, str[4], &idx);
    g_print("find four? %s, pos:%d \n", ret ? "TRUE" : "FALSE", idx);

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

ori: one(addr:0x2208dc0)
ori: two(addr:0x2208de0)
ori: two(addr:0x2208e00)
ori: three(addr:0x2208e20)
ori: four(addr:0x2208e40)
ori: five(addr:0x2208e60)
ori: six(addr:0x2208e80)
find three? TRUE
find four? TRUE, pos:4
排序

排序函数有两个:

// 对指针数组进行排序,排序函数由用户指定。
void	g_ptr_array_sort ()

// 与g_ptr_array_sort功能相同,排序函数带用户自定义参数。
void	g_ptr_array_sort_with_data ()

由于g_ptr_array_sort和g_ptr_array_sort_with_data功能完全一样,这里只演示g_ptr_array_sort的使用方法。
源码见glib_examples\glib_ptr_array\glib_ptr_array_sort

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s \n", (gchar *)user_data, (gchar *)data);
}

static void _ptr_array_free_func(gpointer data)
{
    if(NULL != data) {
        g_free(data);
    }
}

static gint _ptr_array_str_cmp_func(gconstpointer a, gconstpointer b)
{
    gchar *str_a = NULL;
    gchar *str_b = NULL;

    str_a = (gchar *)a;
    str_b = (gchar *)b;

    return g_strcmp0(str_a, str_b);
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;

    gchar str0[8] = {0};
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar *str[3] = {NULL};
    
    g_strlcpy(str0, "AAA", 8);
    g_strlcpy(str1, "BBB", 8);
    g_strlcpy(str2, "CCC", 8);

    str[0] = g_strdup(str0);
    str[1] = g_strdup(str1);
    str[2] = g_strdup(str2);

    parr = g_ptr_array_sized_new(10);
    
    g_ptr_array_add(parr, (gpointer)str[0]);
    g_ptr_array_add(parr, (gpointer)str[2]);
    g_ptr_array_add(parr, (gpointer)str[1]);

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "ori");

    g_ptr_array_set_free_func(parr, _ptr_array_free_func);

    g_ptr_array_sort(parr, _ptr_array_str_cmp_func);

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "sort");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

ori: AAA 
ori: CCC 
ori: BBB 
sort: AAA 
sort: BBB 
sort: CCC

专题

指针数组的内存分布

下面程序演示将指针数组中的每个元素的地址打印出来,如下所示:
源码见glib_examples\glib_ptr_array\glib_ptr_array_const_var_addr

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gint i = 0;
    gchar *str1 = "one";
    gchar *str2 = "two";
    gchar *str3 = "three";

    g_print("str1(addr:%p):%s \n", str1, str1);
    g_print("str2(addr:%p):%s \n", str2, str2);
    g_print("str3(addr:%p):%s \n", str3, str3);

    parr = g_ptr_array_sized_new(10);

    g_ptr_array_add(parr, (gpointer)str1);
    g_ptr_array_add(parr, (gpointer)str3);
    g_ptr_array_insert(parr, 1, (gpointer)str2);

    for(i=0; i<parr->len; i++) {
        g_print("parr[%d](addr:%p):%s(addr:%p) \n", 
            i, 
            &g_ptr_array_index(parr, i), 
            (gchar *)g_ptr_array_index(parr, i), 
            g_ptr_array_index(parr, i));
    }

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "foreach");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

str1(addr:0x400b9a):one
str2(addr:0x400b9e):two 
str3(addr:0x400ba2):three 
parr[0](addr:0x109deb0):one(addr:0x400b9a) 
parr[1](addr:0x109deb8):two(addr:0x400b9e) 
parr[2](addr:0x109dec0):three(addr:0x400ba2) 
foreach: one(addr:0x400b9a) 
foreach: two(addr:0x400b9e) 
foreach: three(addr:0x400ba2)

这里有两组地址,一组以0x400b开头,一组以0x109de开头,指针数组中保存的是哪个地址?
在64位操作系统下,一个指针的长度为8字节,指针数组中保存的指针应该以8字节为单位递增,由此猜测指针数组中保存的是0x109de开头的一组地址。
下面具体分析一下这两组地址。
第一组地址是str1/str2/str2,第二组地址是parr[0]/parr[1]/parr[2]。
那么strX和parr[X]两者之间有没有什么联系?
0x400bxx是字符串常量strX的地址,0x109dexx是指针数组元素parr[X]的地址。0x109dexx地址指向的地址正是0x400bxx。
这里可能会有一个疑惑,str1和str2都是指针,为什么其地址增长以4为单位,而不是以8为单位呢?
其实这是一种巧合,字符串常量one和字符串常量two加上结尾符正好是4个字节,使得误以为str1和str2这两个地址之间差了4个字节,
如果将one改为onex,two改为tw,再查看地址,则会发现其递增顺序不再具有规律。
str1(addr:0x400b9a):onex
str2(addr:0x400b9f):tw
str3(addr:0x400ba2):three
0x400bxx为常量字符串的访问地址,这些地址指向静态存储区。

将上述字符串常量修改为局部变量,查看有什么现象。
源码见glib_examples\glib_ptr_array\glib_ptr_array_local_var_addr

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

static void _ptr_array_add(GPtrArray *parr)
{
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};

    g_strlcpy(str1, "one", 8);
    g_strlcpy(str2, "two", 8);
    g_strlcpy(str3, "three", 8);

    g_ptr_array_add(parr, (gpointer)str1);
    g_ptr_array_add(parr, (gpointer)str3);
    g_ptr_array_insert(parr, 1, (gpointer)str2);

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "in-func");
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gint i = 0;

    parr = g_ptr_array_sized_new(10);

    _ptr_array_add(parr);

    for(i=0; i<parr->len; i++) {
        g_print("parr[%d](addr:%p):%s(addr:%p) \n", 
            i, 
            &g_ptr_array_index(parr, i), 
            (gchar *)g_ptr_array_index(parr, i), 
            g_ptr_array_index(parr, i));
    }

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "out-func");

    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

in-func: one(addr:0x7fffae1224b0) 
in-func: two(addr:0x7fffae1224c0) 
in-func: three(addr:0x7fffae1224d0) 
parr[0](addr:0x1420600):one(addr:0x7fffae1224b0) 
parr[1](addr:0x1420608):two(addr:0x7fffae1224c0) 
parr[2](addr:0x1420610):(addr:0x7fffae1224d0) 
out-func: (addr:0x7fffae1224b0) 
out-func: two(addr:0x7fffae1224c0) 
[Invalid UTF-8] out-func: \x10%\x12\xae\xff\x7f(addr:0x7fffae1224d0)

在局部变量生命周期结束后,指针数组元素的值也变为不确定状态。

使用堆上的内存就不会有这种现象。
源码见glib_examples\glib_ptr_array\glib_ptr_array_heap_var_addr

#include <glib.h>

static void _ptr_array_foreach_print_func(gpointer data, gpointer user_data)
{
    g_print("%s: %s(addr:%p) \n", (gchar *)user_data, (gchar *)data, data);
}

static void _ptr_array_free_func(gpointer data)
{
    if(NULL != data) {
        g_free(data);
    }
}

static void _ptr_array_add(GPtrArray *parr)
{
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};

    g_strlcpy(str1, "one", 8);
    g_strlcpy(str2, "two", 8);
    g_strlcpy(str3, "three", 8);

    g_ptr_array_add(parr, (gpointer)g_strdup(str1));
    g_ptr_array_add(parr, (gpointer)g_strdup(str3));
    g_ptr_array_insert(parr, 1, (gpointer)g_strdup(str2));

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "in-func");
}

gint main(gint argc, gchar **argv)
{
    GPtrArray *parr = NULL;
    gint i = 0;

    parr = g_ptr_array_sized_new(10);

    _ptr_array_add(parr);

    for(i=0; i<parr->len; i++) {
        g_print("parr[%d](addr:%p):%s(addr:%p) \n", 
            i, 
            &g_ptr_array_index(parr, i), 
            (gchar *)g_ptr_array_index(parr, i), 
            g_ptr_array_index(parr, i));
    }

    g_ptr_array_foreach(parr, _ptr_array_foreach_print_func, "out-func");

    g_ptr_array_set_free_func(parr, _ptr_array_free_func);
    g_ptr_array_free(parr, TRUE);

    return 0;
}

运行结果:

in-func: one(addr:0xed9dd0) 
in-func: two(addr:0xeda6b0) 
in-func: three(addr:0xeda690) 
parr[0](addr:0xeda600):one(addr:0xed9dd0) 
parr[1](addr:0xeda608):two(addr:0xeda6b0) 
parr[2](addr:0xeda610):three(addr:0xeda690) 
out-func: one(addr:0xed9dd0) 
out-func: two(addr:0xeda6b0) 
out-func: three(addr:0xeda690)

根据上面的例子和分析,可以得知指针数组的内存存储模型如下。
请将下面这些行逐行复制到文本编辑器并调整字体为等宽字体如宋体、Courier New等再查看。

┌───┬───┬───┬───┬───┬───┬───┬───┬┈┬───┐
│pointer│pointer│ ..... │pointer┤..│len│
└───┼───┴───┼───┴───┼───┴───┼───┴┈┴───┘
  ┌─┴─┐   ┌─┴─┐   ┌─┴─┐   ┌─┴─┐
  │ANY│   │ANY│   │ANY│   │ANY│
  └───┘   └───┘   └───┘   └───┘

多个pointer共同组成GPtrArray结构体的pdata部分,len组成GPtrArray结构体的len部分
下面的ANY是pointer指向的任意数据,注意这一部分的内存不属于GPtrArray。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值