指针、引用与const指针

指针与引用

  • 1,综述
  • 2,指针
    •  2.1 什么是指针
    •  2.2 代码示例1
    •  2.3 代码示例2
  • 3 ,引用
    •   3.1 什么是引用
    •   3.2 引用的代码示例
  • 4,const 迷惑

  鄙人的C++是在网易云课堂上跟着吕鑫老师学的,课程名字叫“C语言|C++|数据结构|MFC|linux吕鑫”。这个课程讲的十分详细,尤其是吕老师对于C++的宏观理解让我多次茅塞顿开。可能大家都会有这种经历,临近考试的时候看书一周不如大神通讲1小时,先建立宏观的脉络和框架然后去学习细节。以前追宋鸿兵的《宏观》里宋老师也提到了为什么他可以掌握这么多知识,那就是因为他不论是历史也好经济也好,先建立宏观的框架,然后把内容套在框架里。换句话说,“观”很重要,带着“观”去看待问题犹如按图索骥,自然会轻松很多。扯远了,还是先感谢吕老师,也同样感谢CSDN的各位博主。
  课程的链接在这里,该课程是完全免费的,还附带资料:
   https://study.163.com/course/introduction.htm?courseId=712019#/courseDetail?tab=1
  学习的过程中就做了一些笔记,后来在CSDN看的多了就萌生了自己写技术博客的想法,于是在某个txt中记录了很多可以总结的点。结果该txt越来越大,回看竟然都感到陌生了,所以要赶紧在博客上总结起来,“忘却的救主快要降临了吧,我正有写一点东西的必要了”。


1,综述

  指针(Pointer)和引用(reference)二者看似不同但又仿佛有相似之处。只有从逻辑和底层彻底搞明白指针和引用,才能思路更清晰地编写代码。言简意赅两句干货:

  指针本身是个变量,存储另一个变量的地址

  引用表面是变量别名,底层是个const 指针


2,指针

 2.1 什么是指针

  定义变量本质是申请空间,譬如:

    int a;

  表示申请一个32位(32/64位系统)的空间,该空间中的二进制码表示数量的规则是整型变量的方式。所以称a为整形变量,范围是-231~231-1(总数232,负数最高位是1,占总数一半,正数和0占另一半)。
  那现在我在a变量里面存储b变量的地址,a变量就成为了一个指针,指向b变量。

    int *a=b;

  因为32/64位系统中寻址空间是232,所以a的长度是32位二进制,这样才可以不重不漏表示完所有的地址。指针也有变量类型,比如int型指针,double 型指针,这是为了语法的无歧义和程序安全考虑。可能各位会碰到各种绕来绕去的指针题目,这种题目不管叠加了多少指针多少*与&,只要一层层取下来就可以表示清楚了。*a就表示取出a中的数,&a表示取出a的地址。

 2.2 代码示例1

  可以看段代码应用一下:

int arr[] = {1,2,3,4,5,6,7,8};
int *p=arr;
*(p++)+=123;       
printf("%d,%d\n", *p,*(++p)); 

  第一行,定义数组arr,元素共八个。
  第二行,定义指针p指向arr数组。
  此时的*p 还是等于arr数组的第0个元素,也就是1。
  第三行,p++里的++是最后才运算,所以先执行*p+=123,也就是arr的第0个元素被赋值为123。此时arr变成{123,2,3,4,5,6,7,8} ,然后是p++,此时*p已经是等于arr的第1个元素了,也就是2。
  第四行,在执行printf时,括号里的参数是从右往左的顺序进行读取的,也就是说先执行 *(++p),也就是p先加一再指针,指向的是arr第2个元素3,然后在执行*p,还是3最后显示的内容就是:

  3,3

  注意:指针虽然是一个变量,但它是有类型的特殊变量。指针中存储的是地址,指针加1不代表该地址加1,比如0x00a2fd68 加1后不是0x00a2fd69,而是根据指针的属性去加。比如Int型是在地址上加4个字节,那就是0x00a2fd6B,这是为了方便移动指针。

 2.3 代码示例2

