栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

一文读懂指针

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一文读懂指针

一文读懂指针

From Wikipedia. In computer science, a pointer is an object in many programming languages that stores a memory address. This can be that of another value located in computer memory, or in some cases, that of memory-mapped computer hardware. A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer.

从维基百科的定义可以得知,指针是一个object,里面存的是内存地址。可以通过对指针进行解引操作得到所指内存地址的值。

那么我们不经想问,指针是数据类型(data type)吗? 以下是来自The C Programming Language的回答。

Pointers are simply a variable that hold an address so one could argue that a pointer is a data type, but it is not defined as a data type (per “The C Programming Language”. Kernighan & Ritchie). 指针数据类型

首先,类型规定了在内存中的数据应该如何被解释。比如int类型的变量,我们从变量的首地址开始读4个字节,按照大小端规则就可以解释出内存中这个值的大小。其次,类型也规定了一些操作集合。比如int类型,可以进行加减乘除等操作。了解了这些,我们重新审视指针。

比如说现在有一个指针int* pointer,它的含义是指向int类型的指针(pointer to int)。int*类型说明了两件事情,一个是此时的变量是指针。第二件是如何对指向的地址进行解析,即取多少个字节。所以我们可以把int*看做是类型。还有一个比较特殊的指针类型就是void*。void就是没有的意思,指针指向的东西是虚无的,说白了就是没有指定数据应该怎么解析,所以sizeof(void)会报错。比如int* ptr = (int*)malloc(sizeof(int));这里malloc返回的就是void*, 从堆里获得这么多字节空间,由用户决定是什么类型。

所以对指针进行赋值的时候要和所声明指针的指向数据类型一致(非常重要)。
这个时候你应该就能理解int a = 1; int* p = &a;这个语句了。

我们对这个进行扩展,如何使用指针指向int a[3]{1, 2, 3};?(以下这种赋值方式是列表初始化方式(list initialization),来自于c++11标准)这个表述实际上是很不准确的,因为a本身是一个数组,a里边的元素类型是int,所以我们是指向a呢,还是指向元素呢?
分别来讨论:

  1. 指向元素。
    前面说了,记住一点。类型要一致。元素是int,所以我们声明一个int* ptr = nullptr;,然后将第一个元素地址赋给指针。即ptr = &a[0];或者ptr=a;。
    值得说一下的是,在数组表示中,a实际上就是一个指针,指向的是a[0]的地址,无论a[0]里边存放的是什么数据类型的值。

  2. 指向数组。
    整个数组类型是int [3],所以我们要定义一个指向包含3个int类型元素数组的指针(pointer to array consisting 3 int elements)。int (*p)[3] = &a;。 &a表示整个数组的地址,它的类型是int [3](不严谨)。

关于数组

从前面的叙述可以得知,数组名实际上是指向数组第一个元素的指针,所以大家经常说数组名就是首地址。但是这句话实际上不准确,说是第一个元素的地址才准确,尽管第一个元素的地址和整个数组的地址是相同的,但是他们所指的数据类型不同。

函数指针

函数指针顾名思义就是(pointer to function),指针里边存的是函数执行语句的首地址。同理,我们要定义类型,所以得和函数类型一致。为了体现他是一个函数指针,所以函数形参也要在指针声明时体现。

const int& f(int a, int b);
const int& (*p)(int, int) = f;
指针变量所占内存大小

指针存的地址,所以和地址总线有关(这里指的是正常申请的指针)。比如32位的机器,地址总线是32位,所以指针是4字节。很好理解吧,因为存的是地址,所以开辟的字节数要和所能表示的地址能力相同。64位就是8字节。

指针变量操作

p + 1;执行到底在地址上加了多少个字节?这实际上是和所指向的数据类型有关。指向int,p+1等价于p+ 1* sizeof(int)。指向int [3],p+1等价于p+ 1* (sizeof(int) *3)。

操作优先级

()> [ ] > * > +,-
// declaration
int *p[5];
int (*p)[5];

//declaration and initialization
int *p[5] = {nullptr, nullptr, nullptr, nullptr, nullptr}; 
int (*p)[5]{nullptr};

想必看到初始化方式的不同,你已经发现区别了。

指针的阅读方式是从右往左读,但是要注意操作的优先级,如上所示。int *p[5];[ ]是一种操作,即指令,从重载下标函数(subscript function)中就可以看出operator[]。所以这里是先声明这个一个数组类型,数组里是5个指向int型的指针。
int (*p)[5]是先说明p是个指针,指向了int[5]的数组,这个就是我们常见的指向二维数组的指针写法,仔细想一想。当然第一种也可以指向二维数组。第一种方式相当于制定二维数组的第一个维度,第二种方式制定了二维数组第二个维度,因此第二个方式更加灵活。

指针常量(Pointer to const)与常量指针(Const pointer)

我个人觉得英文比翻译的要好。
pointer to const(指针常量,就是这个指针指向的常量),无法通过指针修改指向变量的值。常用于函数参数传递。

char c = 'c';
const char b = 'b';
const char* p = nullptr;
p = &b;
p = &c;
*p = 'a'; // error;

const pointer。指针本身的值无法改变。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/917269.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号