c语言-----数组学习

一、一维数组定义

数组是包含给定类型的一组数据,并将这些数据依次存储在连续的内存空间中。,数组可以是多种类型,如 int, char,等,但是数组不可以是void型。

一般数组的定义:类型说明 数组名[常量表达式]

int a[10];

在上述定义中,int就是数组的类型,a是数组的名称,10则是代表数组的长度。我们在定义时一般常量表达式为正整数。而且在数组中的每个元素都有一个序号,这个序号从 0 开始,而不是从我们熟悉的 1 开始,称为下标(Index)。使用数组元素时,指明下标即可,例如:在上述定义的数组中,只有a[9],并没有a[10]。数组名是首元素的地址。

int a[10];              //  整型 数组 a,有 10 个元素。
float b[10]; //  单精度浮点型 数组 b,有 10 个元素。
char c[20];         // 字符数组 ch,有 20 个元素 。

数组有三个特性:单一性有序性连续性。单一性是指在你定义的数组中,每个数组的字节长度是一致的。有序性是指每个数组元素都有顺序,例如a[0]在a[1]之前,a[1]在a[2]之前。连续性:是指当你定义了一个数组之后,数组在计算机内部的存储是连续的,中间并没有存放其他的数据。

注意事项:

在数组当中,例如:arr[10] 中只能用 arr[0]、arr[1]、arr[2]···arr[8]、arr[9],而不能使用 arr[10]。若你定义 arr[10] = 123,会导致出现下标越界的情况。但是此时编译器并不会出现报错的情况,因为编译器只会检查类型是否匹配,这就会导致计算机改写其他内存而造成重大错误。

数组不可以整体引用或赋值。在使用“=”进行赋值时,“=”左右两边的数据类型不匹配,此时会编译器报错,而且数组的数组名是作为数组首元素的地址,在计算机中是一个常量存在,不可更改。

错误示范:

 int a[];
a = 10;

数组的初始化:

int arr[5] = {1,2,3,4,5};   // 完全初始化
int arr1[5] = {1,2,3};        // 不完全初始化,不完全初始化,剩下没有初始化的元素 默认为0

在定义中[ ]并不是下标,只是说明其是数组类型。

二、一维数组的基本使用

1.统计数组中某类元素个数

下面以某一维数组中能被3整除的变量统计做演示:

int a[] = {0,1,2,3,4,5,6,7,8,9};
	int i, counter = 0;;
	int len = sizeof(a) / sizeof(a[0]);

	for(i = 0;i < len;++i)
	{
		if(a[i] % 3 == 0)
		{
			++counter;
		//	printf("%d\n", a[i]);
		}
	}
	printf("%d\n", counter);

2、对数组求和

	int a[] = {1,2,3,4,5,6,7,8,9,0}, i;
	int sum = 0;
	int len = sizeof(a) / sizeof(a[0]);
	
	for(i = 0;i < len;++i)
	{
		sum += a[i];
	}

	printf("%d\n", sum);
	

3、寻找数组中的最大值(拓展次大值)

	int a[] = {10,2,3,4,5,6,7,8,9,0};
	int max, i;
	int len = sizeof(a) / sizeof(a[0]);
	max = a[0];

	for(i = 1;i < len;++i)
	{
		if(max < a[i])
		{
			max = a[i];
		}
	}
	
	printf("%d\n", max);

	int second = INT_MIN;
	for(i = 0;i < len;++i)
	{
		if(second < a[i] &&  a[i] < max)
		{
			second = a[i];
		}
	}

	printf("%d\n", second);

注意:并不是每个数组中都存在最大值与次大值。

4、输出斐波那契数列

int a[10] = {1,1};
	int len = sizeof(a) / sizeof(a[0]);
	int i;

	for(i = 2;i < len;++i)
	{
		a[i] = a[i - 1] + a[i - 2];
	}

	for(i = 0;i < len;++i)
	{
		printf("%d\n", a[i]);
	}

在进行代码的测试时需要注意的是:在定义时,数组为整型,所以输出的数列长度不宜过大,否则会引起整型溢出。

5、逆序

	int a[] = {1,2,3,4,5,6,7,8,9,0};
	int len = sizeof(a) / sizeof(a[0]);
	int i;

	for(i = 0;i < len / 2;++i)
	{
		int t;
		t = a[i];
		a[i] = a[len - i - 1];
		a[len - i - 1] = t;
	}

	for(i = 0;i < len;++i)
	{
		printf("%d\n", a[i]);
	}

三、一维数组中的基本算法

1、选择排序(默认是升序)

	int a[] = {1,2,3,4,5,6,7,8,9,0};
	int len = sizeof(a) / sizeof(a[0]);
	int i, j;

	for(i = 0;i < len - 1;++i)
	{
		for(j = i + 1;j < len;++j)
		{
			if(a[i] > a[j])
			{
				int t;
				t = a[i];
				a[i] = a[j];
				a[j] = t;
			}
		}
	}

