4_05_GLib库入门与实践_字节数组

本文详细讲解了Glib库中的GByteArray和GBytes两种字节数组类型,包括创建、长度获取、内存释放、插入、访问、移除、排序以及GBytes的额外功能,如内存管理、比较和转换。通过实例演示,理解了如何在实际开发中灵活运用这两种数据结构。
摘要由CSDN通过智能技术生成

简介

GLib提供了三种类型的数组,普通数组,指针数组和字节数组,本节讨论字节数组。
字节数组比普通数组和指针数组最大的不同是它可以存储任意值,甚至包括’\0’这种只能出现在普通字符串末尾的特殊字符。

数据结构

本节操作的数据结构有两个,一个是GByteArray,一个是GBytes。
GByteArray结构体定义如下:

struct GByteArray {
  guint8 *data;
  guint	  len;
};

GBytes结构体对用户不透明。

函数列表

GByteArray * 	g_byte_array_new ()
GByteArray * 	g_byte_array_new_take ()
GByteArray * 	g_byte_array_sized_new ()
GByteArray * 	g_byte_array_ref ()
void 	g_byte_array_unref ()
GByteArray * 	g_byte_array_append ()
GByteArray * 	g_byte_array_prepend ()
GByteArray * 	g_byte_array_remove_index ()
GByteArray * 	g_byte_array_remove_index_fast ()
GByteArray * 	g_byte_array_remove_range ()
void 	g_byte_array_sort ()
void 	g_byte_array_sort_with_data ()
GByteArray * 	g_byte_array_set_size ()
guint8 * 	g_byte_array_free ()
GBytes * 	g_byte_array_free_to_bytes ()
GBytes * 	g_bytes_new ()
GBytes * 	g_bytes_new_take ()
GBytes * 	g_bytes_new_static ()
GBytes * 	g_bytes_new_with_free_func ()
GBytes * 	g_bytes_new_from_bytes ()
gconstpointer 	g_bytes_get_data ()
gsize 	g_bytes_get_size ()
guint 	g_bytes_hash ()
gboolean 	g_bytes_equal ()
gint 	g_bytes_compare ()
GBytes * 	g_bytes_ref ()
void 	g_bytes_unref ()
gpointer 	g_bytes_unref_to_data ()
GByteArray * 	g_bytes_unref_to_array ()

函数功能分类

函数功能有三类,一类是对GByteArray字节数组的操作,一类是对GBytes字节的操作,还有一类是GByteArray与GBytes两者转换的操作。

GByteArray字节数组

创建

GByteArray *	g_byte_array_new ()
GByteArray *	g_byte_array_new_take ()
GByteArray *	g_byte_array_sized_new ()
GByteArray *	g_byte_array_set_size ()

测长

GByteArray->len

释放

guint8 *	g_byte_array_free ()

插入

GByteArray *	g_byte_array_append ()
GByteArray *	g_byte_array_prepend ()

访问

GByteArray->data[i]

移除

GByteArray *	g_byte_array_remove_index ()
GByteArray *	g_byte_array_remove_index_fast ()
GByteArray *	g_byte_array_remove_range ()

引用和解引用

GByteArray *	g_byte_array_ref ()
void	g_byte_array_unref ()

排序

void	g_byte_array_sort ()
void	g_byte_array_sort_with_data ()
GBytes字节

创建

GBytes *	g_bytes_new ()
GBytes *	g_bytes_new_take ()
GBytes *	g_bytes_new_static ()
GBytes *	g_bytes_new_with_free_func ()
GBytes *	g_bytes_new_from_bytes ()

访问

gconstpointer	g_bytes_get_data ()
gpointer	g_bytes_unref_to_data ()

测长

gsize	g_bytes_get_size ()

引用和解引用

GBytes *	g_bytes_ref ()
void	g_bytes_unref ()

hash

guint	g_bytes_hash ()

比较

