Linux 下C语言的学习(五)——指针的学习(数组指针,指针数组,数组退化)

指针

指针的概念

  指针实质上就是内存地址,指针变量就是用于存储变量的地址     

 

指针的声明

   数据类型*  变量名;

如:

  int num; //定义一个整型变量,变量名为num

  int* pNum; //定义一个整型指针变量,用于存储一个整型变量的地址       

        

注意事项:

  (1)指针的数据类型取决于所保存的地址上的数据类型      

       (2)声明指针变量的时候,*表示变量是指针,不可以省略

 

指针的初始化以及赋值

  int i;

  int* pi; //野指针

  int* pi = NULL; //空指针

  int* pi = &i;

  => int* pi; pi = &i;

   

    pi = &i;

   *pi = *(&i) = i;

指针作为函数的形参

如:void fn(int*p){}

  int num = 66;

  fn(&num); 

 

指针作为函数的返回值

注意:

   永远不要返回一个局部变量的地址,因为局部变量在函数结束之后,所使用的内存会被释放

 

指针和数组

笔试题:

  void fn(int a[10])

   {

      sizeof(a) = 4; //a相当于是一个指针

      int b[10];

      //数组名是数组的首地址,也就是数组中第一个元素的地址,并且数组名是个常量,不可改变

      sizeof(b) = 40; //数组元素的大小 * 数组元素的个数

  }       

讨论:为什么数组作为函数参数的时候就会退化成指针呢?                 

      

首先先理解以下内容

1、 一维数组与指针的关系

一维数组就是指在栈上定义大小为  数据类型*数据个数的内存空间

        如:

               Int num[5];  代表定义了一个整型数组  大小为sizeof(int)*5 = 20

再看

        保存普通数值的变量,在C语言中就用基本数据类型进行了定义,但是由于保存这些变量都有固定的地址(该地址是系统自动分配,也可以是程序员手动分配),根据需要,我们就得保存起来这些地址,虽然这些地址的实质就是一个普通数值(16进制表示),但是假若都用基本数据类型就无法区分这些数据与地址,所以就用了另一个方式来表达,就不允许用之前方式进行保存,而这个方式称为指针,并且提供机制(*地址)表示该地址上的数值

        Int data = 0;

        Int *p = &data;

        表示在栈上定义了一个能保存int数据变量地址的变量p,变量p也是有地址的,即&p,并把data的地址复制一份给p,假若data的地址是 1 (16进制表示0x01)

那么p的值就是1(16进制表示0x01)

        Int num[5] = {1,2,3,4,5};

        Int* p num= num;

                      num是指num这个数组第一个元素的地址,即&num[0],&num表示整个数组的地址,虽然&num[0]和&num的数值相同

        以上pnum 保存的是第一个元素的地址,假若&num[0]值为0x05

 

注意:&num +1   若num 末尾地址是0x20 那&num +1 等于 0x21

         num+1 若num开头地址是0x10  那么num +1 等于 0x14

 

        为什么不可以(int* pnum = &num)明明地址相同,为什么能进行pnum+1,并且pnum+1 ==num[1];

        原因:

               因为int* pnum, pnum只能保存一个整型变量的地址,而&num代表整个数组,虽然地址一样,但是保存长度都不一样了,pnum只能保存四个字节的变量地址,而&num的表示的长度明显大于4;为什么能pnum+1,因为+1 由pnum当前地址向后面移动4个字节,他的结果还是一个四个字节的变量地址,所以可以进行

 

2、         指针数组与数组指针

 

那么&num用什么进行保存呢

              

        再看,int *po[5];

                Int (*pt)[5];

根据运算符的优先级解释,

int *po[5];  【】的优先级高于*   解释为po是一个长度为10的数组,大小为20,而这个数组都是整型指针,每个指针都能保存整型变量地址,即为指针数组

首先是一个数组,然后数组中每一个元素是个指针

而  Int (*pt)[5];

根据优先级解释,()优先级大于【】,解释为pt是一个指针,这个指针大小为4(因为是整型);而这个指针保存的类型是长度为5的整型数组,即数组指针,只有一个指针,指针保存的是整个数组地址

 

所以

        Int (*pt)[5] = #

 

 

3、二维数组表示与数组指针

 

Int hu[3][2];

表示定义3行2列的二维数组,hu代表第一行地址,注意,这个第一行地址是不是第一个元素hu[0][0]地址,而是包含hu[0][0],hu[0][1]的整个地址,换句话说是整个一维数组的地址,虽然第一个元素地址与第一行地址的数值相同

所以

Int (*pw)[2] =&hu[0];

由于&hu[0] 等价于 hu ,所以

Int (*pw)[2] =hu;

pw+0 == pw[0];

pw+1 == pw[1];

pw[0] = hu;

pw[1] = hu+1;

 

 

最后回归讨论:

例子:

Void foo(inta[],int len)

{

              sizeof(a)= 4; //a相当于是一个指针

      int b[10];

      //数组名是数组的首地址,也就是数组中第一个元素的地址,并且数组名是个常量,不可改变

      sizeof(b) = 40; //数组元素的大小 * 数组元素的个数

 

}

 

void Main()

{

        Int data[5] = {1,2,3,4,5};

        foo(data,5);

}

 

且看,foo传值,我们知道data代表 &data[0],即传过去的是一个 整型变量的地址,不是整个数组地址,那么函数foo就形式参数的类型就必须是int *   ,因为这样才能保存实参传过来的,而为什么写成int a[],或者int a[5],那是因为方便与观看,C标准允许这样的书写,很简单就能看出传过来的有什么,就是这样的书写引起了很多程序员的错误理解,他们都以为传过来是整个数组,所以计算的结果就等于20。但函数只是接收了数组中第一个元素的地址,通过地址就可以遍历数组,所以计算结果就是4

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值