2、冒泡排序

冒泡排序:数组中相邻的两个元素两两比较,小的在前,大的在后。

	int a[] = {1,2,3,4,5,6,7,8,9,0};
	int len = sizeof(a) / sizeof(a[0]);
	int i, j;

	for(j = len - 1;j > 0;--j)
	{
		for(i = 0;i < j;++i)
		{
			if(a[i] > a[i + 1])
			{
				int t;
				t = a[i];
				a[i] = a[i + 1];
				a[i + 1] = t;
			}
		}
	}

3、插入法排序

	int a[] = {1,-2,3,-4,5,-6,7,-8,9,0};
	int len = sizeof(a) / sizeof(a[0]);
	int i, j;


	for(i = 1;i < len;++i)
	{
		int t = a[i];
		j = i;
		while(j > 0 && a[j - 1] > t)
		{
			a[j] = a[j - 1];
			--j;
		}
		a[j] = t;
	}

4、二分法查找

int a[] = {1,-2,3,-4,5,-6,7,-8,9,0};
	int len = sizeof(a) / sizeof(a[0]);
	int i, j;

	for(i = 1;i < len;++i)
	{
		int t = a[i];
		j = i;
		while(j > 0 && a[j - 1] > t)
		{
			a[j] = a[j - 1];
			--j;
		}
		a[j] = t;
	}

	int n = -8;
	int begin = 0;
	int end = len - 1;
	int mid;
	while(begin <= end)
	{
		mid = (begin + end) / 2;
		if(a[mid] > n)
		{
			end = mid - 1;
		}
		else if(a[mid] < n)
		{
			begin = mid + 1;
		}
		else
		{
			break;
		}
	}
	if(begin <= end)
	{

		printf("found! index = %d\n", mid);
	}
	else
	{
		printf("not found!\n");
	}

二分法查找要建立在排序的基础之上,而且二分法找到的位置是有序序列位置

四、字符型数组

一、定义

定义一个字符型数组:一维数组:char 函数名[行 常量表达式] 例如:char c[3];

char c[3] = {a,b}

 初始化过程中,没有被初始化的数组项编译器会给默认赋值’\0’;(整数数组没有被初始化的数组项被赋值为0)。例如:char c[3] = {a,b}; 中 c[3] = ‘\0’;

对于字符数组来说,要求常量表达式大于或等于数组内字符数;而对于字符串数组来说,常量表达式只能大于 字符串数组字符数,因为编译器会为每个字符串末尾赋值一个字符串结束标志‘\0’,所以在对字符串数组初始化时,我们必须预先为结束标志腾出一个字符空间。所以我们在定义字符数组时给的长度要足够大(>len),否则很容易出现越界访问的情况。

二、几个常用的函数

strlen(对象)函数

注意:这个函数并不像sizeof一样可以测量其他类型数据,只能用来测量字符串数组;

功能:用来测量字符串数组的有效长度(这里的长度指的是字节数);

char s1[100] = "Hello";
strlen(S1);
	while(s[i] != '\0')
	{
		++i;
	}

	printf("%d\n", i);

 

程序运行的输出结果为5,而sizeof函数运行结果为6。

puts(对象)函数 

puts函数是对某一字符串数组进行遍历输出。

char c[] = "hello";
puts(c);
	char s[10] = "Hello";
	int i = 0;

	while(s[i])
	{
		putchar(s[i++]);
	}
	putchar('\n');
	return 0;

 上述的两端代码起到的输出是相同的。

gets(对象)函数

gets函数是对字符串数组进行整体的赋值。

char s[100];
	int i = 0;
	gets(s);

 在进行对字符串的赋值时还可以使用scanf函数来进行,但是在使用这两种方法是要注意:它们二者并不会在意字符串数组的长度,只是进行简单的赋值操作,所以当字符串数组长度小于输入的长度时,会越界访问,造成严重的后果,所以我们可以使用fgets函数

fgets(s,sizeof(s),stdin);

 strcpy(对象2,对象1)函数

此函数的作用是将字符串数数组1的值copy给字符串数组2。

	
	char s1[] = "Hello";
	char s2[100];
	int i = 0;
	strcpy(s2, s1);

	while(s1[i] != '\0')
	{
		s2[i] = s1[i];
		++i;
	}
	s2[i] = '\0';

这两端代码的效果是相同的。

strcat(对象1,对象2)函数 

 strcat函数的作用是将字符串数组2复制粘贴到字符串数组1的有效长度之后。

char s1[100] = "Hello";
	char s2[100] = "World!";
	strcat(s1, s2);

	int i = 0;
	while(s1[i] != '\0')
	{
		++i;
	}

	int j = 0;
	while(s2[j] != '\0')
	{
		s1[i] = s2[j];
		++i;
		++j;
	}
	s1[i] = '\0';

