五、数组、字符串以及冒泡排序--附代码案例

本文详细介绍了C语言中一维数组和二维数组的定义、初始化、操作,包括数组名的理解,一维数组的最值查找、逆置和冒泡排序,以及二维数组的定义、初始化和基本应用。同时涵盖了字符数组与字符串的区别、字符串输入输出函数和字符串追加的强化训练。
摘要由CSDN通过智能技术生成

定义:把具有相同类型若干变量有序形式组织起来称为数组。对于同类型若干变量的定义,变量需要一个个定义,而数组只是定义一次即可。
特点:数组就是在内存中连续的相同类型的变量空间。同一个数组所有的成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的。
请添加图片描述
分类:按数组元素类型的不同,数组可分为:数值数组、字符数组、指针数组等类别。
数组元素下标的个数也称为维数,根据维数的不同,可将数组分为一维数组、二维数组、三维数组等。通常情况下,将二维及以上的数组称为多维数组。

5.1 一维数组

5.1.1 一维数组的定义和使用

①数组名的定义跟变量名的定义相同;
②定义时[ ]为元素个数,使用时[ ]为元素下标,范围是[0-元素个数减1];
③数组定义:数据类型 数组名[常量表达式] = {值1,值2,值3…}。

int a[3]表示数组a有3个元素:
其下标从0开始计算,因此3个元素分别为a[0],a[1],a[2]。

#include <stdio.h>
int main(){
	//对于同类型若干变量的定义,变量需要一个个定义,而数组只是定义一次即可
	int a[10] = {10,9,8,7,6,5,4,3,2,1};
	printf("%d\n", a[1]);//打印第2个元素,不是第一个元素
	printf("%d\n", a[0]);//数组下标从0开始,到数组元素个数减1
	printf("%d\n", a[9]);//一条语句打印一次,效率极低
	for (int i = 0; i < 10; i++){
		printf("%d\t", a[i]);//只有下标是变量,可利用循环打印数组
	}
	a[1] = a[0];//数组也可以跟变量一样参与计算
	a[2] = a[1] * 2;
	for (int i = 0; i < 10; i++){
		printf("%d\t", a[i]);
	}
	return 0;
}

请添加图片描述

5.1.2 一维数组的初始化

在定义数组的同时进行赋值,称为初始化。初始部分元素,后面所有元素都默认设置为0。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
	//int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int a[ ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//不写元素个数,会自动根据写出来的元素个数确定数组长度
	for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++){//数组大小/元素大小=元素个数
		printf("a[%d]=%d\t",i,a[i]);
	}
	
	int b[10] = { 1, 2, 3, 4, 5};//初始化前5个元素,后面所有元素都默认设置为0
	for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++){
		printf("b[%d]=%d\t",i,b[i]);
	}

	int c[10];
	for (int i = 0; i < sizeof(c) / sizeof(c[0]); i++){
		printf("c[%d]=%d\t", i, c[i]);//乱码:没有初始化
	}

	int d[10];
	d[0] = 1;
	for (int i = 0; i < sizeof(d) / sizeof(d[0]); i++){
		printf("d[%d]=%d\t", i, d[i]);//乱码:除了第一个其他没有初始化
	}

	//int e[];//error:计算机不知道用户要存多少元素,不知道要开辟多大内存空间
	//int i = 10,e[i];//error:[]里不能是变量,一定是常量表达式
	int e[10];
	printf("plese input sizeof(e)/sizeof(e[0]) integers:");
	for (int i = 0; i < sizeof(e) / sizeof(e[0]); i++){
		scanf("%d",&e[i]);
	}
	for (int i = 0; i < sizeof(e) / sizeof(e[0]); i++){
		printf("e[%d]=%d\t", i, e[i]); 
	}

	int f[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	printf("f[-1]=%d\t", f[-1]); //数组下标越界,范围是0-9,属于运行时异常
	for (int i = 0; i < 20; i++){//数组下标越界,从10-19都是越界
		printf("f[%d]=%d\t", i, f[i]);//计算机会继续读取越界内存的数据,打印出乱码
	}
	return 0;
}

请添加图片描述

5.1.3 数组名

