令人尴尬虽然可能我知道我不是唯一有这个问题的人。
我多年来一直在使用C / C ++。我从来没有遇到过关于地址,指针,指针指针和引用的概念的问题。
但是,我确实经常发现自己在C语法中喋喋不休地表达它们。不是像声明或解除引用这样的基础知识,而是更常见的事情,比如获取指向指针的地址,或指向引用的指针等等。基本上任何超出规范的间接级别或两个级别。通常情况下,我会遇到各种运算符的半逻辑组合,直到我找到正确的运算符。显然,沿着这条线的某个地方,我错过了一两条规则,简化并使其全部落实到位。所以我想我的问题是:你是否知道一个网站或参考文献清楚而深入地涵盖了这个问题?
答案 0 :(得分:7)
我不知道任何网站,但我会尝试用非常简单的术语来解释它。您只需要理解三件事:
variable
将包含变量的内容。这意味着如果变量是指针,它将包含它指向的内存地址。*variable
(仅对指针有效)将包含变量指向的内容。如果它指向的变量是另一个指针ptr2
,那么*variable
和ptr2
将是同一个东西; **variable
和*ptr2
也是一样的。&variable
将包含变量的内存地址。如果它是一个指针,它将是指针本身的内存地址,而 NOT 指向的变量或指向的变量的内存地址。现在,让我们看一个复杂的例子:
void **list = (void **)*(void **)info.List;
list
是指向指针的指针。现在让我们从结尾开始检查作业的正确部分:(void **)info.List
。这也是指向指针的指针。
然后,您会看到*:*(void **)info.List
。这意味着这是指针info.List指向的值。
现在,整个事情:(void **)*(void **)info.List
。这是指针info.List指向要转换为(void **)的值。
答案 1 :(得分:5)
我发现right-left-right rule很有用。它告诉您如何读取声明,以便按顺序获取所有指针和引用。例如:
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"
答案 2 :(得分:3)
当事情变得混乱时,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";
}
答案 3 :(得分:1)
我不确定你到底在找什么,但我发现记住operator precedence and associativity rules会很有帮助。也就是说,如果你感到困惑,你也可以投入更多的麻烦去消除歧义,即使这只是为了你的利益而不是编译器的。
编辑:我想我现在可能会更好地理解你的问题了。我喜欢把一堆指针想象成堆栈,底部的值。解除引用操作符(*)会弹出堆栈,您可以在最后找到值本身。引用运算符(&)允许您将另一级别的间接引入堆栈。请注意,移动另一个步骤总是合法的,但尝试取消引用该值类似于弹出空堆栈。
答案 4 :(得分:1)
您需要知道的是获取对象的地址会返回指向该对象的指针,并且取消引用对象会获取指针并将其转换为指向它的对象。
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
之类的内容。