【C++入门笔记】指针

前言

这是一篇跟着FishC大佬的C++指针部分学习笔记,以前一直对指针折腾的云里雾里,希望这篇能对大家有所帮助。如有纰漏,望各位大佬指正。

指针(一)

想了解指针,必须先了解地址。

首先需要了解:程序是在硬盘上以文件的形式存在的,但它们的运行却是发生在计算机的内存中。

演示一下变量在内存中的存放情况。

  • int a = -12;
  • char b = M;
  • float c =3.14;

这里需要来讨论一下对齐,为什么上图中c要从8开始存储呢??

其实,在C++里,变量类型是根据它们的自然边界进行对齐的!不过这个我们只需要知道即可,因为编辑器会自动帮我们处理这类问题。另外,对齐问题会因为系统平台不同而不同。

接下来谈一下寻址,对于变量可以通过两种方法来对它进行索引。

一种是通过变量名

一种是通过地址。 

这里引入一个新的操作符,叫做:取址操作符:&,它的作用是获得变量的地址。

我们习惯这么使用:

  • int var = 123;
  • std::cout <<"Address is:"<<&var;

使用指针

上边我们对地址这个概念是理解和使用指针的基础。地址是计算机内存中的某个位置,而指针是专门用来存放地址的特殊类型变量。一般情况下,我们用下面的形式来声明指针变量:

type *pointerName;

例如:

  • int *p;
  • int pp = 123;
  • p = &pp;

在创建指针时,空格放在哪里都是没关系的,下边的语法都是可以接受的。

  • int *p1;
  • int * p1;
  • int* p1;

指针变量前边的类型是用来说明指针指向的数据的类型,请务必匹配来使用。

另外,允许void类型的指针变量:void*p

指针(二)

温故而知新:

  • 创建变量时,系统会分配一些内存块用来存储它们的值;
  • 每个内存块都拥有一个独一无二的地址;
  • 变量的地址可以用&variablename语法来取得;(注:&我们称为“取地址”操作符)
  • 可以把地址赋值给一种称为指针的特殊变量;
  • 指针的类型必须与由它保存其地址的变量的类型一致。

接下来举一个栗子:

  • int a = 456;
  • char b = ‘C’;
  • int *aPointer = &a;
  • char *bPointer = &b;

 这会让程序保留四个内存块,两个为变量保留,两个为指针保留。变量a和变量b中存放的是变量的值(456和‘C’的ASCII码值);两个指针变量存放着的是指针的值,这些值是其他变量的地址。

为什么aPointer指针变量中存储的是0而不是0-3呢,因为既然知道存储的a为整形变量,那么只需要存储它的首地址再加上sizeof(a)即可

当我们知道了某个变量在内存中的地址(通过指针),就可以利用指针访问位于改地址的数据。

这需要对指针进行“解引用(Deference)”处理:即在指针名的前面加上型号(*)。

例如:std::count << *aPointer;

这里我们理解一下:把整数变量a的地址存在在aPointer指针之后,*aPointer和变量a将代表同一个值

因此:*aPointer = 123;将会导致下图所示结果:

 

一定要牢记:指针所保存的是内存中的一个地址。它并不保存指向的数据的值的本身。因此,务必确保指针对应一个已经存在的变量或者一块已经分配的内存。

重点部分:关于星号的用途

第一种是用于创建指针:

int *myPointer = &myInt;

第二种是对指针进行解引用:

*myPointer = 3998;

Tips:

C++允许多个指针指向同一个地址,就是多个指针有相同的值,任意一个进行了修改,其从放的值都会发生修改。

C++支持无类型(void)指针,就是没有被声明为某种特定类型的指针,例如

void *vPointer; 

注意:对一个无类型指针进行解引用前,必须先把它转换成一种适当的数据类型。 

指针和数组(三)

在上述两讲中关于地址和指针的栗子中,我们使用的都是标量类型:整数、实数和字符。

所以当我们遇到一个标量类型的变量时,我们可以创建一个与其类型相同的指针来存放它的地址。如果我们遇到的是数组(是一组数,而不是一个数)该怎么办呢?

我们知道,计算机把数组是以一组连续的内存块保存的

例如:int myArray[3] = {1,2,3};

存储形式如下图所示: 

 

这就说明了数组拥有很多个地址,每个地址对应着一个元素。数组的名字其实也是一个指针(指向数组的基地址,也就是第一个元素的地址)

比如上述那个栗子,一下两句话做的事同一件事情:

  • int *ptr1 = &myArray[0];
  • int *ptr2 = myArray;

我们可以轻易地将数组的基地址用指针变量保存起来,那如果我们要通过指针访问其他数组元素,如何办到呢?

试试:ptr1++;

以上运算并不是将地址值简单的做+1处理,而是安装指向的数组的数据类型来递增的,也就是说+sizeof(int)

思考:

如有:

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

int *ptr = Array;

*ptr + 1;

*(ptr + 1);

二者有什么区别呢??

#include <iostream>

int main()
{
	int Array[5] = {1,2,3,4,5};
	int *ptr = Array;
	std::cout << *ptr + 1 << '\n';
	std::cout << *(ptr + 1) << '\n';
	return 0;
}

程序运行之后得到的的结果为2,难道二者没有区别吗?

答案肯定是有区别的。

*ptr+1是指针变量ptr(数组)指向的首地址(第一个)元素的值加1。

*(ptr+1)是指针变量ptr(数组)指向的首地址后的(第二个)元素的值;

注:有括号先算括号里面的,也就是*(ptr+1)中先将指针指向ptr+1,再求值;而*ptr+1,先求*ptr,再加1。所以二者值相等仅仅是巧合而已~

 小结:

指针运算的重要性在高级和抽象的程序设计工作中体现的非常明显,就目前而言,只需要记住数组的名字同时也是指向其第一个元素(基地址)的指针。

数组可以是任意一种数据类型,这意味着我们完全可以创建一个以指针为元素的数组。

 未完待续……

参考资料

FishC的《C++快速入门》大家可以在B站上搜到

https://www.bilibili.com/video/av7595819?from=search&seid=12323634976003104863

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值