数组和指针


前言

在C语言中,数组和指针是比较难的一部分,需要我们反复的学习,思考。只有掌握了数组和指针,我们的C语言水平就可以更进一步。


一、数组

1.数组的定义

数组是一组相同类型元素的集合。主要包括数组名称,元素类型以及元素个数。
语法如下:

类型 数组名称[元素个数];//要用常量或常量表达式来指定元素个数
int main()
{
	int arr1[10];//表示这个数组中有10个int的数据
	int arr2[4 + 6];//表示这个数组中有10个int的数据
	return 0;
}

动态的数组定义:

int main()
{
	int n = 0;
	scanf("%d", &n);
	int arr1[n];//表示这个数组中有n个int的数据
	return 0;
}

动态的数组只能在支持C99标准的编译器上编译,且动态数组不可以进行初始化。
数组也有局部数组和全局数组,静态和非静态数组:

int arr[10];//全局数组
static char charr[10];//全局静态数组
int main()
{
	double doarr[10];//局部数组
	static short sharr[10];//局部静态数组
}

2.一维数组

数组的简单应用:

int main()
{
	int arr[10];//定义了一个名为arr的数组,里面包含10个int类型的数据
	for (int i = 0; i < 10; i++)
	{
		arr[i] = i;//为数组中的元素赋值
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//打印数组中的元素
	}
}

在这里插入图片描述
上述代码中用arr[i]时的[ ]是下标引用操作。数组的下标都是从0开始的。
定义了一个数组,我们可以用sizeof来求取数组所占的空间大小:

int main()
{
	int arr[10];//定义了一个名为arr的数组,里面包含10个int类型的数据
	char charr[10];//定义了一个名为arr的数组,里面包含10个char类型的数据
	printf("arr=%d\ncharr=%d", sizeof(arr), sizeof(charr));
	return 0;
}

在这里插入图片描述
由值我们可以看出szieof求数组的空间为 元素个数 * sizeof(数组类型)。
求数组中元素个数的方法如下:

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr) / sizeof(arr[0]);//用数组总大小除数组一个元素的大小
	printf("%d", sz);
	return 0;
}

2.1一维数组存储

int main()
{
	int arr[10] = { 0 };//把数组中所有元素初始化为0
	char charr[10] = { 0 };//把数组中所有元素初始化为0
	printf("arr中每个元素的地址如下\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%p\n", &arr[i]);//打印数组中每个元素的地址
	}
	printf("\n");
	printf("charr中每个元素的地址如下\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%p\n", &charr[i]);//打印数组中每个元素的地址
	}
	return 0;
}

在这里插入图片描述
从结果可以看出在int型的数组中,每个元素的位置相差一个int的位置,在char型的数组中,每个元素的位置相差一个char的位置,我们由此可以看出数组的地址是连续存储的,且每个元素的地址相差这个数组类型的距离

2.2一维数组初始化

void print(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[5] = { 0 };
	int arr1[5] = { 1 };
	int arr2[5] = { 1,2,3 };
	int arr3[] = { 1,2,3 };//元素数量可以省略,但要进行初始化,系统会自动推断元素个数。
	print(arr, sizeof(arr) / sizeof(arr[0]));
	print(arr1, sizeof(arr1) / sizeof(arr1[0]));
	print(arr2, sizeof(arr2) / sizeof(arr2[0]));
	print(arr3, sizeof(arr3) / sizeof(arr3[0]));
	return 0;
}

在这里插入图片描述
在这里插入图片描述
字符数组如下:

void print(char arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%c ", arr[i]);
	}
	printf("\n");
}
int main()
{
	char charr1[5] = { 'a','b','c' };
	char charr2[5] = "abc";
	char charr3[] = { 'a','b','c' };
	char charr4[] = "abc";
	print(charr1, sizeof(charr1) / sizeof(charr1[0]));
	print(charr2, sizeof(charr2) / sizeof(charr2[0]));
	print(charr3, sizeof(charr3) / sizeof(charr3[0]));
	print(charr4, sizeof(charr4) / sizeof(charr4[0]));
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在字符串数组中,可以用花括号来初始化,也可以用双引号来初始化。对比charr3和charr4的存放结果,我们可以看出用双引号会被自动的放入一个’\0’

int main()
{
	char charr1[5] = { 'a','b','c' };
	char charr2[5] = "abc";
	char charr3[] = { 'a','b','c' };
	char charr4[] = "abc";
	printf("charr1 = %s\n", charr1);
	printf("charr2 = %s\n", charr2);
	printf("charr3 = %s\n", charr3);
	printf("charr4 = %s\n", charr4);
	return 0;
}

在这里插入图片描述
我们可以看到charr打印出了未知的结果,这是因为发生了数组越界,打印数组时会打印到/0的位置,而上面我们有打印限制,所以未出现未知的结果。

3.二维数组

在C语言中多维数组就是元素为数组的数组,二维数组的每个元素是一维数组。多维数组声明时,每个维度用一个方括号表示。

int arr[10][10];//二维数组
char charr[10][10][10]//三维数组

在二维数组中,第一个表示行,第二个表示列。在上面的二维数组中,arr数组有10,每行有10个元素,因此该数组中有10*10共100个元素。在多维数组的声明中,可以省略第一维度的长度,但其他维度都必须指定长度

3.1二维数组初始化

void print(int arr[][3])//打印数组中每个元素
{
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr1[3][3] = { {1,2,3},{4,5,6},{7,8,9} };//按照行进行存储
	int arr2[3][3] = { 1,2,3,4,5,6,7,8,9 };//arr1的省略形式
	int arr3[3][3] = { {1,0,0},{2,0,0},{3,0,0} };//没有初始化的元素,就默认初始化为0。
	int arr4[3][3] = { {1},{2},{3} };//arr3的省略形式
	printf("arr1打印如下:\n");
	print(arr1);
	printf("arr2打印如下:\n");
	print(arr2);
	printf("arr3打印如下:\n");
	print(arr3);
	printf("arr4打印如下:\n");
	print(arr4);
}

在这里插入图片描述

3.2二维数组存储

int main()
{
	int arr1[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
	printf("数组中每个元素的地址\n");
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf("%p\n", &arr1[i][j]);
		}
	}
	printf("数组中每行元素的地址\n");
	for (int i = 0; i < 3; i++)
	{
		printf("%p\n", &arr1[i][0]);
	}
	return 0;
}

