关于指针和引用语法

令人尴尬虽然我可能知道我并不是唯一有这个问题的人。

我多年来一直在使用C / C ++。 我从来没有遇到过解决地址,指针,指针和引用的概念的问题。

然而,我经常发现自己绊倒用C语法表达它们。 不是像声明或解除引用这样的基础知识,而是更常见的事情,例如获取指向指针的地址,或指向引用的指针等。基本上任何超出规范的间接级别或两个级别。 通常我会遇到操作符的各种半逻辑组合,直到我找到正确的操作符。

显然,在某条线上,我错过了一两条规则,简化并使它全部落实到位。 所以我想我的问题是:你是否知道一个网站或参考文献清楚而深入地涵盖了这个问题?

我发现右 – 右 – 右规则很有用。 它告诉您如何读取声明,以便按顺序获取所有指针和引用。 例如:

int *foo(); 

使用右 – 右 – 右规则,您可以将其翻译为英语,因为“foo是一个返回指向整数的指针的函数”。

 int *(*foo)(); // "foo is a pointer to a function returning a pointer to an int" int (*foo[])(); // "foo is an array of pointers to functions returning ints" 

右 – 右 – 右规则的大多数解释是针对C而不是C ++编写的,因此它们倾向于省略引用。 它们就像在这种情况下的指针一样工作。

 int &foo; // "foo is a reference to an integer" 

我不知道任何网站,但我会尝试用非常简单的术语来解释它。 您只需要理解三件事:

  1. variable将包含variable的内容。 这意味着如果变量是指针,它将包含它指向的内存地址。
  2. *variable (仅对指针有效)将包含指向的变量的内容。 如果它指向的变量是另一个指针ptr2 ,那么*variableptr2将是同一个东西; **variable*ptr2也是一样的。
  3. &variable将包含&variable的内存地址。 如果它是一个指针,它将是指针本身的内存地址,而不是指向的变量或指向的变量的内存地址。

现在,让我们看一个复杂的例子:

  void **list = (void **)*(void **)info.List; 

list是指向指针的指针。 现在让我们从结尾开始检查赋值的正确部分: (void **)info.List 。 这也是指向指针的指针。

然后,您会看到*: *(void **)info.List 。 这意味着这是指针info.List指向的值。

现在,整个事情: (void **)*(void **)info.List 。 这是指针info.List指向要转换为(void **)的值。

当事情变得混乱时,Typedef可能是你的朋友。 这是一个例子:

 typedef const char * literal_string_pointer; typedef literal_string_pointer * pointer_to_literal_string_pointer; void GetPointerToString(pointer_to_literal_string_pointer out_param) { *out_param = "hi there"; } 

您需要知道的是获取对象的地址会返回指向该对象的指针,并且取消引用对象会获取指针并将其转换为指向它的对象。

 T x; A a = &x; // A is T* B b = *a; // B is T C c = &a; // C is T** D d = *c; // D is T* 

从本质上讲, &运算符取一个T并给你一个T**运算符取一个T*并给你一个T ,这同样适用于更高的抽象级别,例如在T*上使用&将给你一个T**

考虑它的另一种方式是&运算符在类型中添加**取一个,这导致像&&*&**i == i

我不确定你到底在找什么,但我发现记住运算符优先级和关联性规则很有帮助。 也就是说,如果你感到困惑,你可能会抛出更多的麻烦来消除歧义,即使这只是为了你的利益而不是编译器。

编辑:我想我现在可能会更好地理解你的问题了。 我喜欢把一堆指针想象成一个堆栈,底部有一个值。 解除引用操作符(*)会弹出堆栈,您可以在最后找到值本身。 引用运算符(&)允许您将另一级别的间接引入堆栈。 请注意,移动另一个步骤总是合法的,但尝试取消引用该值类似于弹出空堆栈。