数组初步了解

目录

1.一维数组

1.1一维数组的使用

1.2一维数组的存储 

2.二维数组

2.1二维数组的使用

2.2二维数组的存储

3.数组越界

4.数组作为函数参数

4.1冒泡排序

// 对一组整数升序(从小到大)排列

法一:冒泡排序

法二:普通方法 (难)

4.2数组名

5.字符数组


1.一维数组

1.1一维数组的使用

定义:数组是一组相同类型元素的集合

形式:type_t类型   arr_name名字     [const_n]个数

 例 :      int  arr [10]  ;int  arr2 [5+6]

在C99标准之前,[ ]中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。不同编译器情况不同。

在VS编译器里,[ ] 中只能放常量不能放变量。

不完全初始化时,剩余元素默认初始化为0;

没有指定数组元素个数,编译器会根据初始化的内容来确定数组的元素个数;

数组时使用下标来访问的,下标从0开始,[ ]下标引用操作符;

数组大小:int  sz  =  sizeof ( arr ) / sizeof ( arr [0] ); 

1.2一维数组的存储 

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d]= %p\n", i, &arr[i]);//注意这里不是数组名arr,而是确切的数 
                                             //组中的元素arr[i],所以要加&
	}
	return 0;                              
}

 ​​

观察上述运行结果可知:

地址是以16进制打印的(二进制太长了)且每两个地址之间差值为4,这正好对应了int占用4个字节。

 

一维数组在内存中是连续存放的

随着数组下标的增长,地址是由低到高变化的

 那么我们只要知道第一个元素的地址就可打印出所有元素:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];//找到第一个数组元素的地址
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//就可以找到其他元素
	}
	return 0;
}

 

 是不是很神奇?觉得不可思议?没关系,我们再来验证一下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];
	for (i = 0; i < sz; i++)
	{
		printf("%p——%p\n", p + i, &arr[i]);
	}
	return 0;
}

 结果依旧如此~

2.二维数组

2.1二维数组的使用

int  arr [3][4]          3行4列    12个元素

double  arr[4][5]    4行5列    20个元素

先放行再放列;

行可以省略,但是列不能省略;

行和列都是从零开始的。

int main()
{
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int arr2[3][4] = { 1,2,3,4,5 };
	int arr3[3][4] = { {1,2},{3,4},{5,6} };//第一行放1,2;第二行放3,4
                                           //第三行放5,6,其他未初始化的默认为0。
    int arr4[ ][4] = {1,2,3,4,5,6,7,8,9 };//行可省略列不能省略,结果是第一行1,2,           
                                          //3,4,第二行5,6,7,8,第三行9。
    int arr5[ ][2] = {1,2,3,4,5,6,7,8,9 };//此时行数发生变化,自动适应个数,结果
                                          //为5行2列。
}                                          

2.2二维数组的存储

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}

 分析上述运行结果可知,二维数组在内存中也是连续存放的,在内存中就相当于将每一行拆分后拼在一起,这也是为什么行可省略列不可省略。

第一行arr [ 0 ] [ j ],第二行 arr [ 1 ] [ j ]……

第一列arr [ i ] [ 0 ],第二行 arr [ i ] [ 1 ]……

二位数组也是一维数组的数组(有点小绕哈)

行数:sizeof ( arr ) / sizeof ( arr [ 0 ] )   总大小除以第一行大小即为行数

每行元素个数:sizeof ( arr [ 0 ] ) / sizeof ( arr [ 0 ] [ 0 ] )每一行大小除以其中一个元素大小即为每一行元素个数

3.数组越界

定义:数组下标小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

C语言不做下标越界检查,且编译器不一定报错,但并不代表程序是正确的,所以我们自己要做越界的检查。(不要偷懒哦)

4.数组作为函数参数

4.1冒泡排序

排序:冒泡排序,选择排序,插入排序,快速排序

这里我们只介绍一个简单的方法:冒泡排序(边比边换)

