字符
- char 是整数类型,也是字符类型
- 单引号表示的是字符字面量:
'a'
'1'
''// 这也是一个字符
- %c 占位符 用来输出字符
char a = 1; // 整数 1
char b = '1'; // 字符 1
printf("%d",a==b);
输出
0
变量 a 和变量 b 的值并不想等。
printf("%d\n",a);
printf("%d\n",b);
输出
1
49
得到变量 a 的值是 1 ;变量 b 的值是 49。
char a,c;
int b;
scanf("%c",&a); // 输入的是字符
scanf("%d",&b); // 输入的是整数
c = b;
printf("%d\n",a); // 输出的是整数
printf("%c\n",a);
printf("%c\n",c); // 输出的是字符
输入
1
49
输出
49
1
1
注意:
当变量是char 类型时,scanf中不能用 %d ,如果要使用 %d 需要使用 int 类型。
错误示例:
char b;
scanf("%d",&b); // 输入的是整数
printf("%d\n",b); // 输出的是字符
error 报错。
if( 49 == '1'){
printf("OK!");
}
输出:
OK!
所以说,输入49和输入’1’是一样的。
字符串
构造一个字符数组
char word[] = {'H','e','l','l','o','!'};
这个字符数组不是字符串,因为不能执行字符串的相关运算。
构造真正的字符串
char word[] = {'H','e','l','l','o','!','\0'};
比字符数组多了 ‘\0’,表示的是整数值的0,使得其称为C语言的字符串
- 以0(整数0)结尾的一串字符
-
- 0或’\0’是一样的,但是和’0’不同
- 0标志字符串的结束,但它不是字符串的一部分
-
- 计算字符串长度的时候不包含这个0
- 字符串以数组的形式存在,以数组或指针的形式访问
-
- 更多的是以指针的形式
- string.h 里有很多处理字符串的函数
字符串变量
*str = 'hello';
str[] = 'hello';
str[10] = 'hello';
以上三种都是定义字符串变量的方法。其中虽然hello有5个字母,但是由于结尾有’\0’,所以在数组中占据6个单元。
字符串常量
"Hello"
- ”Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0.
char* s = "Hello, world!";
表示 s 是字符串。
能够通过数组的操作改变s中的某个值。
#include <stdio.h>
int main(void)
{
char* s = "Hello, world!";
s[0] = 'B';
printf("Here! s[0] = %c \n",s[0]);
return 0;
}
结果出错了。
新的程序测试
#include <stdio.h>
int main(void)
{
int i = 0;
char* s = "Hello, world!";
// s[0] = 'B';
char* s2 = "Hello, world!";
printf("&i = %p \n",&i);
printf("s = %p \n",s);
printf("s2 = %p \n",s2);
printf("Here! s[0] = %c \n",s[0]);
return 0;
}
输出结果
&i = 000000000062FE0C
s = 0000000000404000
s2 = 0000000000404000
Here! s[0] = H
--------------------------------
Process exited after 0.005692 seconds with return value 0
请按任意键继续. . .
可以看到,对于初始化相同的字符串变量,它们的地址是相同的。而且,相对于 int 类型的变量,字符串数组的地址更小。
这个地址很小的位置里的数据是只读的,试图修改数据会报错。
- s 是一个指针,初始化为指向一个字符串常量
- 由于这个常量所在的地方,所以实际上 s 是 const char* s ,但由于历史原因,编译器不接受不带 const 的写法。
- 试图对 s 所指的字符串做写入会出错。
如果要做字符串,并且可以修改的,应该使用数组来定义字符串。
char s[] = "Hello, world!";
#include <stdio.h>
int main(void)
{
int i = 0;
char* s = "Hello, world!";
// s[0] = 'B';
char* s2 = "Hello, world!";
char s3[] = "Hello, world!";
printf("&i = %p \n",&i);
printf("s = %p \n",s);
printf("s2 = %p \n",s2);
printf("s3 = %p \n",s3);
s3[0] = 'B';
printf("Here! s3[0] = %c \n",s3[0]);
return 0;
}
输出结果:
&i = 000000000062FE0C
s = 0000000000404000
s2 = 0000000000404000
s3 = 000000000062FDF0
Here! s3[0] = B
--------------------------------
Process exited after 0.00528 seconds with return value 0
请按任意键继续. . .
分析:
看到 s3 的地址很大,说明 s3 的数组在本地变量那里。
指针还是数组
当程序里面需要有字符串时,是写出指针还是字符串。
char *str = "Hello"; // 指针
char word[] = "Hello"; //数组
- 数组:这个字符串在这里,作为本地变量空间自动被回收。
- 指针:字符串不知道在哪里,用于处理参数,动态分配空间。
所以说,要构造字符串——数组;要处理字符串——指针。
** char* 是字符串吗?
- 字符串可以表达为 char * 的形式
- char* 不一定是字符串
- char* 本意是指向字符的指针,可能指向的是字符的数组。就像int*
- 只有 char* 所指的字符数组结尾有 0 ,才能说明它所指的是字符串。
字符串的赋值
char* str1 = "Hello";
char* str2;
str2 = str1;
printf("%p\n",str1);
printf("%p\n",str2);
并没有产生新的字符串,而是将指针str2指向了str1的地址。
0000000000404000
0000000000404000
--------------------------------
Process exited after 0.004713 seconds with return value 0
请按任意键继续. . .
如果需要产生一个新的字符串,需要使用字符串函数。
字符串的输入输出
使用占位符 %s
- scanf 读入一个单词(到空格、TAB、回车为止)
#include <stdio.h>
int main(void)
{
char word[8];
scanf("%s",word);
printf("%s##\n",word);
return 0;
}
输入:
hello world!
输出:
hello##
输出hello,没有读入空格,world也没有。如果程序里面有第二个scanf,则会读到 word。
char word[8];
char word2[8];
scanf("%s",word);
scanf("%s",word2);
printf("%s##%s##\n",word,word2);
输入:
hello world!
输出:
hello##world!##
两次都没有读入 hello world!中的空格,因此这个空格是个分隔符。
- scanf是不安全的,因为不知道要读入的内容的长度。
- 改进: 在%s的%和s中间加数字,表明最多读入几个字符。这个数字应该比数组的大小小一。例如:%7s,因为对于字符串而言,还有最后的’\0’。
常见错误
- 以为 char* 是字符串类型,定义了一个字符串类型的变量就可以直接使用了(×)
char* string;
scanf("%s",string);
这段代码是错误的,因为,char* 定义的是指针变量,是以后要存储某个变量的地址的。但是指针没有被初始化,本地变量是没有默认初始值的,原来内存里面有什么,这个指针变量就是什么值。
- 改进: 指针需要初始化。
- " " 表示空字符串。
char buffer[100]="";
等价于
buffer[0] == '\0';
- 如果没有指定数组长度
char buffer[] = "";
这个数组的长度只有1。这样的buffer 数组里面放不下任何字符串。
字符串函数
C的标准库中的函数,头文件<string.h>。strlen、strcmp、strcpy、strcat、strchr、strstr
strlen
size_t strlen(const char* s)//const指strlen不会修改字符串。
- 返回 s 的字符串长度(不包括结尾的0)
strcmp
compare。
int strcmp(const char* s1,const char* s2);
比较两个字符串,并且返回一个整数。
- 0: s1 == s2
- 1: s1 > s2
- -1: s1 < s2
不相等时,会给出差值。
s1 == s2 //表达的数组数组是否是相同的地址
strcpy
char* strcpy(char* restrict dst,const char* restrict src);
- 把 src 的字符串拷贝到 dst
- restrict 表明 src 和 dst 不重叠
- 返回 dst
src 指向一个字符串,dst对应一个空间,将src的内容拷贝到dst中,包括结尾的0.
strcat
做连接
char* strcat(char* restrict s1,const char* restrict s2);
- 把s2拷贝到s1的后面,(s1末尾的0会去掉),接成一个长的字符串
- 返回s1
- 注: s1必须有足够的空间
安全问题:
- strcpy 和 strcat 可能目的地没有足够的空间
改进安全版本
char* strncpy(char* restrict dst,const char* restrict src, size_t n);
char* strncat(char* restrict s1,const char* restrict s2, size_t n);
int strncmp(const char* s1 , const char* s2, size_t n);
注: 多了一个n,并且指定多少空间。
strncmp是指定比较前几个字符。
字符串中找字符
char* strchr(const char* s,int c);
char* strchr(const char*s , int c);
返回 NULL 表示没有找到
总结:
- C语言的字符串是以字符数组的形态存在的
- 不能用运算符对字符串做运算
- 通过数组的方式可以遍历字符串
- 唯一特殊:字符串字面量可以用来初始化字符数组
- 标准库提供了一系列字符串函数