初学C语言系列之数组

目录

一维数组的创建和初始化

数组的创建

数组的初始化

一维数组的使用

一维数组在内存中的存储

二维数组的创建和初始化

二维数组的创建

二维数组的初始化

二维数组的使用

二维数组在内存中的存储

数组越界

数组作为函数参数

冒泡排序

数组名是什么

正确的冒泡排序

一维数组的创建和初始化

数组的创建

数组是一组相同元素的集合。

数组的创建方式:

type_t   arr_name  [const_n];

type_t:数组中的元素的类型,一个数组的元素必须是同一类型。

arr_name:数组的名字。

[const_n]:数组的大小也就是元素的个数,是一个常量表达式。

//举例

//创建整型元素的数组;
int arr[10];
int arr[5+5];
int arr['W'];
int arr['EW'];
//以上都是正确的创建方式,主要理解创建数组时使用整型表达式

int count=10;
int arr[count];
//以上是使用变量进行创建,在C99标准后,支持C99标准的编译器支持了变长数组,可以用变量进行创建,但是这种创建不能使用初始化。

数组的初始化

初始化就是在创建数组的时候赋值。

int arr[5]={1,2,3,4,5};
//这种是完全初始化,数据将充满整个数组。
int arr2[5]={1};
//非完全初始化,出了初始化的元素,其他的编译器会自动赋值为0。
char ch1[]={'a','b','c'};
//一维数组创建时可以不写常量表达式,由编译器计算数组的元素个数,这个数组元素是3个a,b,c。
char ch2[]="abc"
//字符串,创建字符串时,字符串末尾自带一个'\0',所以ch2的大小是4,元素是a,b,c,\0。

一维数组的使用

数组的使用需要用到" [  ] "下标引用操作符和下标。

下标就是数组元素的标记。

数组10987654321
下标0123

4

56789

下标从0开始,代表数组中每个元素的位置。

//举例

//下面是顺序打印数组中元素的代码,为了打印必须使用数组的下标。
int main()
{
	int arr[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;
}
//arr[i]中,i表示了数组的下标,放在[]操作符中,arr[i]的作用等于*(arr+i),其中arr是数组首元素的地址,i是步长。
//[]操作符有两个操作数,数组名和下标,数组名[下标]。
//数组的长度是可以计算的,sizeof(arr)计算出整个数组的长度,单位是字节,除以单个元素的长度,就是数组的大小

一维数组在内存中的存储

//以下的代码作用是打印数组元素的地址

int main()
{
	int arr[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i;
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d]=%p\n",i, &arr[i]);
	}
	system("pause");
	return 0;
}

从上图的运行结果发现,数组中元素的地址是连续的,而且是间隔相等的,所以数组在内存中是连续存储的,下标实际上就是记录每个元素地址位置的步长。

数组是存放在内存的栈区中,VS2013中创建数组时,开辟的空间是从高地址到低地址,如下图

二维数组的创建和初始化

二维数组的创建

二维数组的创建于一维数组类似,只是坐标多了行坐标。

//举例

int arr[3][5];

注意:创建时可以省略行坐标,但是必须要列坐标,原因是列坐标用来确定每一行的元素有多少个,以此来确定下一行的开头。 

二维数组的初始化

二维数组的初始化与一维数组相同,也是给数组赋值,需要注意的是不能省略列坐标。

//举例

int arr[][5]={{1,2,3},{2,3,4},{3,4,5},{4,5,6}};

上面创建的二维数组,一共4行,每行5个元素,在内存中体现为

第一行12300
第二行23400
第三行34500
第四行4560

0

//举例2

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

 上面创建的是3行,每行5列元素的数组。

第一行12345
第二行678910
第三行111200

0

二维数组的理解:

一、看作一维数组的数组,arr[3][5]。

数组名arr[0]12345
数组名arr[1]678910
数组名arr[2]1112131415

二维数组可以看成一维数组的数组,每一行的数组名是arr[i],其中的某个元素是arr[i][j],因此可以计算出一个二维数组的行和列。

//举例
//计算几行
int main()
{
	int arr[3][5] = { 0 };
	int row = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", row);
	system("pause");
	return 0;
}
代码执行的结果为3,原理是sizeof(arr)计算的是整个数组的大小,sizeof(arr[0])计算的是第一行的大小,因此可以得到arr[0]是第一行的数组名,sizeof只有在计算数组名时,才能得到数组的大小。

//计算几列
int main()
{
	int arr[3][5] = { 0 };
	/*int row = sizeof(arr) / sizeof(arr[0]);*/
	int col = sizeof(arr[0]) / sizeof(arr[0][0]);
	printf("%d\n", col);
	system("pause");
	return 0;
}
代码执行的结果为五5。

二,看作一整个一维数组。

//举例
int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
	int i = 0;
	int row = sizeof(arr) / sizeof(arr[0]);
	int col = sizeof(arr[0]) / sizeof(arr[0][0]);
	for (i = 0; i < row*col; i++)
	{
		printf("%d\n", arr[0][i]);
	}
	system("pause");
	return 0;
}
//这段代码的输出结果是1-15,数组中数组名是首元素的地址,二维数组中arr代表的是第一行的地址,所以不合适,arr[0]表示的是第一行第一个元素的地址,所以可以打印出整个数组,上面的代码中清楚的说明,二维数组可以像一维数组一样操作。

