Pointers 解释指向Javascript开发人员的指针

Pointers 解释指向Javascript开发人员的指针,pointers,Pointers,我开始学习反向编码:高水平优先。这显然有可能遗漏一些我应该明确知道的基本概念,当我试图学习一门低级语言时,它会让我感到困惑 我曾多次尝试理解指针,但解释很快就超出了我的理解范围,通常是因为所有示例代码都使用使用指针的语言,而我不了解其他有关指针的内容,然后我开始旋转 我是Javascript最流利的(而且非常流利) 你如何向像我这样悲伤的Javascript开发人员解释指针?有人能给我举个实际的例子吗? 甚至可以展示,如果Javascript有指针,您可以执行x,指针与原始变量不同,因为yC++

我开始学习反向编码:高水平优先。这显然有可能遗漏一些我应该明确知道的基本概念,当我试图学习一门低级语言时,它会让我感到困惑

我曾多次尝试理解指针,但解释很快就超出了我的理解范围,通常是因为所有示例代码都使用使用指针的语言,而我不了解其他有关指针的内容,然后我开始旋转

我是Javascript最流利的(而且非常流利)

你如何向像我这样悲伤的Javascript开发人员解释指针?有人能给我举个实际的例子吗?
甚至可以展示,如果Javascript有指针,您可以执行
x
,指针与原始变量不同,因为
y

C++规则非常简单且一致。实际上,我发现Javascript处理对象引用和原型的方式更加不直观

前言A:为什么Javascript不是一个好的起点?

在处理指针之前,首先需要从根本上理解变量。你需要知道它们是什么,以及计算机如何跟踪它们

来自Javascript背景,您习惯于分配给作为引用的对象的每个变量。也就是说,两个变量可以引用同一个对象。这本质上是指针,没有任何语法来允许更复杂的使用。您还习惯于隐式复制“基本”类型,如数字。也就是说:

var a = MyObject;
var b = a;
现在如果你改变了b,你也改变了a。您需要显式复制MyObject,以便有两个变量指向它的不同实例

var a = 5;
var b = a;
如果你改变了b,a实际上并没有改变。这是因为当a是简单类型时,将a分配给b将自动为您复制它。使用简单的数字无法获得与对象相同的行为,反之亦然,因此当您希望两个变量引用相同的数字时,必须将其包装在对象中。没有明确的方法来指示如何处理基元类型的引用和副本

您可以看到这种语法上没有变化的不一致行为(但行为上的极端变化)会使变量之间的关系及其包含的内容变得混乱。出于这个原因,我强烈建议在我们继续理解显式指针的过程中暂时摒弃这种心理模式

序言B:YOLO:堆栈上的变量生存期

P> >让我们从这里用C++来讨论。在变量与指针的关系方面,C++是最显式的语言之一。C++是一个很好的入口点,因为它的内存和寿命都足够低,但是在一个抽象的层次上,它足够高,可以理解事物。 <>所以,在C++中,当你创建任何变量时,它都存在于某个范围内。有两种方法可以在堆栈和堆上创建变量

堆栈是指应用程序的调用堆栈。每个大括号对将一个新上下文推送到堆栈上(当它用完时弹出)。创建局部变量时,它存在于特定堆栈帧中,当该堆栈帧弹出时,该变量将被销毁

范围的一个简单示例:

#include <iostream>
#include <string>

struct ScopeTest{
    ScopeTest(std::string a_name):
        name(a_name){
        std::cout << "Create " << name << std::endl;
    }
    ~ScopeTest(){
        std::cout << "Destroy " << name << std::endl;
    }
    ScopeTest(ScopeTest &a_copied){
        std::cout << "Copy " << a_copied.name << std::endl;
        name = a_copied.name + "(copy)";
        a_copied.name += "(original)";
    }

    std::string name;
};

ScopeTest getVariable(){ //Stack frame push
    ScopeTest c("c"); //Create c
    return c; //Copy c + Destroy c(original)
}

int main(){
    ScopeTest a("a"); //Create a
    {
        ScopeTest b("b"); //Create b
        ScopeTest d = getVariable();
    } //Destroy c(copy) + Destroy b
} //Destroy a
这应该清楚地说明变量是如何将其寿命与堆栈联系起来的,它是如何被复制的,以及它何时消亡

序言C:YOLO堆上的变量生存期

从概念上讲,这很有趣,但变量也可以在堆栈之外分配,这称为“堆”内存,因为它基本上是无结构的。堆内存的问题是,实际上没有基于作用域的自动清理。所以你需要一种方法把它绑在某种“把手”上来跟踪它

我将在这里举例说明:

{
    new ScopeTest("a"); //Create a
} //Whoa, we haven't destroyed it!  Now we are leaking memory!
所以,很明显,我们不能只说“新X”而不跟踪它。内存会被分配,但不会将其自身与寿命绑定,因此它会永远存在(就像一个内存吸血鬼!)

在Javascript中,您只需将它绑定到一个变量,当对它的最后一次引用终止时,对象终止。稍后我将讨论C++中更高级的主题,这是允许的,但现在让我们来看简单的指针。 <>在C++中,当用新的变量分配变量时,跟踪它的最好方法是将它分配给指针。 前言D:指针和堆

