【C语言】数组

【C语言】数组



一维数组

  • 元素类型角度:数组是相同类型的变量的有序集合
  • 内存角度:连续的一大片内存空间

int b[10];
我们把b称作数组,因为它是一些值的集合。下标和数名一起使用,用于标识该集合中某个特定的值。例如,b[0]表示数组b的第1个值,b[4]表示第5个值。每个值都是一个特定的标量。

一维数组名

那么问题是b的类型是什么?它所表示的又是什么?一个合乎逻辑的答案是它表示整个数组,但事实并非如此。在C中,在几乎所有数组名的表达式中,数组名的值是一个指针常量,也就是数组第一个元素的地址。它的类型取决于数组元素的类型:如果他们是int类型,那么数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型也就是“指向其他类型的常量指针”。

请问:指针和数组是等价的吗?

答案是否定的。数组名在表达式中使用的时候,编译器才会产生一个指针常量。那么数组在什么情况下不能作为指针常量呢?在以下两种场景下:

1.当数组名作为sizeof操作符的操作数的时候,此时sizeof返回的是整个数组的长度,而不是指针数组指针的长度。
2.当数组名作为**&操作符的操作数**的时候,此时返回的是一个指向数组的指针,而不是指向某个数组元素的指针常量。

void test01()
{
	//一维数组名是不是指针?
	int arr[5] = { 1,2,3,4,5 }; 


	//有两种特殊情况,一维数组名不是指向第一个元素的指针
	//1、sizeof
	//2、对数组名取地址		得到数组指针	步长是整个数组长度
	
	printf("%d\n", sizeof(arr)); //20 
	int len = sizeof(arr) / sizeof(int);
	

	printf("%d\n", &arr); 
	printf("%d\n" ,&arr + 1); 
	

}
//	int arr[]	可读性更高
void printArray(int arr[], int len)	//int arr[]等价于 int*arr
{
	for (int i = 0; i < len; i++)
	{
		printf("%d\n", arr[i]);
	}
}

void test03()
{
	int arr[5] = { 1,2,3,4,5 };
	int len = sizeof(arr) / sizeof(int);
	printArray(arr, len);
}
void test02()
{
	//arr数组名 它是一个指针常量 指针指向不可以修改,而指针指向的值可以改int * counst a
	int arr[5] = { 1,2,3,4,5 };
	arr[0] = 12;
	//arr = NULL;	//error
}

下标引用

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

*(arr + 3) ,这个表达式是什么意思呢?

首先,我们说数组在表达式中是一个指向整型的指针,所以此表达式表示arr指针向后移动了3个元素的长度。然后通过间接访问操作符从这个新地址开始获取这个位置的值。这个和下标的引用的执行过程完全相同。所以如下表达式是等同的

*(arr + 3)
arr[3]

问题:数组下标可否为负值

void test03()
{
	//数组索引 可不可以是负数	答案:可以
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr; 
	p = p + 3;
	printf("%d\n", p[-1]);	//3
	printf("%d\n", *(p - 1)); //3
}

答案是:可以的。

那么是用下标还是指针来操作数组呢?对于大部分人而言,下标的可读性会强一些。

//	int arr[]	可读性更高
void printArray(int arr[], int len)	//int arr[]等价于 int*arr
{
	for (int i = 0; i < len; i++)
	{
		printf("%d\n", arr[i]);
	}
}

void test04()
{
	int arr[5] = { 1,2,3,4,5 };
	int len = sizeof(arr) / sizeof(int);
	printArray(arr, len);
}

数组和指针

指针和数组并不是相等的。为了说明这个概念,请考虑下面两个声明:

int a[10];
int *b;

声明一个数组时,编译器根据声明所指定的元素数量为数组分配内存空间,然后再创建数组名,指向这段空间的起始位置。声明一个指针变量的时候,编译器只为指针本身分配内存空间,并不为任何整型值分配内存空间,指针并未初始化指向任何现有的内存空间。
因此,表达式a是完全合法的,但是表达式b却是非法的。*b将访问内存中一个不确定的位置,将会导致程序终止。另一方面b++可以通过编译,a++却不行,因为a是一个常量值

数组指针

数组指针的定义方式:
1、先定义数组类型,再通过类型定义数组指针

typedef int(ARRARY_TYPE)[5]; // ARRARY_TYPE 代表存放5个int类型元素的数组 的数组类型


//数组指针的定义方式
//1、先定义数组类型,在通过类型定义数组指针
void test05()
{
	int arr[5] = { 1,2,3,4,5 };

	typedef int(ARRARY_TYPE)[5]; // ARRARY_TYPE 代表存放5个int类型元素的数组 的数组类型

	ARRARY_TYPE arr2; 
	for (int i = 0; i < 5; i++)
	{
		arr2[i] = 100 + i;
	}
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", arr2[i]);
	}

	ARRARY_TYPE* arrP = &arr;
	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", (*arrP)[i]);
	}

}

2、先定义数组指针类型,再通过类型定义数组指针

typedef int(*ARRARY_TYPE)[5];

//2、先定义数组指针类型,在通过类型定义数组指针
void test06()
{
	int arr[5] = { 1,2,3,4,5 };
	typedef int(*ARRARY_TYPE)[5];

	ARRARY_TYPE arrP = &arr;

	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", (*arrP)[i]);
	}
}

3、直接定义数组指针变量

int arr[5] = { 1,2,3,4,5 };
int(*p)[5] = &arr;

