您遇到了一些有趣的事情:变量,严格来说,不是值,而是引用值。 8 是一个整数值。在int i=8 之后,i 指一个整数值。不同之处在于它可以引用不同的值。
为了获取该值,必须取消引用i,即必须获取存储在i代表的内存位置中的值。每当请求变量引用的类型的值时,此取消引用在 C 中隐式执行:i=8; printf("%d", i) 导致与printf("%d", 8) 相同的输出。这很有趣,因为变量本质上是地址的别名,而数字文字是立即值的别名。在 C 中,这些非常不同的东西在句法上是相同的。变量可以代表表达式中的文字,并将自动取消引用。生成的机器代码使这一点非常清楚。考虑下面的两个函数。两者都具有相同的返回类型 int。但是f 在 return 语句中有一个变量,必须取消引用才能返回它的值(在这种情况下,它是在寄存器中返回的):
int i = 1;
int g(){ return 1; } // literal
int f(){ return i; } // variable
如果我们忽略管理代码,则每个函数都会转换为一条机器指令。对应的汇编器(来自 icc)是为g:
movl $1, %eax #5.17
这很简单:将 1 放入寄存器 eax。
相比之下,f 转换为
movl i(%rip), %eax #4.17
这会将值放在寄存器 rip 中的地址加上寄存器 eax 中的偏移量 i。看到变量名只是编译器的地址(偏移)别名,令人耳目一新。
必要的取消引用现在应该很明显了。写return *i 来返回 1 会更合乎逻辑,而写return i 只用于返回引用或指针的函数。
在你的例子中,在某种程度上确实不合逻辑
int j=8;
int* p = &j;
printf("%d\n", *p);
打印 8(即 p 实际上被取消引用两次);但是 &(*p) 产生 p 所指向的对象的地址(这是存储在 p 中的地址值),并且不会被解释为 &(8)。原因是在地址运算符的上下文中,变量(或者,在这种情况下,通过取消引用 p 获得的 L 值)没有像在其他上下文中那样隐式取消引用。 p>
当试图创建一种逻辑的、正交的语言——Algol68——时,int i=8 确实为 8 声明了一个别名。为了声明一个变量,长格式应该是 refint m = loc int := 3。因此,我们所说的指针或引用将具有 ref ref int 类型,因为实际上需要两次取消引用才能获得整数值。