冒泡排序思想:两两相邻元素比较,一趟冒泡排序搞定一个数字,让这个数字来到最终应该出现的位置上,n个数字进行n-1趟。

// 对一组整数升序(从小到大)排列

法一:冒泡排序

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void sort(int arr[],int sz)
{
	//外层决定趟数
	int i = 0;
	for (i = 0; i < sz -1 ; i++)
	{
		//内层决定一趟冒泡排序进行多少对比较
		int j = 0;
		for (j = 0;j < sz-1-i; j++)//这里注意趟数在变
		{
			if (arr[j] > arr[j + 1])//相邻两元素比较
			{
				int tmp = arr[j];//如果前面的数大于后面的数则交换
				arr[j] = arr[j + 1];//否则开启下一轮循环
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 2,4,3,7,9,6,1,5,8 ,0};
	int sz = sizeof(arr) / sizeof(arr[0]);//这行代码不能放在sort函数里
	sort(arr,sz);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

法二:普通方法 (难)

其实除了冒泡排序,用基本的方法(先选后换)也能写出来 :

说实话,这种方法没有冒泡排序简单。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	int arr[30];
	int i = 0;
	int n = 0;//数组中有几个元素
	printf("你想对几个元素排序?:");
	scanf("%d", &n);
	printf("请输入%d个整数:", n);
	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);
	}
	int j = 0;
	int pos = 0;//pos为最小数的位置
	for (i = 0; i < n - 1; i++)//每一次循环搞定一个数
	{
		pos = i;//每次假定最小数为arr[i]

		//for循环用来查找,找n-i个数中最小的数
		for (j = i + 1; j < n; j++)//查找从 i + 1 个数开始有没有比arr[i]小的
		{
			if (arr[j] < arr[pos])//如果arr[j]小于arr[i]
			{
				pos = j;//改变最小值为arr[j]
			}
		}

		//if语句用来排序
		//找到n-i个数中最小的数后,将该数放在指定的位置上
		if (i != pos)//如果最小值就是arr[i],不需要交换
		{            //如果最小值不是arr[i],将最小值arr[pos]与arr[i]交换
			int tmp = arr[i];
			arr[i] = arr[pos];
			arr[pos] = tmp;
		}
	}
	printf("交换后的数组为:");
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;

}

4.2数组名

由上图可知数组名是数组首元素的地址

两个例外:1> sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节;2> &数组名,这里的数组名也表示整个数组,&数组名取出的是数组的地址

观察上述代码运行结果可知,前两组数据都加了4,而最后一组数据加了28(16进制),转换为十进制为40,说明&arr 指的是整个数组,加一跳过的是整个数组的字节(4*10)。

其实指针+1,到底跳过几个字节,是和指针类型有关的 

传参时,写成数组或指针都可以

数组传参本质上只传了首元素的地址

5.字符数组

scanf使用空格,Tab和回车符作为输入数据分隔符;

gets函数不将空格作为数据分割符,而是将回车作为字符串输入结束符;

puts函数逐个字符输出字符串时,将字符串结束符'\0'转换为回车换行。 

由上述知识可知,使用scanf输入字符串需要借助循环来实现。 

int main()
{
	char arr[30];
	while (scanf("%s",arr) != EOF)//不能写成 &arr,arr已经是数组首元素的地址
	{
		printf("%s ", arr);
	}
	return 0;
}

这样就可以一直输入语句啦~ 

 

如果使用gets函数就会简单很多: 

int main()
{
	char arr[30];
	printf("请输入字符串:");
	gets(arr);
	printf("输入的字符串是%s", arr);
	return 0;
}

这里我们又看见了一个不同点,使用scanf输出时是一个单词一个单词输出的,这也说明scanf以空格作为分隔符;而使用gets函数时是一整句话一起输出的,这也说明gets函数只将回车作为字符串结束符。 

That‘s all~下回见啦,今天仍是爱敲代码的菜菜,有bug请指正哦~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值