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
)