【C语言】数组详解(一维数组、二维数组)

本文详细介绍了C语言中一维和二维数组的创建、初始化方法,内存中的存储结构,以及数组越界和传参的注意事项。特别强调了数组作为指针传递的特性及其在冒泡排序等实际应用中的使用。
摘要由CSDN通过智能技术生成

一、一维数组

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

1.一维数组的创建

数组类型 数组名[数组大小]

char arr1[10];
int arr2[20];
double arr[30];

数组大小是一个常量表达式
也可以这样写

int n = 100;
int arr1[n];//此时n是一个常量

#define M 10 //宏定义的标识符常量
char arr2[M];

但在一些编译器中下面这样写会报错

int n;
scanf("%d", &n);
int a[n];

const int m = 20;
int b[m];

在vs2019中,这样写是错误的。这里的m是一个常变量,本质上还是一个变量。n也是变量。

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

2.一维数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

int arr1[5] = { 1, 2, 3, 4,5 };
int arr2[] = { 1,2,3 };
int arr3[10] = { 1,2,3 };
char arr4[3] = { 'a','b','c' };
char arr5[] = { 'a','b', 'c'};
char arr6[] = "abc";

定义数组时,如果后面跟有初始化列表,并且初始化列表中的值的个数就是预期的数组大小,则可省略括号中的数组大小。

数组在创建时可以选择不初始化,部分初始化,完全初始化。
arr3就是不完全初始化,初始化元素个数小于数组大小

在这里插入图片描述
不完全初始化时,其余未初始化元素默认初始化为0
实际应用中,通常采用部分元素赋初值的方法对数组元素进行初始化,如:int arr[100]={0};

arr5和arr6的区别:
在这里插入图片描述

可以看出,arr5有三个元素,arr6有四个元素,最后一个字符为’\0’。

字符串数组用双引号“”初始化时,且数组个数未定义时,默认最后一个元素是’\0’,数组大小比初始化字符多一位。

如果字符数组大小已定义,且数组个数 > 已初始化元素个数,则其余剩下元素均初始化为’\0’

在这里插入图片描述

3.一维数组的使用

数组的访问需要用到 [ ] 下标引用操作符。

#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]);//计算数组长度,这里sz=10
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

1.数组是使用下标和操作符[ ]来访问的,下标从0开始。
2.数组的大小可以通过计算得到。
3.操作符sizeof()以字节为单位求数组大小。sizeof(数组名)是整个数组的大小,再除以一个元素的大小(通常为首元素即下标为0),可以得到整个数组的元素个数。

4.一维数组在内存中的存储

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

在这里插入图片描述

不难发现,数组元素地址每个递增4,这是因为int类型大小是4个字节。
结论:数组在内存中是连续存放的,随着数组下标的增长,地址是由低到高变化的

二、二维数组

1.二维数组的创建

数组类型 数组名[行数][列数]

int arr1[3][4];
char arr2[5][5];
double arr3[2][7];

2.二维数组的初始化

int arr1[2][3] = { 1,2,3,4 };
int arr2[2][3] = { {1,2},{3,4} };
int arr3[][3] = { {1,2} ,{3,4} };

来看看这三个的区别:
在这里插入图片描述

三者均为两行三列的数组,花括号表示一维数组的初始化,也就是每个花括号表示一行数组;
没有花括号则按照顺序依次存放,一行存满则下一行,多余空间初始化为0。
二维数组在初始化时行数可以省略,但列数不能省略,因为可以根据列数确定行数,但不能根据行数确定列数。

3.二维数组的使用

二维数组的使用也是通过下标的方式,用到双重for循环。

#include<stdio.h>
int main()
{
	int arr[2][3] = { 0 };
	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			arr[i][j] = i + j;
		}
	}
	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

4.二维数组在内存中的存储

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

在这里插入图片描述
结论:与一维数组一样,二维数组在内存中也是连续存放的,随着数组下标的增长,地址是由低到高变化的,也就是说二维数组的下一行首元素的地址是与上一行末尾元素的地址相邻的。
在这里插入图片描述
二维数组本质上也是一维数组

三、数组越界

数组的下标范围是0~n-1(n为元素个数),超出这个范围就是越界访问。

例如一维数组越界访问
在这里插入图片描述
二维数组同样。

注:

C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以写代码时,最好自己做越界的检查。

四、数组传参

1.一维数组传参

数组传参时,[]中的大小可以写也可以不写

在这里插入图片描述

可以发现,这里的sz结果是1,与前面的结果10不一样。这是因为数组作为函数参数时,传的是数组首元素的地址,而地址本质是指针类型,在32位机器中占用4个字节,数组元素是int类型也占用4个字节,所以结果是1。

因为数组传参本质上传的是地址,所以一般数组传参时也可以这样写:

void test(int* arr)
{
	//代码......
}

数组名一般情况下是数组首元素的地址

在这里插入图片描述

但是有两种特殊情况:

在这里插入图片描述
可以看到,sizeof(数组名)计算的是整个数组的大小(单位字节);
arr+1指向数组第二个元素的地址,&arr+1跳过整个数组,指向整个数组的后面的地址。
在这里插入图片描述

总结:

1.sizeof(数组名),计算的是整个数组的大小,此处的数组名表示整个数组。

2.&数组名,取出的是整个数组的地址。

3.除了上述两种情况外,所有的数组名都表示数组首元素的地址

4.数组传参,本质传的是地址,也就是指针,指针的类型就是数组元素的类型。

2.二维数组传参

在这里插入图片描述
arr是2行5列的数组,arr+1跳过一行5个元素共20个字节,&arr+1跳过整个数组

这里简单略过,具体内容等写指针的时候详细说明。

3.具体应用

写一个简单的冒泡排序
函数传参本质传的是指针,所以可以数组作为函数参数可以用指针接收,同时避免因为数组的临时拷贝造成资源浪费和栈溢出。

#include<stdio.h>
void bubble_sort(int* arr, int sz)
{
	for (int i = 0; i < sz - 1; i++)
	{
		//每走一趟将最大值换到最后
		for (int 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[10] = { 1,5,9,3,6,4,7,0,2,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

ok,数组的内容暂时总结到这些,感谢大家的观看!点赞关注不迷路哦。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值