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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4955198/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:01:17  来源:igfitidea点击:

What does "dereferencing" a pointer mean?

c++cpointersdereference

提问by asir

Please include an example with the explanation.

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

回答by Tony Delroy

Reviewing the basic terminology

回顾基本术语

It's usuallygood enough - unless you're programming assembly - to envisage a pointercontaining a numeric memory address, with 1 referring to the second byte in the process's memory, 2 the third, 3 the fourth and so on....

这是通常不够好-除非你正在编写组件-想象一个指针包含数字的内存地址,1指的是第二个字节的进程的内存,2个第三,3,第四等等....

  • What happened to 0 and the first byte? Well, we'll get to that later - see null pointersbelow.
  • For a more accurate definition of what pointers store, and how memory and addresses relate, see "More about memory addresses, and why you probably don't need to know"at the end of this answer.
  • 0 和第一个字节发生了什么?好吧,我们稍后再谈——参见下面的空指针
  • 有关指针存储内容以及内存和地址如何关联的更准确定义,请参阅本答案末尾的“有关内存地址的更多信息,以及您可能不需要知道的原因”

When you want to access the data/value in the memory that the pointer points to - the contents of the address with that numerical index - then you dereferencethe pointer.

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

Different computer languages have different notations to tell the compiler or interpreter that you're now interested in the pointed-to object's (current) value - I focus below on C and C++.

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

A pointer scenario

指针场景

Consider in C, given a pointer such as pbelow...

考虑在 C 中,给定一个指针,p如下所示......

const char* p = "abc";

...four bytes with the numerical values used to encode the letters 'a', 'b', 'c', and a 0 byte to denote the end of the textual data, are stored somewhere in memory and the numerical address of that data is stored in p. This way C encodes text in memory is known as ASCIIZ.

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

For example, if the string literal happened to be at address 0x1000 and pa 32-bit pointer at 0x2000, the memory content would be:

例如,如果字符串字面量恰好在地址 0x1000 和p32 位指针在 0x2000,则内存内容将是:

Memory Address (hex)    Variable name    Contents
1000                                     'a' == 97 (ASCII)
1001                                     'b' == 98
1002                                     'c' == 99
1003                                     0
...
2000-2003               p                1000 hex

Note that there is no variable name/identifier for address 0x1000, but we can indirectly refer to the string literal using a pointer storing its address: p.

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

Dereferencing the pointer

取消引用指针

To refer to the characters ppoints to, we dereference pusing one of these notations (again, for C):

为了引用指向的字符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]

You can also move pointers through the pointed-to data, dereferencing them as you go:

您还可以在指向的数据中移动指针,随时取消引用它们:

++p;  // Increment p so it's now 0x1001
assert(*p == 'b');  // p == 0x1001 which is where the 'b' is...

If you have some data that can be written to, then you can do things like this:

如果您有一些可以写入的数据,那么您可以执行以下操作:

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

Above, you must have known at compile time that you would need a variable called x, and the code asks the compiler to arrange where it should be stored, ensuring the address will be available via &x.

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

Dereferencing and accessing a structure data member

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

In C, if you have a variable that is a pointer to a structure with data members, you can access those members using the ->dereferencing operator:

在 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_

Multi-byte data types

多字节数据类型

To use a pointer, a computer program also needs some insight into the type of data that is being pointed at - if that data type needs more than one byte to represent, then the pointer normally points to the lowest-numbered byte in the data.

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

So, looking at a slightly more complex example:

所以,看一个稍微复杂的例子:

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]

Pointers to dynamically allocated memory

指向动态分配内存的指针

Sometimes you don't know how much memory you'll need until your program is running and sees what data is thrown at it... then you can dynamically allocate memory using malloc. It is common practice to store the address in a pointer...

有时您不知道需要多少内存,直到您的程序运行并看到向其抛出的数据……然后您可以使用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

In C++, memory allocation is normally done with the newoperator, and deallocation with delete:

在 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;

See also C++ smart pointersbelow.

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

Losing and leaking addresses

地址丢失和泄露

Often a pointer may be the only indication of where some data or buffer exists in memory. If ongoing use of that data/buffer is needed, or the ability to call free()or deleteto avoid leaking the memory, then the programmer must operate on a copy of the pointer...

通常,指针可能是某些数据或缓冲区存在于内存中的唯一指示。如果需要持续使用该数据/缓冲区,或者需要调用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);

...or carefully orchestrate reversal of any changes...

...或仔细安排任何更改的逆转...

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

C++ smart pointers