在这里插入图片描述

由此可以看出二维数组的储存方式,二维数组的存储时连续的,在地址可以看成一个一维数组。这也是为什么可以省略第一维度的长度,但其他维度都必须指定长度。其他维度决定间隔的地址。

3.数组名

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%d\n", *arr);
	return 0;
}

在这里插入图片描述
可以看出数组名是数组首元素的地址。
注意:sizeof(数组名),这里表示整个数组,是计算整个数组的大小,单位是字节。&数组名则表示整个数组,取出整个数组的地址。

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%d\n", sizeof(arr));
	printf("%p\n", &arr);
	printf("%p\n", &arr[0]);
	return 0;
}

在这里插入图片描述
整个数组的地址也是数组首元素地址。

二、初识指针

1.指针与地址

C语言和其他编程语言相比,C语言可以直接操作硬件,而操作硬件就要依靠C语言的指针。
指针的含义:1.指向的地址。2.指针有类型,类型表示从首地址开始取多少个字节。

//类型 * 指针变量
int main()
{
	int* p;//指向整形变量的指针变量p
	char* ch;//指向字符变量的指针变量ch
	return 0;
}

在C语言中提供了两个特别的运算符:
&:取地址运算符。
*:取值运算符(解引用运算符)。

1.1指针的赋值

int main()
{
	int* p;//未初始化
	int* p1 = NULL;//把p1初始化为空指针
	int a = 1;
	int* p2 = &a;//取a的地址,使p2指向a
	printf("%p\n", &a);// 打印a的地址
	printf("%p\n", &p2);//打印p2的地址
	printf("%p\n", p2);//打印p2存放的地址
	printf("%d\n", *p2);//打印p2解引用的值
	return 0;
}

在这里插入图片描述
我们可以看到指针存放的是地址,是指针所绑定的地址。指针有自己的地址。我们可以通过解引用获得指针所绑定的值。

1.2指针的偏移

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = &arr;//p指向数组的第一个元素
	printf("%d\n", *p);//打印p解引用的值
	printf("%p\n", p);//打印p存放的地址
	(*p)++;//p解引用的值递增加1
	printf("%d\n", *p);//打印p解引用的值
	printf("%p\n", p);
	p++;//p指向的地址偏移一个int型地址
	printf("%d\n", *p);//打印p解引用的值
	printf("%p\n", p);
	return 0;
}

在这里插入图片描述
当指针指向数组时,对指针进行相加相减或者递增递减操作,会使指针指向操作后的地址。

1.3指针和数组的区别

1.赋值:同类型的指针可以相互赋值,但数组不可以,只能一个元素一个元素的赋值或着拷贝。
2.sizeof大小不同:sizeof(数组名)求的是数组的大小,sizeof(指针名)求的是指针的大小,在32位机器下,所有指针的大小都为4。在64位机器下,所有指针的大小都为8。
3.传参:数组传参时,会退化为指针。因为C语言只会以拷贝的方式传递参数,在参数传递时,拷贝整个数组会使效率大大降低,并且参数位于栈上,太大的数组拷贝会导致栈溢出。用数组传参时可以把数组名看作一个常量指针,传递的是首元素的地址。
4.数组名可以看作指针常量。

总结

我们用一个经典的冒泡排序来总结今天的知识。
冒泡排序的思想:
1,从待排序的数组首元素开始,比较相邻两个元素得大小,让大的元素向后移动。直到把最大的元素移动到最后的位置。
2,第二轮把次大的数移动到倒数第二个位置。
3,重复上述过程。

void print(int* arr, int size)
{
	for (int i = 0; i < size; i++)
	{
		printf("%d ", arr[i]);
	}
}
void swap(int* p1, int* p2)//交换元素
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void Bubbling(int *arr,int size)//数组退化为指针,可以直接用指针接受。
{
	for (int i = 0; i < size; i++)
	{
		for (int j = 1; j < size - i; j++)//控制循环时最后的位置,注意不要越界
		{
			if (arr[j - 1] > arr[j])//找到大的数,如果大的数在前,则执行下面交换操作。
			{
				swap(&arr[j - 1], &arr[j]);//进行交换
			}
		}
	}
}
int main()
{
	int arr[10] = { 9,5,1,7,3,6,8,4,2,0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	Bubbling(arr, size);
	print(arr, size);
	return 0;
}

在这里插入图片描述

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡拉肖克·小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值