“取消引用”指针是什么意思?

请说明一个例子。


回顾基本术语

它通常足够好 - 除非你正在编程程序集 - 设想一个包含数字内存地址的指针 ,其中1指向进程内存中的第二个字节,2第三个,第三个,第四个等......

  • 0和第一个字节发生了什么? 那么,我们将在稍后介绍 - 请参阅下面的空指针。
  • 要更准确地定义指针存储的内容以及内存和地址的关系,请参阅“关于内存地址的更多信息,以及您可能不需要知道的原因”。
  • 当你想要访问指针指向的内存中的数据/值时 - 使用该数字索引的地址内容 - 然后解除指针的引用

    不同的计算机语言有不同的符号来告诉编译器或解释器,你现在对指向值感兴趣 - 我将重点放在C和C ++上。

    指针场景

    考虑在C中,给出如下p的指针...

    const char* p = "abc";
    

    ...用于编码字母'a','b','c'的数字值和用于表示文本数据结尾的0字节的四个字节被存储在存储器中的某个位置,并且数字地址数据被存储在p

    例如,如果字符串文字恰好位于地址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中,如果有一个变量是指向具有数据成员的结构的指针,则可以使用-> dereferencing操作符访问这些成员:

    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)
    assert(++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 = 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("%sn", p); // Only q was modified
    free(p);
    

    ...或仔细协调逆转任何变化......

    const size_t n = ...;
    p += n;
    ...
    p -= n;  // Restore earlier value...
    

    C ++智能指针

    在C ++中,最好的做法是使用智能指针对象来存储和管理指针,当智能指针的析构函数运行时自动释放它们。 由于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共享所有权(使用引用计数)...

    {
        std::shared_ptr<T> p(new T(3.14, "pi"));
        number_storage.may_add(p); // Might copy p into its container
    } // p's destructor will only delete the T if number_storage didn't copy
    

    空指针

    在C中, NULL0以及C ++中的nullptr可用于指示指针当前不保存变量的内存地址,并且不应将其解除引用或用于指针算术。 例如:

    const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
    char c;
    while ((c = getopt(argc, argv, "f:")) != EOF)
        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 ,也没有bools ,以false ,指针并不总是设置为NULL 。 当它们是static变量或(仅限C ++)静态对象的直接或间接成员变量或它们的基础,或者经历零初始化(例如, new T();以及new T(x, y, z);在包括指针的T的成员上执行零初始化,而new T;不)。

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

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

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

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

    例如,一个int*正确初始化为指向一个int变量,可能会 - 在转换为float*之后 - 访问“GPU”内存中与int变量完全不同的值,然后一次强制转换为函数指针可能指向不同的内存保持机器操作码的功能。

    像C和C ++这样的3GL编程语言倾向于隐藏这种复杂性,例如:

  • 如果编译器给你一个指向变量或函数的指针,你可以自由地对其进行解引用(只要变量没有被破坏/解除分配),这是编译器的问题,例如,是否需要事先恢复特定的CPU寄存器,机器代码指令使用

  • 如果你得到一个指向数组中元素的指针,你可以使用指针算法来移动数组中的任何其他地方,甚至可以形成一个地址的一个结尾,这是合法的,以便与其他元素指针进行比较在数组中(或者通过指针运算被类似地移动到相同的一个过去的最终值); 再次在C和C ++中,由编译器来确保这个“正常工作”

  • 特定的操作系统功能,例如共享内存映射可能会给你指针,并且它们将在地址范围内“正常工作”,这对他们来说是有意义的

  • 尝试将合法指针移动到这些边界之外,或者将任意数字转换为指针,或将指针转换为不相关的类型,通常都会有未定义的行为,所以应该在更高级别的库和应用程序中避免使用,但是为操作系统,设备驱动程序等可能需要依赖C或C ++未定义的行为,这是由其特定硬件决定的。


  • 解引用指针意味着获取存储在指针指向的内存位置中的值。 运算符*用于执行此操作,称为解引用运算符。

    int a = 10;
    int* ptr = &a;
    
    printf("%d", *ptr); // With *ptr I'm dereferencing the pointer. 
                        // Which means, I am asking the value pointed at by the pointer.
                        // ptr is pointing to the location in memory of the variable a.
                        // In a's location, we have 10. So, dereferencing gives this value.
    
    // Since we have indirect control over a's location, we can modify its content using the pointer. This is an indirect way to access a.
    
     *ptr = 20;         // Now a's content is no longer 10, and has been modified to 20.
    

    指针是对参考值的“引用”。很像图书馆电话号码是对书籍的参考。 “取消引用”电话号码实际上正在通过并检索该书。

    int a=4 ;
    int *pA = &a ;
    printf( "The REFERENCE/call number for the variable `a` is %pn", pA ) ;
    
    // The * causes pA to DEREFERENCE...  `a` via "callnumber" `pA`.
    printf( "%dn", *pA ) ; // prints 4.. 
    

    如果图书不在那里,图书管理员会开始大喊大叫,关闭图书馆,并且会有几个人调查一个人去找一本不在那里的书的原因。

    链接地址: http://www.djcxy.com/p/40473.html

    上一篇: What does "dereferencing" a pointer mean?

    下一篇: What is the meaning of prepended double colon "::"?