第1节 字符的妙用
在第2章中我们就已经知道,存储整数可以用int,存储小数可以用float,存储单个字符可以用char。
我们用char a;定义一个字符变量a,用scanf(“%c”, &a);来从键盘读取一个字符并存放在变量a中,用printf(“%c”, a);来输出变量a中的字符。下面这段代码的作用就是从键盘读入一个字符并将其输出:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a;
scanf("%c", &a);
printf("你刚才输入的字符是%c", a);
system("pause");
return 0;
}
如何给一个字符变量赋值呢?很简单:
char a;
a='x';
请注意,x的两边是单引号,千万不要输错了。或者可以简写为:
char a='x';
下面的内容要注意啦!
a='1';
是把字符1赋值给字符变量a。请注意’1’、1、"1"是不同的。第1个是字符,所以两边是单引号;第2个是整数1;第3个是字符串,所以两边是用双引号,只不过"1"这个字符串看起来里面只有1个字符罢了(请注意,这里看起来只有一个字符,其实并不是,后面会讲到)。
好了,了解上面有关字符的知识后,我们可以做一个稍微复杂的计算器啦。用户可以输入a+b、a-b、a*b、a/b(这里的a和b指的是任意整数)的任意一种形式,程序便可以自动识别此时是要进行加法运算、减法运算、乘法运算还是除法运算。
由于输入的格式是“整数 运算符 整数”,所以我们需要3个变量:两个整数变量用来存储两个整数,一个字符变量用来存储运算符。
int a, b;
char c;
接下来就是读入部分了,输入的顺序是“整数 运算符 整数”,所以我们scanf的顺序就是"%d%c%d"。注意双引号中没有空格。
scanf("%d%c%d", &a, &c, &b);
读入之后,第1个整数存储在整型变量a中,第2个整数存储在整型变量b中,运算符存储在字符变量c中。接下来就要对字符变量c所存储的内容进行分情况讨论,如果是加号则进行加法运算,如果是减号则进行减法运算,如果是乘号则进行乘法运算,如果是除号则进行除法运算。代码如下:
if(c=='+')
printf("%d", a+b);
if(c=='-')
printf("%d", a-b);
if(c=='*')
printf("%d", a*b);
if(c=='/')
printf("%d", a/b);
至此,我们已经完成了整个程序。完整的代码如下:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
int a, b;
char c;
scanf("%d%c%d", &a, &c, &b);
if(c=='+')
printf("%d", a+b);
if(c=='-')
printf("%d", a-b);
if(c=='*')
printf("%d", a*b);
if(c=='/')
printf("%d", a/b);
system("pause");
return 0;
}
怎么样,请输入5-6或5+6试一试吧,看看计算机能否计算出正确答案呢?
第2节 字符的本质
你猜字符1和1是什么关系?字符a和97又是什么关系呢?我们先来看一段代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
for(i=0; i<=127; i++)
{
printf("%d %c\n", i, i);
}
system("pause");
return 0;
}
在上面的代码中,循环变量i从1开始循环,一直循环到128。在输出时,将整型变量i的值输出了两次:第一次以“%d”的方式输出,第二次以“%c”的方式输出。显然,变量i是整型变量,我们用“%d”的方式来输出变量i的值是没有任何问题的。关键问题是:我们用“%c”来输出整型变量i的值时计算机会输出什么呢?“%c”不是用来输出字符的吗?我们来看看运行效果,如图7-4所示。
图7-4 ASCⅡ字符
经过观察发现,“%d”确实输出了整数(1~128),这个没有问题,但是“%c”却输出了一些“乱七八糟”的字符。例如:i的值为1时,通过“%c”竟然输出了一个“笑脸”;当i为3、4、5、6时,输出了扑克牌中的一些符号。
再通过仔细观察可以发现,i为48时输出字符0, i为49时输出字符1, i为50时输出字符2……i为57时输出字符9。
i为65时输出大写字母A, i为66时输出大写字母B, i为67时输出大写字母C……i为90时输出大写字母Z。
i为97时输出小写字母a, i为98时输出小写字母b, i为99时输出小写字母c……i为122时输出小写字母z。
你可以看看“+”、“-”、“*”、“/”、“>”、“<”等所对应的数字分别是多少。
你肯定会觉得很奇怪,为什么1~128每一个整数在计算机中都对应1个字符呢(7~10你可能看不到)?其实计算机本质上只能存储0和1,任意整数都可以通过进制转换的方式变化成0和1的序列。所以表示字符最简单的方法就是把字符用整数来代替。例如,字符a就用97来表示,此处的97就是字符a的ASCII码。有关ASCII码的详细信息,有兴趣的同学可以自己在课下学习。
从某种角度来说,97有两层含义,第1层含义是整数97,第2层含义是字符a。当你需要以整数的形式打印出来时就用“%d”,当你需要以字符的形式打印出来时就用“%c”。
好了,你应该知道1和字符1的区别有多大了。1就是整数1,而字符1换算成整数却是49。
第3节 人名怎么存储呢
到目前为止,学了这么久,我们竟然还不知道如何存储人名!先来解决如何存储“英文人名”的问题。人名说白了就是一串字符,单个字符我们已经知道如何存储了,代码如下:
char a;
这样就定义了一个字符变量a来存储单个字符。那么如何存储多个字符呢?我们又想到了第6章学到的数组。没错,字符也有数组形式,叫作字符数组,也叫作字符串,形式如下:
char a[10];
这样就定义了一个字符数组a,或者叫字符串a。它有10个小空间,即a[0]~a[9]。
这里需要注意的是:虽然有10个小空间,但实际上只能存储9个字符,因为最后一个小空间需要用来存储字符串的结束标记‘\0’,用来表示字符串的结尾。不要小看这个结束标记,很多地方都需要利用它。
那么如何读取一行字符串呢?有很多种方法。
scanf("%s", a);
请注意,a前面没有取址符“&”。这里确实很特殊,在用scanf进行读入时,只有与“%s”配合使用来读取一行字符串时,才不需要在变量前加取址符“&”。至于为什么以后再说吧,有兴趣的同学去问问“谷哥”或者“度娘”!
输出一行字符串同样很简单:
printf("%s", a);
请看下面一段代码:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
char a[10];
scanf("%s", a);
printf("%s", a);
system("pause");
return 0;
}
上面代码的功能是,从键盘输入一行字符串,然后原封不动地将输入的字符串再次输出。假如你输入的是hello,那么也会输出hello,如图7-5所示。
图7-5 输出一个字符串
强调一下,此处的字符数组a(或者称作字符串a)只申请了10个空间,但只能存9个有效字符,因为最后一个需要用来存储字符串的结束标记‘\0’。
好了,我们来看1个题目:第1行先输入1个人的名字,空1格后输入这个人的分数,第2行还是先输入1个人的名字,空1格后输入这个人的分数。代码如下:
Jack 90
Tom 99
然后请输出分数较高的这个人的名字,对于上面的输入,我们应该输出Tom。想一想应该怎么做呢?
我们的程序需要接收4个信息,分别是第1个人的名字和分数,以及第2个人的名字和分数。人名我们可以用字符数组来存储,分数可以用整型来存储。因此我们需要两个字符数组和两个整型变量。
char a[101], b[101];
int x, y;
这里字符数组a和b的大小都是101,因为一般人的名字应该不会超过100个字符吧!
然后需要解决的就是如何输入了。根据输入的规则,先是1个人名和1个整数,接下来还是1个人名和1个整数。
scanf("%s", a);
scanf("%d", &x);
scanf("%s", b);
scanf("%d", &y);
请注意,在输入时,变量a和b前面没有取址符“&”,而x和y前面有取址符“&”。第1个人的名字存储在字符数组a中,第1个人的分数存储在整型变量x中。第2个人的名字存储在字符数组b中,第2个人的分数存储在整型变量y中。接下来就是判断大小了:
if(x>y)
{
printf("%s", a);
}
else
{
if(x<y)
{
printf("%s", b);
}
else
{
printf("%s和%s的分数相同", a, b);
}
}
好了,完整的代码如下:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
char a[101], b[101];
int x, y;
scanf("%s", a);
scanf("%x", &x);
scanf("%s", b);
scanf("%x", &y);
if(x>y)
{
printf("%s", a);
}
else
{
if(x<y)
{
printf("%s", b);
}
else
{
printf("%s和%s的分数相同", a, b);
}
}
system("pause");
return 0;
}
其实,读取字符串除了用scanf外还可以用gets,用法如下:
char a[101];
gets(a);
它们有细微的区别,请分别运行代码1和代码2,运行时请输入:
Tom Smith
代码1如下:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
char a[10];
scanf("%s", a);
printf("%s", a);
system("pause");
return 0;
}
代码2如下:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
char a[10];
gets(a);
printf("%s", a);
system("pause");
return 0;
}
分别运行后,你会发现代码1输出了:
Tom
但是代码2却输出了:
Tom Smith
由此可见,用scanf进行字符串读入时,遇到空格就提前终止了,但是用gets进行读入时却可以读入一整行。
同样,输出字符串除了用printf以外还可以用puts,用法如下:
puts(a);
使用puts(a)输出时,会在末尾自动换到下一行,相当于printf(“%s\n”, a)。
给单个字符赋初始值很简单,但是如何给一个字符数组赋初始值呢?也很简单,在字符串的两边加上双引号和花括号就可以。例如:
char a[10]={"hello"};
第4节 字母的排序
前面,我们已经学习过如何对整数进行排序。本节我们将学习如何对1行字母进行排序。即读入1行小写字母,然后将这行字母从a到z进行排序。
例如,如果输入:
dzapytrbtc
则需要输出:
abcdprttyz
之前已经讨论过字符的本质是整数。字符的排序和整数的排序是完全一样的。
首先申请一个字符数组a,然后用gets( )进行读入。
char a[101]; //假设读入的字符不超过100个
gets(a);
接下来要知道读入的字符串有多长,可以用strlen( )来获取字符串的长度。定义一个整型变量len(你可以改为你喜欢的名字)来存储字符串的长度:
int len;
len = strlen(a);
需要说明的一点就是,如果你用了strlen( )函数,就需要在程序的最开始(第一行),增加一条语句:
#include <string.h>
最后,添加已经学习过的“选择排序”的代码,完整的代码如下:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a[101], t;
int len, i, j;
gets(a);
len=strlen(a);
for(i=0; i<=len-2; i++)
{
for(j=i+1; j<=len-1; j++)
{
if(a[i]>a[j])
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
puts(a);
system("pause");
return 0;
}
第5节 字典序
我们刚刚已经知道如何对单个字符进行排序了,那如果是一个字符串呢?比如apple和pear哪一个排在前面呢?当然是apple排在pear的前面。因为apple在英语字典中就排在pear的前面。我们在翻字典时,从第1页开始翻,会先看到apple这个单词,然后再看到pear,这个就是字典序。
我们来完成这样一个例子:输入两个单词,然后按照字典序输出这两个单词。
例如,我们输入:
pear
apple
需要输出:
apple
pear
读入和输出都很简单,关键是如何比较两个字符串。字符的比较可以用“>”、“<”、“<=”、“<=”或者“==”,但是字符串却不可以。两个字符串的比较可以用函数strcmp( )。strcmp(a, b)就是比较字符串a和字符串b在字典中的顺序。
如果字符串a和字符串b完全相同,那么返回值为0。
如果字符串a在字典中比字符串b先出现,那么返回值小于0。
如果字符串a在字典中比字符串b后出现,那么返回值大于0。
举一个例子:假设a和b是两个字符数组,分别存储两个字符串,然后把a和b按照字典序输出。
if ( strcmp(a, b) < 0) // a在b前面
{
puts(a);
puts(b);
}
if ( strcmp(a, b) > 0) // a在b后面
{
puts(b);
puts(a);
}
if ( strcmp(a, b) == 0) // a和b 是同一个字符串
{
puts(a);
puts("一样的");
}
好了,回到本节的题目:输入任意两个字符串,将其按字典序输出。
还有,如果你用了strcmp( )函数,也需要在程序的第一行增加一条语句:
#include <string.h>
完整的代码如下:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main( )
{
char a[101], b[101];
gets(a);
gets(b);
if ( strcmp(a, b) <= 0)
{
puts(a);
puts(b);
}
else
{
puts(b);
puts(a);
}
system("pause");
return 0;
}
第6节 多行字符
之前我们学习了如何存储一行字符,但如果要存储多行字符该怎么办?例如,我们需要存储5个人或者5 000个人的名字该怎么办呢。
这里我们需要使用二维字符数组,其实与我们在第6章学习的普通二维数组差不多:
char a[5][11];
上面的语句就定义了一个二维的字符数组,这个字符数组有5行,每行有11列,也就是说可以存储5个长度不超过10的字符串(想一想为什么不是长度不超过11的字符串)。其实你可以这样理解:二维字符数组a有5行,每行都可以用来存储1行字符串。
下面的代码就是读入5行字符串,然后将这5行字符串原封不动地输出。请注意在输入时每行字符串不要超过10个字符:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a[5][11];
int i;
for(i=0; i<=4; i++)
{
gets(a[i]);
}
for(i=0; i<=4; i++)
{
puts(a[i]);
}
system("pause");
return 0;
}
现在来解决这样一个问题:输入5个单词,然后把这些单词按照字典序输出。
例如,输入:
book
books
car
zoo
apple
需要输出:
apple
book
books
car
zoo
首先需要定义一个5行11列的二维字符数组(这里定义11列是因为常见的英文单词都在10个字母以内):
char a[5][11];
接下来读入这5个单词:
for(i=0; i<=4; i++)
gets(a[i]);
再接下来就是排序,下面的代码就是我们熟悉的选择排序:
for(i=0; i<=3; i++)
{
for(j=i+1; j<=4; j++)
{
if(a[i]>a[j])
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
原来在对整数或者字符进行排序时,a[i]、a[j]和t都是整数或字符。但是现在a[i]、a[j]和t都是一行字符串。如果想把整个字符串a[j]赋值给a[i],是不能写成a[i]=a[j]的,需要用到字符串复制函数strcpy( )。strcpy(a[i], a[j]);的意思就是把字符串a[j]的内容原封不动地复制到字符串a[i]中,从而替换掉字符串a[i]中原来的内容。
使用strcpy( )函数,也需要在程序的第一行加上:
#include <string.h>
另外,在本章第6节中,我们也已经讲过两个字符串的比较也不能直接用“>”、“<”或者“==”,而要用字符串比较函数strcmp( )。strcmp(a[i], a[j])的作用就是比较字符串a[i]和a[j]在字典中的顺序。
好了,完整的代码如下:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
char a[5][11], t[11];
int i, j;
for(i=0; i<=4; i++)
{
gets(a[i]);
}
for(i=0; i<=3; i++)
{
for(j=i+1; j<=4; j++)
{
if( strcmp(a[i], a[j])>0 )
{
strcpy(t, a[i]);
strcpy(a[i], a[j]);
strcpy(a[j], t);
}
}
}
for(i=0; i<=4; i++)
{
puts(a[i]);
}
system("pause");
return 0;
}
关于字符串的处理函数还有strcat( )等,有兴趣的同学可以自己去问“谷哥”或者“度娘”。
最后说一下,如果你需要使用二维字符数组中的某一个字符也是可以的。例如,第0行第0列就是a[0][0],第1行第2列就是a[1][2]……。
a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] a[0][5]a[0][6]a[0][7] a[0][8] a[0][9] a[0][10]
a[1][0] a[1][1] a[1][2] a[1][3] a[1][4] a[1][5]a[1][6]a[1][7] a[1][8] a[1][9] a[1][10]
a[2][0] a[2][1] a[2][2] a[2][3] a[2][4] a[2][5]a[2][6]a[2][7] a[2][8] a[2][9] a[2][10]
a[3][0] a[3][1] a[3][2] a[3][3] a[3][4] a[3][5]a[3][6]a[3][7] a[3][8] a[3][9] a[3][10]
a[4][0] a[4][1] a[4][2] a[4][3] a[4][4] a[4][5]a[4][6]a[4][7] a[4][8] a[4][9] a[4][10]
第7节 存储一个迷宫
好了,又到了本章的最后1节。我们已经学习了如何存储多行字符,但是一直忽略了一个问题,就是如何对二维字符数组进行初始化。例如,要存储一个迷宫该怎么办?
###########
#O # ###
# ## ## #
# # # #
# #### ## #
# #
###########
其实二维字符数组的初始化和一维数组的初始化差不多,我们现在回忆一下之前是如何给一维字符数组进行初始化的:
char a[10]={"hello"};
一维字符数组的初始化很简单,直接在字符串的两边加上双引号和花括号就可以。二维字符数组的初始化代码如下:
char a[2][10]={"hello", "world"};
或者
char a[2][10]={"hello",
"world"};
写成两行,主要是为了美观,更形象地表现出我们是在对二维字符数组进行初始化。
仔细观察后你会发现,字符数组a有两行,因此在初始化时有两个带双引号的字符串,并用逗号隔开。
如果要初始化本节开头的那个迷宫,我们需要定义足够大的二维字符数组,这个迷宫有7行,每行有11列。因此在定义字符数组时也要有7行,但是每行要有12列(不要忘记每行字符串的结尾要有‘\0’)。
char a[7][12]={"###########",
"#O # ###",
"# ## ## #",
"# # # #",
"# #### ## #",
"# # ",
"###########"};
现在我们可以用for循环和puts( )来把这个迷宫输出到屏幕上。因为有7行,但是字符数组是从第0行开始的,所以循环变量i是从0到6。从第0行到第6行,而每1行是一个一维字符串,因此直接用puts(a[i])就可以。代码如下:
for(i=0; i<=6; i++)
puts(a[i]);
完整的代码如下:
#include <stdio.h>
#include <stdlib.h>
int main( )
{
int i;
char a[7][12]={"###########",
"#O # ###",
"# ## ## #",
"# # # #",
"# #### ## #",
"# # ",
"###########"};
for(i=0; i<=6; i++)
puts(a[i]);
system("pause");
return 0;
}
评论区