二维数组的使用

二维数组的使用也是通过下标和" [  ] "运算符。

//举例
//打印一个二维数组,从二维数组的角度打印
//思路,二维数组有行和列,所以循环打印的时候应当嵌套打印,row变量当作行,col变量当作列

int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
	int row = 0, col = 0;
	for (row = 0; row < 3; row++)
	{
		for (col = 0; col < 5; col++)
		{
			printf("%d\n", arr[row][col]);
		}
	}
	system("pause");
	return 0;
}

二维数组在内存中的存储

先看以下代码

//举例
int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
	int row = 0, col = 0;
	for (row = 0; row < 3; row++)
	{
		for (col = 0; col < 5; col++)
		{
			printf("&arr[%d][%d]=%p\n",row,col, &arr[row][col]);
		}
	}
	system("pause");
	return 0;
}
//这段代码是为了打印每一个二维数组的地址。

代码运行结果如下:

其中每个元素之间的地址相差4个字节,即使行下标变换后也是如此,因此可以知道,二维数组的保存也是将整个数组作为一个一维数组进行保存,在栈区中连续存储的。

数组越界

数组的越界是指数组的在使用时,下标超出数组的范围,该行为的会引发不确定的结果,同时编译器也很难检测。

//一维数组越界
int main()
{
	int arr[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
	int i = 0;
	for (i = 0; i < 16; i++)
	{
			printf("%d ", arr[i]);
	}
	system("pause");
	return 0;
}
//二维数组越界
int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
	int row = 0, col = 0;
	for (row = 0; row < 3; row++)
	{
		for (col = 0; col < 6; col++)
		{
			printf("%d ", arr[row][col]);
		}
		printf("\n");
	}
	system("pause");
	return 0;
}

以上的越界仅仅是打印出随机的值,具体编程中越界可能会造成多种问题。 

数组作为函数参数

实际编程中经常会传递数组,把数组作为函数参数传递,有两种方式,一种是数组,二是指针。

冒泡排序

思路:冒泡排序就是将相邻的两个数进行交换,没完成两个数之间的交换称为对,当一个数移动到合适的位置称为趟。

以下图为例,将10从第一个元素移动到最后一个元素是一趟,其中经过了9对交换。

 每趟交换后,会将一个数放入正确的位置,所以该数组总共需要9趟交换,因为最后一个数字不需要交换。同时每趟交换时,因为有一个数字已经放入正确的位置,所以每趟的对数也会减少1,规律如下:

需要移动的元素第几趟对数
1019
928
837
746
655
564
473
382
291
100

 上表可以看到,10个元素的数组,冒泡排序需要9趟,每次的对数是从9开始递减的,所以可以得出趟数=元素个数-1,对数=元素个数-趟数。

//把降序的数组进行升序的排列
#define MAX 10
void BoboSort(int arr[])
{
	int i = 0, j = 0;//i表示趟数,j表示对数
	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的长度
	int tmp = 0;
	printf("排序函数内\n");
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j]>arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}




int main()
{
	int arr[MAX] = { 10,9,8,7,6,5,4,3,2,1 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	BoboSort(arr);
	printf("排序函数外\n");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;
}

 上面这段代码的运行后,并没有将数组重新排序,根本原因是因为,数组传参是传递的是指针,因此BoboSort函数内的sz计算的是指针除以指针的大小结果为1,sz-1=0,i必须在小于0的时候才能进入循环,i的初始值为0,所以不能进入循环。

上面的代码也说明了数组传参时,传递的并不是数组,而是一个指针,那么数组名是什么的指针呢?

数组名是什么

上面的代码中,增加打印arr数组名以及arr形参的地址。

可以看到arr数组名与arr形参的值相同,同时与内存中数组首元素的地址相同。

 

因此可以得出结论数组名就是首元素的地址。

但是又两个特殊情况需要注意:

1、sizeof计算数组长度时,()内单独放一个数组名,此时数组名表示整个数组。

int main()
{
	int arr[10];
	int sz = sizeof(arr);
	printf("%d", sz);
	system("pause");
	return 0;
}

2、&arr时,数组名表示整个数组。

下面这段代码,运行后arr+1的地址会比arr多40个字节。

int main()
{
	int arr[10];
	printf("arr的地址%p\n", &arr);
	printf("arr+1的地址%p\n", &arr+1);
	system("pause");
	return 0;
}

正确的冒泡排序

考虑到数组名传参时只是传递首元素地址,所以函数要增加传递数组长度的形参。

//把降序的数组进行升序的排列
#define MAX 10
void BoboSort(int arr[],int sz)
{
	int i = 0, j = 0;//i表示趟数,j表示对数
	int tmp = 0;
	printf("排序函数内\n");
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - i - 1; j++)
		{
			if (arr[j]>arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}


int main()
{
	int arr[MAX] = { 10,9,8,7,6,5,4,3,2,1 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	BoboSort(arr,sz);
	printf("排序函数外\n");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	system("pause");
	return 0;
}

 数组的类型

int arr[10];

去掉数组名的就是数组的类型。

int [10];

int main()
{
	int arr[10];
	int sz = sizeof(int[10]);
	printf("%d", sz);
	system("pause");
	return 0;
}

sizeof与strlen

sizeof计算的是类型的空间,strlen是计算'\0'之前的字符个数,前者不看空间内容,后者看空间内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值