C语言.数组(二维数组)和指针混合学习

本文深入探讨了指针与数组的定义、操作及应用,包括指针类型分析、数组定义、指针运算规则,以及复杂类型解析。通过实例讲解,帮助读者掌握指针与数组的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.指针研究

整型指针 --> 指向内容: int
二级指针 --> 指向内容: 指针
数组指针 --> 指向内容: 数组
函数指针 --> 指向内容: 函数
字符指针 --> 指向内容: char

如何定义一个指针变量?
指针指向的对象: int a;
1)先写一个 *
2)再写一个指针变量名在*后面 (*p)
3)确定指向的对象是什么 -> int a
4)把指向对象的变量名字去掉 -> int
5)把剩余的部分加在 *p前面 int (*p)
练习:定义一个指向二维数组int a[2][3]的数组指针

答:  按照上述步骤定义,则

        *   ->>  (*p) 
		int a[2][3] ->> int [2][3]

结合:int (*p)[2][3]

至此,就定义了一个指针,指向了想要指向的类型int [2][3];

总结:将要指向的对象的变量名字去掉然后添加(*变量名)就为要定义的指针

注意:保持良好的习惯记得加加括号(*p),否则会定义成其他类型,这个后续再讲。

  • ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  • ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

说完如何定义一个想要的指针,接下来说说如何分析一个指针。首先搞懂指针的两个概念,一个是它的类型,第二个是它指向的类型,如何初学者就是因为搞不懂这个,经常对指针操作赋值运算等无从下手(比如我),所以要熟练运用,首先要搞懂这两个概念。

简而言之,
一个指针划掉变量名就是其数据类型;
一个指针划掉变量名和它左边的*号就是其指向的数据类型;

那么如何具体分析一个指针或者类型可以参考下面这个博客:

引用:https://blog.youkuaiyun.com/constantin_/article/details/79575638

int p; //这是一个普通的整型变量  
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针  
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组  
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组  
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针  
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.  
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据  
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针  
int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数. 

此至,如果已经学会了定义一个指针以及看到一个指针懂得分析其类型和其指向的数据类型,就可以进行下一步实操了

  • ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  • ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    实际指针操作中,问题非常之多,在这里只列举一下初步应用的问题:
  • ①赋值:
//假设定义了一个一维数组int a[3],和一个指向int [3]类型的指针
 int a[3];  
 int (*p)[3];    // (*p) -> int (*p)[3] 没搞懂的话看文章开头
//那么下一部赋值(指向)的代码该怎么写?
 p=&a;     p=a;   p=&a[0];   ?
//这三个都是地址,哪一个才能赋给p呢

首先:
要给出两个概念:

  • &a 的运算结果是一个指针,指针的类型是a 的类型加个*,指针所指向的类型是a 的类型,指针所指向的地址,那就是a 的地址
  • 数组的名字等价于首元素的地址(后面二维数组会详细分析),a=&a[0]

然后我们接下来考虑数据类型匹配的问题:

首先先判断p的数据类型,从上面文章可知,其数据类型为int (*)[3] ;(划掉*变量名为其数据类型)
那么要类型匹配,我们就要让=号右边的数据类型也为int (*)[3];
现在我们分析这三条赋值(指向)语句:
p=a=&a[0] ; a=&a[0]其类型为a[0]的数据类型加个 *,所以其类型为int *,p的类型为int (*)[3],所以类型不匹配。而第一句p=&a,a的数据类型为int [3],取址运算后的指针类型为int (*)[3],类型匹配。

  • ②指针运算:
    指针的运算是以单位为基准的,如p+1,并非地址值+1,而是+1个单位,地址增加的字节为单位的字节。单位即是指针指向的类型,如int *p;p+1其指向类型为int ,p+1增加4(4x1)字节,char *p,p+2;其指向的类型为char,p+2(1x2)增加2字节。

二.研究数组

整型数组 --> 成员:int
二维数组 --> 成员:一维数组
字符数组 --> 成员:char

如何定义一个二维数组?

回想: 如何定义一维数组? 整型数组?
1)给一个数组名 A
2)定义数组的大小,使用[]括住它,跟在变量名后面 A[5]
3)确定数组中每一个成员的数据类型是什么 成员:int x --> 类型: int
4)把数据类型结合到A[5]中 int A[5]

定义二维数组步骤与一维数组是一样:
1)给一个数组名 B
2)定义数组的大小,使用[]括住它,跟在变量名后面 B[3]
3)确定数组中每一个成员的数据类型是什么 成员: int x[4] --> 类型: int [4]
4)把数据类型结合到B[3]中 int B[3][4];

