c++ - "dereferencing"指针是什么意思?

标签 c++ c pointers dereference

请在说明中包含一个示例。

最佳答案

回顾基本术语

通常情况下就足够了 - 除非您正在编写程序集 - 设想一个 指针包含一个数字内存地址,其中 1 表示进程内存中的第二个字节,2 表示第三个字节,3 表示第四个字节,依此类推....

  • 0 和第一个字节发生了什么?好吧,我们稍后再谈——参见下面的空指针。
  • 有关指针存储内容以及内存和地址如何关联的更准确定义,请参阅本答案末尾的“有关内存地址的更多信息,以及您可能不需要知道的原因”。

  • 当您想访问指针指向的内存中的数据/值时 - 具有该数字索引的地址的内容 - 那么您 取消引用 指针。

    不同的计算机语言有不同的符号来告诉编译器或解释器您现在对指向对象的(当前)值感兴趣 - 我在下面关注 C 和 C++。

    指针场景

    考虑在 C 中,给定一个指针,例如 p以下...
    const char* p = "abc";
    

    ...带有用于编码字母“a”、“b”、“c”的数值的四个字节和一个表示文本数据结尾的 0 字节,存储在内存中的某处,以及它的数字地址数据存储在 p .这种 C 在内存中编码文本的方式被称为 ASCIIZ .

    例如,如果字符串文字恰好位于地址 0x1000 和 p 0x2000 处的 32 位指针,内存内容将是:
    Memory Address (hex)    Variable name    Contents
    1000                                     'a' == 97 (ASCII)
    1001                                     'b' == 98
    1002                                     'c' == 99
    1003                                     0
    ...
    2000-2003               p                1000 hex
    

    请注意,地址 0x1000 没有变量名称/标识符,但我们可以使用存储其地址的指针间接引用字符串文字:p .

    取消引用指针

    引用字符p指向,我们取消引用 p使用这些符号之一(同样,对于 C):
    assert(*p == 'a');  // The first character at address p will be 'a'
    assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
                         // p and 1 times the size of the things to which p points:
                         // In this case they're char which are 1 byte in C...
    assert(*(p + 1) == 'b');  // Another notation for p[1]
    

    您还可以在指向的数据中移动指针,随时取消引用它们:
    ++p;  // Increment p so it's now 0x1001
    assert(*p == 'b');  // p == 0x1001 which is where the 'b' is...
    

    如果您有一些可以写入的数据,那么您可以执行以下操作:
    int x = 2;
    int* p_x = &x;  // Put the address of the x variable into the pointer p_x
    *p_x = 4;       // Change the memory at the address in p_x to be 4
    assert(x == 4); // Check x is now 4
    

    上面,您一定在编译时就知道您需要一个名为 x 的变量。 ,并且代码要求编译器安排它应该存储的位置,确保地址可以通过 &x 获得。 .

    取消引用和访问结构数据成员

    在 C 中,如果您有一个变量是指向具有数据成员的结构的指针,您可以使用 -> 访问这些成员。解引用运算符:
    typedef struct X { int i_; double d_; } X;
    X x;
    X* p = &x;
    p->d_ = 3.14159;  // Dereference and access data member x.d_
    (*p).d_ *= -1;    // Another equivalent notation for accessing x.d_
    

    多字节数据类型

    要使用指针,计算机程序还需要深入了解所指向的数据类型——如果该数据类型需要多个字节来表示,那么指针通常指向数据中编号最低的字节。

    所以,看一个稍微复杂的例子:
    double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
    double* p = sizes;
    assert(p[0] == 10.3);  // Knows to look at all the bytes in the first double value
    assert(p[1] == 13.4);  // Actually looks at bytes from address p + 1 * sizeof(double)
                           // (sizeof(double) is almost always eight bytes)
    ++p;                   // Advance p by sizeof(double)
    assert(*p == 13.4);    // The double at memory beginning at address p has value 13.4
    *(p + 2) = 29.8;       // Change sizes[3] from 19.4 to 29.8
                           // Note earlier ++p and + 2 here => sizes[3]
    

    指向动态分配内存的指针

    有时你不知道你需要多少内存,直到你的程序运行并看到什么数据被抛出......然后你可以使用 malloc 动态分配内存。 .通常的做法是将地址存储在指针中......
    int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
    *p = 10;            // Dereference the pointer to the memory, then write a value in
    fn(*p);             // Call a function, passing it the value at address p
    (*p) += 3;          // Change the value, adding 3 to it
    free(p);            // Release the memory back to the heap allocation library
    

    在 C++ 中,内存分配通常使用 new 完成。运算符,并使用 delete 解除分配:
    int* p = new int(10); // Memory for one int with initial value 10
    delete p;
    
    p = new int[10];      // Memory for ten ints with unspecified initial value
    delete[] p;
    
    p = new int[10]();    // Memory for ten ints that are value initialised (to 0)
    delete[] p;
    

    另请参阅下面的 C++ 智能指针。

    地址丢失和泄露

    通常,指针可能是某些数据或缓冲区存在于内存中的唯一指示。如果需要持续使用该数据/缓冲区,或者能够调用 free()delete为了避免内存泄漏,程序员必须对指针的拷贝进行操作......
    const char* p = asprintf("name: %s", name);  // Common but non-Standard printf-on-heap
    
    // Replace non-printable characters with underscores....
    for (const char* q = p; *q; ++q)
        if (!isprint(*q))
            *q = '_';
    
    printf("%s\n", p); // Only q was modified
    free(p);
    

    ...或仔细安排任何更改的逆转...
    const size_t n = ...;
    p += n;
    ...
    p -= n;  // Restore earlier value...
    free(p);
    

    C++ 智能指针

    在 C++ 中,最好使用 smart pointer对象来存储和管理指针,当智能指针的析构函数运行时自动释放它们。由于 C++11 标准库提供了两个, unique_ptr 当分配的对象只有一个所有者时......
    {
        std::unique_ptr<T> p{new T(42, "meaning")};
        call_a_function(p);
        // The function above might throw, so delete here is unreliable, but...
    } // p's destructor's guaranteed to run "here", calling delete
    

    ...和 ​​ shared_ptr 对于股权(使用 reference counting )...
    {
        auto p = std::make_shared<T>(3.14, "pi");
        number_storage1.may_add(p); // Might copy p into its container
        number_storage2.may_add(p); // Might copy p into its container    } // p's destructor will only delete the T if neither may_add copied it
    

    空指针

    在 C 中,NULL0 - 另外在 C++ 中 nullptr - 可用于指示指针当前不保存变量的内存地址,不应取消引用或在指针运算中使用。例如:
    const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
    int c;
    while ((c = getopt(argc, argv, "f:")) != -1)
        switch (c) {
          case f: p_filename = optarg; break;
        }
    if (p_filename)  // Only NULL converts to false
        ...   // Only get here if -f flag specified
    

    在 C 和 C++ 中,就像内置数字类型不一定默认为 0 ,也不是 boolsfalse , 指针并不总是设置为 NULL .当它们是 static 时,所有这些都设置为 0/false/NULL变量或(仅限 C++)静态对象或其基类的直接或间接成员变量,或进行零初始化(例如 new T();new T(x, y, z); 对包括指针在内的 T 成员执行零初始化,而 new T; 不会)。

    此外,当您分配 0 时, NULLnullptr对于指针,指针中的位不一定全部复位:指针在硬件级别可能不包含“0”,或者在您的虚拟地址空间中引用地址 0。如果有理由,允许编译器在那里存储其他东西,但无论它做什么 - 如果你来比较指针到 0 , NULL , nullptr或分配了其中任何一个的另一个指针,比较必须按预期工作。因此,在编译器级别的源代码之下,“NULL”在 C 和 C++ 语言中可能有点“神奇”......

    更多关于内存地址,以及为什么你可能不需要知道

    更严格地说,初始化的指针存储一个位模式标识 NULL或(通常是 virtual )内存地址。

    简单的情况是,这是进程整个虚拟地址空间的数字偏移量;在更复杂的情况下,指针可能与某个特定的内存区域相关,CPU 可以根据 CPU“段”寄存器或位模式中编码的某种段 ID 方式选择该内存区域,和/或根据使用地址的机器代码指令。

    例如,一个 int*正确初始化为指向 int变量可能 - 在转换为 float* 之后- 访问“GPU”内存中的内存与 int 中的内存截然不同变量是,然后一旦转换为函数指针并用作函数指针,它可能会指向程序的其他不同内存保存机器操作码(int* 的数值实际上是这些其他内存区域内的随机、无效指针)。

    像 C 和 C++ 这样的 3GL 编程语言倾向于隐藏这种复杂性,例如:
  • 如果编译器给你一个指向变量或函数的指针,你可以自由地取消引用它(只要变量没有同时被破坏/释放),这是编译器的问题,例如需要预先恢复特定的 CPU 段寄存器,或使用不同的机器代码指令
  • 如果您获得指向数组中某个元素的指针,则可以使用指针算术移动数组中的任何其他位置,甚至可以在数组末尾处形成一个地址,该地址与指向其他元素的指针进行比较是合法的在数组中(或通过指针算术类似地移动到相同的最后一个值);再次在 C 和 C++ 中,由编译器来确保它“正常工作”
  • 特定的操作系统功能,例如共享内存映射,可能会为您提供指针,并且它们将在对它们有意义的地址范围内“正常工作”
  • 尝试将合法指针移出这些边界,或将任意数字转换为指针,或使用转换为不相关类型的指针,通常具有 undefined behaviour ,因此应该避免在更高级别的库和应用程序中使用,但是操作系统、设备驱动程序等的代码可能需要依赖 C 或 C++ 标准未定义的行为,但它们的特定实现或硬件已经很好地定义了这些行为。
  • 关于c++ - "dereferencing"指针是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4955198/

    相关文章:

    c++ - xerces-c++ 编译/链接问题

    c++ - 如何在 Windows 中用 C++ 创建定时器

    c++ - 如何从 shared_ptr ResourceManager 释放资源

    c - 如何从 ARM 微 Controller 的 GPIO 端口读取值?

    c - 为什么 makecontext/swapcontext 不适用于 pthread_mutex_lock/pthread_mutex_unlock

    c++ - 错误 : cannot convert Elem to Elem* for argument 1 to void addHead(Elem*, 元素 *)

    c++ - 为什么我会在 MSVC 上收到此警告?

    c - 使用 stdin 读取单列数据文件并使用 C 中的函数值返回值

    c++ - 为什么不能跨程序集删除指针?

    c++ - 指向类对象的空指针: Initialization inside a function