宽窄字符串

字符串是以 ASCII 字符或 Unicode 字节排列并且以 NUL (即’\0’) 结尾表示的字符序列,Unicode 的字符串即宽字符串。

字符串池

以字符串字面量定义字符串时会将其分配到字面量池中,这个内存区域通常保存组成字符串的字符序列,该内存区域通常被认为是全局/静态的。字符字面量在池中通常只有一份副本并且是只读的,这样可以减少程序的内存占用率。

  1. #include <stdio.h>
  2. char *g="Hello";
  3. int main(int argc, char const *argv[])
  4. {
  5. char s[]="Hello";
  6. char *c="Hello";
  7. printf("字符指针c的内存地址:%p\n",&c);
  8. printf("数组s的内存地址:%p\n",&s);
  9. printf("字符指针g的内存地址:%p\n",&g);
  10. printf("字符指针c指向Hello的的内存地址: %p\n",&c[0]);
  11. printf("字符指针g指向Hello的的内存地址: %p\n",&g[0]);
  12. printf("变量s数组的Hello副本的内存地址:%p\n",&s[0]);
  13. printf("Hello字面量的尺寸 %lu\n",sizeof("Hello"));
  14. return 0;
  15. }
  16. //字符指针c的内存地址:0x7ffeefbff550
  17. //数组s的内存地址:0x7ffeefbff55a
  18. //字符指针g的内存地址:0x100008010
  19. //字符指针c指向Hello的的内存地址: 0x100003e9e
  20. //字符指针g指向Hello的的内存地址: 0x100003e9e
  21. //变量s数组的Hello副本的内存地址:0x7ffeefbff55a
  22. //Hello字面量的尺寸 6

全局区声明并以字符串字面量初始化了字符指针 g ,g 的内存地址是 0x100008010 ,这个地址位于全局数据区内,而指针 g 它指向的 “Hello” 字面量地址是 0x100003e9e 。这个地址位于字面量池内,同时,我们也从 main 函数内部定义了两个局部变量字符指针变量 c ,c 指针指向的字面量的内存地址和全局字符指针指向的 “Hello” 字面量是一样的。

image.png

  • 将同样一份字符串字面量的地址直接赋给不同的字符指针,不会产生额外字符串的副本,这些字符指针指向同一份字符串字面量。

  • 以字符串数组初始化字符串字面量会在对应的函数栈内生成另外一份的字面量副本。char s[]=”HELLO” 等价于 strcpY(s,”HELLO”)

  • 再次,如果你在全局区以字符串数组初始化会,会在全局数据区产生同样的字符串副本。

  • 在大部分编译器中,我们将字符串字面量看做常量,无法修改字符串。不过,在有些编译器中,字符串字面量是可修改的。

定义字面量时通常会将其分配在字面量池中,这个内存区域保存了组成字符串的字符序列。多次用到同一个字面量时,字面量池中通常只有一份副本。这样会减少应用程序占用的内存。通常认为字面量是不可变的,因此只有一份副本不会有什么问题。

不过,认定只有一份副本或者字面量不可变不是一种好做法,大部分编译器有关闭字面量池的选项,一旦关闭,字面量可能生成多个副本,每个副本拥有自己的地址。

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