--62ee71d72714f39d x-next-cache-tags: _N_T_/layout,_N_T_/blog/layout,_N_T_/blog/[...slug]/layout,_N_T_/blog/[...slug]/page,_N_T_/blog/c%E8%AF%AD%E8%A8%80/%E5%AD%97%E7%AC%A6%E4%B8%B2 vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch 字符串 | YBinary
Published on

字符串

Authors

定义

C 语言没有单独的字符串类型,字符串被当作字符数组,即char类型的数组

编译器会给数组分配一段连续内存,所有字符储存在相邻的内存单元之中。 在字符串结尾,C 语言会自动添加一个全是二进制0的字节,写作\0字符,表示字符串结束。 字符\0不同于字符0,前者的 ASCII 码是0(二进制形式00000000), 后者的 ASCII 码是48(二进制形式00110000)。 所以,字符串“Hello”实际储存的数组是{'H', 'e', 'l', 'l', 'o', '\0'}

char localString[10];

上面示例声明了一个10个成员的字符数组,可以当作字符串。由于必须留一个位置给\0,所以最多只能容纳9个字符的字符串。

字符串写成数组的形式,是非常麻烦的。C 语言提供了一种简写法,双引号之中的字符,会被自动视为字符数组。

{'H', 'e', 'l', 'l', 'o', '\0'}

// 等价于
"Hello"

上面两种字符串的写法是等价的,内部存储方式都是一样的。双引号里面的字符串,不用自己添加结尾字符\0,C 语言会自动添加。

注意,双引号里面是字符串,单引号里面是字符,两者不能互换。如果把Hello放在单引号里面,编译器会报错。

字符串变量的声明

// 写法一
char s[14] = "Hello, world!";
char s[] = "Hello, world!";

// 写法二
char* s = "Hello, world!";

上面两种写法都声明了一个字符串变量s。 如果采用第一种写法,由于字符数组的长度可以让编译器自动计算,所以声明时可以省略字符数组的长度。

字符指针字符数组,这两种声明字符串变量的写法基本是等价的,但是有两个差异

第一个差异是,指针指向的字符串,在 C 语言内部被当作常量,不能修改字符串本身

char* s = "Hello, world!";
s[0] = 'z'; // 错误

上面代码使用指针,声明了一个字符串变量,然后修改了字符串的第一个字符。 这种写法是错的,会导致难以预测的后果,执行时很可能会报错。

为什么字符串声明为指针时不能修改,声明为数组时就可以修改? 原因是系统会将字符串的字面量保存在内存的常量区,这个区是不允许用户修改的。 声明为指针时指针变量存储的值是一个指向常量区的内存地址,因此用户不能通过这个地址去修改常量区。 但是,声明为数组时,编译器会给数组单独分配一段内存,字符串字面量会被编译器解释成字符数组, 逐个字符写入这段新分配的内存之中,而这段新内存是允许修改的。

第二个差异是,指针变量可以指向其它字符串

char* s = "hello";
s = "world";

上面示例中,字符指针可以指向另一个字符串。

但是,字符数组变量不能指向另一个字符串。

char s[] = "hello";
s = "world"; // 报错

上面示例中,字符数组的数组名,总是指向初始化时的字符串地址,不能修改。

为什么数组变量不能赋值为另一个数组? 原因是数组变量所在的地址无法改变,或者说,编译器一旦为数组变量分配地址后, 这个地址就绑定这个数组变量了,这种绑定关系是不变的。 C 语言也因此规定,数组变量是一个不可修改的左值,即不能用赋值运算符为它重新赋值。

Refernce

https://wangdoc.com/clang/string

--62ee71d72714f39d--