数组知识

什么是数组:

相同数据类型连续的集合
数组下标: 数组中元素的位置的索引或指示.从0开始
数组元素:构成数组的数据.每个数组元素可以用数组名称
和下标去操作,可以理解成一个独立的变量

优点:
1.存储内存大
2.能存储大量数据
3.快速查询(直接通过下标达到想要的值)

缺点:
1.数组只能存储一种数据类型
2.增加,删除元素较慢
3.数组大小确定之后不能改变

一维数组:
格式: 数据类型 数组名[数组元素个数]={数组初始化值}
int arr[10];
注意:数组大小 = 数组元素类型大小 * 数组元素个数
数组初始化方式:
1 全部初始化
int arr[5] = {10,1,3,2,6};
注意:错误int arr[5]; arr[5] = { 1, 2, 3, 4, 5 };
2.部分初始
int arr[5] = {10,1};
注意:部分初始化中如果没有初始化的默认是0
3.特殊部分初始化
int arr[5] = {0}
4.缺少数组大小的初始化方式
int arr[] = {1,2,3,6};
注意:数组小标 = 是数组元素个数-1

数组元素个数获取方式:
个数 = sizeof(数组名)/sizeof(第一个元素);
案例:sizeof(arr1)/sizeof(arr1[0])

	int arr[] = {1,2,3};
	int x = 10;
	char arr1[5] = { x, 2, 13, 4 };
	//char str[10] = { 'a', 'b', 'a' };
	char str[10] = "aba";
	printf("sizeof(arr) = %d\n", sizeof(arr));
	for (int i = 0; i < sizeof(arr1)/sizeof(arr1[0]); i++)
	{
		printf("arr[%d] = %d\t",i ,arr1[i]);
	}

二维数组:数组中的元素是数组.
格式: 类型 数组名[行数][列数] = {初始化元素};
int arr[3][2] = { 0 };
初始化方式:
1.全部分组初始化
int arr[3][2] = { { 1, 4 }, { 2, 5 }, {35,1} };

2.部分分组初始化
int arr[3][2] = { {0 }, { 2, 5 }, { 35, 1 } };
注意:没有初始化的默认0

3.部分系统初始化
int arr[3][2] = { 1,2,3,4};
int arr[3][2] = { 1,2,{3},{4}};//arr[3][2]

4.缺省行标初始化
int arr[][2] = { 1, 2, 3, 4, 5, 6 };
注意:行下标= 行数-1 ; 列下标= 列数-1

int main()
{
	srand(time(NULL));
	//int arr[3][2] = { { 1, 4 }, { 2, 5 }, {35,1} };
	//int arr[3][2] = { {0 }, { 2, 5 }, { 35, 1 } };
	//int arr[3][2] = { 1,2,3,4,5,6 };
	//int arr[3][2] = { (1, 2),( 3, 4), (5, 6) }; 错误
	
	int arr[][2] = { 1, 2, 3, 4, 5, 6 };
	printf("arr[0][1] = %d\n", arr[2][1]);
	printf("sizeof(arr) = %d\n", sizeof(arr));
	//对二维数组赋值-通过循环嵌套
	for (int x = 0; x < 3;++x) //行数
	{
		for (int y = 0;y < 2;++y)//列数
		{
			arr[x][y] = rand() % 100 + 1;
		}
	}

	return 0;
}

指针数组和数组指针

指针数组

定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++)
p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

数组指针(也称行指针)

定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:

*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

优先级:()>[]>*

下面到底哪个是数组指针,哪个是指针数组呢:
A)
int *p1[10];
B)
int (*p2)[10];
每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。

“[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,
即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。
至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,
指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。
数组在这里并没有名字,是个匿名数组。
那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。
我们可以借助下面的图加深理解:

在这里插入图片描述

1.int (*)[10] p2-----也许应该这么定义数组指针
这里有个有意思的话题值得探讨一下:平时我们定义指针不都是在数据类型后面加上指针变量名么?这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2:

 int (*)[10] p2;

int (*)[10]是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想。_
2. 再论a 和&a 之间的区别
既然这样,那问题就来了。前面我们讲过a 和&a 之间的区别,现在再来看看下面的代码:

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[5] = &a;
   char (*p4)[5] = a;
   return 0;
}

上面对p3 和p4 的使用,哪个正确呢?p3+1 的值会是什么?p4+1 的值又会是什么?毫无疑问,p3 和p4 都是数组指针,指向的是整个数组。&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。在C 语言里,赋值符号“=”号两边的数据类型必须是相同的,如果不同需要显示或隐式的类型转换。p3 这个定义的“=”号两边的数据类型完全一致,而p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。在Visual C++6.0 上给出如下警告:
warning C4047: ‘initializing’ : ‘char (*)[5]’ differs in levels of indirection from ‘char *’。
还好,这里虽然给出了警告,但由于&a 和a 的值一样,而变量作为右值时编译器只是取变量的值,所以运行并没有什么问题。不过我仍然警告你别这么用。

既然现在清楚了p3 和p4 都是指向整个数组的,那p3+1 和p4+1 的值就很好理解了。

但是如果修改一下代码,把数组大小改小点,会有什么问题?p3+1 和p4+1 的值又是多少呢?

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[3] = &a;
   char (*p4)[3] = a;
   return 0;
}

测试结果:

(1).char (*p2)[5]=a;必须使用强制转换,如:char (*p2)[5]=(char (*)[5])a;
(2).把数组大小改变,都会编译不通过,提示:
error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[3]'
error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[10]'

把以上程序测试代码如下:

int main()
{
  	char a[5]={'a','b','c','d'};
  	char (*p1)[5]= &a;
  	char (*p2)[5]=(char (*)[5])a;

  	printf("a=%d\n",a);
  	printf("a=%c\n",a[0]);
  	printf("p1=%c\n",**p1);
  	printf("p2=%c\n",**p2);
  	printf("p1+1=%c\n",**(p1+1));
  	printf("p2+1=%c\n",**(p2+1));

  	return 0;
}
输出:
a=1638208
a=a
p1=a
p2=a
p1+1=?
p2+1=?
Press any key to continue

结论:

根据指针类型及所指对象,表示指针大小,每次加1,表示增加指针类型大小的字节.----后面还会有解释说明.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值