gboolean	g_bytes_equal ()
gint	g_bytes_compare ()
GByteArray和GBytes转换
GBytes *	g_byte_array_free_to_bytes ()
GByteArray *	g_bytes_unref_to_array ()

函数功能说明及综合演示

GByteArray创建
GByteArray *	g_byte_array_new ()
GByteArray *	g_byte_array_new_take ()
GByteArray *	g_byte_array_sized_new ()
GByteArray *	g_byte_array_set_size ()
// 创建一个字节数组
GByteArray *
g_byte_array_new (void);

// 创建字节数组,并填充为data指定的值。数组创建完后,其长度即为len
GByteArray *
g_byte_array_new_take (guint8 *data,
                       gsize len);
// 创建字节数组,并预分配内存空间。避免之后添加元素而多次内存申请或重新调整大小。注意此时字节数组的实际长度仍为0。
GByteArray *
g_byte_array_sized_new (guint reserved_size);

// 函数g_byte_array_new和g_byte_array_set_size组合使用等效于g_byte_array_sized_new
GByteArray *
g_byte_array_set_size (GByteArray *array,
                       guint length);
GByteArray测长

字节数组也没有测长的函数,可以直接访问其成员变量len

GByteArray->len
GByteArray释放
guint8 *	g_byte_array_free ()
// 字节数组释放,如果free_segment设置为TRUE,其实际字节内容也会被释放。
guint8 *
g_byte_array_free (GByteArray *array,
                   gboolean free_segment);
GByteArray插入
GByteArray *	g_byte_array_append ()
GByteArray *	g_byte_array_prepend ()
// 字节数组只有头插和尾插两种操作,没有insert操作
GByteArray *
g_byte_array_append (GByteArray *array,
                     const guint8 *data,
                     guint len);
GByteArray *
g_byte_array_prepend (GByteArray *array,
                      const guint8 *data,
                      guint len);

下面是以上功能的示例代码:
源码见glib_examples\glib_byte_array\glib_byte_array_insert

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)"World", 5);
    g_byte_array_prepend(barr, (guint8 *)"Hello", 5);

    g_print("barr:(len:%d)%s \n", barr->len, barr->data);
    g_print("barr->data: \n");
    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }
    g_print("\n");
    
    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

[Invalid UTF-8] barr:(len:10)HelloWorld\xef{\xe2\x7f 
barr->data: 
H e l l o W o r l d

可以看出,append和prepend添加的两个字符串,被拼在了一起,由于没有结尾符,打印出来有一部分会显示乱码。

字符数组可以存储任意值,这是普通动态数组和指针数组无法做到的。
在HelloWorld后面添加一个’\0’符号。
源码见glib_examples\glib_byte_array\glib_byte_array_insert_end_zero

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)"World", 5);
    g_byte_array_prepend(barr, (guint8 *)"Hello", 5);
    g_byte_array_append(barr, (guint8 *)"\0", 1);

    g_print("barr:(len:%d)%s \n", barr->len, barr->data);
    g_print("barr->data: \n");
    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }
    g_print("\n");
    
    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

barr:(len:11)HelloWorld 
barr->data: 
H e l l o W o r l d

当然,’\0’可以添加到字节数组的任意位置。
源码见glib_examples\glib_byte_array\glib_byte_array_insert_mid_zero

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)"Hello", 5);
    g_byte_array_append(barr, (guint8 *)"\0", 1);
    g_byte_array_append(barr, (guint8 *)"World", 5);

    g_print("barr:(len:%d)%s \n", barr->len, barr->data);
    g_print("barr->data: \n");
    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }
    g_print("\n");
    
    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

barr:(len:11)Hello 
barr->data: 
H e l l o W o r l d

此时虽然使用%s无法打印出来,但仍可以通过数组地址直接访问到对应的数据。

GByteArray访问

字节数组没有专门访问的函数,在上一节,我们也展示了如何访问字节数组的成员。
字节数组的每一个元素都是一个字节,我们可以根据地址直接访问它。

barr->data[i];

