Loading... # 四、一维数组与字符数组 ## 1.数组的定义 我们可以借助C语言提供的数组, **通过一个符号来访问多个元素。** 数据的特点如下: (1) 具有相同的数据类型。 (2) 使用过程中需要保留原始数据。 C 语言为了方便操作这些数据, 提供了一种构造数据类型——数组。所谓数组, 是指一组具有相同数据类型的数据的有序集合。 一维数组的定义格式为 ```c 类型说明符 数组名 [常量表达式]; int a[10];//定义一个数组,数组名a,他有10个元素 ``` 声明数组时要遵循以下规则: **(1) 数组名的命名规则和变量名的相同, 即遵循标识符命名规则。** **(2) 在定义数组时, 需要指定数组中元素的个数, 方括号中的==常量==表达式用来表示元素的个数, 即数组长度。** **(3) 常量表达式中可以包含常量和符号常量, 但不能包含变量. 也就是说, C语言不允许对数组的大小做动态定义, 即数组的大小不依赖于程序运行过程中变量的值.** 错误例子 ```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 开始编号  数组的初始化方法。 1. 在定义数组时对数组元素赋初值。 ```c 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} ``` 2. 可以只给一部分元素赋值。 ```c int a[10]={0,1,2,3,4}; ``` 这表示只给前 5 个元素赋初值,后 5 个元素的值为0。 3. 要使一个数组中全部元素的值为0,那么可以写为 ```c int a[10]={0,0,0,0,0,0,0,0,0,0}; ``` 或 ```c int a[10]={0}; ``` 4. 在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组的长度。 ```c int a[]={1,2,3,4,5}; ``` ## 3.数组的访问越界 ```c #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; } ``` ```c 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.数组的传递 ```c #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.字符串数组初始化及传递 定义方式类似 ```c char c[10]; ``` 字符数组的初始化 1. 对每个字符单独赋值进行初始化 ```c 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'; ``` 2. 对整个数组进行初始化 ```c char c[10]={'I','a','m','h','a','p','p','y'} ``` 3. 通常采用的初始化方式 ```c char c[10]= "hello"; ``` C 语言规定字符串的结束标志为'\0',而系统会对字符串常量自动加一个'\0',为了保证处理方法一致,一般会人为地在字符数组中添加'\0',所以字符数组存储的字符串长度必须比字符数组少 1 字节。例如,**char c[10]最长存储 9 个字符,剩余的 1个字符用来存储'\0'。** 【例】字符数组初始化及传递 ```c #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 ```c hello����U how how ``` ## 6. scanf函数读取字符串 【例】scanf读取字符串 ```c #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类似,==遇到空格就停了,读不到空格==)。结果如下 ```c hello hello are you c=are,d=you ``` ## 7. gets()函数与puts() gets 函数类似于 scanf 函数,用于读取标准输入。**scanf 函数在读取字符串时遇到空格就认为读取结束,所以当输入的字符串存在空格时,我们需要使用 gets 函数进行读取。** gets函数的格式如下: ```c 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中。 ```c char *fgets(char *str, int size, FILE *stream); ``` puts 函数类似于 printf 函数,用于输出标准输出。puts函数的格式如下: ```c int puts(char *str); ``` 函数 puts 把 str(字符串)写入 STDOU(标准输出)。puts 会将数组 c 中存储的"how are you"字符串打印到屏幕上,同时打印换行,相对于 printf 函数,**puts 只能用于输出字符串,同时多打印一个换行符**,等价于printf(“%s\n”,c)。 ```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; } ``` ```c how are you how are you ``` ## 8. str 系列字符串操作函数 (初试没那么重要,对于机试更重要一些) str 系列字符串操作函数主要包括 strlen、strcpy、strcmp、strcat 等。strlen 函数用于统计字符串长度,strcpy 函数用于将某个字符串复制到字符数组中,strcmp 函数用于比较两个字符串的大小,strcat 函数用于将两个字符串连接到一起。各个函数的具体格式如下所示 ```c #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 系列字符串操作函数的使用 ```c #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”)。 ```c 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 ```c #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; } ``` End 本文作者: 彼岸花露 文章标题:四、一维数组与字符数组(C语言) 本文地址:https://www.tianlei.work/archives/111/ 版权说明:若无注明,本文皆悟思记原创,转载请保留文章出处。 最后修改:2024 年 04 月 14 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏
此处评论已关闭