四、一维数组与字符数组
四、一维数组与字符数组
1.数组的定义
我们可以借助C语言提供的数组, 通过一个符号来访问多个元素。
数据的特点如下:
(1) 具有相同的数据类型。
(2) 使用过程中需要保留原始数据。
C 语言为了方便操作这些数据, 提供了一种构造数据类型——数组。所谓数组, 是指一组具有相同数据类型的数据的有序集合。
一维数组的定义格式为
类型说明符 数组名 [常量表达式];
int a[10];//定义一个数组,数组名a,他有10个元素
声明数组时要遵循以下规则:
(1) 数组名的命名规则和变量名的相同, 即遵循标识符命名规则。
(2) 在定义数组时, 需要指定数组中元素的个数, 方括号中的常量表达式用来表示元素的个数, 即数组长度。
(3) 常量表达式中可以包含常量和符号常量, 但不能包含变量. 也就是说, C语言不允许对数组的大小做动态定义, 即数组的大小不依赖于程序运行过程中变量的值.
错误例子
① float a[0]; /* 数组大小为0没有意义 */
② int b(2)(3); /* 不能使用圆括号 */
③ int k=3,a[k]; /* 不能用变量说明数组大小*/
2.一维数组在内存中的储存
语句 int mark[100];定义的一维数组 mark ,每个元素都是整型元素,占用 4 字节,数组元素的引用方式是“数组名[下标]”,所以访问数组 mark 中的元素的方式是**mark[0],mark[1],…,mark[99]**。没有元素 mark[100],数组元素是从 0 开始编号
数组的初始化方法。
在定义数组时对数组元素赋初值。
int a[10]={0,1,2,3,4,5,6,7,8,9};//正确 //以下错误写法 int a[10];a[10]={0,1,2,3,4,5,6,7,8,9}
可以只给一部分元素赋值。
int a[10]={0,1,2,3,4};
这表示只给前 5 个元素赋初值,后 5 个元素的值为0。
要使一个数组中全部元素的值为0,那么可以写为
int a[10]={0,0,0,0,0,0,0,0,0,0};
或
int a[10]={0};
在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度。
int a[]={1,2,3,4,5};
3.数组的访问越界
#include <stdio.h>
int main() {
int a[5]={1,2,3,4,5};
int j=20;
int i=10;
a[5]=6;//访问越界
a[6]=7;
printf("i=%d\n",i);//i我们并没有赋值,但是值却变化了
return 0;
}
i is 7
这就是访问越界的危险性——未对变量i 赋值,其值却发生了改变
操作系统对内存中的每个位置也给予一个编号, Windows 32 位控制台应用程序,编号的范围从 0x00 00 00 00 到 0xFF FF FF FF,总计为 2 的 32 次方,大小为4G。这些编号称为地址(64 位程序,地址显示的是64位)。
在变量窗口中输入sizeof(a),可以看到数组a的大小为20字节,其实就是sizeof(int)*5:数组中有 5 个整型元素,每个元素的大小为 4 字节,共有 20 字节。访问元素的顺序是依次从a[0]到 a[4],a[5]=6、a[6]=7 均为访问越界。
编译器并不检查程序对数组下标的引用是否在数组的合法范围内。好处是不需要浪费时间对有些已知正确的数组下标进行检查,坏处是这样做将无法检测出无效的下标引用。
4.数组的传递
#include <stdio.h>
//子函数把某一个常用功能,封装起来的作用
//数组名传递到子函数后,子函数的形参接收到是数组的起始地址,因此不能把数组的长度传递给子函数
void print(int b[],int length)
{
int i;
for(i=0;i<length;i++)
{
printf("%3d",b[i]);
}
b[3]=20;
printf("\n");
}
//main函数就是主函数
int main() {
int a[5]={1,2,3,4,5};
print(a,5);//数组在传递给子函数时,它的长度传递不过去
printf("a[3]=%d\n",a[3]);
return 0;
}
一维数组在传递时,其长度是传递不过去的,所以我们通过 len来传递数组中的元素个数,实际数组名中存储的是数组的首地址,在调用函数传递时,是将数组的首地址给了变量 b(其实变量 b 是指针类型,在 b[]的方括号中填写任何数字都是没有意义的。这时我们在 print 函数内修改元素 b[4]=20,可以看到数组 b 的起始地址和 main 函数中数组 a 的起始地址相同,即二者在内存中位于同一位置,当函数执行结束时,数组a 中的元素a[4]就得到了修改
5.字符串数组初始化及传递
定义方式类似
char c[10];
字符数组的初始化
对每个字符单独赋值进行初始化
c[0]='I';c[1]='';c[2]='a';c[3]='m';c[4]='';c[5]='h';c[6]='a';c[7]='p';c[8]='p';c[9]='y';
对整个数组进行初始化
char c[10]={'I','a','m','h','a','p','p','y'}
通常采用的初始化方式
char c[10]= "hello";
C 语言规定字符串的结束标志为’\0’,而系统会对字符串常量自动加一个’\0’,为了保证处理方法一致,一般会人为地在字符数组中添加’\0’,所以字符数组存储的字符串长度必须比字符数组少 1 字节。例如,char c[10]最长存储 9 个字符,剩余的 1个字符用来存储’\0’。
【例】字符数组初始化及传递
#include <stdio.h>
//模拟printf("%s",c)操作
void print(char d[])
{
int i=0;
while(d[i])//当走到结束符时,循环结束
{
printf("%c",d[i]);
i++;
}
printf("\n");
}
//如何初始化字符数组,字符串如何输出
//输出字符串乱码时,要去查看字符数组中是否存储了结束符'\0'
int main() {
char c[5]= "hello";//使用这种方式初始化字符数组 //会发现打印出来乱码
char d[5]="how";
printf("%s\n",c);//使用%s来输出一个字符串,直接把字符数组名放到printf后面位置
printf("%s\n",d)
print(d);
return 0;
}
执行结果如下。对数组赋值”hello”却打印出乱码,是因为printf 通过%s 打印字符串时,原理是依次输出每个字符,当读到结束符’\0’时,结束打印;(要正常输出改为char c[6]= “hello”)。我们通过 print 函数模拟实现 printf 的%s 打印效果,当 c[i]为’\0’时,其值是 0,循环结束,也可以写为c[i]!=’\0
hello����U
how
how
6. scanf函数读取字符串
【例】scanf读取字符串
#include <stdio.h>
//scanf读取字符串操作,会自动往字符数组中放结束符
int main() {
char c[10];
char d[10];
scanf("%s",c);//字符数组名c中存储了数组的起始地址,因此不需要取地址
printf("%s\n",c);
scanf("%s%s",c,d);
printf("c=%s,d=%s\n",c,d);
return 0;
}
scanf 通过%s 读取字符串,对 c 和 d 分别输入”are”和”you”(中间加一个空格),scanf在使用%s 读取字符串时,会忽略空格和回车(这一点与%d和%f类似,遇到空格就停了,读不到空格)。结果如下
hello
hello
are you
c=are,d=you
7. gets()函数与puts()
gets 函数类似于 scanf 函数,用于读取标准输入。scanf 函数在读取字符串时遇到空格就认为读取结束,所以当输入的字符串存在空格时,我们需要使用 gets 函数进行读取。
gets函数的格式如下:
char *gets(char *str);
gets 函数从 STDIN(标准输入)读取字符并把它们加载到 str(字符串)中,直到遇到换行符(\n)。如下例所示,执行后,我们输入”how are you”,共 11 个字符,gets 会读取空格,同时可并未给数组进行初始化赋值,但是最后有’\0’,这是因为 gets 遇到\n 后,不会存储\n,而是将其翻译为空字符’\0’。
换成更通用的fgets,fgets()函数的第2个参数指明了读入字符的最大数量。如果该参数的值是n,那么fgets()函数从输入流中读取至多n - 1个字符(因为会自动加\0),或者遇到换行符(’\n’)为止。fgets()函数的第3个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中。
char *fgets(char *str, int size, FILE *stream);
puts 函数类似于 printf 函数,用于输出标准输出。puts函数的格式如下:
int puts(char *str);
函数 puts 把 str(字符串)写入 STDOU(标准输出)。puts 会将数组 c 中存储的”how are you”字符串打印到屏幕上,同时打印换行,相对于 printf 函数,puts 只能用于输出字符串,同时多打印一个换行符,等价于printf(“%s\n”,c)。
#include <stdio.h>
int main() {
char c[20];
//gets(c);//gets中放入我们字符数组的数组名即可
fgets(c,20,stdin);
puts(c);//puts等价于printf("%s\n",c); puts内放的参数是字符数组名
return 0;
}
how are you
how are you
8. str 系列字符串操作函数
(初试没那么重要,对于机试更重要一些)
str 系列字符串操作函数主要包括 strlen、strcpy、strcmp、strcat 等。strlen 函数用于统计字符串长度,strcpy 函数用于将某个字符串复制到字符数组中,strcmp 函数用于比较两个字符串的大小,strcat 函数用于将两个字符串连接到一起。各个函数的具体格式如下所示
#include <string.h>
size_t strlen(char *str);
char *strcpy(char *to, const char *from);
int strcmp(const char *str1, const char *str2);
char *strcat(char *str1, constchar*str2);
传参类型char*,直接放入字符数组的数组名即可
【例】str 系列字符串操作函数的使用
#include <stdio.h>
#include <string.h>
int mystrlen(char c[])
{
int i=0;
while(c[i])//找到结束符后,循环结束,从而得出字符串长度
{
i++;
}
return i;
}
int main() {
int len;
char c[20];
char d[100]="world";
char e[100];
gets(c);
puts(c);
len=strlen(c);//统计字符串的长度
printf("len=%d\n",len);
len= mystrlen(c);
printf("my len=%d\n",len);
strcat(c,d);//把d中的字符串拼接到c中
puts(c);
strcpy(e,c);//把c中的字符串复制到e中
puts(e);
//c大于“how",返回值是正值,相等是0,c小于”how",返回负值
printf("c?d=%d\n",strcmp(c,"how"));
return 0;
}
我们输入”hello”后的执行结果,通过 strlen 函数计算的字符串长度为 5,我们自己写的函数就是 strlen 函数的计算原理,即通过判断结束符来确定字符串的长度。strcpy 函数用来将字符串中的字符逐个地赋值给目标字符数组。例中我们将 c 复制给 e,就是将 c 中的每个字符依次赋值给 d,也会将结束符赋值给 d。注意,目标数组一定要大于字符串大小,即 sizeof(e)>strlen(c),否则会造成访问越界。
strcmp 函数用来比较两个字符串的大小,。如果 c 中的字符串大于 d,那么返回值为正值;如果 c 中的字符串小于 d,那么返回值为负值。如何比较两个字符串:从头开始,比较相同位置字符的 ASCII码值,若发现不相等则直接返回,否则接着往后比较。例如,strcmp(“hello”,”how”)的返回值是负值,即”hello”小于”how”,因为第一个字符 h 相等,接着比较第二个位置的字符,e 的 ASCII 码值小于 o 的,
strcat 函数用来将一个字符串接到另外一个字符串的末尾。例中字符数组 c 中存储的是”hello”,我们将 d 中的”world”与 c 拼接,最终结果为”helloworld”。注意,目标数组必须大于拼接后的字符串大小,sizeof(c)>strlen(“helloworld”)。
hello
hello
len=5
my len=5
helloworld
helloworld
c?d=-10
9. 例题
1、如果字符串没有结束符’\0’,也可以使用 strlen 正确统计长度
A 正确 B 错误
答案:B解释:strlen 是通过结束符’\0’来判断字符串长度的,如果没有结束符,无法统计字符串长度
2、读取一个字符串,字符串可能含有空格,将字符串逆转,原来的字符串与逆转后字符串相同,输出0,原字符串小于逆转后字符串输出-1,大于逆转后字符串输出1。例如输入 hello,逆转后的字符串为 olleh,因为hello 小于 olleh,所以输出-1
#include <stdio.h>
#include <string.h>
//字符串翻转,翻转后比较与原字符串是否相等
//使用增量编写法
int main() {
char c[100];//原字符串
char d[100]={0};//翻转后的,初始化的目的是为了d有结束符
gets(c);
int i,j;
for(i=0,j= strlen(c)-1;i< strlen(c);i++,j--)
{
d[j]=c[i];
}
// puts(d);
int result=strcmp(c,d);
if(result>0)
{
printf("%d\n",1);
} else if(result<0)
{
printf("%d\n",-1);
} else{
printf("%d\n",0);
}
return 0;
}