C++ 智能指针

In C++, it's best practice to use smart pointerobjects to store and manage the pointers, automatically deallocating them when the smart pointers' destructors run. Since C++11 the Standard Library provides two, unique_ptrfor when there's a single owner for an allocated object...

在 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

...and shared_ptrfor share ownership (using reference counting)...

...以及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

Null pointers

空指针

In C, NULLand 0- and additionally in C++ nullptr- can be used to indicate that a pointer doesn't currently hold the memory address of a variable, and shouldn't be dereferenced or used in pointer arithmetic. For example:

在 C 中,NULL以及0- 此外在 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

In C and C++, just as inbuilt numeric types don't necessarily default to 0, nor boolsto false, pointers are not always set to NULL. All these are set to 0/false/NULL when they're staticvariables or (C++ only) direct or indirect member variables of static objects or their bases, or undergo zero initialisation (e.g. new T();and new T(x, y, z);perform zero-initialisation on T's members including pointers, whereas new T;does not).

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

Further, when you assign 0, NULLand nullptrto a pointer the bits in the pointer are not necessarily all reset: the pointer may not contain "0" at the hardware level, or refer to address 0 in your virtual address space. The compiler is allowed to store something else there if it has reason to, but whatever it does - if you come along and compare the pointer to 0, NULL, nullptror another pointer that was assigned any of those, the comparison must work as expected. So, below the source code at the compiler level, "NULL" is potentially a bit "magical" in the C and C++ languages...

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

More about memory addresses, and why you probably don't need to know

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

More strictly, initialised pointers store a bit-pattern identifying either NULLor a (often virtual) memory address.

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

The simple case is where this is a numeric offset into the process's entire virtual address space; in more complex cases the pointer may be relative to some specific memory area, which the CPU may select based on CPU "segment" registers or some manner of segment id encoded in the bit-pattern, and/or looking in different places depending on the machine code instructions using the address.

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

For example, an int*properly initialised to point to an intvariable might - after casting to a float*- access memory in "GPU" memory quite distinct from the memory where the intvariable is, then once cast to a function pointer might point into further distinct memory holding machine opcodes for the program (but the value of the int*is effectively a random, invalid pointer within these other memory regions).

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

3GL programming languages like C and C++ tend to hide this complexity, such that:

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

  • If the compiler gives you a pointer to a variable or function, you can dereference it freely (as long as the variable's not destructed/deallocated meanwhile) and it's the compiler's problem whether e.g. a particular CPU segment register needs to be restored beforehand, or a distinct machine code instruction used

  • If you get a pointer to an element in an array, you can use pointer arithmetic to move anywhere else in the array, or even to form an address one-past-the-end of the array that's legal to compare with other pointers to elements in the array (or that have similarly been moved by pointer arithmetic to the same one-past-the-end value); again in C and C++, it's up to the compiler to ensure this "just works"

  • Specific OS functions, e.g. shared memory mapping, may give you pointers, and they'll "just work" within the range of addresses that makes sense for them

  • Attempts to move legal pointers beyond these boundaries, or to cast arbitrary numbers to pointers, or use pointers cast to unrelated types, typically have undefined behaviour, so should be avoided in higher level libraries and applications, but code for OSes, device drivers, etc. may need to rely on behaviour left undefined by the C or C++ Standard, that is nevertheless well defined by their specific implementation or hardware.

  • 如果编译器给你一个变量或函数的指针,你可以自由地取消引用它(只要变量没有同时被破坏/释放),这是编译器的问题,例如特定的 CPU 段寄存器是否需要预先恢复,或者使用了不同的机器代码指令

  • 如果获得指向数组中某个元素的指针,则可以使用指针算术移动数组中的任何其他位置,甚至可以在数组末尾处形成一个地址,该地址与指向元素的其他指针进行比较是合法的在数组中(或通过指针算术类似地移动到相同的最后一个值);再次在 C 和 C++ 中,由编译器来确保它“正常工作”

  • 特定的操作系统函数,例如共享内存映射,可能会给你一些指针,它们会在对它们有意义的地址范围内“正常工作”

  • 尝试将合法指针移出这些边界,或将任意数字转换为指针,或使用转换为不相关类型的指针,通常具有未定义的行为,因此应避免在更高级别的库和应用程序中使用,但应避免用于操作系统、设备驱动程序等的代码. 可能需要依赖 C 或 C++ 标准未定义的行为,但它们的特定实现或硬件已经很好地定义了这些行为。

回答by Mahesh

Dereferencing a pointer means getting the value that is stored in the memory location pointed by the pointer. The operator * is used to do this, and is called the dereferencing operator.

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

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.

回答by bobobobo

A pointer is a "reference" to a value.. much like a library call number is a reference to a book. "Dereferencing" the call number is physically going through and retrieving that book.

指针是对值的“引用”……就像图书馆索书号是对书的引用一样。“取消引用”电话号码正在物理上通过并检索该书。

int a=4 ;
int *pA = &a ;
printf( "The REFERENCE/call number for the variable `a` is %p\n", pA ) ;

// The * causes pA to DEREFERENCE...  `a` via "callnumber" `pA`.
printf( "%d\n", *pA ) ; // prints 4.. 

If the book isn't there, the librarian starts shouting, shuts the library down, and a couple of people are set to investigate the cause of a person going to find a book that isn't there.

如果这本书不在那里,图书管理员就会开始大喊大叫,关闭图书馆,然后几个人开始调查一个人会找到一本不在那里的书的原因。

回答by Fahad Naeem

In simple words, dereferencing means accessing the value from a certain memory location against which that pointer is pointing.

简而言之,取消引用意味着从该指针指向的某个内存位置访问值。

回答by atp

Code and explanation from Pointer Basics:

Pointer Basics 中的代码和解释:

The dereference operation starts at the pointer and follows its arrow over to access its pointee. The goal may be to look at the pointee state or to change the pointee state. The dereference operation on a pointer only works if the pointer has a pointee -- the pointee must be allocated and the pointer must be set to point to it. The most common error in pointer code is forgetting to set up the pointee. The most common runtime crash because of that error in the code is a failed dereference operation. In Java the incorrect dereference will be flagged politely by the runtime system. In compiled languages such as C, C++, and Pascal, the incorrect dereference will sometimes crash, and other times corrupt memory in some subtle, random way. Pointer bugs in compiled languages can be difficult to track down for this reason.

取消引用操作从指针开始,并按照它的箭头来访问它的指针对象。目标可能是查看指针对象状态或更改指针对象状态。指针上的取消引用操作仅在指针具有指针对象时才有效——指针必须被分配并且指针必须设置为指向它。指针代码中最常见的错误是忘记设置指针对象。由于代码中的错误而导致的最常见的运行时崩溃是取消引用操作失败。在 Java 中,运行时系统会礼貌地标记不正确的取消引用。在 C、C++ 和 Pascal 等编译语言中,不正确的解引用有时会崩溃,有时会以某种微妙的、随机的方式破坏内存。

void main() {   
    int*    x;  // Allocate the pointer x
    x = malloc(sizeof(int));    // Allocate an int pointee,
                            // and set x to point to it
    *x = 42;    // Dereference x to store 42 in its pointee   
}

回答by stsp

I think all the previous answers are wrong, as they state that dereferencing means accessing the actual value. Wikipedia gives the correct definition instead: https://en.wikipedia.org/wiki/Dereference_operator

我认为以前的所有答案都是错误的,因为它们指出取消引用意味着访问实际值。维基百科给出了正确的定义:https: //en.wikipedia.org/wiki/Dereference_operator

It operates on a pointer variable, and returns an l-value equivalent to the value at the pointer address. This is called "dereferencing" the pointer.

它对指针变量进行操作,并返回与指针地址处的值等效的左值。这称为“解除引用”指针。

That said, we can dereference the pointer without ever accessing the value it points to. For example:

也就是说,我们可以在不访问它指向的值的情况下取消引用该指针。例如:

char *p = NULL;
*p;

We dereferenced the NULL pointer without accessing its value. Or we could do:

我们取消引用 NULL 指针而不访问它的值。或者我们可以这样做:

p1 = &(*p);
sz = sizeof(*p);

Again, dereferencing, but never accessing the value. Such code will NOT crash: The crash happens when you actually accessthe data by an invalid pointer. However, unfortunately, according the the standard, dereferencing an invalid pointer is an undefined behaviour (with a few exceptions), even if you don't try to touch the actual data.

同样,取消引用,但从不访问该值。这样的代码不会崩溃:当您通过无效指针实际访问数据时会发生崩溃。然而,不幸的是,根据标准,取消引用无效指针是一种未定义的行为(有一些例外),即使您不尝试接触实际数据。

So in short: dereferencing the pointer means applying the dereference operator to it. That operator just returns an l-value for your future use.

简而言之:取消引用指针意味着对其应用取消引用运算符。该运算符只返回一个 l 值供您将来使用。