C语言学习笔记
字符与字符串
字符介绍
- 基本数据类型分为数值类型和字符类型,数值类型分为整形和浮点型,字符类型分为字符和字符串。
- 什么是字符:我们屏幕上出现的所有数字、汉字、符号等等全部是字符;我们使用的字符分为三类,ASCII字符(ASCII码表),拓展ASCII码(制表符),宽字符(汉字和特殊字符)
- 字符类数据也有自己的转义字符,就像%d对应int,%u对应unsigned,%lf对应double一样,
%c
对应的是字符型数据。 - 常见的输出方法:
printf("%c",'A');
putchar('a');
这两种方法都能正确的输出A或者a,但是一定要记得单引号,否则会被系统认为是未声明的变量。 - 每一个字符都对应着一个编号,每一个编号也对应一个字符
字符变量的定义
- 字符的本质也是数字,比如用%d打印’A’,其结果还是65。
- 定义方法:
char c
这样就声明了一个字符变量。我们也可以直接定义char c = 'A'
,但是不能在单引号内写多个字符,被认为是错误写法。
字符输入的实质
- 我们输入的所有内容全部储存在缓冲区,当我们按下回车时,表示输入结束,此时,有输入函数在缓冲区依次取数据,并存入变量的空间。
- 输入字符很容易被缓冲区存留的呢绒影响,所以在输入字符前,我们把缓冲区清空,就可以去掉影响了。
- 清空输入缓冲区的三种函数:
setbuf(stdin,NULL);
fflush(stdin);
while(c = getchar() != '\n' && c != EOF);
- 其中第二种非C语言标准,有的编译器不支持,所以不建议使用。
scanf_s的用法
- 编译器常见警告:
scanf
不安全,解决办法就是换用scanf_s
scanf_s("%c",&c,1);
这样的语句是其标准写法。- 1指示了c的字节数
- 读数值型变量没有问题,读字符型变量需要多加一个参数,就是这个数字1。
- 后面学到字符串也要注意这个参数问题。
getchar()用法
- 其函数原型是将缓冲区的一个字符作为返回值
c = getchar()
这样的语句就是将一个字符给变量c。
_getch()用法
- 随输入随读取,他的优点是不需要回车来确认。
- 在很多游戏的制作中都大量使用了这个函数
getch()
函数已经不被认可,所有的getch都应换用_getch。- 头文件
<conio.h>
字符串
- 概念:以
'\0'
为结尾的字符数组,如char str1[5] = {'a','b','c','d',0}
char str2[5] = {'a','b','c','d','\0'}
char str3[5] = {'a','b','c','\0'}
char str4[5] = {'a','b','c'}
这些全都是字符串。 - 字符串的输出:
printf("%s",str)
,%s是字符串专属的转义字符。意义是从给定的字符串地址开始,一个一个的输出,直到遇到'\0'
结束,如果没有遇到'\0'
,就会继续向下输出,直到被'\0'
结束,这是初学者能经常遇到的烫烫烫烫烫
。同理,字符数组只能使用循环一个一个输出。
字符串的首地址也有两种表达方法:str
和&str[0]
,类似于普通一维数组。 - puts函数用法:专为字符串输出而制定的函数,直接
puts(str)
就可以完成。
字符串的声明方式
- 有元素个数:和上述的声明方式相同,但是一定要注意别越界。
- 无元素个数:
char str[] = {'a','b','c','d','\0'}
一定要手写0,否则极易导致越界。 - 常量字符串:例如
"hello world"
要记得双引号,它和1,‘a’,12.3一样都是常量,是固定的,不能被赋值修改。
输出方式:printf("%s","hello world");
这样是不会导致越界操作的,常量字符串自带了0。char *p = "hello world";
printf("%s",p);
这样用指针来操作常量字符串也是没有任何问题的。printf(p);
printf转定义,这与其本质有关。 - 常量字符串初始化数组,这样数组在初始化时就不需要带大括号了,直接用双引号就可以。同样也是自带了0,但是一定要注意数组大小要大于等于常量字符串,否则会有警告,并且无法正常输出。
双引号的作用
- 返回常量是字符串的首地址,
hello world
这是字符串本身 - 因此上述
printf(p)
的写法才成立。
字符串的输入
- 字符串的输入方式:
scanf("%s",str)
,str还是字符数组的首地址,%s的输入方式会自动在字符串的后面补上'\0'
,因此要记得在输入的时候只能输入n-1个字符。同时scanf
函数还无法读取空格,空格会被认为是分隔符。 - 更加安全的输入方法
scanf_s("%s",str,19);
这样会给'\0'
自动的留下一个位置。 gets(str)
函数:首先它与scanf
相比,可以读取空格,但是一定要小心越界。- 更安全的版本是
gets_s(str,19)
,他也是会自动给'\0'
留下一个位置。 - 这四个函数中,不安全的版本如果输入越界,会在程序执行完成后发生报错。这样的错误应尽量避免,纠正很复杂。安全的版本如果越界了,则直接不进行输入,第一个字符就被记作
'\0'
。
多种其他函数介绍
- 本节中介绍的所有函数的头文件均为
<string.h>
,因篇幅有限,不进行一一列举。 - 拷贝赋值函数:
strcpy(str, str1)
,其意义是将str1赋值给str,其中str,str1都是char定义的数组。该函数的返回值是str。str1可以等价替换为正确初始化的指针,或者字符串。如果越界赋值,会在程序结束后报错。 - 更安全的版本:
strcpy_s(str, n , str1)
这里参数n是指定复制的字节数,目标是str。 strncpy(str,str1,m)
这里的m是指定替换的字符数,对应的目标是str1strncpy_s(str,n,str1,m)
同上- 计算字符串长度函数:
strlen(p)
参数应是字符串/数组的首地址,其返回值是计算的长度。 - 比较函数:
strcmp(str1,str2)
其意义是比较第一个具有相同下标的数据的ASCII值大小,不是比较长度,返回值只有三种类型:如果1>2输出一个正数,1==2输出0,1<2输出一个负数,具体结果是其ASCII差值。 strcmp(str1,str2,n)
是比较了前n个字符- 拼接函数:
strcat(str,str1)
将str1拼接到str后面,拼接原理是取代str中的'\0'
,并且向后顺延。 strcat_s(str,20,str1)
将str保护在20个字符以内。strncat(str,str1,n)
是拼接n个到str后面strncat_s(str, n,str1 ,m)
意义同上。- 将字符串转换为整数:
atoi(str)
,返回值是转好的整数,类型是int,如果字符串从第一个向后出现了非数字字符,返回值是前面所有字符,或者0(第一个字符就不是) - 将整数转化为字符串:
itoa(232,str,10)
参数一是被转化的整数,参数二是转好后装到哪里,参数三是以几进制来进行转化这里加不加下划线_都可以。 - 更安全的版本:
_itoa_S(233,str,4,10)
这样的第三个参数就是指定接受的长度,注意下划线_。 - 注意:以上几个转化函数的头文件是
<stdlib.h>
- 格式化字符串:
sprintf("str,..." , ...)
str是一个字符串,后面的参数和printf相同,意义是将缓冲区的内容存入str中,将不同类型的数据全部转化为字符串。 - 更安全的版本:
sprintf("str,20,..." , ...);
意义和上面的_s版本完全相同。
汉字与特殊字符
- 一个汉字占两个字节,也就是需要两个char类型变量来装,这是因为国际认可的汉字数共有40000多个,只能用两个来记
- 我们可以直接使用
char str{3];scanf("%s",str[0])
来进行输入,或者用两个连续的%c来转义。 - 可以通过百度查询“汉字国标码查询”来找到每个汉字对应的数码但只能找到16位的数据,我们可以通过计算器来转化,也可以通过下列代码
printf("%x",*(short*)str);
来得到汉字对应的10进制数。
转义字符和转换说明符
- 认识:转义字符
\
,它不但可以配合一些字母输出\t \n \r
等等,还可以使用它加一些特殊符号,来表示正确地输出该特殊字符,例如\\ \? \"
等。在学到文件操作的时候,也需要使用\\
来进行路径的表示
转换说明符%
,,%%
可以输出一个%。 - 转义字符的字节大小可以用
strlen
函数来进行表示:printf("%u\n",strlen("\w"));
这样的结果是1,+字母的字节数全部是1,即使是未定义的也是1。
+数字和+0+数字表示的都是八进制数,且有个数限制,前者后面的数字只能是13位,也就是0377的范围,对应10进制0255;后者后面加的数字只能是12个,八进制077,10进制063;+x+数字后面也是加12个数字,16进制0ff,对应10进制0~255。
这些全是1字节,但是如果输入了\58这类的非法字符,系统就会自动认为这是两个字符,输出的结果就是2字节了。 - 上述情况的测试:
strlen("\58") = 2
strlen("\555") = 2
strlen("\400") = 报错(有的系统会认为是两个字符)
strlen("\5555") = 同上,系统会检测前三个数
strlen("\018") = 2
strlen("\xfff") = 报错(不能超过两个)
strlen("\xffw") = 2 这样就不会报错了,w不是16进制数
字符串数组和二维字符数组
- 字符串数组的定义:
char *str[3] = {"asd","asdsa","asdasddsa"};
每一个元素都是char*类型,即其每一个元素都是指向地址的。可以用printf(str[0])
这样的代码来测试。也不能对其进行直接的修改,因为字符串是常量,不能被修改,但是我们可以通过str[1] = "123"
这样的方式,详见双引号的作用。它的本质就是指针数组,或者叫地址数组。 - 二维字符数组:
char str[2][4] = {"asd","12"}
其中的注意点很多,建议反复阅读本文章。这是在用字符常量区赋值栈区,原理相当于char str1[4] = "asd"
。
本周小结:本周顺利的学完了递归函数以及字符与字符串的主要内容。下周我们或将开始着手做一些较大的项目,但是还是要先稳扎稳打,先学习完结构体的内容。