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 |