void main()
{
	int a[5]={1,2,3,4,5};
	int *ptr=(int *)(&a+1);
	printf(%d,%d”,*(a+1),*(ptr-1));
}

  正确答案是:

      2,5

  这段代码牵扯到另一个问题,数组与指针。第一个2很好出来,因为a为该数组的首地址,即a为a[0]的地址,那么a+1,就是a[1]的地址,按照该地址取内容就是a[1]=2。
  第二个有点复杂,关键在于,a与&a的值一样类型不一样。这也是鄙人困惑的地方之一。在之前的叙述中,鄙人认为指针本身也是一个变量,那么a既然是一个指针就应该存在于内存当中,就应该有地址,a与&a肯定是不一样的。可以看到下图:

指针、引用与const指针_第1张图片

  事实是,a与&a值是相同的,类型不一样,前者为数组元素指针,后者为数组指针。所以按照2.2中的叙述,前者加1偏移4个字节,后者加1偏移4*5=20个字节。所以将&a+1强制类型转换为*int 赋值给ptr后,ptr为*int类型,减1偏移4个字节,所以取内容是a[4]=5。
  鄙人觉得这个语法用起来虽然方便,但难以理解a到底在哪,它的地址是什么。为什么a和&a一样的,为什么不能用通用的指针去理解a。唯一能想到的解释只能是:a单独出现时,表示数组首元素的指针,而&a中的a表示的是数组这个抽象的东西。还请看到本博客的各位大神赐教,指出计算机底层是怎么处理的,类似的问题看哪本书比较好。


3 ,引用

  3.1 什么是引用

  学过c/c++的肯定都听过引用就是起别名,或者叫替身,有点像量子纠缠那样,替身和本体同时变化。当然啦,计算机才没有那么大本事,所以引用内部还是在用指针,其实都是一个对象。数据是对实体世界的一次抽象,引用和指针又是对数据的第二次抽象,这样才会让数据摆脱存储的桎梏,让数据在逻辑上具有很强的操作性。
  先看一下引用怎么定义吧:

int a = 10;
int &b = a;

  称b是a的引用,假如b的值改变了,那么a的值也会改变,即a,b是同体的。那么这是否与2.1中所说的申请变量是开辟内存空间相矛盾呢,其实不矛盾,因为引用是C++的一个障眼法,底层还是指针。请看下面的对比代码:

int i=10;                  int i=10;
int &j=i;                  int * const p=&i;
j=-1;                      *p=-1;

  这两份代码的效果是一样的,实际上在C++内部就是这样来操作的,不过抽象出了一个引用的概念而已。从int * const p的角度看,理解引用的特性就非常简单了:

  • 引用型变量必须初始化
  • 引用变量必须挂在一个现有的同类型变量上
  • 引用只能在初始化的时候引用一次,不能再引用其他的变量

  3.2 引用的代码示例

指针、引用与const指针_第2张图片

  骗你的,没有代码,放个图片休息一下。


4,const 迷惑

  既然说到了引用,就不得不提const了,当它加入代码中,又会产生一些迷惑人的东西。
  首先看这一段代码:

const int i=1;
i=2;

  这是错误的,因为i被const 修饰了,表示i不能被更改。也就是说,被const修饰表示不能修改,那么指针呢。

  const int * a; //指向整形常量 的指针,它指向的值不能修改
  int *const b; //指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。
  const int *const c; //指向整形常量 的常量指针 。它既不能再指向别的常量,指向的值也不能修改。

  这三句话请反复琢磨三遍,其实无非是在限定俩东西,指针本身与指向的内容,换句话说就是指针本身作为变量能不能改,该变量作为地址找到的内存空间的数能不能改。
  但是很容易记混乱吧,技巧是看const的右边是什么就限定了什么。第一个const右边是int ,那说明是内容不能改。第二个const右边是b,说明b本身不能改。第三个就是俩都限定了。
  如此看来在3.1中,既然指针不能再指向其他的内容,那么引用初始化时肯定要挂在一个确定的变量上啊,而且不能改动。
  const 还有一个知识是修饰成员函数时的语法,这个以后再说。

你可能感兴趣的:(C与CPP基础学习,编程语言,C,Cpp,引用,指针)