五、Gtk4-Strings and memory management

本文介绍了C语言中字符串的概念,包括其作为字符数组的性质,以及内存的静态、栈和堆分配。讨论了字符串常量的只读性,字符串数组的可写性,以及在堆上动态分配和释放内存的函数如g_new和g_free。此外,还提到了在GTK+环境中,GtkTextView和GtkTextBuffer组件如何与字符串交互,强调了内存管理在使用这些组件时的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GtkTextView和GtkTextBuffer有使用字符串参数或返回字符串的函数。字符串和内存管理的知识对理解如何使用这些函数很有帮助。

1 字符串和内存

字符串是一个以’\0’结尾的字符数组。String不是char、int、float或double等C语言类型,而是一个字符数组。在其他语言中,它的行为与字符串类似。因此,它的指针通常被称为a string

下面是一个示例程序:

char a[10], *b;

a[0] = 'H';
a[1] = 'e';
a[2] = 'l';
a[3] = 'l';
a[4] = 'o';
a[5] = '\0';

b = a;
/* *b is 'H' */
/* *(++b) is 'e' */

数组a被定义为char类型数组,其大小为10。前5个元素是’H’, ‘e’, ‘l’, ‘l’, ‘o’。它们是字符编码。例如,H表示0x48或72。第6个元素是’\0’,意思是0,表示数据序列到这里就结束了。数组表示字符串"Hello"。

数组的大小是10,所以4个字节没有被使用。但没关系。它们只是被忽略了。(如果a定义在函数之外,或者它的类是静态的,它们会被赋值为0。否则,也就是说,类是auto或register,它们是未定义的。)

变量b是一个指向字符的指针。它被赋值为a,因此b指向a的第一个元素(字符’H’)。数组a是不可变的。所以a=a+1会导致语法错误。

另一方面,b是指针类型的变量,是可变的。所以++b, b加1,是允许的。

如果指针为NULL,它不指向任何东西。指针不是字符串。它不同于空字符串。空字符串是指向\0的指针。

有四种情况。

  • 字符串是只读的
  • 字符串在静态内存区中
  • 字符串在stack中
  • 字符串在从堆区域分配的内存中

2 常量字符串

一个字符串被双引号:

char *s;
s = "Hello"

"Hello"是一个字符串常量,只读。因此,下面的程序是非法的。

*(s+1) = 'a';

结果未定义。可能会发生不好的事情,例如,段错误。

注意:常量字符串的内存是在程序编译时分配的。可以使用strings命令查看字符串常量。

$ strings src/tvf/a.out
/lib64/ld-linux-x86-64.so.2
cN<5
... ... ...
... ... ...
Once upon a time, there was an old man who was called Taketori-no-Okina. It is a japanese word that means a man whose work is making bamboo baskets.
One day, he went into a mountain and found a shining bamboo. "What a mysterious bamboo it is!," he said. He cut it, then there was a small cute baby girl in it. The girl was shining faintly. He thought this baby girl is a gift from Heaven and took her home.
His wife was surprized at his story. They were very happy because they had no children. 
... ... ...
... ... ...

它告诉我们字符串常量嵌入到程序的二进制代码中。

3 字符串数组

如果将字符串定义为数组,则将其存储在静态内存区或栈中。这取决于数组的类。如果数组的类是静态的,那么它被放置在静态内存区域中。分配的内存在程序的整个生命周期内都存在。这个区域是可写的。

如果数组的类是auto,则将其放置在stack中。如果数组是在函数中定义的,它的默认类是auto。当函数返回到调用者时,栈区域将消失。栈上定义的数组是可写的。

static char a[] = {'H', 'e', 'l', 'l', 'o', '\0'};

void
print_strings (void) {
  char b[] = "Hello";

  a[1] = 'a'; /* Because the array is static, it's writable. */
  b[1] = 'a'; /* Because the array is auto, it's writable. */

  printf ("%s\n", a); /* Hallo */
  printf ("%s\n", b); /* Hallo */
}

数组a是由函数定义的。即使没有静态类,它也会被放置在静态内存区中。编译器计算元素的数目(6),并在静态内存区中分配6个字节。然后,它将“Hello”常量字符串数据复制到内存中。

数组b是在这个函数内部定义的,所以它的类是auto。编译器计算字符串字面量中元素的个数。它是6,因为它有\0终止符。编译器会在栈中分配6字节,并将“Hello”常量字符串复制到栈内存中。

a和b都是可写的。

内存由程序自动分配和释放,所以你不需要分配或释放内存。数组a在程序的生命周期内是alive。当函数被调用时,数组b是alive,直到函数返回到调用者。

4 字符串在堆空间

您可以从堆区域获取、使用和释放内存。标准C库提供了malloc来获取内存和释放内存。GLib提供了函数g_new和g_free。它们类似于malloc和free。

g_new (struct_type, n_struct)

g_new是一个宏,用于为数组分配内存。

  • struct_type是数组元素的类型。
    0 n_struct是数组的长度。
    返回值是一个指向数组的指针。它的类型是一个指向struct_type的指针。

例如,

char *s;
s = g_new (char, 10);
/* s points an array of char. The size of the array is 10. */

struct tuple {int x, y;} *t;
t = g_new (struct tuple, 5);
/* t points an array of struct tuple. */
/* The size of the array is 5. */

g_free 释放内存

void
g_free (gpointer mem);

如果mem为NULL, g_free什么都不做。gpointer是一种通用指针。它等同于void *。此指针可以强制转换为任何指针类型。相反,任何指针类型都可以强制转换为gpointer。

g_free (s);
/* Frees the memory allocated to s. */

g_free (t);
/* Frees the memory allocated to t. */

如果参数不指向分配的内存,它将导致错误,特别是段错误。

一些GLib函数用于分配内存。例如,g_strdup分配内存并复制作为参数给出的字符串。

char *s;
s = g_strdup ("Hello");
g_free (s);

字符串常量"Hello"有6个字节,因为字符串末尾有’\0’。g_strdup从堆区域获取6字节,并将字符串复制到内存中。s被赋值为内存的起始地址。g_free将内存释放回到堆区域。

g_strdup在GLib API参考中描述。以下内容摘自参考文献。

当不再需要时,返回的字符串应该用g_free()释放。

如果你忘记释放分配的内存,它将一直保留到程序结束。重复的分配和没有释放会导致内存泄漏。这是一个bug,可能会带来严重的问题。

const 限定

可以通过赋值const限定变量来初始化它。一旦被初始化,它就永远不能被修改或释放。

const int x = 10; /* initialization is OK. */

x = 20; /* This is illegal because x is qualified with const */

如果函数返回const char*类型,则字符串不能更改或释放。如果一个函数有一个const char *类型的形参,它确保这个形参在函数中没有改变(free和change content)

// You never change or free the returned string.
const char*
gtk_label_get_text (
  GtkLabel* self
)

// Str keeps itself during the function runs
void
gtk_label_set_text (
  GtkLabel* self,
  const char* str
)

参考:Gtk4-tutorial Strings and memory management

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值