//3、直接定义数组指针变量
void test07()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;

	for (int i = 0; i < 5; i++)
	{
		printf("%d\n", (*p)[i]);
	}
}

二维数组

二维数组名

一维数组名的值是一个指针常量,它的类型是“指向元素类型的指针”,它指向数组的第1个元素。多维数组也是同理,多维数组的数组名也是指向第一个元素,只不过第一个元素是一个数组。例如:

int arr[3][10]

可以理解为这是一个一维数组,包含了3个元素,只是每个元素恰好是包含了10个元素的数组。arr就表示指向它的第1个元素的指针,所以arr是一个指向了包含了10个整型元素的数组的指针。

除了两种特殊情况外 ,二维数组名称是 指向第一个一维数组 数组指针
特殊情况1 sizeof
特殊情况2 对数组名取地址 &arr 获取的是二维数组的 数组指针 int(*p)[3][3] = &arr;

void test08()
{
	//可读性高
	int arr[3][3] = {
		{1,2,3},
		{2,3,4},
		{3,4,5},
	};
	//除了两种特殊情况外 ,二维数组名称是 指向第一个一维数组  数组指针
	//特殊情况1  sizeof
	//特殊情况2  对数组名取地址 &arr  获取的是二维数组的 数组指针 int(*p)[3][3] = &arr;

	printf("%d\n", sizeof(arr));	//36

	int(*pArray)[3] = arr;
	//访问二维数组中的 4 这个元素
	printf("%d\n", arr[1][2]);	//给人看
	printf("%d\n", *(*(pArray + 1) + 2));	//给机器看
}

二维数组做函数参数

//void printArray02(int (*array)[3], int row, int col)
//void printArray02(int array[][3], int row ,int col)
void printArray02(int array[3][3], int row, int col) //array[3][3] 等价于 一维数组指针   int (*array)[3]
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			//printf("%d ", array[i][j]);
			printf("%d ", *(*(array + i) + j));
		}
		printf("\n");
	}
}

//二维数组做函数参数
void test09()
{
	int arr[3][3] = {
		{ 1, 2, 3 },
		{ 4, 5, 6 },
		{ 7, 8, 9 }
	};

	printArray02(arr, 3, 3);

}

二维数组排序

选择排序

在这里插入图片描述
选择排序算法是通过遍历数组,选择出数组的最小或最大值,与指定位置交换数据,遍历完整个数组的所有位置就完成排序。

遍历第一趟数组,找出数组的最小值,与第一个数据交换。
遍历第二趟数组,继续找出最小值,与第二个数据交换。
重复上述动作,遍历完数组就得到一个有序数组。

void mySort(int arr[], int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		int min = i;	//记录最小值下标为i
		for (int j = i + 1; j < len; j++)
		{
			if (arr[min] > arr[j])
			{
				//更新真实最小值下标
				min = j;

			}
		}
		if (i != min)
		{
			int tmp = arr[i];
			arr[i] = arr[min];
			arr[min] = tmp; 
		}
	}
}


void printArray(int *arr,int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d\n", *(arr+i));
	}
}

void test10()
{
	//从小到大 排序 利用选择排序
	int arr[] = { 2,5,1,3,4 };

	int len = sizeof(arr) / sizeof(arr[0]);
	mySort(arr,len);

	printArray(arr, len);

练习:
对指针数组进行排序,排序的算法利用 选择排序 从大到小
char * pArray[] = { “aaa”, “fff”, “bbb”, “ddd”, “ccc”, “eee” };

void selectSort(char** array,int len)
{
	for (int i = 0; i < len-1; i++)
	{
		int max = i;
		for (int j = i + i; j < len; j++)
		{
			if (strcmp(array[max], array[j]) < 0)
			{
				max = j;
			}
		}
		if (max != i)
		{
			char* tmp = array[i];
			array[i] = array[max];
			array[max] = tmp;

		}

	}
}


void printArray2(char*array[],int len) {
	for (int i = 0; i < len; i++)
	{
		printf("%s\n", array[i]);
	}
}

void test11()
{
	//对指针数组进行排序,排序的算法利用 选择排序   从大到小
	char* pArray[4] = { "aaaaa","fff","bbbb","ddd" };

	int len = sizeof(pArray) / sizeof(pArray[0]); 


	selectSort(pArray, len);

	printArray2(pArray, len);
}

总结

到这里这篇文章的内容就结束了,谢谢大家的观看,如果有好的建议可以留言喔,谢谢大家啦!

相关推荐

  1. C语言数组语法解剖

    2024-04-21 13:44:02       32 阅读
  2. C语言 数组指针

    2024-04-21 13:44:02       41 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-21 13:44:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-21 13:44:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-21 13:44:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-21 13:44:02       20 阅读

热门阅读

  1. 什么是关键信息基础设施及其安全保护条例

    2024-04-21 13:44:02       14 阅读
  2. 浏览器原理之本地存储

    2024-04-21 13:44:02       11 阅读
  3. 续传查询SQL不规范导致漏数的问题

    2024-04-21 13:44:02       15 阅读
  4. linux的内存管理

    2024-04-21 13:44:02       11 阅读
  5. 提升用户体验的UUID设计策略

    2024-04-21 13:44:02       19 阅读
  6. 个人网站开(九)五系统前端react

    2024-04-21 13:44:02       12 阅读
  7. PyTorch项目实战开发教程:智能家居与IoT

    2024-04-21 13:44:02       55 阅读
  8. PostCSS概述

    2024-04-21 13:44:02       33 阅读