在C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中,字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。C语言规定,可以将字符串直接赋值给字符数组。
例如:
char str[30] = {"c.zhongguo.com"};//可以使用str[i]来访问元素
char str[30] = "c.zhongguo.com"; //这种形式更加简洁,实际开发中常用//也可以不指定长度
数组第 0 个元素为'c'
,第 1 个元素为'.'
,第 2 个元素为'z'
,后面的元素以此类推。
首先明确一点,同数组一样字符串中的所有字符在内存中是连续排列的。
先观察一下下边的输出:
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int main() {
char str[] = "http://c.biancheng.net";
char *pstr = str;//将字符串首地址给它
cout << str << endl;//输出http://c.biancheng.net
printf(str);////输出http://c.biancheng.net//最正规的是使用上格式控制符%s输出字符串,%p输出地址。
return 0;
}
结果展示:
总结:字符数组归根结底还是一个数组,上节讲到的关于指针和数组的规则同样也适用于字符数组。
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int main() {
char str[] = "china.zhongguo.com";//未定义长度
//p 指向的数组元素是 char 类型,所以 p 的类型必须也是char *
char *pstr = str;//定义一个指针变量pstr,该指针变量初始化为str字符数组即字符串的首地址。
int len = strlen(str), i;
//使用*(pstr+i)
for (i = 0; i<len; i++) {
printf("%c", *(pstr + i));//先地址加再取元素
}
printf("\n");
//使用pstr[i] 就是上节课里边的p[i]即地址[i]
for (i = 0; i<len; i++) {
printf("%c", pstr[i]);
}
printf("\n");
//使用*(str+i)
for (i = 0; i<len; i++) {
printf("%c", *(str + i));
}
printf("\n");
return 0;
}
结果展示:
----------以上是使用字符数组来表示字符串,所以除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,如下:(此处为什么直接将字符串赋给指针变量,需要不是应该是地址吗?,规定直接使用一个指针指向字符串)
char *str = "ABC//DEF.COM";
//或者
char *str;
str = "http://c.biancheng.net";//此处为什么直接将字符串赋给指针变量
字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char
,所以 str 的类型也必须是char *
。
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int main() {
char *str = "ABC//DEF.COM";
int len = strlen(str), i;
//直接输出字符串
printf("%s\n", str);
//使用*(str+i)
for (i = 0; i<len; i++) {
printf("%c", *(str + i));//地址先加再取元素
}
printf("\n");
//使用str[i]
for (i = 0; i<len; i++) {
printf("%c", str[i]);//使用地址[i]取数据
}
printf("\n");
return 0;
}
结果展示:
这样去取元素的方式和字符数组是非常相似,它们都可以使用%s
输出整个字符串,都可以使用*
或[ ]
获取单个字符,但是这两种表示字符串的方式还是有区别的,它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其他数据)只有读取权限,没有写入权限。
内存权限的不同导致的一个明显结果就是,字符数组在定义后可以读取和修改每个字符,而对于第二种形式的字符串,一旦被定义后就只能读取不能修改,任何对它的赋值都是错误的。
我们将第二种形式的字符串称为字符串常量,意思很明显,常量只能读取不能写入。请看下面:
#include <stdio.h>
int main()
{
char *str = "Hello China!";
str = "I love C!"; //正确
str[3] = 'P'; //错误
return 0;
}
这段代码能够正常编译和链接,但在运行时会出现段错误(Segment Fault)或者写入位置错误。
第5行代码是正确的,可以更改指针变量本身的指向;第6行代码是错误的,不能修改字符串中的字符。
到底使用字符数组还是字符串常量
在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。
获取用户输入的字符串就是一个典型的写入操作,只能使用字符数组,不能使用字符串常量,请看下面的代码:
#include <stdio.h>
int main(){
char str[30];
gets(str);
printf("%s\n", str);
return 0;
}
PS总结:最后我们来总结一下,C语言有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,它们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。