关于指针和引用语法
令人尴尬虽然我可能知道我并不是唯一有这个问题的人。
我多年来一直在使用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"
我不知道任何网站,但我会尝试用非常简单的术语来解释它。 您只需要理解三件事:
-
variable
将包含variable
的内容。 这意味着如果变量是指针,它将包含它指向的内存地址。 -
*variable
(仅对指针有效)将包含指向的变量的内容。 如果它指向的变量是另一个指针ptr2
,那么*variable
和ptr2
将是同一个东西;**variable
和*ptr2
也是一样的。 -
&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
。
我不确定你到底在找什么,但我发现记住运算符优先级和关联性规则很有帮助。 也就是说,如果你感到困惑,你可能会抛出更多的麻烦来消除歧义,即使这只是为了你的利益而不是编译器。
编辑:我想我现在可能会更好地理解你的问题了。 我喜欢把一堆指针想象成一个堆栈,底部有一个值。 解除引用操作符(*)会弹出堆栈,您可以在最后找到值本身。 引用运算符(&)允许您将另一级别的间接引入堆栈。 请注意,移动另一个步骤总是合法的,但尝试取消引用该值类似于弹出空堆栈。