结果: int B[3][4];
B[3]: 该数组中有两个成员
int [4]: 每一个成员都是具有3个成员,而且每一个成员都是int类型的数组

其在内存中的分布,及标号的意义如下:

引用: https://blog.youkuaiyun.com/pl0020/article/details/81199164
在这里插入图片描述

重要结论:
&B: 整个二维数组的基地址 。其结果是指针,为B的数据类型加个*号,为 int (*)[3][4]
B: 二维数组的名字等价于二维数组首元素B[0]的地址:B = &B[0] ,结果为 int (*)[4]
B[0]: 二维数组第一个成员的名字,等价于第一个成员二维数组第一个成员的地址 B[0]=&B[0][0] ,结果为int *
B[0][0]: 二维数组第一个成员二维数组第一个成员 类型: int

结合我们上面对指针的理解,就很好理解上图了:

①a[1]+2
a[1]其数据类型为int *,a[1]+2向上移动2个单位,移动了2个int (2x4个字节),所以指向的是a[1][2]的地址
或公式a[1]+2=&a[1][0]+2=&a[1][2]

②*(a+1)+2
a=&a[0]结果为int()[4],a+1为向上移动了1个单位,移动了1个int [4](1x4x4字节),(a+1)=a[1],然后a[1]+2就是①中的步骤里,最后是指向a[1][2]的地址
或公式*(a+1)+2=*(&a[0]+1)+2=a[1]+2=&a[1][0]+2=a[1][2]

总结:
通过数组名访问二维数组某个元素的方法 a[n][m]=*(*(a+n)+m)
A[0] = *(A+0) = *(&A[0]+0)
A[3] = *(A+3) = *(3+A) = 3[A]

  • /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  • /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

三.练习

综合练习:
①定义一个字符指针数组:

答:也就是定义一个数组,其每个元素都是指向char字符的指针
定义数组的过程:
1.定义一个数组:a[];   其每一个元素都是指向char型的指针:char *p;
2.合并:char *a[];

其在内存中的情况:
在这里插入图片描述

②定义一个指向由四个指向int类型的指针组成的数组的指针

答:定义一个指针,指针指向一个含有4个元素的数组,数组里的每一个元素都是指针,元素里的指针指向int类型的数据
定义指针的过程:
1.定义一个指针: (*p)
2.要指向的内容:数组
					定义一个数组:a[4]
					数组的元素:int *q
  数组合并:int *a[4];
3.指针合并:int *(*p)[4]

③定义一个由整型变量参数和字符型指针数组参数组成并返回无值型指针变量的函数

答:void *p(int n, char *m[])
返回值 函数名 (参数)

④请说明以下复杂类型是什么,若为指针类型,请说明其数据类型和其指向的数据类型。若为数组,请说明其存放的数据是什么。若为函数,请说明其返回值及参数是什么;

int(*ptr)[3]; 
int**ptr;
int (*func)( int *p, int (*f )( int*) );
int func(void) [5];

参考:

 1.首先从ptr处开始,先与*结合成(*ptr),说明ptr是一个指针。然后(*ptr)再与[3]结合,说明指针所指向的内容是一个数组。
   然后再与int结合,说明数组里的每一个元素是整型的数据.所以ptr 是一个指向(由整型数据组成的数组)的指针。
 
 2.首先从ptr开始,先与*结合成(*ptr),说是ptr是一个指针,然后再与*结合,说明指针所指向的元素是指针。
   然后再与int 结合,说明该指针所指向的元素是整型数据。

 3.int为类型符,(*func)为标识符。func先与*结合成(*func),说明func是一个指针。然后与右边的()结合,说明指向的是一个函数。
   到右边的括号里,可以知道指向的函数有两个形参,一个形参是(int *p)指向整型数据的指针,
   另外一个形参(int (*f)(int *))是一个指向(有一个指向整型数据指针作为形参,并返回整型数据的)函数的指针,
   最后与最前面的int结合,说明被指向的函数返回整型数据。
   所以,func是一个指向一个有两个形参(一个指向整型数据的指针,一个指向
   (有一个指向整型数据的指针作为形参并返回一个整型数据的)函数的指针)并返回整型数据的函数的指针。

 4.该声明实际上是一个非法的声明。func是一个返回值为具有5个int元素的数组的函数。但C语言的函数返回值不能为数组,
   这是因为如果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,
   但C语言的数组名是一个右值,它不能作为左值来接收另一个数组(即数组名只能用来赋值而不可被赋值),
   因此函数返回值不能为数组,该声明非法。 在实际的编程过程中,这样的复杂声明并不提倡。

https://blog.youkuaiyun.com/lizhiguo0532/article/details/5778908

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值