4_03_GLib库入门与实践_普通数组

简介

数组是一种常见的数据结构,其特点是内存连续。
GLib提供了三种类型的数组,普通数组,指针数组和字节数组,本节讨论普通数组。
普通数组非常简单,GLib提供了对普通数组的常用操作,如创建、测长、释放、插入、访问、移除及排序等等。

数据结构

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

函数列表

GArray *	g_array_new ()
GArray *	g_array_sized_new ()
GArray *	g_array_ref ()
void	g_array_unref ()
guint	g_array_get_element_size ()
#define	g_array_append_val()
GArray *	g_array_append_vals ()
#define	g_array_prepend_val()
GArray *	g_array_prepend_vals ()
#define	g_array_insert_val()
GArray *	g_array_insert_vals ()
GArray *	g_array_remove_index ()
GArray *	g_array_remove_index_fast ()
GArray *	g_array_remove_range ()
void	g_array_sort ()
void	g_array_sort_with_data ()
#define	g_array_index()
GArray *	g_array_set_size ()
void	g_array_set_clear_func ()
gchar *	g_array_free ()

函数功能分类

创建

GArray *	g_array_new ()
GArray *	g_array_sized_new ()
GArray *	g_array_set_size ()

测长

guint	g_array_get_element_size ()
GArray->len

释放

gchar *	g_array_free ()
void	g_array_set_clear_func ()

插入

#define	g_array_append_val()
GArray *	g_array_append_vals ()
#define	g_array_prepend_val()
GArray *	g_array_prepend_vals ()
#define	g_array_insert_val()
GArray *	g_array_insert_vals ()

访问

#define	g_array_index()

移除

GArray *	g_array_remove_index ()
GArray *	g_array_remove_index_fast ()
GArray *	g_array_remove_range ()

引用和解引用

GArray *	g_array_ref ()
void	g_array_unref ()

排序

void	g_array_sort ()
void	g_array_sort_with_data ()

函数功能说明及综合演示

创建

GArray * g_array_new ()
GArray * g_array_sized_new ()
GArray * g_array_set_size ()

//创建数组,第一个参数表示该数组是否为0结尾,第二个参数表示是否清空数组,第三个参数表示每个元素的长度
GArray *
g_array_new (gboolean zero_terminated,
             gboolean clear_,
             guint element_size);

//与g_array_new相似,但可以预分配数组长度,避免多次插入数组元素时导致的内存频繁申请问题。但注意此时数组的实际长度仍是0。
GArray *
g_array_sized_new (gboolean zero_terminated,
                   gboolean clear_,
                   guint element_size,
                   guint reserved_size);

//设置数组的长度。本函数和g_array_new函数相结合可以起到与g_array_sized_new相同的效果。
GArray *
g_array_set_size (GArray *array,
                  guint length);
测长

guint g_array_get_element_size ()
注意本函数不是得到数组的长度,而是得到数组中每一个元素的长度
如果想得到数组的长度,可以直接引用数组中的长度成员变量。
GArray->len
在标准宏(Standard Macros)一节,还有一个G_N_ELEMENTS用来计算数组长度,但要求传入的参数不能是指针类型。

释放

gchar * g_array_free ()

//数组释放,本函数不会自动释放数组元素中的数据。
//如果第二个参数设置为FALSE,则需要用户自行释放数组元素,
//如果第二个参数设置为TRUE,则需要先调用g_array_set_clear_func函数设置数组元素的释放函数。
gchar *
g_array_free (GArray *array,
              gboolean free_segment);

//释放函数。当g_array_free的第二个参数设置为TRUE时,本函数将被设置,用来释放数组元素中的某些内存空间(后面有例子会讲到)。
void	g_array_set_clear_func ()

下面是针对上面三类函数的示例程序。
源码见glib_examples\glib_array\glib_array_new_len_free

#include <glib.h>

