字符串字面量
转义序列
用一对双引号括起来的字符序列,如:”When you come to a fork in the road, take it.”
字符串字面量可以含有转移序列
如:”Candy\nIs dandy\nBut liquor\nIs quiker.\n —Ogden Nash\n”
Candy
Is dandy
But liquor
Is quicker.
—Ogden Nash
也可以有八进制数和十六进制数的转义序列,但是没有字符转义序列那样常见。
延长字符串
- 可以使用 \ 把行或更多行的代码连接在一行(C 语言称为“拼接”)。
- 用空格字符把两会或更多行字符串分割,编译器会把它们合并成一条字符串。
printf("When you come to a fork in the road, take it. \
--Yogi Berra");
printf("When you come to a fork in the road, take it" " --Yogi Berra");
存储字符串
C 语言把字符串字面量作为字符数组来处理,当 C 语言编译器在程序中遇到长度为 n 的字符串字面量时,会为它分配长度为 n+1 的内存空间。以及一个用来标记字符串末尾的额外字符(空字符),空字符是一个所有位为 0
的字节。因此用来转义序列 ‘\0’ 来表示。
“abc”
尽然作为数组来存储,编译器会把它看作是 char * 类型的指针。
printf 函数和 scanf 函数都接受 char * 类型的指针作为它们的第一个参数。
如:printf(“abc”) 当调用 printf 函数时,会传递 “abc” 的地址(即指向存储字母a的内存单元的指针)。
字符串的操作
C 语言允许在使用 char * 指针的地方使用字符串字面量。
使 p 指向字符串的第一个字符。
char *p;
p = "abc";
C 语言允许取下标
char ch;
ch = "abc"[1];
把 0~15 转成对应的十六进制字符。
char digit_to_hex_char(int digit)
{
return "0123456789ABCDEF"[digit];
}
字符串字面量
初始化
char date1[8] = "June 14";
把字符串 “June 14” 中的字符串复制到数组 date1 中,然后追加一个空字符从而使 date1 可以作为字符串使用。
这里的 “June 13” 看起来像字符串字面量,但其实不然,C 编译器会看成是数组初始化式的缩写形式。
如果初始化式太短而不能填满字符串变量,在这一点上和 C 语言处理数组初始化是一致的。
char date2[9] = "June 14";
一定要确保数的长度要长于初始化式的长度,否则,编译器将忽略空字符,这将使得数组无法作为字符串使用。
可以忽略声明中的长度,这种情况下,编译器会自动计算长度。
char date3[] = "June 14";
不指定长度并不意味着可以改变数组的长度,一但编译了程序数值的长度就会确定。
**
字符数组和字符指针区别
char date[] = "June 14";
char *date = "June 14";
前者声明 date 为数组,后者声明 date 是一个指针。由于数组和指针的紧密关系,使得上面的这两个声明中的 date 都可以作为字符串。任何期望传递字符数组或字符指针的函数都可以用这两种声明方式的 date 作为参数。
区别:
- 在声明为数组时,就像任意数组元素一样,可以修改 date 中的字符。在声明为指针时,date 指向的字符串字面量,这个字符串字面量是不可修改的。
- 在声明为数组时,date 是数组名。在声明为指针时,date 是变量,这个变量可以在程序执行期间指向其他字符串。
字符串的读写操作
printf 函数和 puts 函数写字符串
char str[] = "Are we having fun yet?";
printf("%s", str); // Are we having fun yet?
转换说明 %s 允许 printf 函数写字符串,printf 函数会逐个写字符串中的字符,直到遇到空字符为止。
转换说明 %.ps 中的 p 是要显示的字符数量。
printf("%.6s\n", str); // Are we
转换说明 %ms 会在大小为 m 的字段内显示字符串(对于超过 m 个字符的字符串,printf 函数会显示出整个字符串,而不会截断),如果字符串少于 m 个字符,则会在字段内右对齐输出。如果要强制左对齐,可以在 m 前加一个 - 号。m 值和 p 值可以组合使用:转换说明 %m.ps 会使字符串的前 p 个字符在大小为 m 的字段内显示。
还可以使用 puts 函数输出。
只有一个参数,在写完字符串后,pusts 函数总会添加一个额外的换行符。
puts(str);
scanf 函数和 gets 函数读字符串
scanf("%s", str);
不需要在 str 前添加运算符 &,因为 str 是数组名,编译器在把它传递给函数时会把它当作指针来处理。
scanf 函数会跳过空白字符,然后读入字符并存储到 str 中,直到遇到空字符为止。scanf 函数始终会在字符串末尾存储一个空字符。
gets(str);
类似于 scanf 函数,gets 函数把读入的字符串放到数组中,然后存储一个空字符。
区别:
- gets 函数不会在开始读取字符串之前跳过空白字符(scanf 函数会)。
- gets 函数会持续读入直到找到换行符才停止(scanf 函数会在任意空白字符处停止)。此外,gets 函数会忽略掉换行符,不会把它存储到数组中,用空字符代替换行符。
scanf 函数和 gets 函数都无法检测数组何时被填满。
**
逐个读取字符串
scanf 函数和 gets 函数都有风险且不够灵活。
设计一个函数,功能:不会跳过空白字符,在第一个换行符(不存储到字符串中)处停止读取,并且忽略额外的字符。
int read_line(char str[], int n)
{
int ch, i = 0;
while((ch = getchar()) != '\n')
if(i < n)
str[i++] = ch;
str[i] = '\0';
return i;
}
访问字符窜中的字符
以一个统计字符串中空格数量的函数为例。
int count_spaces(const char s[])
{
int count = 0, i;
for(i = 0; s[i] != '\0'; i++)
if(s[i] == ' ')
count++;
return count;
}
简化版
int count_spaces(const char *s)
{
int count = 0;
for(; *s != '\0'; s++)
if(*S == ' ')
count++;
return count;
}
注意,const 没有阻止 count_spaces 函数对 s 的修改,它的作用是阻止函数改变s所指向的字符。而且,因为 s 是传递给 count_spaces 函数的指针的副本,所以对 s 进行自增操作不会影响原始的指针。