目录
字符串的读和写
变量、常量与字面量的区别
字面量是指由字母,数字等构成的字符串或者数值,它只能作为右值出现(右值是指等号右边的值)。
常量和变量都属于变量,只不过常量是赋过值后不能再改变的变量,而普通的变量可以再进行赋值操作。
字符串常量在C语言里称为字符串字面量。本文主要介绍字符串变量和字符串字面量。
字符串字面量
解释
字符串字面量是用一对双引号括起来的字符序列:"when you came to a fork in the road,take it."
字符串字面量常常作为格式串出现在printf函数和scanf函数的调用中。
注意:
const char b = "when you came to a fork in the road,take it.";
这里面b是一个常量,"when you came to a fork in the road,take it."是字符串字面量。
通常情况下可以在任何C语言允许使用char *指针的地方使用字符串字面量。
char *p; p = "abc";
char ch; ch = "abc"[1]; 是对指针取下标,这里等于 ch = p[1];即ch 的值将会变成 b;
当然,当字符串字面量只包含一个字符时,字符串字面量和字符(串)常量就会变得不一样:字符串字面量"a"是用指针来表示的,字符(串)常量"a"是一个整数。
警告:不要在该使用字符串的时候使用字符,也不要在该使用字符的时候,使用字符串。
字符串字面量的延续
如果发现字符串字面量太长而无法防止在单独的一行以内,只要把第一行用字符\结尾,那么C语言就允许在下一行延续字符串字面量。除了(看不到的)末尾的换行符,在同一行不可以有其他字符跟在后面,当然使用"\"来延续有一个缺陷:字符串字面量必须从下一行的起始位置继续,不可以缩进:
printf(""when you came to a fork in the road, take it. \ -- Yogi Berra");
改进:当两条或者更多的字符串字面量相邻时(仅用空白字符分隔),编译器会把它们合并成一条字符串。这条规则允许把字符串分隔放在两行或者更多行中。
printf("when you came to a fork in the road, take it." " -- Yogi Berra");
字符串字面量存储
字符串字面量是按照数组的方式来存储的。
对于长度为n的字符串字面量,c语言会给分配一个长度为n+1的内存空间(+1是给标志字符串末尾的额外字符(空字符)。字符:\0,而不是0。因为字符串字面量是按照数组的方式来存储的,编译器会把它看成是char *类型的指针。即调用时,指针指向首地址。
字符串变量
C语言声明字符串
只要保证字符串是以空字符结尾的,任何一维的字符数组都可以用来存储字符串。
[惯用法] #define STR_LEN 80
…
char str[STR_LEN +1] ;
这是程序员的惯用法,这样这个字符串最多存储80个字符。
初始化字符串变量
字符串变量可以在声明时进行初始化:char date1[8] = "June 14";
在这里,"June 14" 并不是字符串字面量,我们可以将上面的式子看成:
char date1[8] = {'J', 'u', 'n', 'e', ' ', '1', '4', '\0' };
在C语言中没有纯粹的c语言字符串变量,可以通过一个字符数组等的形式来体现,这样就可以就可以将字符串字面量变成变量形式,对字符数组中的内容进行改变就是对字符串变量做出改变。
字符串变量初始化和数组的初始化特别相似,数组未初始化的会被自动初始化为0,那么存放字符串得数组会自动的初始化成\0,\0…
字符串变量可以在声明中可以省略它的长度:char date1[] = "June 14";这种情况下,编译器会自动计算长度。
字符数组与字符指针
以下两个声明看起来很相似:
char date[] = "June 14"; char *date = "June 14";
但是前者声明的date是一个数组,后者是一个指针。
在声明成一个数组时,存储在数组date中的字符是可以被修改的。此时date是一个数组名。在声明成一个指针时,指针指向字符串字面量,字符串字面量是不可以被修改的。此时date是一个变量,这个变量可以在程序执行期间指向其他字符串。 也就是说,当声明成指针时, "June 14"就不可以改变了,但指针可以不指向 "June 14";,去指向其他地址。
很严重的错误:
使用未初始化的指针变量作为字符串是非常严重的错误。
例:char *p;
p[0] = 'a'; p[1] = 'b'; p[2] = 'c';p[3] = '\0';
因为这时未说明p指针指向哪里,p[0]没有指向a。
字符串的读和写
以字符串:char str[] = "Are we having fun yet ?";来举例说明
printf函数:
char str[] = "Are we having fun yet ?";
printf("%s\n",str);
//输出结果:Are we having fun yet ?
/*
这里是逐个写字符串中的字符,遇到空字符停止。如果遇不到空字符,
printf函数会越过字符串的末尾继续写,直到最终在内存的某个地方找到空字符为止。
*/
char str[] = "Are we having fun yet ?";
printf("%.6s\n",str);
//输出结果:Are we
/*
如果只想显示字符串的一部分,可以使用转换说明%.ps,p是要显示的字符数量。
*/
puts函数:puts(str);
puts函数只有一个参数,即需要显示的字符串str。在写完字符串后,puts函数总会自动添加一个额外的换行符,从而前进到下一个输出行的开始处。
scanf函数:
%s允许scanf函数把字符串读入字符数组:scanf("%s",str);
但是这里不需要&符号,因为str是一个数组名,编译器在把它传递给函数时会把它当作指针来处理。调用时,scanf函数在开始读取时会跳过空白字符,然后读入字符并存储到str中,直到再次遇到空白字符为止,因此scanf函数读入字符串永远不会包含空白字符。换行符、制表符等都能使scanf函数停止读入。
scanf函数始终会在字符串的末尾存储一个空字符。
scanf函数通常不会读入一整行输入。
char sentence [SENT_ENCE + 1];
printf("Enter a sentence : \n");
scanf("%s",sentence);
//假设输入的是 To C, Or not to C: that is the question.
那么scanf函数只会存储To到sentence里。
gets函数:
为了使一次读入一整行的输入,可以使用gets函数。
gets函数不会在开始读取时会跳过空白字符。
gets函数会持续输入,直到找到换行符为止,会自动忽略换行符,不会把它存储到数组中,用空字符代替换行符。
char sentence [SENT_ENCE + 1];
printf("Enter a sentence : \n");
gets(sentence);
//假设输入的是 To C, Or not to C: that is the question.
gets函数会将输入全部存入sentence。
逐个字符读取字符串:
很多时候,我们想兼顾上面两个读取函数的优点:
假设我们所需要的函数不会跳过空白字符,在第一个换行符(不存储到字符串中)处停止读取,并且忽略额外的字符,那么我们可以: int read_line (char str[],int n); str 表示用来存储输入的数组,n是读入字符的最大数量。
这种情况下,read_line函数主要由一个循环构成、只要str中还有空间(读取的个数没有超过n),此循环就会调用getchar函数逐个读入字符并把它们存储到str中。在读入换行符时循环终止。
scanf函数和gets函数等标准函数会自动在输入字符串的末尾放置一个空字符;其他情况下,我们需要手动添加空字符。
int read_line(char str[], int n)
{
int ch, i = 0;
//ch的类型是int型,而不是char型,这是因为getchar函数会把它读取的字符作为int类型的值返回。
while((ch = getchar()) != '\n')
{
if(i < n)
{
str[i++] = ch;
}
}
str[i] = '\0';
return i;
}