#include <stdio.h>
int main(){
	int b[10] = {1,2,3,4,5,6,7,8,9,10};
	//b = 100;//error,表达式必须是可修改的左值,出现与const常变量一样的错误,说明数组名是个常量
	printf("数组名b的地址:%p\n", b);//打印后发现跟b[0]的地址相同,说明数组名是个指向数组首地址的地址常量
for (int i = 0; i < 10; i++){
		printf("b[%d]的地址:%p\n", i,&b[i]);//根据优先级先求b[i],再求&
	}//打印出是一段连续的地址,数值相差4,说明数组在内存中存储是连续相同的空间
	printf("数组b[10]的大小:%d\n", sizeof(b));//数组大小 = 元素个数*数据类型的字节数
	printf("数组b[10]的元素大小:%d\n", sizeof(b[0]));
	printf("数组b[10]的元素个数:%d\n", sizeof(b) /sizeof(b[0]));
	for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++){
		printf("%d\t", b[i]);
	}
	return 0;
}

请添加图片描述

5.1.4 强化训练

5.1.4.1 一维数组的最值

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
	int a[10],max;-
	printf("plese input sizeof(a)/sizeof(a[0]) integers:");
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++){
		scanf("%d", &a[i]);
	}
	max = a[0]; //默认第一个数最大
	for (int i = 1; i < sizeof(a) / sizeof(a[0]); i++){
		if (max < a[i]){
			max = a[i];
		}
	}
	printf("max = %d\n", max);
	return 0;
}

请添加图片描述

5.1.4.2 一维数组的逆置

#include <stdio.h>
int main(){
	int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (int i = sizeof(a) / sizeof(a[0]) -1; i > -1; i--){
		printf("%d\t", a[i]);
	}
	putchar('\n');

	int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }, i = 0, j = sizeof(b) / sizeof(b[0]) -1;
	while(i<j){//i = j是自己跟自己交换,没意义
		int temp = b[i];//定义临时变量进行交换
		b[i] = b[j];
		b[j] = temp;
		i++;
		j--;
	}
	for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++){
		printf("%d\t", b[i]);
	}
	return 0;
}

在这里插入图片描述

5.1.4.3 冒泡法排序

将无序数据变为有序数据的过程。

#include <stdio.h>
int main() {
	int a[] = { 4,2,8,0,5,7,1,3,9 };
	int length = sizeof(a) / sizeof(a[0]);
	//从a[0]开始,对相邻元素进行比较大小(a[0]=4与a[1]=2)
	//if判断大小,把较大的元素放在后面(a[0]=2与a[1]=4)
	//继续比较a[1]与a[2],重复以上两步操作
	//直到比较到最后一个数a[length-1]
	//一共比较了length-1次
	//至此,最大值为a[length-1] = 9
	//最大值确定后放最后面,不用再来比较,不参与下次循环

	//第2次外循环,减少1个数据比较
	//直到比较到倒数第2个数a[length-1-1]
	//一共比较了length-1-1次
	//至此,最大值为a[length-1-1] = 8
	//第2大值确定,不用再来比较,不参与下次循环

	//第3次外循环,减少2个数据比较
	//直到比较到倒数第3个数a[length-1-2]
	//一共比较了length-1-2次
	//至此,最大值为a[length-1-2] = 7
	//第3大值确定,不用再来比较,不参与下次循环
	//.......

	//第i+1次外循环,减少i个数据比较(i和j从0开始)
	//直到比较到倒数第个数a[length-1-i]
	//一共比较了length-1-i次
	//嵌套循环中,外层控制行(需要比较几趟),内层控制列(同一趟需要比较的次数)
	for (int i = 0; i < length - 1; i++) {	//外层循环:n个数一轮循环确定1个最大值,需要循环n-1轮
		for (int j = 0; j < length - 1 - i; j++) {//第i+1轮比较n-1-i,i表示已执行次数
			if (a[j] > a[j + 1]) {
				int swap = a[j];
				a[j] = a[j + 1];
				a[j + 1] = swap;
			}
		}
	}
	for (int i = 0; i < length; i++) {
		printf("%d\t", a[i]);
	}
	return 0;
}

请添加图片描述

5.2 二维数组

一维数组的数组。

5.2.1 二维数组的定义、使用、初始化

定义:类型说明符 数组名[常量表达式1][常量表达式2]
其中常量表达式1表示行下标的长度,常量表达式2 表示列下标的长度,例如int a[3][4];其命名规则同一维数组,定义了一个3行4列的数组,数组名为a,其元素类型为整型,该数组的元素个数为3×4个,即:
请添加图片描述
二维数组a是按行进行存放的,先存放a[0]行,再存放a[1]行、a[2]行,并且每行有四个元素,也是依次存放的。
在内存中并不存在二维数组,二维数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组,即放完一行之后顺次放入第二行,和一维数组存放方式是一样的。

