简介
数组是一种常见的数据结构,其特点是内存连续。
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等再查看。
)
┌───┬───┬───┬───┬┈┬───┐
│int│int│...│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这个成员变量所指向的内存,与本动态数组的内存是不连续的,这些地址在另外的堆空间上。