字符串可以表达为char*的形式,char*不一定是字符串,只有它所指的字符数组有结尾的0,才能说它所指的是字符串。
char word[] = {'H','e','l','l','o'};
这不是字符串,这是字符数组,因为不能用字符串的方式做计算,下面的代码才是字符串:
char word[] = {'H','e','l','l','o','\0'}
'\0'和0一样,但是和'0'不同,0标志字符串的结束,但不是字符串的一部分(计算字符串长度时不包含这个0),字符串以数组的形式存在,以数组或指针的形式访问。
字符串变量
char *str = "Hello";
char word[]="Hello";
char line[10] = "Hello";
字符串常量(字面量)
"Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6。
char *s ="Hello, World!";
char *s2="Hello, World!";//s和s2实际上指向同一块空间
s是一个指针,初始化为指向一个字符串常量。由于这个常量所在的地方(很小),所以实际上s是const char *s(只读),但是由于历史的原因,编译器接受不带const的写法。但是 试图对s所指的字符串做写入会导致严重的后果。
如果要修改字符串,应该用数组:
char s[] = "Hello, World!";
如何选择?指针还是数组?
char *str ="Hello";
char word[] = "Hello";
数组:这个字符串在这里。作为本地变量空间自动被回收。
指针:这个字符串不知道在哪里。处理参数或动态分配空间。
- 如果要构造一个字符串-->数组
- 如果要处理一个字符串-->指针
字符串的连接
"Hello""World"
"Hello"/"World"
字符串输入和输出
char string[8];
scanf("%s",string);
printf("%s",string);
- scanf读入一个单词(到空格、tab或回车为止)
- scanf是不安全的,因为不知道要读入的内容的长度
如果上面的代码输入12345678,那么就会发生缓冲区溢出,溢出的字符会被写入到s
数组后面的内存空间中,这可能会覆盖其他重要的数据,比如局部变量的值、函数的返回地址等,从而导致程序出现未定义行为。这种未定义行为可能表现为程序崩溃、输出错误的结果、或者在某些情况下看起来似乎正常运行但实际上数据已经被破坏。
可以修改为下面的代码:
char string[8];
scanf("%7s",string);
printf("%s",string);
常见错误
char *string;
scanf("%s",string);
以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了,由于没有对string初始化为0,所以不一定每一次运行都出错。
空字符串
char buffer[100]="";//这是一个空的字符串,buffer[0]=='\0'
char buffer[]="";//这个数组的长度只有1
字符串数组
char **a;//a是一个指针,指向另一个指针,那个指针指向一个字符(串)
char a[][];//二维数组,但是没有初始化列数
char*a[];//a是一个指针数组,这个数组存储的是指向char类型数据的指针
char **a
声明的是一个指向字符指针的指针,本身是一个指针,如果要使用它来访问类似的数据,需要先让它指向一个有效的指针数组或者动态分配内存来模拟指针数组的结构。例如:
char *b[] = {"one", "two"};
char **a = b;
printf("%s\n", a[0]); // 输出 "one"
char *a[]
是一个数组,有固定的大小(除非是在动态分配内存的情况下),可以通过下标来访问数组中的元素(指针),也就是说,a
是一个指针数组,这个数组存储的是指向char
类型数据的指针。
char *a[] = {"hello", "world", "C"};
for (int i = 0; i < 3; i++) {
printf("%s\n", a[i]);
}
这里每个元素(a[0]
、a[1]
、a[2]
)都是一个指针,分别指向存储字符串常量"hello"
、"world"
和"C"
的内存地址。a[i]
表示获取指针数组a
中的第i
个指针,printf("%s\n", a[i]);
则是通过这个指针输出其所指向的字符串。
程序参数
int main(int argc, char *argv[])
argc
表示命令行参数的个数,包括程序本身的名称。argv
是一个字符指针数组。argv[0]
指向程序名称的字符串(命令本身),argv[1]
指向第一个命令行参数的字符串,以此类推。
int main(int argc, char const* argv[]) {
int i;
for (i = 0; i < argc; i++) {
printf("%d:%s\n", i, argv[i]);
}
return 0;
}
上面的代码打印出程序运行时传入的命令行参数及其对应的索引:
string.h
strlen
size_t strlen (const *s);
和sizeof类似,用于计算字符串长度的函数,但是不包括结尾的\0
strcmp
用于比较两个字符串的函数。
- 如果
s1
小于s2
,返回一个负整数。 - 如果
s1
等于s2
,返回 0。 - 如果
s1
大于s2
,返回一个正整数。
char s1[] = "abc";
char s2[] = "Abc";
printf("%d\n", strcmp(s1, s2));//输出1
strcpy
char * strcpy(char *restrict dst, const char *restrict src)
把src的字符串拷贝到dst,restrict表明src和dst不重叠,复制过程包括从src
中逐个字符读取,直到遇到字符串结束符'\0'
,然后将这个结束符也复制到dst
中,确保dest
也是以'\0'
结尾的字符串。返回dst
,即目标字符串的起始地址。
char src[] = "Hello";
char dst[6];
strcpy(dst, src);
printf("%s\n", dst);//输出Hello
在这个例子中,strcpy(dst, src)
将字符串"Hello"
从src
复制到dest
。需要注意的是,目标字符串dst
的空间必须足够大,以容纳源字符串src
及其结束符。如果目标字符串空间不足,可能会导致缓冲区溢出错误。
复制一个字符串
char *dst = (char*)malloc(strlen(src)+1);
strcpy(dst,src);
字符串中找字符
strchr和strrchr
char *strchr(const char *s, int c);//从左开始
char *strrchr(const char *s, int c);//从右开始
如何找第二个:
char s[] = "hello";
char* p = strchr(s, 'l');
printf("%s\n", p);//llo
p = strchr(p + 1, 'l');
printf("%s\n", p);//lo
字符串中找字符串
char *strstr(const char *s1,const char *s2);
char *strcasestr(const char *s1,const char *s2);//忽略大小写