#include <stdio.h>
int main(){
	int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
	//int a[2][3] =
	//{
	//	{ 1, 2, 3 },
	//	{ 4, 5, 6 }
	//};
	//int a[2][3] ={1,2,3,4,5,6};//允许把二维数组写成一维数组形式
	//int a[2][] = {1,2,3,4,5,6 };//error,不允许缺少列大小,二维数组是按行存放的
	//多维数组只允许挨着数组名的下标可以省略常量表达式,其余不行
	//int c[][3] = {1,2,3,4,5,6};//允许只缺少行大小
	//int c[][3] = {1,2,3,4};//实质是{1,2,3,4,0,0}
	//int a[][3] = { 0 };//实质是{0,0,0},一行三列,对二维数组没什么意义
	a[1][2] = 7;//二维数组参与运算
	for (int i = 0; i < 2; i++){
		for (int j = 0; j < 3; j++){
			printf("%d  ", a[i][j]);
		}//双重循环打印二维数组
		printf("\n");
	}

	printf("二维数组的大小:%d\n",sizeof(a));//行*列*数据类型
	printf("二维数组的一行大小:%d\n",sizeof(a[0]));
	//二维数组看作是一维数组的数组,二维数组行数 = 二维数组大小 / 二维数组行大小
	printf("二维数组的行数:%d\n", sizeof(a) / sizeof(a[0]));
	//二维数组列数 = 二维数组行大小 / 一个元素大小
	printf("二维数组的列数:%d\n", sizeof(a[0]) / sizeof(a[0][0]));

	printf("二维数组首地址:%p\n", a);//a是不可修改的左值,是指向首地址的常量
	printf("二维数组首地址:%p\n", a[0]);//a[0]是不可修改的左值,是指向首地址的常量
	for (int i = 0; i < 2; i++){
		for (int j = 0; j < 3; j++){
			printf("%p  ", &a[i][j]); //a[0][0]是元素值,所以加&
		}
		printf("\n");
	}
	return 0;
}

5.2.2 强化训练

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
	int a[5][3];//存储5名学生3门成绩,也可以写成a[3][5]
	printf("please input 5*3 results:");
	for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++){
		for (int j = 0; j < sizeof(a[0]) / sizeof(a[0][0]); j++){
			scanf("%d", &a[i][j]);
		}
	}
	
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++){
		int sum = 0;
		for(int j = 0; j < sizeof(a[0]) / sizeof(a[0][0]); j++){
			sum +=a[i][j];
		}
		printf("学生%d总成绩=%d,平均成绩=%f\n",i+1,sum,sum/3.0);
	}

	for (int i = 0; i < sizeof(a[0]) / sizeof(a[0][0]); i++){
		int sum = 0;
		for (int j = 0; j < sizeof(a) / sizeof(a[0]); j++){
			sum += a[j][i];
		}
		printf("科目%d总成绩=%d,平均成绩=%f\n", i + 1, sum, sum /5.0);
	}
	return 0;
}

请添加图片描述

5.3 多维数组(了解)

int a[3][4][5]定义了一个3层4行5列的三维数组,数组的名字是a,看作是二维数组的数组。N维数组的初始化需要N个{ }。

5.4 字符数组与字符串

5.4.1 字符数组与字符串区别

C语言中没有字符串这种数据类型,可以通过char的数组来替代;
字符串是一个字符数组,但字符数组未必是字符串;数字0(和字符’\0’等价)结尾的字符数组就是一个字符串,但如果字符数组没有以数字0结尾,就不是一个字符串,只是一个普通字符数组,所以字符串是特殊的字符数组。

#include <stdio.h>
int main(){
	char a[5] = { 'C', 'h', 'i', 'n', 'a' };//没有结束标志位
	for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)//这里不能写成i<strlen(a),因为strlen是求指定指定字符串s的长度,因为没有\0(改成a[6]就OK),所以不是字符串, strlen(a)≠5
		printf("%c", a[i]);
	//printf("%s\n", a);以字符串方式打印,没有遇到结束标志位会乱码,改为a[6]就不会

	char b[] = "China";//等价char b[6] = {"China"}和char b[6] = {'C','h','i','n','a'}和char b[6] = {'C','h','i','n','a','\0'}和char *b = "China"
	for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++)
		printf("%c", b[i]);
	return 0;
}