typedef struct employee_info_tag {
    gint id;
    gchar *name;
}employee_info_t;

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(gint));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);
    g_array_free(arr, TRUE);

    arr = g_array_new(TRUE, TRUE, sizeof(gint));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);
    g_array_free(arr, TRUE);

    arr = g_array_new(FALSE, TRUE, sizeof(employee_info_t));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);
    g_array_free(arr, TRUE);

    arr = g_array_sized_new(FALSE, TRUE, sizeof(employee_info_t), 100);
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);
    g_array_free(arr, TRUE);


    return 0;
}

运行结果:

element size:4, array len:0 
element size:4, array len:0 
element size:16, array len:0 
element size:16, array len:0
插入

#define g_array_append_val()
GArray * g_array_append_vals ()
#define g_array_prepend_val()
GArray * g_array_prepend_vals ()
#define g_array_insert_val()
GArray * g_array_insert_vals ()
一组数组插入函数
其中,g_array_append_val,g_array_prepend_val,g_array_insert_val是宏,因此第二个参数只能传输变量,不能传入常量。
g_array_xxx_vals系列函数,可以一次添加多个元素到数组中。

访问

#define g_array_index()
根据数组元素的下标得到数组元素的值

数组的插入及访问举例:
源码见glib_examples\glib_array\glib_array_insert_index

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    gint i = 0;
    gint x = 0;
    guint n = 0;
    gint a[3] = {0,0,0};

    arr = g_array_new(FALSE, FALSE, sizeof(gint));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    x = 1;
    g_array_append_val(arr, x);
    x = 3;
    g_array_append_val(arr, x);

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("append-val arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }

    a[0] = 5;
    a[1] = 7;
    g_array_append_vals(arr, a, 2);

    for(i=0; i<arr->len; i++) {
        g_print("append-vals arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }

    x = 2;
    g_array_prepend_val(arr, x);
    x = 4;
    g_array_prepend_val(arr, x);

    for(i=0; i<arr->len; i++) {
        g_print("prepend-val arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }

    a[0] = 8;
    a[1] = 6;
    g_array_prepend_vals(arr, a, 2);

    for(i=0; i<arr->len; i++) {
        g_print("prepend-vals arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }

    x = 9;
    g_array_insert_val(arr, arr->len, x);
    x = 10;
    g_array_insert_val(arr, 0, x);

    for(i=0; i<arr->len; i++) {
        g_print("insert-val arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }

    a[0] = 12;
    a[1] = 11;
    g_array_insert_vals(arr, 5, a, 2);

    for(i=0; i<arr->len; i++) {
        g_print("insert-vals arr[%d]:%d \n", i, g_array_index(arr, gint, i));
    }
 
    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:4, array len:0 
element size:4, array len:2 
append-val arr[0]:1 
append-val arr[1]:3 
append-vals arr[0]:1 
append-vals arr[1]:3 
append-vals arr[2]:5 
append-vals arr[3]:7 
prepend-val arr[0]:4 
prepend-val arr[1]:2 
prepend-val arr[2]:1 
prepend-val arr[3]:3 
prepend-val arr[4]:5 
prepend-val arr[5]:7 
prepend-vals arr[0]:8 
prepend-vals arr[1]:6 
prepend-vals arr[2]:4 
prepend-vals arr[3]:2 
prepend-vals arr[4]:1 
prepend-vals arr[5]:3 
prepend-vals arr[6]:5 
prepend-vals arr[7]:7 
insert-val arr[0]:10 
insert-val arr[1]:8 
insert-val arr[2]:6 
insert-val arr[3]:4 
insert-val arr[4]:2 
insert-val arr[5]:1 
insert-val arr[6]:3 
insert-val arr[7]:5 
insert-val arr[8]:7 
insert-val arr[9]:9 
insert-vals arr[0]:10 
insert-vals arr[1]:8 
insert-vals arr[2]:6 
insert-vals arr[3]:4 
insert-vals arr[4]:2 
insert-vals arr[5]:12 
insert-vals arr[6]:11 
insert-vals arr[7]:1 
insert-vals arr[8]:3 
insert-vals arr[9]:5 
insert-vals arr[10]:7 
insert-vals arr[11]:9
移除
//根据下标移除数组元素,下标从0开始,返回移除数组元素后的新数组首地址
GArray *	g_array_remove_index ()
//根据下标移除数组元素,但从最后一个元素补上被移除元素的空缺,其效率要比g_array_remove_index要高,返回移除数组元素后的新数组首地址
GArray *	g_array_remove_index_fast ()
//根据下标移除多个数组元素,返回移除数组元素后的新数组首地址
GArray *	g_array_remove_range ()

示例代码如下:
源码见glib_examples\glib_array\glib_array_remove

#include <glib.h>

typedef struct foobar_tag {
    gint foo;
    gchar bar[4];
}foobar_t;

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    foobar_t *foobar[5] = {NULL, NULL, NULL, NULL, NULL};
    gint i = 0;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(foobar_t));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    foobar[0] = g_new0(foobar_t, 1);
    foobar[1] = g_new0(foobar_t, 1);
    foobar[2] = g_new0(foobar_t, 1);
    foobar[3] = g_new0(foobar_t, 1);
    foobar[4] = g_new0(foobar_t, 1);

    foobar[0]->foo = 2;
    foobar[1]->foo = 3;
    foobar[2]->foo = 1;
    foobar[3]->foo = 2;
    foobar[4]->foo = 3;
    
    g_strlcpy(foobar[0]->bar, "AA", 4);
    g_strlcpy(foobar[1]->bar, "BBB", 4);
    g_strlcpy(foobar[2]->bar, "C", 4);
    g_strlcpy(foobar[3]->bar, "DD", 4);
    g_strlcpy(foobar[4]->bar, "EEE", 4);


    g_array_append_val(arr, *foobar[0]); // Not foobar[0]
    g_array_append_val(arr, *foobar[1]);
    g_array_append_val(arr, *foobar[2]);
    g_array_append_val(arr, *foobar[3]);
    g_array_append_val(arr, *foobar[4]);

    // Caution: only the first element can be added success
    //g_array_append_vals(arr, *foobar, 3);  

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    arr = g_array_remove_index(arr, 1);

    for(i=0; i<arr->len; i++) {
        g_print("[remove(1)] arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    arr = g_array_remove_index_fast(arr, 0);

    for(i=0; i<arr->len; i++) {
        g_print("[remove-fast(0)] arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    arr = g_array_remove_range(arr, 0, 2);

    for(i=0; i<arr->len; i++) {
        g_print("[remove-range(0,2)] arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    g_free(foobar[0]);
    g_free(foobar[1]);
    g_free(foobar[2]);
    g_free(foobar[3]);
    g_free(foobar[4]);
    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:8, array len:0 
element size:8, array len:5 
arr[0]:foo=2,bar=AA(addr:0x9067c0) 
arr[1]:foo=3,bar=BBB(addr:0x9067c8) 
arr[2]:foo=1,bar=C(addr:0x9067d0) 
arr[3]:foo=2,bar=DD(addr:0x9067d8) 
arr[4]:foo=3,bar=EEE(addr:0x9067e0) 
[remove(1)] arr[0]:foo=2,bar=AA(addr:0x9067c0) 
[remove(1)] arr[1]:foo=1,bar=C(addr:0x9067c8) 
[remove(1)] arr[2]:foo=2,bar=DD(addr:0x9067d0) 
[remove(1)] arr[3]:foo=3,bar=EEE(addr:0x9067d8) 
[remove-fast(0)] arr[0]:foo=3,bar=EEE(addr:0x9067c0) 
[remove-fast(0)] arr[1]:foo=1,bar=C(addr:0x9067c8) 
[remove-fast(0)] arr[2]:foo=2,bar=DD(addr:0x9067d0) 
[remove-range(0,2)] arr[0]:foo=2,bar=DD(addr:0x9067c0)
引用和解引用

GArray * g_array_ref ()
void g_array_unref ()
//TODO

排序

void g_array_sort ()
void g_array_sort_with_data ()
排序函数,g_array_sort_with_data函数允许比较函数带参数

数组排序举例:
源码见glib_examples\glib_array\glib_array_sort

#include <glib.h>

static gint _array_cmp_func(gconstpointer a, gconstpointer b)
{
    gint x, y;

    x = *(gint *)a;
    y = *(gint *)b;

    return (x > y ? +1 : x == y ? 0 : -1);
}

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    gint i = 0;
    gint x = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(gint));

    x = 1;
    g_array_append_val(arr, x);
    x = 5;
    g_array_append_val(arr, x);
    x = 3;
    g_array_append_val(arr, x);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:%d(addr:%p) \n", i, g_array_index(arr, gint, i), &g_array_index(arr, gint, i));
    }

    g_array_sort(arr, _array_cmp_func);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:%d(addr:%p) \n", i, g_array_index(arr, gint, i), &g_array_index(arr, gint, i));
    }

    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

arr[0]:1(addr:0x2333dd0) 
arr[1]:5(addr:0x2333dd4) 
arr[2]:3(addr:0x2333dd8) 
arr[0]:1(addr:0x2333dd0) 
arr[1]:3(addr:0x2333dd4) 
arr[2]:5(addr:0x2333dd8)

排序前后,数组元素的值发生了变化,但数组元素的地址未发生变化。

专题

GArray内存及内存释放专题

内存在动态普通数组中是怎样存放的呢,先看一下GArray这个结构体。

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

可以猜测,数据都保存在data中,len是数组中元素的个数。下面写个程序来验证一下。
源码见glib_examples\glib_array\glib_array_mem

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    gint i = 0;
    gint x = 0;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(gint));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    x = 1;
    g_array_append_val(arr, x);
    x = 3;
    g_array_append_val(arr, x);
    x = 5;
    g_array_append_val(arr, x);

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:%d(addr:%p) \n", i, g_array_index(arr, gint, i), &g_array_index(arr, gint, i));
    }

    g_print("element0:%d \n", ((gint *)arr->data)[0]);
    g_print("element1:%d \n", ((gint *)arr->data)[1]);
    g_print("element2:%d \n", ((gint *)arr->data)[2]);
 
    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:4, array len:0 
element size:4, array len:3 
arr[0]:1(addr:0x213c620) 
arr[1]:3(addr:0x213c624) 
arr[2]:5(addr:0x213c628) 
element0:1 
element1:3 
element2:5

可以看到,通过g_array_index取出来的元素,其地址是连续的,由于gint占4个字节,因此地址以4字节增长。也可以通过直接将arr->data转换成整型指针数组的方式来获取其中的值,((gint *)arr->data)[0]代表第0个数组。

数组中的元素为固定长度的结构体,又是如何存放的?
源码见glib_examples\glib_array\glib_array_struct_mem

#include <glib.h>

typedef struct foobar_tag {
    gint foo;
    gchar bar[4];
}foobar_t;

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    foobar_t *foobar[3] = {NULL, NULL, NULL};
    gint i = 0;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(foobar_t));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    foobar[0] = g_new0(foobar_t, 1);
    foobar[1] = g_new0(foobar_t, 1);
    foobar[2] = g_new0(foobar_t, 1);

    foobar[0]->foo = 2;
    foobar[1]->foo = 3;
    foobar[2]->foo = 1;
    
    g_strlcpy(foobar[0]->bar, "ab", 4);
    g_strlcpy(foobar[1]->bar, "AAA", 4);
    g_strlcpy(foobar[2]->bar, "c", 4);


    g_array_append_val(arr, *foobar[0]); // Not foobar[0]
    g_array_append_val(arr, *foobar[1]);
    g_array_append_val(arr, *foobar[2]);

    // Caution: only the first element can be added success
    //g_array_append_vals(arr, *foobar, 3);  

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    g_free(foobar[0]);
    g_free(foobar[1]);
    g_free(foobar[2]);
    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:8, array len:0 
element size:8, array len:3 
arr[0]:foo=2,bar=ab(addr:0x1216780) 
arr[1]:foo=3,bar=AAA(addr:0x1216788) 
arr[2]:foo=1,bar=c(addr:0x1216790)

可以看到内存仍是连续的,结构体foobar_t占用8个字节,因此数组地址也以8为单位递增。

下面演示一种包含指针的结构体情况。
源码见glib_examples\glib_array\glib_array_ptr_struct_mem

#include <glib.h>

typedef struct foobar_tag {
    gint foo;
    gchar bar[4];
    gchar *str;
}foobar_t;

static void _array_free_func(gpointer data)
{
    foobar_t *foobar = (foobar_t *)data;

    if(NULL != foobar) {
        g_free(foobar->str);
    }

    //g_free(foobar); //Caution: can not free foobar here
}

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    foobar_t *foobar[3] = {NULL, NULL, NULL};
    gint i = 0;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(foobar_t));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    foobar[0] = g_new0(foobar_t, 1);
    foobar[1] = g_new0(foobar_t, 1);
    foobar[2] = g_new0(foobar_t, 1);

    foobar[0]->foo = 2;
    foobar[1]->foo = 3;
    foobar[2]->foo = 1;
    
    g_strlcpy(foobar[0]->bar, "ab", 4);
    g_strlcpy(foobar[1]->bar, "AAA", 4);
    g_strlcpy(foobar[2]->bar, "c", 4);

    foobar[0]->str = g_strdup("aaab");
    foobar[1]->str = g_strdup("AAAAAA");
    foobar[2]->str = g_strdup("c");

    g_array_append_val(arr, *foobar[0]); // Not foobar[0]
    g_array_append_val(arr, *foobar[1]);
    g_array_append_val(arr, *foobar[2]);

    // Caution: only the first element can be added success
    //g_array_append_vals(arr, *foobar, 3);  

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:foo=%d,bar=%s,str=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            (g_array_index(arr, foobar_t, i)).str,
            &g_array_index(arr, foobar_t, i));
    }

    g_array_set_clear_func(arr, _array_free_func);
    g_free(foobar[0]);
    g_free(foobar[1]);
    g_free(foobar[2]);
    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:16, array len:0 
element size:16, array len:3 
arr[0]:foo=2,bar=ab,str=aaab(addr:0x240d7e0) 
arr[1]:foo=3,bar=AAA,str=AAAAAA(addr:0x240d7f0) 
arr[2]:foo=1,bar=c,str=c(addr:0x240d800)

程序分析:
可以看到,地址每次增长0x10即16个字节,结构体foobar_t现在的长度是4字节gint、4字节数组和8字节指针,正好16字节。
指针在动态数组中被存了下来,但指针所指向的内容并没有存在数组中。
上述程序,在数组释放前,设置了一个释放函数,g_array_set_clear_func,对数组元素中的指针变量所指向的内存进行了释放,但没有释放数组元素本身,这是为什么呢?

下面通过一个例子说明这个问题。
源码见glib_examples\glib_array\glib_array_free

#include <glib.h>

typedef struct foobar_tag {
    gint foo;
    gchar bar[4];
}foobar_t;

static void array_append_value(GArray *arr)
{
    foobar_t *foobar[3] = {NULL, NULL, NULL};

    foobar[0] = g_new0(foobar_t, 1);
    foobar[1] = g_new0(foobar_t, 1);
    foobar[2] = g_new0(foobar_t, 1);

    g_print("foobar[0] address: %p \n", foobar[0]);
    g_print("foobar[1] address: %p \n", foobar[1]);
    g_print("foobar[2] address: %p \n", foobar[2]);

    foobar[0]->foo = 2;
    foobar[1]->foo = 3;
    foobar[2]->foo = 1;
    
    g_strlcpy(foobar[0]->bar, "ab", 4);
    g_strlcpy(foobar[1]->bar, "AAA", 4);
    g_strlcpy(foobar[2]->bar, "c", 4);

    g_array_append_val(arr, *foobar[0]); // Not foobar[0]
    g_array_append_val(arr, *foobar[1]);
    g_array_append_val(arr, *foobar[2]);

    g_free(foobar[0]);
    g_free(foobar[1]);
    g_free(foobar[2]);
}

gint main(gint argc, gchar **argv)
{
    GArray *arr = NULL;
    
    gint i = 0;
    guint n = 0;

    arr = g_array_new(FALSE, FALSE, sizeof(foobar_t));
    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    array_append_value(arr);

    n = g_array_get_element_size(arr);
    g_print("element size:%d, array len:%d \n", n, arr->len);

    for(i=0; i<arr->len; i++) {
        g_print("arr[%d]:foo=%d,bar=%s(addr:%p) \n", i, 
            (g_array_index(arr, foobar_t, i)).foo, 
            (g_array_index(arr, foobar_t, i)).bar,
            &g_array_index(arr, foobar_t, i));
    }

    g_array_free(arr, TRUE);

    return 0;
}

运行结果:

element size:8, array len:0 
foobar[0] address: 0xa0e620 
foobar[1] address: 0xa0e640 
foobar[2] address: 0xa0e760 
element size:8, array len:3 
arr[0]:foo=2,bar=ab(addr:0xa0e780) 
arr[1]:foo=3,bar=AAA(addr:0xa0e788) 
arr[2]:foo=1,bar=c(addr:0xa0e790)

对比链表一节的专题《浅谈链表的内存空间》,可以发现,即使数组的节点已经释放,仍可以得到正确的数组元素值。这就是链表与数组的区别。链表的data指针只指向用户数据,不会开辟新的内存空间,而数组则会开辟新的内存空间,并将要插入的数据拷贝到新的内存空间。稍加思索也可以得知,数组要求空间是连续的,在插入数组元素时,肯定需要realloc之前的数组空间,才能保证这一连续性。
在上面一个例子中,只释放了数组中的指针节点所指向的内存,因为该内存是在堆上分配的,只要有指针地址就可以释放,但不能释放数组元素本身,因为g_array_free会再次释放该数组元素。当然,由于数组元素和foobar[]并不是同一内存空间,因此也无法通过数组元素来释放g_new0出来的foobar[],只能手动释放foobar[]。

我们从GArray数据结构画图分析一下普通数组的内存模型
GArray数据结构

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

(注:下面的讨论都是基于64位操作系统,64位操作系统的指针占8个字节。)

gint型数组的内存模型
请将下面三行逐行复制到文本编辑器并调整字体为等宽字体如宋体、Courier New等再查看。

┌───┬───┬───┬───┬┈┬───┐
│intint...int..│len│
└───┴───┴───┴───┴┈┴───┘

每个int占4字节,这些int共同组成了data,还有一个和data地址不连续的len,占4字节,代表前面的数组元素的总个数,也叫数组长度。

typedef struct foobar_tag {
    gint foo;
    gchar bar[4];
    gchar *str;
}foobar_t;

上述struct型数组的内存模型
请将下面四行逐行复制到文本编辑器并调整字体为等宽字体如宋体、Courier New等再查看。

┌───┬───┬───┬───┬───┬───┬───┬───┬..┬───┐
│foo.bar│foo.bar│.......│foo.bar┤..│len│
│..*str.│..*str.│.......│.*str..├..┴───┘
└───┴───┴───┴───┴───┴───┴───┴───┘

每个foobar_t结构体占4+4+8=16个字节,这些foobar_t结构体共同组成了data,还有一个和data不连续的len,占4字节,代表前面的数组元素总个数,也叫数组长度。每个foobar_t结构体与前后都是内存连续的,但str这个成员变量所指向的内存,与本动态数组的内存是不连续的,这些地址在另外的堆空间上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值