注意,有些字符是非显示字符,尽管存在字节数组中,但无法通过%c打印出来,此时可通过%d打印。

GByteArray移除
GByteArray *	g_byte_array_remove_index ()
GByteArray *	g_byte_array_remove_index_fast ()
GByteArray *	g_byte_array_remove_range ()
// 根据数组下标移除数组元素,后面的数组元素会依次补上。
GByteArray *
g_byte_array_remove_index (GByteArray *array,
                           guint index_);

// 根据数组下标移除数组元素,最后一个数组元素会补上此空缺。
GByteArray *
g_byte_array_remove_index_fast (GByteArray *array,
                                guint index_);

// 根据数组下标和个数移除多个数组元素
GByteArray *
g_byte_array_remove_range (GByteArray *array,
                           guint index_,
                           guint length);

字节数组移除示例:
源码见glib_examples\glib_byte_array\glib_byte_array_remove

#include <glib.h>

static void _byte_array_print(GByteArray *barr, gconstpointer user_data)
{
    guint i = 0;
    g_print("%s", (gchar *)user_data);

    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }

    g_print("\n");
}

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)"Hello,World", strlen("Hello,World"));
    _byte_array_print(barr, "ori barr: ");

    g_byte_array_remove_index(barr, 5);
    _byte_array_print(barr, "remove_index(5) barr: ");

    g_byte_array_remove_index_fast(barr, 5);
    _byte_array_print(barr, "remove_index_fast(5) barr: ");

    g_byte_array_remove_range(barr, 5, 3);
    _byte_array_print(barr, "remove_range(5,3) barr: ");

    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

ori barr: H e l l o , W o r l d 
remove_index(5) barr: H e l l o W o r l d 
remove_index_fast(5) barr: H e l l o d o r l 
remove_range(5,3) barr: H e l l o l
GByteArray排序
void	g_byte_array_sort ()
void	g_byte_array_sort_with_data ()
void
g_byte_array_sort (GByteArray *array,
                   GCompareFunc compare_func);
对字节数组进行排序,排序函数由用户指定。
void
g_byte_array_sort_with_data (GByteArray *array,
                             GCompareDataFunc compare_func,
                             gpointer user_data);
与g_byte_array_sort功能相同,排序函数带用户自定义参数。

由于g_byte_array_sort和g_byte_array_sort_with_data功能完全一样,这里只演示g_byte_array_sort的使用方法。

示例代码如下:
源码见glib_examples\glib_byte_array\glib_byte_array_sort

#include <glib.h>

static void _byte_array_print(GByteArray *barr, gconstpointer user_data)
{
    guint i = 0;
    g_print("%s", (gchar *)user_data);

    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }

    g_print("\n");
}

static gint _byte_array_cmp_func(gconstpointer a, gconstpointer b)
{
    //return (guint8 *)a - (guint8 *)b;  // Caution: Wrong method! address, not value!

    return *(guint8 *)a - *(guint8 *)b;
}

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)"Hello,World", strlen("Hello,World"));
    _byte_array_print(barr, "ori barr: ");

    g_byte_array_sort(barr, _byte_array_cmp_func);
    _byte_array_print(barr, "sorted barr: ");

    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

ori barr: H e l l o , W o r l d
sorted barr: , H W d e l l l o o r
GBytes创建
GBytes *	g_bytes_new ()
GBytes *	g_bytes_new_take ()
GBytes *	g_bytes_new_static ()
GBytes *	g_bytes_new_with_free_func ()
GBytes *	g_bytes_new_from_bytes ()
// 最简单的创建GBytes的方式。要注意,传入的data会被拷贝一份,至于如何释放,将在释放一节详细展开。
GBytes *
g_bytes_new (gconstpointer data,
             gsize size);
GBytes访问
gconstpointer	g_bytes_get_data ()
gpointer	g_bytes_unref_to_data ()
GBytes测长
gsize	g_bytes_get_size ()
GBytes引用和解引用
// 当解引用时,如果GBytes对象不再有引用值,则GBytes会自动释放所有内存
GBytes *	g_bytes_ref ()
void	g_bytes_unref ()

