最近各大工作室都发布了一些与字符串有关的题目,同学们在写有关字符串的题目时候会感到吃力,或是无从下手,没有思路,或是一些基本的知识不明白。这里我就讲解下与字符串有关的基本知识
字符串,它是由多个字符组成的序列。可以想象成葡萄架上垂挂着的一串葡萄,每一粒葡萄都相当于是字符;字符串也是如此,它在存储上类似字符数组,且其单个元素都是可以提取的,这点像每一粒葡萄都可以单独取下来;
对于字符串,我们需要了解以下几点:
1:字符串的定义与如何初始化。
2:字符在数组里面如何存放的。
3:字符串的注意事项。
4: 结合到题目该如何写
我就这三点展开来说明,首先对于第一点:
1:字符串的定义与如何初始化。
上一篇博客讲了有关数组的基础知识,里面也介绍了点关于字符数组的定义与初始化:
char Str1[50]; //定义一个长度为50的字符数组
char Str2[20] = "Hydrogen"; //定义一个字符数组,并且赋初始值 Hydrogen
//char Str2[20] = {"Hydrogen"}; 和上面等价
这里需要注意两点
1:C语言中字符串都会有一个结束标志,即 ‘\0’ ,这个字符在ACSII码表中为第一个,其值为0。空字符,这是字符串中作为结束的判断标记。
当输出函数列入 puts(); printf();
在输出数据的时候,遇到 ‘\0’ 就会结束输出。
而这个空字符 ,因为是字符,所以也会占据一个字节的空间,这就导致我们在定义数组长度的时候要考虑这个空字符。以下举例说明:
char Str[8] = "Hydrogen"; // 错误
char Str[9] = "Hydrogen"; //正确
//只考虑字母只有8个,但是由于空字符的存在,所以这里必须是9个长度
2:在你定义了一个字符数组并且为其赋值后,其后没有填上字符的元素,编译器会自动帮你加上 \0.
如图所示,那些未填充字符的数组元素全部都是 空 的。
str[0] | str[1] | str[2] | str[3] | str[4] | str5] | str[6] | str[7] | str[8] | str[9] | … |
---|---|---|---|---|---|---|---|---|---|---|
H | y | d | r | o | g | e | n | \0 | \0 | \0 |
这里拓展一点关于printf()函数输出顺序的知识:有同学可能注意到了,我的 整形变量i
一开始是从0开始的,而这里输出的确是从1开始的,这是为啥呢? 原因是在printf();函数中,数据输出是从右往左的。
所以在这里首先执行的是 i++, 自增1后在输出i,也就是从1开始了。
2:字符在数组里面如何存放的。
结合上面那张图片,可以注意到一个数组元素存放着一个字符,也就是说,字符在数组里面存放是一个一个存放的;字符包是表示数据和信息的字母、数字或其他符号。
A,B,C,a,b,c是字符,1, 2, 3 也是字符 , . / ? ! = - * & ( ) 同样也是字符
空格也是一个字符,也可以存放进数组里面
这里直观的演示下一个字符串的输出和每一个字符对应的ACSII码的程序:
可以注意到,一个空格对应的ACSII码是 32.
还可以注意到,当我用%d
输出一个字符的时候,得到的结果是它的ASCII码。
3:字符串的注意事项。
① 输入字符串有多种方式,上一篇博客中讲到了那几点。这里再强调一下,字符串若是有空格的输入,那么不能选择scanf()函数来进行。
② 要小心回车的存在,这个可能会使一段逻辑语法都没错的代码无法运行出正确的结果。下面用一个例子来说明:
我想分别输入两个字符H 和 2, 并且输入一次回车一次:
结果确实
观察运行结果可以知道,c2的值对应的ASCII码的值是10,这个换行符在ASCII码表中对应的字符是一模一样的。
也就是说,在我输入数据的时候,H 回车 就结束了,c2获取的值是回车的值,而这个回车是我们原本就不需要的,这时候咋办呢?就可以借助 fflush(stdin);
如何解释这种奇怪的结果呢? 结合第一篇博客所讲的内容,缓冲区和scanf()函数读取数据的知识就可以解释了,输入数据回车后,回车遗留在了缓冲区内,等待下一个scanf()函数调用,即把回车这个ASCII码赋给了c2。 所以这里只需要清空下缓冲区即可。
另一个可以解决这种问题的方法是在第一个scanf()函数后面加一个getchar(); 该函数也会从缓冲区读取一个字符,这里将回车给读取掉了。
输入数据的时候:
缓冲区: | H | \n |
---|---|---|
调用一次scanf函数读取 | ↑ |
scanf首先读走了H,此时缓冲区:
缓冲区: | \n | NULL |
---|---|---|
调用一次scanf函数读取 | ↑ |
下一次调用scanf();函数的时候,\n 就给了c2。
若加了fflush(stdin);后,在输入H回车 完毕后,
缓冲区就空掉了:
缓冲区: | NULL | NULL |
---|---|---|
调用一次scanf函数读取 |
4: 结合到题目该如何写
例一:
输入两串字符串:10 20 要求输出它们相加的结果 30
如果直接让他们对应的个位十位相加得出结果会是怎样的呢?
答案肯定是不正确的,代码和结果如下:
这里为什么是1086呢?
这是因为,直接让字符相加,他们的结果是对应字符的ASCII码进行相加,
字符 | 0 | 1 | 2 | … |
---|---|---|---|---|
ASCII | 48 | 49 | 50 | … |
所以(49+50) * 10 + 96 = 1086
知道了计算法则是对应元素的ASCII码相加后,那就好办了:
我们只需要让该字符减去某一个数字,让它相加的时候就是以该数字本身相加即可。
观察上面的表格,不二之选就是减去’0’啦,注意这里是减去字符0,或者减去0本身对应的ASCII码。二者一样。
字符-48 | 0-48 | 1-48 | 2-48 | … |
---|---|---|---|---|
ASCII | 0 | 1 | 2 | … |
这样更改后:
就可以运行出我们想要的结果了。
例2:
给定一个字符串,判断其是否为回文字符串
从最简单的一串回文串进行分析:ABCCBA
假如我们人眼进行判断,要么从外向内一一比较:AABBCC
要么从内向外CCBBAA。
只要是有一个不符合的,则就不是回文串。那思路就很明显了:
从字符串的两侧进行一一比较,若找到一对不相等的字符,则输出否。
全部比较完毕后,还没有找到不相等的,则就是回文的。
并且比较的次数是字符串长度的一半。
其他测试结果:
其他需要用到字符串的题目,也是如此,先分析题目,在结合与字符串有关的知识进行解答,合理的运用循环,判断等语法知识就可以了。
最后说一点,字符串里面重要的一个字符是结束符(空字符),它决定了字符串在哪里结束,有些时候我们需要自己设置结束符,或者在某些题目里面,比如两个字符元素的两两交换,就有可能会改变空字符的位置,从而导致一些无法预知的bug出现。
若遇到这种情况,我们大可用printf() + 循环结构,来输出此时数组内部元素的情况,以此来更好的找出bug,解决bug。
(如有错误欢迎指出)