3.8.2 指针运算

3.8.2 指针运算

C 语言允许对指针进行运算,而计算出来的值会根据该指针引用的数据类型的大小进行伸缩。也就是说,如果 p 是一个指向类型为 T 的数据的指针,p 的值为 xp,那么表达式 p+i 的值为 xp + L · i,这里 L 是数据类型 T 的大小。

单操作数操作符 &* 可以产生指针和间接引用指针。也就是,对于一个表示某个对象的表达式 Expr&Expr 是给出该对象地址的一个指针。对于一个表示地址的表达式 AExpr*AExpr 给出该地址处的值。因此,表达式 Expr*&Expr 是等价的。可以对数组和指针应用数组下标操作。数组引用 A[i] 等同于表达式 *(A + i)。它计算第 i 个数组元素的地址,然后访问这个内存位置。

扩展一下前面的例子,假设整型数组 E 的起始地址和整数索引 i 分别存放在寄存器 %rdx%rcx 中。下面是一些与 E 有关的表达式。我们还给出了每个表达式的汇编代码实现,结果存放在寄存器 %eax(如果是数据)或寄存器 %rax(如果是指针)中。

表达式 类型 汇编代码
E int * xE movq %rdx,%rax
E[0] int M[xE] movl (%rdx),%eax
E[i] int M[xE + 4i] movl (%rdx,%rcx,4),%eax
&E[2] int * xE + 8 leaq 8(%rdx),%rax
E+i-1 int * xE + 4i - 4 leaq -4(%rdx,%rcx,4),%rax
*(E+i-3) int M[xE + 4i - 12] movl -12(%rdx,%rcx,4),%eax
&E[i]-E long i movq %rcx,%rax

在这些例子中,可以看到返回数组值的操作类型为 int,因此涉及 4 字节操作(例如 movl)和寄存器(例如 %eax)。那些返回指针的操作类型为 int *,因此涉及 8 字节操作(例如 leaq)和寄存器(例如 %rax)。最后一个例子表明可以计算同一个数据结构中的两个指针之差,结果的数据类型为 long,值等于两个地址之差除以该数据类型的大小。

练习题 3.37 假设短整型数组 S 的地址 xS 和整数索引 i 分别存放在寄存器 %rdx%rcx 中。对下面每个表达式,给出它的类型、值的表达式和汇编代码实现。如果结果是指针的话,要保存在寄存器 %rax 中,如果数据类型为 short,就保存在寄存器元素 %ax 中。

表达式 类型 汇编代码
S + 1
S[3]
&S[i]
S[4*i + 1]
S + i - 5