以上函数的示例代码:
源码见glib_examples\glib_byte_array\glib_bytes_basic

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    gsize size = 0;
    gconstpointer mem = NULL;

    bytes = g_bytes_new("abc", 3);

    size = g_bytes_get_size(bytes);

    mem = g_bytes_get_data(bytes, NULL);

    g_print("mem(len:%d):%s \n", (gint)size, (gchar *)mem);

    g_bytes_unref(bytes);

    return 0;
}

运行结果:

mem(len:3):abc

使用valgrind工具检测一下是否有内存泄露。

# valgrind --tool=memcheck --leak-check=full ./glib_bytes_basic
==3588== Memcheck, a memory error detector
==3588== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==3588== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==3588== Command: ./glib_bytes_basic
==3588== 
==3588== Invalid read of size 1
==3588==    at 0x518ACB3: vfprintf (vfprintf.c:1661)
==3588==    by 0x524A384: __vasprintf_chk (vasprintf_chk.c:66)
==3588==    by 0x4EC11EB: g_vasprintf (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x4E9CD3F: g_strdup_vprintf (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x4E87424: g_print (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x400964: main (glib_bytes_basic.c:15)
==3588==  Address 0x5961043 is 0 bytes after a block of size 3 alloc'd
==3588==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3588==    by 0x4E856D0: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x4E9CC17: g_memdup (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x4E5C434: g_bytes_new (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==3588==    by 0x400922: main (glib_bytes_basic.c:9)
==3588== 
mem(len:3):abc 
==3588== 
==3588== HEAP SUMMARY:
==3588==     in use at exit: 2,214 bytes in 9 blocks
==3588==   total heap usage: 19 allocs, 10 frees, 35,978 bytes allocated
==3588== 
==3588== LEAK SUMMARY:
==3588==    definitely lost: 0 bytes in 0 blocks
==3588==    indirectly lost: 0 bytes in 0 blocks
==3588==      possibly lost: 0 bytes in 0 blocks
==3588==    still reachable: 2,214 bytes in 9 blocks
==3588==         suppressed: 0 bytes in 0 blocks
==3588== Reachable blocks (those to which a pointer was found) are not shown.
==3588== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==3588== 
==3588== For counts of detected and suppressed errors, rerun with: -v
==3588== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

可以看到,有一处内存泄露。
通过查看调用栈,发现此处内存泄露是g_print导致,原因是GBytes里面只有3个字节,没有字符串常有的’\0’结束符,导致打印时内存访问越界。
我们将下面一行对内存的访问去掉,改为只打印GBytes字节的长度。
g_print("mem(len:%d):%s \n", (gint)size, (gchar *)mem);
改为
g_print("mem len:%d \n", (gint)size);
再次使用valgrind检查,无内存泄露。
可见,printf会导致内存访问越界。

GBytes引用和解引用

// 前面已经讲过,unref用来解引用一个对象,当最后一个计数为0时,对象销毁。
GBytes *	g_bytes_ref ()
void	g_bytes_unref ()
GBytes hash
// 在创建字节型hash表时作为参数传给g_hash_table_new
guint	g_bytes_hash ()

GBytes比较

// 判断两个字节是否相等
// 比较两个字节大小
// 另外,b_bytes_equal也可以在创建字节hash表时作为参数传给g_hash_table_new
gboolean	g_bytes_equal ()
gint	g_bytes_compare ()

字节比较举例如下:
源码见glib_examples\glib_byte_array\glib_bytes_compare

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes1;
    GBytes *bytes2;
    gint ret = 0;
    gboolean equal = FALSE;
    
    bytes1 = g_bytes_new ("blah", 4);
    
    bytes2 = g_bytes_new ("blah", 4);
    ret = g_bytes_compare (bytes1, bytes2);
    equal = g_bytes_equal(bytes1, bytes2);
    g_print("ret:%d, equal:%s \n", ret, equal ? "TRUE" : "FALSE");
    g_bytes_unref (bytes2);
    
    bytes2 = g_bytes_new ("bla", 3);
    ret = g_bytes_compare (bytes1, bytes2);
    equal = g_bytes_equal(bytes1, bytes2);
    g_print("ret:%d, equal:%s \n", ret, equal ? "TRUE" : "FALSE");
    g_bytes_unref (bytes2);
    
    bytes2 = g_bytes_new ("abcd", 4);
    ret = g_bytes_compare (bytes1, bytes2);
    equal = g_bytes_equal(bytes1, bytes2);
    g_print("ret:%d, equal:%s \n", ret, equal ? "TRUE" : "FALSE");
    g_bytes_unref (bytes2);

    g_bytes_unref (bytes1);

    return 0;
}

运行结果:

ret:0, equal:TRUE 
ret:1, equal:FALSE 
ret:1, equal:FALSE
GByteArray和GBytes转换
// g_byte_array_free_to_bytes 字节数组转换为字节
// g_bytes_unref_to_array 字节转换为字节数组
// g_byte_array_free_to_bytes 等价于 g_byte_array_free 加 g_bytes_new_take
// g_bytes_unref_to_array 等价于 g_bytes_unref_to_data 加 g_byte_array_new_take
GBytes *	g_byte_array_free_to_bytes ()
GByteArray *	g_bytes_unref_to_array ()

字节数组转换为字节
源码见glib_examples\glib_byte_array\glib_byte_array_to_bytes

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    GBytes *bytes = NULL;
    gconstpointer data = NULL;
    gsize size = 0;

    barr = g_byte_array_new ();
    g_byte_array_append (barr, (guint8 *)"Hello", 6);

    bytes = g_byte_array_free_to_bytes (barr);
    data = g_bytes_get_data (bytes, &size);
    g_print("size:%d, data:%s \n", (gint)size, (gchar *)data);

    g_bytes_unref (bytes);

    return 0;
}

运行结果:

size:6, data:Hello

字节转换为字节数组
源码见glib_examples\glib_byte_array\glib_bytes_to_byte_array

#include <glib.h>

static const gchar *HELLO = "Hello";
static const gsize N_HELLO = 5;

gint main(gint argc, gchar **argv)
{
    gconstpointer memory;
    GByteArray *array;
    GBytes *bytes;
    gint ret = -1;

    bytes = g_bytes_new (HELLO, N_HELLO);
    memory = g_bytes_get_data (bytes, NULL);
    array = g_bytes_unref_to_array (bytes);

    ret = memcmp(array->data, (guint8 *)memory, array->len);
    g_print("ret:%d \n", ret);

    ret = memcmp(array->data, (guint8 *)HELLO, N_HELLO);
    g_print("ret:%d \n", ret);

    g_byte_array_unref (array);

    return 0;
}

运行结果:

ret:0 
ret:0

专题

GByteArray数据结构的内存模型

之前的例子,都是把常量字符串添加到字节数组中,下面演示把局部变量及堆内存添加到字节数组的效果。

局部变量添加到字节数组中
示例代码如下:

#include <glib.h>

static void _byte_array_append(GByteArray *barr)
{
    gchar str0[8] = {0};
    gchar str1[8] = {0};
    gchar str2[8] = {0};
    gchar str3[8] = {0};

    g_strlcpy(str0, "Hello", 8);
    g_strlcpy(str1, ",", 8);
    g_strlcpy(str2, "World", 8);
    g_strlcpy(str3, "\0", 8);

    g_byte_array_append(barr, (guint8 *)str0, 5);
    g_byte_array_append(barr, (guint8 *)str1, 1);
    g_byte_array_append(barr, (guint8 *)str2, 5);
    g_byte_array_append(barr, (guint8 *)str3, 1);

    g_print("barr(in-func):(len:%d)%s \n", barr->len, barr->data);
}

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;

    barr = g_byte_array_new();

    _byte_array_append(barr);

    g_print("barr(out-func):(len:%d)%s \n", barr->len, barr->data);

    g_print("barr->data: \n");
    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }
    g_print("\n");

    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

barr(in-func):(len:12)Hello,World 
barr(out-func):(len:12)Hello,World 
barr->data: 
H e l l o , W o r l d

堆上申请的内存
示例代码如下:

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;
    gchar *str[4] = {NULL};

    str[0] = g_strdup("Hello");
    str[1] = g_strdup(",");
    str[2] = g_strdup("World");
    str[3] = g_strdup("\0");

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)str[0], 5);
    g_byte_array_append(barr, (guint8 *)str[1], 1);
    g_byte_array_append(barr, (guint8 *)str[2], 5);
    g_byte_array_append(barr, (guint8 *)str[3], 1);

    memset(str[0], 0, strlen(str[0]));
    memset(str[1], 0, strlen(str[1]));
    memset(str[2], 0, strlen(str[2]));
    memset(str[3], 0, strlen(str[3]));

    g_print("barr:(len:%d)%s \n", barr->len, barr->data);
    g_print("barr->data: \n");
    for(i=0; i<barr->len; i++) {
        g_print("%c ", barr->data[i]);
    }
    g_print("\n");

    g_free(str[0]);
    g_free(str[1]);
    g_free(str[2]);
    g_free(str[3]);
    
    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