5.4.2 字符串的初始化

#include <stdio.h>
int main(){
	//不指定长度,有结束标志位
	char a[] = { 114, 105, 103, 104, 116, 0, 97};//转化成ASCII码,直到打印至0为止
	printf("a[] = %s\n", a);//是字符串

	//不指定长度,无结束标志位
	char b[] = { 101, 114, 114, 110, 114};//没有找到结束标志位
	printf("b[] = %s\n", b);//不是字符串,乱码,只能用%c打印

	//指定长度大于字符数(不包括0),定义后直接初始化
	char c[4] = { 'a', 'b', 'c' };//自动补0
	printf("c[4] = %s\n", c);//是字符串

	//指定长度等于字符数(不包括0),定义后直接初始化
	char d[3] = { 'a', 'b', 'c' };//没有找到结束标志位
	printf("d[3] = %s\n", d);//不是字符串,乱码

	//char e[2] = { 'a', 'b', 'c' };//指定长度小于字符数(不包括0):直接报错

	char f[4];//只是定义
	f[0] = 'a', f[1] = 'b', f[2] = 'c';//后面赋值,不赋值f[3],没有结束标志位
	printf("f[4] = %s\n", f);//f[3]乱码,不是字符串,跟c[4]不同

	char g[10] = { 0 };//所有元素赋值为0
	printf("g[10] = %s\n", g);//直接结束标志位,无输出

	//'\0'后面最好不要连着数字,有可能几个数字连起来刚好是一个转义字符
	//'\ddd'八进制字义字符,'\xdd'十六进制转移字符,所以\012相当于\n
	char str[] = "\012abc";
	printf("str == %s\n", str);
	return 0;
}

请添加图片描述

5.4.3 字符串的输入输出

5.4.3.1 scanf()

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
	char e[10];
	printf("please input string:");
	scanf("%s", e);//因为e本身就是地址常量,所以不加&
	//scanf("%9s", e);//限定输入长度为9,即使后面输入字符多了,忽略输入过多的字符
	printf("%s\n", e);//如果输入超过length - 1个字符就会报错(如果没有格式限定),因为最后一个字符是结束标志位被占用
	return 0;
}

5.4.3.2 gets()

函数char *gets(char *s);
头文件#include <stdio.h>
参数s:字符串首地址
功能从标准输入设备读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止
返回值成功:读入的字符串
失败:NULL

gets(str)与scanf(“%s”,str)的区别:
①gets(str)允许输入的字符串含有空格
②scanf(“%s”,str)不允许含有空格
注意:由于scanf()和gets()无法知道字符串s大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出)的情况。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(){
	char ch[100];//常量给大一点,防止字符数组越界(缓冲区溢出)
	printf("pleae input string by gets(ch):");
	gets(ch);//gets()允许输入的字符串含有空格
	printf("%s\n", ch);

	printf("scnaf的正则表达式输入:");
	scanf("%[^\n]", ch);//接收非'\n'的所有内容(可以接收空格)
	printf("%s\n", ch);

	printf("pleae input string by scnaf:");
	scanf("%s", ch);//scanf(“%s”,str)不允许含有空格
	printf("%s\n", ch);
	return 0;
}

请添加图片描述

5.4.3.3 fgets()

