文章目录
前言
上一期 forever 为大家介绍了一维数组和二维数组,接下来就继续来看数组中还有一类重点且需要学的字符数组吧!
一、字符数组介绍
数组元素类型是 char 的数组就是字符数组,它也可以有一维数组、二维数组或者多维数组。我们之前所接触的字符串的就可以存在字符数组里面。
二、字符数组的创建和初始化
这里字符数组的定义格式以及一般的情况和之前介绍的一维数组和二维数组类似,我们接下来就说说字符数组需要注意的一些特殊情况吧!
若给一个字符数组里存入 Hello,everyone! 此时在初始化字符数组时有两种情况:
(1)可以去掉定义时候的元素数,用元素初值列表的元素个数来定义i元素数,此时字符数组的元素数目就等于花括号里的字符数目。
示例:
#include <stdio.h>
int main(void)
{
char arr[] = { 'H','e','l','l','o',',','e','v','e','r','y','o','n','e','!' };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
for (int i = 0; i < sz; i++)
{
printf("%c", arr[i]);
}
}
运行结果:
总结:
这里数组的长度是15,后面不会自动添加字符串结束的标志’\0’。
(2)这里将字符串直接放在花括号里或者直接放在 “=” 后面。
示例1:
将其放在花括号里:
#include <stdio.h>
int main(void)
{
char ch[] = {"Hello,everyone!"};
int sz = sizeof(ch) / sizeof(ch[0]);
printf("%d\n", sz);
printf("%s\n", ch);
for (int i = 0; i < sz; i++)
{
printf("%c", ch[i]);
}
}
运行结果:
示例2:
将其直接放在 “=” 后面:
#include <stdio.h>
int main(void)
{
char ch[] = "Hello,everyone!";
int sz = sizeof(ch) / sizeof(ch[0]);
printf("%d\n", sz);
printf("%s\n", ch);
for (int i = 0; i < sz; i++)
{
printf("%c", ch[i]);
}
}
运行结果:
总结:
(1)这里两种不同的形式其本质效果是一样的,都可以用来字符数组的初始化。
(2)这里需要注意,在数组内部结尾面有一个字符串结束的标志 ‘\0’,这是系统自动添加的,因此此时数组的长度会在字符元素的基础上加1,即为15+1=16,ch[0]~ch[14]里面存字符元素,ch[15]存 ‘\0’。
三、字符串常量和字符数组
字符串常量是一对用双引号括起来的字符串序列。字符数组可以用来存储字符串常量,此时字符数组的元素个数应该大于或等于字符串的长度加1。字符数组中如果没有字符串的结束标志 ‘\0’ ,就不能当作字符数组使用,否则可能会出错。
四、字符数组的输入与输出
%c——逐个元素的输入和输出字符。
%s——整体一次性输入和输出字符串。
1、用格式符号 %c 来输入和输出字符
%c——这种格式,是只能逐个的输入和输出,一般将 scanf() 和 printf() 放到循环中,用数组元素作为输入和输出项,元素下标在循环中不断变化,逐个进行元素的输入和输出。输入时候数组前面一定要加上取地址符号 &。
注意:这种直接输入,不像有的数组初始化里面那样有 ‘\0’ ,这里面系统不会自动加 ‘\0’ ,输出时候也不会自动检测 ‘\0’ 。
程序示例:
用%c逐个输入输出字符。
#include <stdio.h>
int main()
{
char ch[5] = { 0 };
int sz = sizeof(ch) / sizeof(ch[0]);
printf("输入字符:\n");
for (int i = 0; i < sz; i++)
{
scanf("%c", &ch[i]);
}
printf("输出字符:\n");
for (int i = 0; i < sz; i++)
{
printf("%c", ch[i]);
}
return 0;
}
运行结果:
2、用格式符号 %s 来整体输入和输出字符串
(1)利用 %s 输入字符串:
利用 scanf() 中用格式串 “%s” 指定格式,直接用数组名作输入项整体输入字符串。
示例:
#include<stdio.h>
int main(void)
{
char ch1[10],ch2[10],ch3[10];
scanf("%s%s%s", ch1,ch2,ch3);//这里不需要加取地址操作符&
printf("%s %s %s",ch1,ch2,ch3);
}
运行结果:
总结:
(1)数组名本身就代表该数组的首地址(即第一个元素的地址),所以在 scanf() 中输入的时候不需要带取地址操作符 &。
(2)这里是整体输入,所以要清楚这里在输入字符串的结尾,系统会加上 ‘\0’。
(3)还有这里是用多个数组输入多个字符串中间可以用空格隔开。
示例:如果这里输入:You are students!<回车>
则在ch1、ch2、ch3 中分别存入的是 ‘You’,‘are’,‘students!’。
(4)scanf()中空格是多个字符的分隔符,所以不能给一个字符数组里输入这种带空格的字符串,如果输入了,那么就会只有第一个空格前面的字符串有效。
以上总结(4)的示例:
#include<stdio.h>
int main(void)
{
char ch1[15];
scanf("%s", ch1);
printf("%s", ch1);
}
运行结果:
(2)利用 %s 来输出字符串
在 printf() 中用字符串 “%s”指定格式,用数组名做输出项整体输出字符数组。
示例:
#include<stdio.h>
int main(void)
{
char ch[] = { "I am a student!" };
int sz = sizeof(ch) / sizeof(ch[0]);//计算结果为字符串数加1——系统自动在结尾添加 '\0'
printf("%d\n", sz);
printf("%s\n", ch);
}
运行结果:
总结:
(1)如果数组长度大于字符串长度,则遇到 ‘\0’ 就会结束;
(2)如果数组中有多个 ‘\0’ ,输出时遇到第一个 ‘\0’ 即结束;
(3)如果数组中没有 ‘\0’ ,则用此格式输出时会将内存中该数组中的所有内容一起输出,直到遇到第一个 ‘\0’ 为止。因此,这种情况最好改用 “%c” 输出。
(4)只有字符数组能够整体输入和输出,其他数组都不行。
五、字符串处理函数
在这之前,forever 带大家也接触了函数,其中有 scanf() 这类的库函数,也有自定义函数。然而在字符数组这里,经常需要对字符进行整体处理,如整体比较、整体复制、整体连接、整体输入和输出等等。但字符数组不能整体复制,也不能整体比较,因此为了方便处理字符,在 C语言库中就给大家提供了一些专门处理字符串的库函数。
下面我们介绍几种常用的函数:
1、字符串整行输入函数gets()
函数使用格式:gets(字符数组);
功能:从键盘将带空格的字符序列(以回车为结束标志)全部输入到指定的字符数组中,并且加上字串结束的标志 ‘\0’ 。该函数的返回值是字符数组的首地址。
注意:
这个函数在vs编译环境里:从vs2015起 gets() 函数就不用了,因为这个函数可能会造成缓冲区溢出,甚至程序崩溃,故就不推荐使用。但是在其他编译环境下有的也可以使用,这里因为编译环境较多,所以 forever 也没有完全去了解,各位友友们如果感兴趣可以去李姐李姐哈!
然后现在在vs里可以用 gets_s() 函数代替 gets() 函数,就拿 forever 用的vs2019 编译环境来举例使用吧!
示例:
#include<stdio.h>
int main(void)
{
char ch[20];
gets_s(ch, 15);
for (int i = 0; i < 15; i++)
{
printf("%c", ch[i]);//ch为字符数组,15为要输进去字符串的长度加1,这里千万别忘了还有体统的 '\0'
}
printf("\n");
printf("%s\n", ch);//这里还可以用%s整体输出
}
运行结果:
总结:
哈哈哈哈!forever 可没有骂人的意思哈!这里“You are a pig!” 刚好是14个字符长度再加1就是15啦!
(1)gets_s()函数在这里其实和gets()函数的用法类似,起到相同的作用;
(2)gets_s(ch, 15);//ch为字符数组,15为要输进去字符串的长度加1,这里千万别忘了还有体统的 ‘\0’。
(3)这里输出时也可以用 格式符%s 整体输出。
2、字符串整体输出函数puts函数
格式:puts(字符数组或字符串);
功能:将指定的字符串(以’\0’为结束标志)作为一行输出到终端。
puts(str) 和 printf(“%s\n”,str) 功能相同。str中可以是字符串常量或者含有 ‘\0’ 的字符数组,字符串中也可以有转义字符。
示例:
#include<stdio.h>
int main(void)
{
char ch[20];
gets_s(ch, 15);
printf("%s\n", ch);
puts(ch);
}
运行结果:
总结:
(1)puts(ch) 和 scanf(“%s\n”,ch) 功能相同。因此可以说 puts 其实也具有自动换行的功能。
(2)puts 可以输出含有空格(即 ‘\0’ )的字符数组。
3、测量字符串长度函数
strlen()函数,相信友友们对它也不陌生啦!对 forever 在之前篇章内容中也给大家介绍了它,并且还拿它和 sizeof 做了对比哈!但在这里我再带大家回顾一下吧!
格式:strlen(字符串);
功能:返回字符串长度,不包括 ‘\0’。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
char ch[20];
gets_s(ch, 20);//vs2019中的gets()函数
int len1 = strlen(ch);
printf("%d\n", len1);
int len2 = strlen("You are a pig!");
printf("%d\n", len2);
return 0;
}
运行结果:
总结:
(1)strlen()是一个函数,是专门计算字符串长度的,它计算到 ‘\0’ 前一个字符;
(2)这里gets_s() 函数输入后系统会自动加 ‘\0’ 因此strlen()会计算到结尾。
4、字符串比较函数
在数值中我们经常会进行大小等一系列比较,当然字符串这里也需要比较啦!而字符串不能用 “>、<、>=、<=、==” 来比较,因此为了更好的比较字符串,C语言库中提供了 strcmp() 函数,实际上是:string compare ,字符比较之意思。
格式:strcmp(字符串1,字符串2);
功能:字符串比较函数,返回比较结果。它对于字符串1和字符串2中的字符从左到右逐个按其ASCⅡ值进行比较,直到字符值不相等或遇到字符串结束标志 ‘\0’ 时结束。如果两个字符相等,则函数返回整数0;如果两个字符值不相等,且字符串1的字符较大,则函数返回正整数;否则就返回负整数。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
char x[5] = "abc";
char y[5] ="def";
char z[5] = "ABC";
printf("strcmp(x,y):%d\n", strcmp(x, y));
printf("strcmp(x,z):%d\n", strcmp(x, z));
return 0;
}
运行结果:
5、字符串复制函数
字符串复制函数其实就是字符串赋值函数,因为字符串不能采用 “=” 来赋值,因此就利用 strcmp 函数来给字符串赋值。实际上是 string copy 之意。
格式:strcpy(字符数组1,字符串2);
功能:将字符串2包含 ‘\0’ 全部复制到字符数组1中去,字符串2可以是字符串常量或字符数组名,而字符数组1只能是字符数组名。此函数的返回值是字符数组1的首地址。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
char ch1[12]="qaz";
printf("ch1修改前:%s\n", ch1);
char ch2[10];
printf("ch2:");
gets_s(ch2, 10);
strcpy(ch1, ch2);
printf("ch1修改后:");
puts(ch1);
return 0;
}
运行结果:
总结:
(1)这里字符数组1应该有足够的长度,以便于能够存下字符串2或字符数组2。
6、字符串连接函数
strcat()字符串连接函数。是 string connect 之意。
格式:strcat(字符数组1,字符数组2);
功能:将字符串2连接到字符串1中的字符串后面,字符串2可以是字符串常量或字符数组名,而字符数组1只能是字符数组名。此函数返回的是字符数组1的首地址。
示例:
#include<stdio.h>
#include<string.h>
int main()
{
char ch1[12]="qaz";
printf("ch1连接前:%s\n", ch1);
char ch2[10];
printf("ch2:");
gets_s(ch2, 10);
strcat(ch1, ch2);//strcat()函数实现字符连接
printf("ch1连接后:");
puts(ch1);
return 0;
}
运行结果:
总结:
(1)字符数组1的长度要足够长,以至于能存下字符数组2;
(2)连接时字符数组1的字符串尾的 ‘\0’ 被覆盖,两个字符串连接为一个,在新的字符数组中只保留新字符串后面的 ‘\0’ 。
六、字符数组的使用
1、示例一:字符串字母大小写转换
本题要求编写程序,对一个以“#”结束的字符串,将其小写字母全部转换成大写字母,把大写字母全部转换成小写字母,其他字符不变输出。
输入格式:
输入为一个以“#”结束的字符串(不超过30个字符)。
输出格式:
在一行中输出大小写转换后的结果字符串。
代码如下:
#include<stdio.h>
#include<string.h>
int main(void)
{
char ch[30] = { 0 };
printf("请输入字符串:\n");
gets_s(ch, 30);
int len = strlen(ch);
ch[len - 1] = '\0';//这里是用来保证以 '#' 结尾的
for (int i = 0; i < len-1; i++)
{
if (ch[i] >= 'a' && ch[i] <= 'z')
{
ch[i] -= 32;
}
else if (ch[i] >= 'A' && ch[i] <= 'Z')
{
ch[i] += 32;
}
}
printf("转换后的字符串:\n");
puts(ch);
}
运行结果:
总结:
(1)这里的gets_s()函数是因为编译器环境的原因,在 vs2019 中无法使用 gets() 函数,故而 gets_s() 函数代替使用;
(2)大写字母转换成小写字母其ASCⅡ值加32;反之,小写字母转换成大写字母,其ASCⅡ值减32。'A’的ASCⅡ值为65,'a’的ASCⅡ值为97。
2、示例二:删除重复字符
本题要求编写程序,将给定字符串去掉重复的字符后,按照字符ASCII码顺序从小到大排序后输出。
输入格式:
输入是一个以回车结束的非空字符串(少于80个字符)。
输出格式:
输出去重排序后的结果字符串。
代码如下:
#include <stdio.h>
#include <string.h>
void paixu(char ch1[], int len1)
{
char temp = 0;
for (int i = 0; i < len1 - 1; i++)
{
for (int j = 0; j < len1 - i - 1; j++)
{
if (ch1[j] > ch1[j + 1])
{
temp = ch1[j];
ch1[j] = ch1[j + 1];
ch1[j + 1] = temp;
}
}
}
}
int main(void)
{
char ch[80] = { 0 };
char ch1[81] = { 0 };
printf("请输入字符串:\n");
gets_s(ch);
int len = strlen(ch);
for (int i = 0; i < len - 1; i++)
{
for (int j = i + 1; j < len; j++)
{
if (ch[i] == ch[j])
{
ch[j] = '#';//先做标记
}
}
}
for (int i = 0, j = 0; i < len; i++)
{
if (ch[i] != '#')
{
ch1[j] = ch[i];//这里进行删除
j++;
}
}
int len1 = strlen(ch1);
paixu(ch1, len1);//排序函数,对删除后的数组内部字符从小到大排序
printf("输出处理后的字符串:\n");
puts(ch1);
}
运行结果:
总结:
(1)这里删除相同字符是采用先标记,再删除。先比较,把相同的字符用一个符号标记起来,之后再对标记符号进行删除;
(2)strcmp() 函数是用来比较字符串的,在比较数组内部单个字符时候,不能用 strcmp() 函数,此时直接用 “>”,“<”,“=”等符号来比较;
3、示例三:字符串排序
用键盘输入学生的姓名,然后按照字典排序。
程序代码如下:
//字符串排序
#include <stdio.h>
#include <string.h>
#define N 5
#define M 15
void paixu(char name[N][M], char temp[])
{
for (int i = 0; i < N - 1; i++)
{
for (int j = 0; j < N - i - 1; j++)
{
if (strcmp(name[j], name[j + 1]) > 0)
{
strcpy(temp, name[j]);
strcpy(name[j], name[j + 1]);
strcpy(name[j + 1], temp);
}
}
}
printf("排序后的名字输出:\n");
for (int i = 0; i < N; i++)
{
puts(name[i]);
}
}
int main()
{
char name[N][M] = { 0 };
char temp[M] = { 0 };
printf("请输入需要排序的名字:\n");
for (int i = 0; i < N; i++)
{
gets_s(name[i]);
}
paixu(name, temp);//排序函数,对字符串进行排序
return 0;
}
运行结果:
总结:
(1)注意这里创建的中间变量 temp 必须是数组才能来通过 strcpy 交换;
这里是创建的二维数组,因此在使用 gets_s() 和 puts() 函数输入和输出时候,必须要利用循环带上行下标。
结语
foever 今天接着上一篇数组来继续给大家分享了字符数组,其中包含了字符数组的认识、字符串处理函数以及相关应用等等,希望能够给一些初学者带来一些帮助。如有不足之处或错误之处,还望大佬们评论批评指正哈!
谢谢观看哈!
再见啦!
以上代码均可运行,所用编译环境为 vs2019 ,运行时注意加上编译头文件#define _CRT_SECURE_NO_WARNINGS 1