barr:(len:12)Hello,World 
barr->data: 
H e l l o , W o r l d

这说明字节数组拥有自己的内存空间,并不像指针数组那样,指针数组元素本身有自己的内存空间,但其指向的内存并不属于指针数组。

把字节数组中各元素的地址打印出来:

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GByteArray *barr = NULL;
    guint i = 0;
    gchar *str[4] = {NULL};

    str[0] = g_strdup("Hello");
    str[1] = g_strdup(",");
    str[2] = g_strdup("World");
    str[3] = g_strdup("\0");

    barr = g_byte_array_new();

    g_byte_array_append(barr, (guint8 *)str[0], 5);
    g_byte_array_append(barr, (guint8 *)str[1], 1);
    g_byte_array_append(barr, (guint8 *)str[2], 5);
    g_byte_array_append(barr, (guint8 *)str[3], 1);

    g_print("barr:(len:%d)%s \n", barr->len, barr->data);

    for(i=0; i<barr->len; i++) {
        g_print("barr->data[%d](addr:%p):%c \n", i, &barr->data[i], barr->data[i]);
    }
    g_print("\nlen=%d(addr:%p)\n", barr->len, &barr->len);

    g_free(str[0]);
    g_free(str[1]);
    g_free(str[2]);
    g_free(str[3]);
    
    g_byte_array_free(barr, TRUE);

    return 0;
}

