C - 不可变字符串

日期 2018-10-18
Coding/编程
作者 Webster Zhang

前言

忙得要死,一大堆需求向我扑来……最近在C语言的字符串上摔了一跤,还好后果还不算太严重,但是还是先随便写点东西记录下来好了。

字符串

不像Pascal和Delphi,在C语言中是没有String这种变量类型的。所以在C语言里面是用一个以\0结尾的一维数组来表示字符串的。字符串的长度总比数组的长度小一个。那么,以下是其中两种声明字符串的方法。

char string1[] = "Hello";
char *string2 = "Hello";

不可变字符串

Seg Fault

上面两种声明的方法,看上去是一样的,但却有那么一点点的区别。而这点区别甚至会导致一些严重的后果。string2被称为不可变字符串(Immutable String)。其实这样定义的字符串和写成下面的形式是没有区别的

const char *string2 = "Hello"; // makes no difference

对于string1来说,当尝试去使用一些函数或者去变化里面的值的时候,基本上不会碰到什么大问题。但是尝试去更改string2的时候,就会遇到Seg Fault。根本的原因是string2其实是一个常量,是写在内存的“只读区域”的。所以在尝试更改的时候会出现和访问无效内存地址时遇到的一样的错误。

Keep_calm_seg_fault

正确的操作

要在一个黑箱里判断一个字符串是不是不可变字符串,其实是有点难度的,但是这并不能妨碍我们修改它。只要先使用动态内存申请一个新的足够长的数组,然后复制一份。这样一来就得到了一个新的和原来字符串相同的可变字符串了。

char string1[] = "hello\n"              // mutable string
                 "world\n";

char *string2 = "hello\nworld\n";       // immutable string

char *string3 = (char *)string1;        // a black box - mutable
char *string4 = (char *)string2;        // a black box - immutable

assert(strcmp(string3, string4) == 0);  // assert passed 

char *new_string4 = calloc(strlen(string3) + 1, sizeof(char));
strcpy(new_string4, string4);           // now new_string4 is mutable

assert(strcmp(new_string4, string4) == 0);  // assert passed

printf("%s\n", strsep(&string3, "\n"));     // hello
printf("%s\n", strsep(&string4, "\n"));     // seg fault
printf("%s\n", strsep(&new_string4, "\n")); // hello          

参考

C字符串
C语言中的指针与字符串
Stack Overflow