我开始向后学习编码:首先是高级别的。这显然会遗漏一些我绝对应该知道的基本概念,当我尝试学习一门低级语言时,它会让我失望。
我曾多次尝试理解指针,但是这些解释很快就超出了我的脑海,通常是因为所有示例代码都使用了使用指针的语言,我不了解其他方面的内容,然后我旋转。
我最(并且非常)精通 Javascript。
甚至可以展示,如果 Javascript 有指针,你可以如何做x,并且指针与原始变量不同,因为y.
这是根据第一原则尝试给出一个独立的答案。
指针是允许实现引用语义的类型系统的一部分。就是这样。我们假设我们的语言有一个类型系统,每个变量都属于某种类型。C 是一个很好的例子,但许多语言都是这样工作的。所以我们可以有一堆变量:
int a = 10;
int b = 25;
Run Code Online (Sandbox Code Playgroud)
此外,我们假设函数参数总是从调用者作用域复制到函数作用域中。(对于许多实际语言来说也是如此,尽管当类型系统对用户“隐藏”时,细节很快就会变得微妙(例如在 Java 中))。所以让我们有一个函数:
void foo(int x, int y);
Run Code Online (Sandbox Code Playgroud)
调用时foo(a, b),变量a和b被复制到局部变量中x并y与形参相对应,并且这些副本在函数作用域内可见。无论函数做什么x,y对变量a和b调用点都没有影响。整个函数调用对于调用者来说是不透明的。
现在让我们继续讨论指针。对于每个对象类型,带有指针的语言都包含T一个相关类型T *,即“指向T”的类型。type 的值T *是通过获取type 的现有对象的地址T来生成的。所以拥有指针的语言也需要有一种产生指针的方法,这就是“获取某物的地址”。指针的作用是存储对象的地址。
但这只是图片的一半。另一半是如何处理对象的地址。关心对象地址的主要原因是能够引用正在存储地址的对象。该对象是通过第二个操作(适当地称为解引用)获得的,当应用于指针时,会生成“指向”的对象。重要的是,我们得到的不是对象的副本,而是实际的对象。
在 C 中,取址运算符拼写为&,解引用运算符拼写为*。
int * p = &a; // p stores the address of 'a'
*p = 12; // now a == 12
Run Code Online (Sandbox Code Playgroud)
最终赋值的第一个操作数是*p对象本身。和都是同一个对象。aa*p
为什么这有用?因为我们可以将指针传递给函数,以允许函数更改函数自身范围之外的内容。指针允许间接寻址,从而允许引用。您可以告诉该函数“其他内容”。这是标准示例:
void swap(int * p, int * q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
Run Code Online (Sandbox Code Playgroud)
我们可以告诉函数我们swap 的变量a,并b给它这些变量的地址:
swap(&a, &b);
Run Code Online (Sandbox Code Playgroud)
通过这种方式,我们使用指针来实现函数的引用语义swap。该函数可以引用其他地方的变量并可以修改它们。
因此,引用语义的基本机制可以总结为:
调用者获取要引用的对象的地址:
T a;
mangle_me(&a);
Run Code Online (Sandbox Code Playgroud)被调用者接受一个指针参数并取消引用该指针以访问引用的值。
void mangle_me(T * p)
{
// use *p
}
Run Code Online (Sandbox Code Playgroud)引用语义对于编程的许多方面都很重要,并且许多编程语言以某种方式提供它们。例如,C++ 向该语言添加了本机引用支持,很大程度上消除了对指针的需求。Go 使用显式指针,但有时通过自动取消引用指针来提供一些符号上的“便利”。Java 和Python 在它们的类型系统中“隐藏”了指针,例如,变量的类型在某种意义上是指向对象类型的指针。在某些语言中,某些类型(例如整数)是裸值类型,而其他类型(例如列表和字典)是“包含隐藏指针”的引用类型。您的里程可能会有所不同。