运行结果:

barr:(len:12)Hello,World 
barr->data[0](addr:0x102de50):H 
barr->data[1](addr:0x102de51):e 
barr->data[2](addr:0x102de52):l 
barr->data[3](addr:0x102de53):l 
barr->data[4](addr:0x102de54):o 
barr->data[5](addr:0x102de55):, 
barr->data[6](addr:0x102de56):W 
barr->data[7](addr:0x102de57):o 
barr->data[8](addr:0x102de58):r 
barr->data[9](addr:0x102de59):l 
barr->data[10](addr:0x102de5a):d 
barr->data[11](addr:0x102de5b):
len=12(addr:0x102e008)

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

┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬┈┬───┐
└H┴e┴l┴l┴o┴,┴W┴o┴r┴l┴d┴0┤..│len│
                        └┈┴───┘

这里需要注意的是,data和len的内存地址并不是连续的。

GBytes字节的释放

GBytes没有像普通数组、指针数组及字节数组类似的g_xxx_free()释放函数。

有以下几种释放GBytes字节内存的方法。
g_bytes_new + g_bytes_unref

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;

    bytes = g_bytes_new("abc", 3);

    g_bytes_unref(bytes);

    return 0;
}

g_bytes_new + g_bytes_unref_to_data
其中,g_bytes_unref_to_data = g_bytes_get_data + g_bytes_unref

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    gpointer data = NULL;
    gsize size = 0;

    bytes = g_bytes_new("abc", 3);

    data = g_bytes_unref_to_data(bytes, &size);

    g_free(data);

    return 0;
}

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    gpointer data = NULL;
    gsize size = 0;

    gchar *str = g_strdup("abc");

    bytes = g_bytes_new(str, 3);

    g_free(str);

    data = g_bytes_unref_to_data(bytes, &size);

    g_free(data);

    return 0;
}