正如我所建议的,我们可以用指针跟踪堆上分配的内存。我们以前的漏洞程序可以这样修复:

{
    ScopeTest *a = new ScopeTest("a"); //Create a
    delete a; //Destroy a
}
范围测试*a;创建一个指针,并将其分配给一个新的ScopeTest(“a”)给我们一个句柄,我们可以实际使用它来清理和引用堆内存中存在的变量。我知道堆内存听起来有点混乱,但它基本上是一堆杂乱的内存,你可以指着它说“嘿,你,我想要一个没有寿命的变量,做一个,让我指着它”

使用new关键字创建的任何变量后面必须紧跟1(且不超过1)delete,否则它将永远存在,耗尽内存。如果您多次尝试删除除0以外的任何内存地址(这是一个无操作),则可能是在删除不受程序控制的内存,从而导致未定义的行为

范围测试*a;声明一个指针。从现在开始,每当你说“a”时,你指的是一个特定的内存地址*a将引用该内存地址处的实际对象,您可以访问它的属性(*a).name。C++中的A-是一个特殊的操作符,它与(*A)的操作相同。 其中007FB430是内存地址的十六进制表示

因此,在最纯粹的意义上,指针实际上是一个内存地址,并且能够将该地址视为一个变量

指针和变量之间的关系

不过,我们不必只使用带有堆分配内存的指针!我们可以
{
    ScopeTest *a = new ScopeTest("a"); //Create a
    delete a; //Destroy a
}
{
    ScopeTest *a = new ScopeTest("a"); //Create a
    std::cout << a << ": " << (*a).name << ", " << a->name << std::endl;
    delete a; //Destroy a
}
007FB430: a, a
int a = 5; //variable named a has a value of 5.
int *pA = &a; //pointer named pA is now referencing the memory address of a (we reference "a" with & to get the address).
std::cout << *pA << ", " << a << std::endl; //output 5, 5
a = 6;
std::cout << *pA << ", " << a << std::endl; //output 6, 6
*pA = 7;
std::cout << *pA << ", " << a << std::endl; //output 7, 7
int b = 20;
pA = &b;
std::cout << *pA << ", " << a << ", " << b << std::endl; //output 20, 7, 20
*pA = 25;
std::cout << *pA << ", " << a << ", " << b << std::endl; //output 25, 7, 25
pA = &a;
std::cout << *pA << ", " << a << ", " << b << std::endl; //output 7, 7, 25
*pA = 8;
std::cout << *pA << ", " << a << ", " << b << std::endl; //output 8, 8, 25
b = 30;
pA = &b;
std::cout << *pA << ", " << a << ", " << b << std::endl; //output 30, 8, 30
char text[] { 'H', 'e', 'l', 'l', 'o', '\0' }; //could be char text[] = "Hello"; but I want to show the \0 explicitly
char* letter = text;

for (char* letter = &text[0]; *letter != '\0';++letter){
    std::cout << "[" << *letter << "]";
}
std::cout << std::endl;
struct Node {
    int value = 0;
    Node* previous = nullptr;
    Node* next = nullptr;
};
struct List {
    List(){
        head = new Node();
        tail = head;
    }
    ~List(){
        std::cout << "Destructor: " << std::endl;
        Node* current = head;
        while (current != nullptr){
            Node* next = current->next;
            std::cout << "Deleting: " << current->value << std::endl;
            delete current;
            current = next;
        }
    }
    void Append(int value){
        Node* previous = tail;
        tail = new Node();
        tail->value = value;
        tail->previous = previous;
        previous->next = tail;
    }

    void Print(){
        std::cout << "Printing the List:" << std::endl;
        Node* current = head;
        for (Node* current = head; current != nullptr;current = current->next){
            std::cout << current->value << std::endl;
        }
    }
    Node* tail;
    Node* head;
};
List sampleList;
sampleList.Append(5);
sampleList.Append(6);
sampleList.Append(7);
sampleList.Append(8);
sampleList.Print();
{
    new ScopeTest("a"); //Create a
} //Whoa, we haven't destroyed it!  Now we are leaking memory!
{
    std::shared_ptr<ScopeTest> a(new ScopeTest("a")); //Create a
}//Destroy a
{
    std::shared_ptr<ScopeTest> showingSharedOwnership;
    {
        std::shared_ptr<ScopeTest> a(new ScopeTest("a")); //"Create a" (ref count 1)
        showingSharedOwnership = a; //increments a's ref count by 1. (now 2)
    } //the shared_ptr named a is destroyed, decrements ref count by 1. (now 1)
} //"Destroy a" showingSharedOwnership dies and decrements the ref count by 1. (now 0)
int a = 10;
int b = 25;
void foo(int x, int y);
int * p = &a;    // p stores the address of 'a'

*p = 12;         // now a == 12
void swap(int * p, int * q)
{
    int tmp = *p;
    *p = *q;
    *q = tmp;
}
swap(&a, &b);
 T a;
 mangle_me(&a);
 void mangle_me(T * p)
 {
     // use *p
 }