所以在我们讨论指针和引用之间的区别之前,我觉得我们需要先谈谈声明语法,部分是为了解释为什么指针和引用声明是这样写的,部分是因为许多 C++ 程序员的写法指针和引用声明歪曲了该语法(请放心,这需要一段时间)。
在 C 和 C++ 中,声明由一系列 声明说明符 和一系列 声明符1 组成。在像
这样的声明中
static unsigned long int a[10], *p, f(void);
声明说明符是static unsigned long int,声明符是a[10]、*p和f(void)。
Array-ness、pointer-ness、function-ness 和 C++ 中的 reference-ness 都被指定为 declarator 的一部分,而不是声明说明符。这意味着当你写类似的东西时
int* p;
它被解析为
int (*p);
由于一元 * 运算符是唯一标记,编译器不需要空格来将其与 int 类型说明符或 p 标识符区分开来。你可以写成int *p;,int* p;,int * p;,甚至int*p;
这也意味着在像这样的声明中
int* p, q;
只有 p 被声明为指针 - q 是常规 int。
这个想法是变量的声明与其在代码中的使用非常匹配(“声明模仿使用”)。如果您有一个名为 p 的指向 int 的指针,并且您想访问指向的值,您可以使用 * 运算符取消引用它:
printf( "%d\n", *p );
表达式*p的类型为int,所以p的声明写成
int *p;
这告诉我们变量p 具有“指向int 的指针”类型,因为p 和一元运算符* 的组合为我们提供了@987654351 类型的表达式 @。大多数 C 程序员将编写如上所示的指针声明,* 明显与 p 分组。
现在,Bjarne 和随后的几代 C++ 程序员认为强调 p 的指针性比 *p 的 int-ness 更重要,因此他们引入了
int* p;
约定。但是,对于任何除了简单指针(或指向指针的指针)的约定,这个约定都失效了。它不适用于指向数组的指针:
int (*a)[N];
或指向函数的指针
int (*f)(void);
或指向函数的指针数组
int (*p[N])(void);
等等。将指针数组声明为
int* a[N];
只是表示思维混乱。由于[] 和() 是后缀,因此您不能通过编写将数组或函数与声明说明符相关联
int[N] a;
int(void) f;
就像你可以使用一元 * 运算符一样,但一元 * 运算符以与 [] 和 () 运算符完全相同的方式绑定到声明符。2
C++ 引用打破了关于“声明模仿使用”的规则hard。在非声明语句中,表达式&x always 产生一个指针类型。如果x 的类型为int,则&x 的类型为int *。所以& 在声明中的含义与在表达式中的含义完全不同。
这就是语法,让我们来谈谈指针与引用。
pointer 只是一个地址值(尽管带有额外的类型信息)。您可以对指针进行(一些)算术运算,可以将它们初始化为任意值(或NULL),可以将[] 下标运算符应用于它们,就好像它们是一个数组一样(实际上,数组下标操作是 定义指针操作)。指针在首次创建时不需要有效(即包含对象在该对象生命周期内的地址)。
reference 是对象或函数的另一个名称,而不仅仅是该对象或函数的地址(这就是为什么在处理引用时不使用 * 运算符的原因)。您不能对引用进行指针运算,不能为引用分配任意值等。实例化时,引用必须引用有效的对象或函数。没有具体说明引用在内部是如何表示的。
- 这是 C 术语 - C++ 术语略有不同。
- 如果现在还不清楚,我认为
T* p; 习惯用法是不好的做法,并且会对指针声明语法造成不小的混淆;但是,既然 C++ 社区决定这样做,那我就是这样编写 C++ 代码的。我不喜欢它,它让我发痒,但为此争论或使用格式不一致的代码是不值得的。