如果您采用“黄金法则”(也不仅仅适用于指针),它自然而直观地遵循,并且在 C/C++ 中声明变量时避免了很多错误和陷阱。不应违反“黄金法则”(很少有例外,例如const
应用于数组 typedef,它传播const
到 C++ 附带的基本类型和引用)。
K&R,附录 A,第 8.4 节,声明符的含义指出:
每个声明符都被视为一个断言,即当与声明符形式相同的构造出现在表达式中时,它会产生指定类型和存储类的对象。
要在 C/C++ 中声明一个变量,您应该真正考虑应用到它以获取基本类型的表达式。
1)应该有一个变量名
2) 然后表达式 as valid* 从声明语句中出来,应用于变量名
3)然后是声明的剩余信息和属性,如基类型和存储
存储不是您始终可以赋予表达式结果的特性,例如与 constness 相反。仅在声明时才有意义。所以存储必须来自 2 之外的其他地方。
int * const *pp;
/*valid*/
int * static *pp;
/*invalid, this clearly shows how storage makes no sense for 2 and so breaks */
/*the golden rule. */
/*It's not a piece of information that goes well in the middle of a expression.*/
/*Neither it's a constraint the way const is, it just tells the storage of */
/*what's being declared. */
我认为 K&R 希望我们在声明变量时使用反向推理,这通常不是常见的习惯。使用时,它避免了大多数复杂的声明错误和困难。
*valid 不是严格意义上的,因为会发生一些变化,例如 x[]、x[size、not indexing]、constness 等......所以 2 是一个映射良好的表达式(对于声明用法),“相同形式”,一种反映变量使用的形式,但不严格。
外行人的黄金法则奖金
#include <iostream>
int (&f())[3] {
static int m[3] = {1, 2, 3};
return m;
}
int main() {
for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i)
std::cout << f()[i] << std::endl;
return 0;
}
在声明的上下文中,&
不是获取地址的操作,它只是告诉什么是引用。
f()
:f
是一个函数
&
return : 它的返回是一个参考
- 参考
[3]
:参考是一个由 3 个元素组成的数组
int
array[i] : 一个元素是一个 int
所以你有一个函数返回一个对 3 个整数数组的引用,并且由于我们有数组大小的正确编译时间信息,我们可以sizeof
随时检查它 =)
最后的黄金提示,对于任何可以放在类型之前的东西,当在多个声明中时,它会一次应用于所有变量,因此不能单独应用。
这const
不能放在前面int
:
int * const p;
所以以下是有效的:
int * const p1, * const p2;
这个可以:
int const *p; // or const int *p;
所以以下是无效的:
int const *p1, const *p2;
可兑换const
适用于所有人:
int const *p1, *p2; // or const int *p1, *p2;
声明公约
正因为如此,我总是把所有不能放在类型之前的东西,更靠近变量(int *a
,int &b
),而任何可以放在前面的东西,我都放在(volatile int c
)之前。
在http://nosubstance.me/post/constant-bikeshedding/上有更多关于这个主题的内容。