g_bytes_new_take+g_bytes_unref
g_bytes_new_take会将传入的内存据为己有,其他人不能再修改该内存。
当GBytes最后一个引用被解除后,会自动调用g_free函数释放该内存空间。

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;

    gchar *str = g_strdup("abc");

    bytes = g_bytes_new_take(str, 3);

    g_bytes_unref(bytes);

    // g_free(str) // Caution: can not free here!

    return 0;
}

注意:以下代码会导致段错误,原因是g_bytes_new_take的第一个参数要求的必须是堆上分配的内存。官方文档如下。

Because of this data must have been created by a call to g_malloc(), g_malloc0() or g_realloc()
or by one of the many functions that wrap these calls (such as g_new(), g_strdup(), etc)

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;

    bytes = g_bytes_new_take("abc", 3);

    g_bytes_unref(bytes);

    return 0;
}

g_bytes_new_static+g_bytes_unref
常量字符串无法使用g_bytes_new_take创建字节结构,但g_bytes_new_static可以创建。
g_bytes_new_static传入的第一个参数可以是一个常量字符串或者地址不被修改的字符串。
尽管官方文档特意指明不被修改或释放(data must be static (ie: never modified or freed)),但在实际例子中仍可以证明,字符串的值可以被修改,甚至可以被释放,当然,释放之后,就无法获取到值了。尽管如此,当使用的是一个堆上的内存时,最好在unref之后再对其进行释放。

常量字符串

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;

    bytes = g_bytes_new_static("abc", 3);

    g_bytes_unref(bytes);

    return 0;
}

或者

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    const char *str = NULL;

    str = "abc";

    bytes = g_bytes_new_static(str, 3);

    g_bytes_unref(bytes);

    return 0;
}

地址不被修改的字符串
(这里演示当内存被释放时,通过g_bytes_get_data无法获取到该值)

#include <glib.h>

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    gchar *str = NULL;
    gsize size = 0;
    gconstpointer data = NULL;

    str = g_strdup("abc");

    bytes = g_bytes_new_static(str, 4);

    data = g_bytes_get_data(bytes, &size);
    g_print("data(ori):%s \n", (const gchar *)data);

    g_strup(str);

    data = g_bytes_get_data(bytes, &size);
    g_print("data(strup):%s \n", (const gchar *)data);

    g_free(str);

    data = g_bytes_get_data(bytes, &size);
    g_print("data(after free):%s \n", (const gchar *)data);

    g_bytes_unref(bytes);

    return 0;
}

上述代码运行结果:

data(ori):abc 
data(strup):ABC 
[Invalid UTF-8] data(after free):\x80\xb2G\x01

g_bytes_new_with_free_func+g_bytes_unref

#include <glib.h>

static void _bytes_free_func(gpointer data)
{
    if(NULL != data) {
        g_print("free data: %s \n", (gchar *)data);
        g_free(data);
    }
}

gint main(gint argc, gchar **argv)
{
    GBytes *bytes = NULL;
    gchar *str = NULL;
    gsize size = 0;
    gconstpointer data = NULL;

    str = g_strdup("abc");

    bytes = g_bytes_new_with_free_func(str, 4, _bytes_free_func, str);

    data = g_bytes_get_data(bytes, &size);
    g_print("data:%s \n", (const gchar *)data);

    g_bytes_unref(bytes);

    return 0;
}

运行结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值