字符串
字符串常量是双引号括起的任意字符序列。如:"Hello World","Fantasy","Please enter
your full name:",……
C语言没有专门定义字符串数据类型(如其他语言中的string) ,所谓的字符串,只是
对字符数组的一种特殊应用而已,它用以'\0'结尾的字符数组来表示一个逻辑意义上的字符串。
在字符串常量中,显然不能直接写双引号,因为这将被认为是字符串的结束。转义序列在字符串常量中要包含双引号,需要用“\"”表示。如:"Hello \"Accp\""
与字符数组不同的是:在存完字符串常量的所有字符之后,还要另存一个空字符'\0'作为结束的标志,空字符是ASCII码值为0的字符,C语言中用'\0'标识字符串的结束,所以也称为结束符。如果在程序里写了字符串:char hello[]="HELLO"或{"HELLO"};虽然只有5个字符,在内存中却需要占用6个字节存储,其中'\0'表示空字符。存储情况如:
H |
E |
L |
L |
O |
\0 |
5005 |
5006 |
5007 |
5008 |
5009 |
500A |
而字符数组char hello[]={'H','E','L','L','O'};的长度和大小则为5.
根据字符串存储形式的规定,只要在数组里顺序存入所需字符,随后存一个空字符,这个字符数组里的数据就有了字符串的表现形式,这个数组也就可以当作字符串使用了。分析char str[]="abc";因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为char str[3] = {'a','b','c'};根据上面的总结,所以char str[]="abc";的最终结果是char str[4] = {'a','b','c','\0'};即char str[] = {'a','b','c','\0'}<==> char str[]="abc"
值得注意的是只有在程序中对字符串进行处理时,才考虑字符串结束标志的问题。考虑字符数组char a[4]={'B','O','Y','\0'};a数组中存放的是字符串“BOY”。如果执行语句a[1]= '\0';后,a数组的内容为'B','\0','Y','\0'。此时系统认为a数组中存放了一个字符串“B”,printf("a=%s\n",a);输出的结果是a=B; printf("length a=%d\n",strlen(a))输出的结果是length a=1, 而printf("sizeof a=%d\n",sizeof(a));输出结果为4.不要认为数组中只有'B'和'\0'了,只不过按系统对字符串的处理方式来看就是这样的。
char s3[10]="HELLO";//s3是编译期大小已经固定的数组
int a=strlen(s3); //a=5;//strlen()在运行起确定
int b=sizeof(s3); //b=10;//sizeof()在编译期确定
|
sizeof(s) |
strlen(s) |
char *s="HELLO"; |
4 |
|
char s[]="HELLO"; |
6 |
5 |
char s[10]="HELLO"; |
10 |
5 |
注意printf("hello is %s\n",hello);和cout<
char hello1[]="HELLO";
char hello2[]={"HELLO"}; //等同于hello1
char hello3[]={'H','E','L','L','O','\0'};//等价于hello1
char *hello4="HELLO";//区别:hello1是字符数组,hello4是字符指针。
用strlen函数测试hello1, hello2, hello3, hello4的结果均为5,因为不计末尾结束符’\0’。
常用hello1或hello4来定义字符串。
字符串与指针
C语言中许多字符串的操作都由指向字符数组的指针及指针的运算来实现的。因为对字符串来说一般都是严格顺序存取,使用指针可以打破这种存取方式,使字符串的处理更加灵活。
以下代码利用指针简洁的实现了字符串拷贝函数:
void strcpy(s,t)//copy t to s
char *s,*t;
{
while(*s++=*t++);
}
解析:*s++=*t++,先赋值*s=*t,然后指针后移t++,s++,当遇到*t= '\0'时,则拷贝操作完成。
字符串常量
当一个字符串常量出现于表达式中时,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。
"xyz"+1;//这个表达式计算“指针值加上”的值,返回一个指针,指向字符串中的第二个字符'y'
*"xyz";//返回第一个字符'x'
"xyz"[2];//返回第三个字符'z'
*("xyz"+4);//偏移量超过了这个字符串的范围,返回一个不可预测的字符
那什么时候用会用到上述表达式呢?我们考虑把二进制转换为字符并把它们打印出来。
remainder = value % 16;//value对求余保存到remainder中
if(remainder < 10)//0~9
putchar(remainder + '0');
else////10~15
putchar(remainder - 10 + 'A');
下面的代码用一种不同的方法解决这个问题:
putchar("0123456789ABCDEF"[value % 16]);
************************************************
更详细地说, 如果我们定义了:
char *text_pointer;
那么, 语句: text_pointer="A char-string"; 把指向字符串 "A char-string" 的指针赋值给 text_pointer。
但是, 如果定义如下:
char text[80];
那么, 语句: text="This is not valid"; 是不正确的!!
字符串指针和字符数组
C++/C 程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。
数组要么在静态存储区被创建 (如全局数组), 要么在栈上被创建。 数组名对应着 (而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。 指针可以随时指向任意类型的内存块,它的特征是“可变” ,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
如下例所示,字符数组的容量是6个字符,其内容为hello\0.a的内容可以改变,如a[0]='x'。指针p指向常量字符串"world"(位于静态存储区,内容为world\0),常量字符串的内容是不可以被修改的,从语法上来看,编译器并不认为p[0]='x'有什么不妥,但是该语句企图修改常量字符串的内容而导致运行错误。
char a[] = “ hello” ;
a[0] = ‘ X’ ;
cout << a << endl;
char *p = “ world” ; // 注意 p 指向常量字符串
p[0] = ‘ X’ ; // 编译器不能发现该错误
cout << p << endl;