指针的大小不一定与 CPU 的本机字大小相关;例如,最初的 Macintosh 在只有 24 条地址线的 32 位处理器(摩托罗拉 68000)上运行,因此指针被限制为 24 位。指针值存储在 32 位字中,但没有使用前 8 位。一些有进取心的程序员使用前 8 位来存储其他数据和指针。当 68020 出现时,这引起了一些胃灼热,它有 32 个地址行。重写该代码以使其“32 位干净”需要一段时间。
还要注意,指向不同类型的指针不必具有相同的大小。
实际上,在任何现代桌面系统(阅读:x86)上,所有指针类型都是 32 位或 64 位宽。但不要依赖于所有架构都是如此。
至于“通过引用传递”和“通过指针传递”,不,它们不仅仅是同一个概念的不同措辞。
在按引用传递系统中,函数定义中的形参和函数调用中的实参指定相同的内存(或至少改变一个反映在另一个上)。查看一些老式的 Fortran 代码:
C234567890
PROGRAM CALLSW
INTEGER M, N
M = 1
N = 2
WRITE(*,*) M, N
CALL ISWAP(M, N)
WRITE(*,*) M, N
STOP
END
C234567890
SUBROUTINE ISWAP(A, B)
INTEGER A, B
INTEGER TMP
TMP = A
A = B
B = TMP
RETURN
END
ISWAP 中的形参A 与主程序中的M 指定内存中的相同对象,因此写入A 会更改M 中的值。您可以将A 视为指向M 的指针(或者A 和M 都是指向同一个对象的指针),但是该语言对程序员隐藏了这种指针。
C 通过 value 传递所有内容;形参和实参总是在内存中指定不同的对象,所以写入一个不会影响另一个。当我们想在子例程中修改一个对象时,我们必须使用& 操作符显式传递它的地址,然后在子例程中使用* 操作符取消引用它:
void iswap(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int m, n;
m = 1;
n = 2;
printf("m = %d, n = %d\n", m, n);
iswap(&x, &y);
printf("m = %d, n = %d\n", m, n);
return 0;
}
我们没有将m 和n 传递给iswap,而是传递表达式 &m 和&n 的结果,它们是指向这两个对象的指针。同样,在iswap 函数中,我们不会写入a 或b,而是写入表达式 *a 和*b 的结果。 a 和 m 指的是内存中两个完全不同的对象,b 和 n 也是如此。写信给a 根本不会影响m。