C字符串

字符串是以ASCII字符NUL结尾的字符序列。ASCII字符NUL表示为\0。字符串通常存储在数组或者从堆上分配的内存中。不过,并非所有的字符数组都是字符串,字符数组可能没有NUL字符。字符数组也用来表示布尔值等小的整数单元,以节省内存空间。

C中有两种类型的字符串。

  • 单字节字符串
    char数据类型组成的序列。
  • 宽字符串
    wchar_t数据类型组成的序列。

wchar_t数据类型用来表示宽字符,要么是16位宽,要么是32位宽。这两种字符串都以NUL结尾。可以在string.h中找到单字节字符串函数,而在wchar.h中找到宽字符串函数。除非特别指明,本章用到的都是单字节字符串。创建宽字符主要用来支持非拉丁字符集,对于支持外语的应用程序很有用。

字符串的长度是字符串中除了NUL字符之外的字符数。为字符串分配内存时,要记得为所有的字符再加上NUL字符分配足够的空间。

警告 记住,NULLNUL不同。NULL用来表示特殊的指针,通常定义为((void*)0),而NUL是一个char,定义为\0,两者不能混用。

字符常量是单引号引起来的字符序列。字符常量通常由一个字符组成,也可以包含多个字符,比如转义字符。在C中,它们的类型是int,如下所示:

printf("%d\n",sizeof(char));
printf("%d\n",sizeof('a'));

执行上述代码可以看到char的长度是1,而字符字面量的长度是4。这个看似异常的现象乃语言设计者有意为之。

字符串声明

声明字符串的方式有三种:字面量、字符数组和字符指针。字符串字面量是用双引号引起来的字符序列,常用来进行初始化,它们位于字符串字面量池中,我们会在下一节讨论。

不要把字符串字面量和单引号引起来的字符搞混后者是字符字面量。在后面的各节我们会看到,把字符字面量当做字符串字面量用会出问题。

下面是一个字符数组的例子,我们声明了一个header数组,最多可以持有31个字符。因为字符串需要以NUL结尾,所以如果我们声明一个数组拥有32个字符,那么只能用31个元素来保存实际字符串的文本。字符串在内存中的位置取决于声明的位置,我们会在字符串初始化中探究这个问题。

char header[32];

字符指针如下所示,由于没有初始化,也就没有引用字符串,当前还没有指定字符串的长度和位置。

char *header; 

字符串字面量池

定义字面量时通常会将其分配在字面量池中,这个内存区域保存了组成字符串的字符序列。多次用到同一个字面量时,字面量池中通常只有一份副本。这样会减少应用程序占用的内存。通常认为字面量是不可变的,因此只有一份副本不会有什么问题。不过,认定只有一份副本或者字面量不可变不是一种好做法,大部分编译器有关闭字面量池的选项,一旦关闭,字面量可能生成多个副本,每个副本拥有自己的地址。

注意 GCC用-fwritable-strings选项来关闭字符串池。在Microsoft Visual Studio中,/GF选项会打开字符串池。

图5-1说明了字面量池的内存分配方式。

enter image description here

图5-1:字符串字面量池

字符串字面量一般分配在只读内存中,所以是不可变的。字符串字面量在哪里使用,或者它是全局、静态或局部的都无关紧要,从这个角度讲,字符串字面量不存在作用域的概念。

字符串字面量不是常量的情况

在大部分编译器中,我们将字符串字面量看做常量,无法修改字符串。不过,在有些编译器中(比如GCC),字符串字面量是可修改的。看下面这个例子:

char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // 打印"Lound"

这样会把字面量改成"Lound",这通常不是我们期望的结果,因此应该避免这么做。像下面这样把变量声明为常量可以解决一部分问题。任何修改字符串的尝试都会造成编译时错误:

const char *tabHeader = "Sound";

赞(2)

评论 抢沙发

评论前必须登录!

 

C指针