深入理解计算机系统之四 -- 指针
C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。指针是一种保存变量地址的变量。
指针声明
每个指针都对应一个类型。这个类型表明该指针指向的是哪一类对象 。以下面的指针声明为例:
int *ip
char **cpp
变量 ip
是一个指向 int
类型对象的指针,而 cpp
指针指向的对象自身就是一个指向char
类型对象的指针。通常 ,如果对象类型为T
,那么指针的类型为: T*
。特殊的void*
类型代表通用指针。
NULL指针
每个指针都有一个值。这个值是某个指定类型的对象的地址。特殊的NULL(O)
值表示该指针没有指向任何地方。
#include "stdio.h"
int main()
{
int *p = NULL;
printf("p的地址为%p\n", p);
return 0;
}
// p的地址为(nil)
数组与指针
对于数据类型 T
和整型常数 N
, 声明如下,
T A[N]
起始位置表示为Xa
, 这个声明有两个效果。首先,它在内存中分配一个L*N
字节的连续区域,这里L
是数据类型: T
的大小(单位为字节 )。其次,它引人了标识符A
,可以用A
来作为指向数组开头的指针,这个指针的值就是Xa
,可以用0~ N-1
的整数索引来访问该数组元素。数组元素i
会被存放在地址为为Xa+L*i
的地方。
单操作数操作符 ‘&
’ 和 ‘*
’ 可以产生指针和间接引用指针。也就是,对于一个表示某个对象的表达式 Expr
, &Expr
是给出该对象地址的一个指针。对于一个表示地址的表达式 AExpr
,*AExpr
给出该地址处的值。因此,表达式Expr
与*&Expr
是 等价的 。可以对数组和指针应用数组下标操作。数组引用 A[i]
等同于表达式* (A+i)
。 它计算第个数组元素的地址,然后访问这个内存位置。
数组与指针紧密联系。一个数组的名字可以像一个指针变量一样引用(但是不能修改)。数组引用(例如 a[3]
) 与指针运算和间接引用(例如 * (a+ 3)
) 有一样的效果。数组引用和指针运算都需要用对象大小对偏移量进行伸缩 。当我们写表达式 p+ i
,这里指针P
的值为p
,得到的地址计算为P +L*i
,这里L
是与 p
相关联的数据类型的大小。
#include "stdio.h"
int main()
{
// 声明一个int类型的数组,这个数组有10个元素
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
// 声明int类型的指针变量
int *p;
int *p1;
int *p2;
// 声明一个int类型的变量
int sub;
// 对指针进行初始化,p将指向数组 a 的第 1 个元素 a[0]
p = &a[0];
// 数组类型的变量或表达式的值是该数组第 1 个元素的地址,且数组名所代表的的就是该数组第 1 个元素的地址,所以:p = a;
printf("a的地址为:%p\n", a);
printf("a[0]的地址为:%p\n", &a[0]);
printf("p的地址为:%p\n", &p);
// 打印指针 p 的地址,并不是指针所指向的地方的地址
// a的地址为:0x7ffe2ad296d0
// a[0]的地址为:0x7ffe2ad296d0
// p的地址为:0x7ffe2ad296b8
p1 = &a[2];
p2 = &a[8];
sub = p2 - p1;
printf("sub=%d\n", sub);
// sub=6
p += 2;
printf("*(p+2)的值为:%d\n", *p);
// *(p+2)的值为:3
// 输出结果为 3,*(p+2)指向了 x[2]
return 0;
}
将指针从一种类型强制转换成另一种类型,只改变它的类型,而不改变它的值。强制类型转换的一个效果是改变指针运算的伸缩 。例如,如果P
是一个 char*
类型的指针,它的值为p
,那么表达式 (int*)(p+7)
计算为p+7
。(强制类型转换的优先级高于加法。)
指针与函数
指针也可以指向函数 。这提供了一个很强大的存储和向代码传递引用的功能,这些引用可以被程序的某个其他部分调用。
#include "stdio.h"
// 参数为普通的 int 变量
void swap1(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
// 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
void swap2(int *a, int *b)
{
// 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 1, y = 2;
swap1(x, y); // 将 x,y 的值本身作为参数传递给了被调函数
printf("%d %5d\n", x, y); // 输出结果为:1 2
swap2(&x, &y); // 将 x,y 的地址作为参数传递给了被调函数,传递过去的也是一个值,与传值调用不冲突
printf("%d %5d\n", x, y); // 输出结果为:2 1
return 0;
}