函数char *fgets(char *fgets(char *s, int size, FILE *stream)
头文件#include <stdio.h>
参数s:字符串首地址
size:指定最大读取字符串的长度(size - 1)
stream:文件指针,如果读键盘输入的字符串,固定写为stdin
功能从stream指定文件内读入字符,保存到s所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 ‘\0’ 作为字符串结束
返回值成功:读入的字符串
失败:读到文件尾或出错,NULL

fgets()读取通过键盘输入的字符串时,同时把输入的回车也做为字符串的一部分。通过scanf和gets输入一个字符串的时候,不包含结尾的“\n”,但通过fgets结尾多了“\n”。fgets()函数是安全的,不存在缓冲区溢出的问题。

#include <stdio.h>
int main(){
	char ch[10];
	printf("pleae input string by fgets(ch):");
	//如果输入字符长度大于sizeof(ch),也不会报错,只取前几个字符
	//fgets获取的字符串少于元素个数size-1会有\n,大于等于就没有
	fgets(ch, sizeof(ch), stdin);//此函数将'\n'也算入ch内
	printf("%s", ch);//用户输入的内容+'\0'+'\n',所以代码不用'\n'
	return 0;
}

输入字符数小于size-1,换行
在这里插入图片描述
输入字符数大于等于size-1,不换行
请添加图片描述

5.4.3.4 puts()

函数int puts(const char *s)
头文件#include <stdio.h>
参数s:字符串首地址
功能标准输出设备输出s字符串,遇到’\0’自动停止,输出完成后自动输出一个’\n’
返回值成功:非负数
失败:-1
#include <stdio.h>
int main(){
	char ch[] = "hello world\0wwwww";//遇到0停止
	puts(ch);//自带换行,等价printf("%s\n", ch)
	return 0;
}

5.4.3.5 fputs()

函数int fputs(const char * str, FILE * stream)
头文件#include <stdio.h>
参数str:字符串
stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
功能将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0’不写入文件。
返回值成功:0
失败:-1

fputs()是puts()的文件操作版本,但fputs()不会自动输出一个’\n’

#include <stdio.h>
int main(){
	char ch[] = "hello world";
	fputs(ch, stdout); //不带换行,等价printf("%s", ch)
	return 0;
}

5.4.3.6 strlen()

函数size_t strlen(const char *s);
头文件#include <string.h>
参数s:字符串首地址
功能计算指定指定字符串s的长度,不包含字符串结束符‘\0’
返回值成功:字符串s的长度。
#include <stdio.h>
#include <string.h>
int main(){
	char ch1[100] = "hello world";
	printf("数组大小:%d\n", sizeof(ch1));//sizeof()求的是数组长度,100
	printf("不包括结束符的字符串长度:%d\n", strlen(ch1));//11

	//自定义实现
	char ch2[] = "hello world";
	int len = 0;
	while(ch2[len])
		len++;
	//不能只写while (ch2[len++] != '\0');举例:假如第一个字符是'\0',又自增一次
	printf("不包括结束符的字符串长度:%d\n", len);//11
	return 0;
}

5.4.4 强化训练:字符串追加

#include <stdio.h>
int main(){
	char ch1[] = "hello",char ch2[] = "world";
	char ch3[20];//长度一定要比预算的多,否则会报错
	int i = 0, j = 0;//涉及到i会在另一个循环内使用,因此不能定义在某个循环里面变成局部变量
	while (ch1[i] != '\0'){
		ch3[i] = ch1[i];
		i++;
	}
	//printf("i = %d\n", i);//最后一次循环,得到i=5
	//printf("%s\n", ch3);//此时打印会乱码,因为没有以'\0'结束。因为定义后没有初始化,然后后面进行部分赋值,所以会出现乱码,跟指定长度并局部初始化,自动补0不同
	while (ch2[j] != '\0'){
		ch3[i + j] = ch2[j];//从ch[5]开始打印
		j++;
	}
	ch3[i + j] = '\0';
	printf("%s\n", ch3);

	//用for语句写字符串的拼接
	char ch4[] = "hello";
	char ch5[] = "world";
	char ch6[20];//长度一定要比预算的多,否则会报错
	int m = 0, n = 0;//涉及到m会在另一个循环内使用,因此不能定义在某个循环里面变成局部变量
	for( ; m < sizeof(ch4) / sizeof(ch4[0]); m++){
		if (ch4[m] != '\0'){
			ch6[m] = ch4[m];
		}
		//printf("m = %d\n", m);//最后一次循环,得到m=5
	}
	//printf("m = %d\n", m);//由于m++后m=6,不满足6<6,不参与循环并输出i
	//printf("%s\n", ch6);//此时打印会乱码,因为没有以'\0'结束
	for( ; n < sizeof(ch5) / sizeof(ch5[0]); n++){
		if (ch5[n] != '\0'){
			ch6[m-1+n] = ch5[n];//由前面可得m=6,画图可得m需要-1,令ch[5]开始打印
		}
	}
	ch6[m - 1 + n -1] = '\0';//由前面可得n-1,画图可得n需要-1,令ch[10]打印0
	printf("%s\n", ch6);
	return 0;
}

请添加图片描述

码字不易,如果大家觉得有用,请高抬贵手给一个赞让文章上推荐让更多的人看到吧,也可以评论提出意见让后面的文章内容越来越生动丰富。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值