C 语言的字符串
1 | // 字符数组 |
所以:可以用访问数组的方式访问字符串~
注意
是'\0'
,而不是 ‘0’
另外,0
也能将字符数组变为字符串
但**推荐用 ‘\0’**,因为这能从字面上强调 「字符串的结束标志占 1
Byte」!
1 | char str1[] = { 'a', 's', '\0' }; |
下例代码编译运行输出 3,但其实字符串 str 的长度为 2,因为:**’\0’ 仅标志着字符串的结束,而并非字符串的一部分**
1 | char str[] = { 'h', 'i', '\0' }; |
字符串常量
≥ 0 个字符,被双引号引起来,就是字符串常量(eg:””、”hello”、…)
eg:"asd"
=> 字符串常量 => 会被转换成{'a', 's', 'd', '\0'}
=> 系统只会保留一份 => 放在一片只读的存储空间中 => 共享
1 | char * str1 = "asd"; |
试想:
如果字符串常量所存放的位置不是只读的
那当有很多指针指向它,其中任何一个指针对其的修改,都会导致其它指针对这种修改的不知情
从而可能导致不可预知的严重后果
举例如下
1 | char * s = "hello"; |
解释:
s 是一个指针,初始化为指向一个字符串常量
所以这行代码实际上是 const char * s = "hello";
虽然由于历史原因,在这里,编译器不接受带 const 的写法
但任何试图对 s 所指向的内存空间做写入的操作都会导致严重后果 !
相邻的字符串常量默认会被连接起来 !
1 | printf("%s\n", "hel" "lo world"); |
字符串变量
可用字符串常量初始化字符串变量
1 | char str[] = "hello"; |
解释:
用只读的 {'h', 'e', 'l', 'l', 'o', '\0'}
初始化一个可读写的普通字符数组 str
‘\0’ 让字符数组变成了字符串
当然写成下面这样也完全 ok
1 | char str[10] = "hello"; |
空字符串 “” 其实是 { ‘\0’ }
1 | char str[] = ""; |
字符串数组
1 | // 类比字符数组 |
习题
1 | char* arr[] = { |
I/O
scanf() 其实“破绽百出”
- 「空格、tab、回车」 都是 scanf 输入结束的标志,这就导致 scanf 其实只能读入“单词”
1 | char str[20]; |
注意:「空格、tab、回车」 只是输入结束的标志,不会被输到 str 中(在 Linux 上验证如下)
关于上图中的 $ 符号,请看 => DOS 与 Linux 的断行字元
- 单论读取“单词”而言,由于 scanf 并不知道需要读入多少字符,当输入的字符串过长,就可能导致缓冲区溢出,所以 scanf 并不安全
1 | // 安全的做法:在 %s 中间加一个数字,以此告诉 scanf 最多能读入几个字符 |
那如果我们就是要读入一个英文句子,而不仅仅是一个单词
在 C 语言的官方文档中可查阅到曾有人设计了gets()函数,但该函数已被废除,因为文档描述了这样一段话:
另外,如果你是一名 c++ 开发者,可以使用getline()函数,但我偏爱更简单纯粹的 C :)
(完)