两端代码运行的结果均为HelloWorld!

strcmp(对象1,对象2)函数 

strcmp函数的作用是对字符串数组1与字符串数组2进行大小比较,函数的返回值就是函数运行的结果。

	char s1[100] = "Hello";
	char s2[100] = "He";
    printf("%d\n", strcmp(s1, s2));

	char s1[100] = "Hello";
	char s2[100] = "He";

    int i = 0;
	while(s1[i] == s2[i] && s1[i] != '\0' && s2[i] != '\0')
	{
		++i;
	}

	printf("%d\n", s1[i] - s2[i]);

 两段代码运行结果一致,运行结果为ACSII码相减后得到的值,结果为正说明字符串数组1大于字符串数组2,为负则反之。


注意事项: 

1.上述函数须在头文件#include<string.h>下运行

2.在调用strcpy进行copy时不会考虑能否存放的下的问题,所以需要空间足够的大

3.在调用srtrcat函数时,需要对象1足够大,最小长度要大于strlen(s1 + s2) + 1。

函数使用练习:

最大值:

int main(void)
{
	char s1[100] = "Hello World, china";
	char s2[100] = "World";
	char s3[100] = "china";
	char max[100];

	if(strcmp(s1, s2) > 0)
	{
		strcpy(max, s1);
	}
	else
	{
		strcpy(max, s2);
	}

	if(strcmp(max, s3) < 0)
	{
		strcpy(max, s3);
	}
		
	puts(max);

	return 0;
}

 字符串数组的逆序:

	char s[100] = "Hello";
	int len = strlen(s);
	int i;



	for(i = 0;i < len / 2;++i)
	{
		char t;
		t = s[i];
		s[i] = s[len - i - 1];
		s[len - i - 1] = t;
	}
	
	puts(s);

 五、二维数组

一、定义

二维数组的声明与一维数组相同,一般形式如下类型说明符 数组名 [常量表达式 1] [常量表达式 2]; 其中 "常量表达式1" 被称之为行下标,"常量表达式2" 被称之为是列下标。二维数组下标的取值范围,如下所示:  行下标的取值范围是:0~n-1。  列下标的取值范围是:0~m-1。  二维数组最大元素下标识:a[n-1][m-1]。

int a[3][3];

上述代码声明了一个 3 行 3 列的二维数组,其 数组名 是 a,其下标变量的类型为整形。

在二维数组中同样具有单一性、有序性、连续性3个特点。在 C语言中,二维数组是按行排列的,即按行顺序存放,先存放 a[0] 行,再存放 a[1] 行,接着存放 a[2] 行。每行有 3 个元素,也是其依次存放的。

二维数组元素的引用一般形式如下: 数组名[下标][下标]; 说明:二维数组的下标可以是 整形常量 或 整形表达式。 例如: a[3][4];表示 a 数组三行四列的元素。 下标变量和数组说明在形式中有些相似,但这两者具有完全不同的含义。数组说明的方括号中给出的是某一维的长度,即可取下标的最大值;而数组元素中的下标是该元素在数组中的位置标识。前者只能是常量,后者可以是常量,变量或表达式。 注意:不管是 行下标 还是 列下标,其中的索引值都是从 0 开始的。 需要注意:和一维数组是一样的,我们都需要注意下标越界的问题。

二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值, 也可按行连续赋值。下面是一个带有 3 行 4 列的数组:

按分段初始化可写为:

int a[3][4] = {
 {0, 1, 2, 3} ,   //0行
 {4, 5, 6, 7} ,   //1行
 {8, 9, 10, 11}   //2行 };

也可以整体赋值:

int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

起到的效果是一样的

在进行初始化时要注意:

对二维数组初始化还需注意几点 1.可以只对部分元素赋值,未赋值的元素自动取"0"值。例如:

int a[3][4] = {0,1,2,3,4,5}

  在对数组全部元素进行数组的初始化时可以省略行数,但是不能省略列数。

int a[][4] = {1,2,3,4, 5,6,7,8, 9, 10,11,12, 13};

 对二维数组的简单使用:求数组元素平均值。

int main(void)
{
	int a[][4] = {1,2,3,4, 5,6,7,8, 9, 10,11,12, 13};

	int i, j;
	int rows = sizeof(a) / sizeof(a[0]);
	int cols = sizeof(a[0]) / sizeof(a[0][0]);
	int sum = 0;

	for(i = 0;i < rows;++i)
	{
		for(j = 0;j < cols;++j)
		{
			sum += a[i][j];
		}
	}

	printf("%f\n", sum / (double)(rows * cols));

 本文中所有代码均是在Ubuntu18.04中编译运行

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值