指针变量: 什么是地址
声明指针变量
& 和 * 两个运算符
指针赋值
任务: 指针追踪
课堂小测
理解指针的第一步是在机器级别观察它们表示的内容. 内存按字节划分, 每个字节存 8 位:
每个字节都有唯一的地址。
内存共 n 个字节, 地址就是从 \(0\) 到 \(n-1\) 的编号:
程序中的每个变量都占用一个或多个字节的内存. 变量占的第一个字节的地址, 就是变量的地址。 下图中变量 i 的地址是 2000:
地址可以存储在特殊的指针变量中. 把变量 i 的地址存进指针变量 p, 就说 p指向i。 图形表示:
int* p;
声明指针变量时, 名字前面必须有星号: p 是一个指针变量, 能够指向 int 类型的对象 (用"对象"而非"变量", 因 p 可能指向不属于变量的内存区域)
int i, j, a[10], b[20], *p, *q;
指针变量可以与其他变量一起声明
int* p; /* 只指向整数 */
double *q; /* 只指向双精度 */
char *r; /* 只指向字符 */
C 要求每个指针变量只能指向特定类型的对象; void * 较特殊: 可存放任意类型对象的地址
int *p; /* 没有指向特定对象 */
int i, *p;
…
p = &i;
int i;
int *p = &i;
int i, *p = &i;
声明指针只给它留了空间, 还没指向任何对象:
在使用p之前需要初始化它.
初始化指针的一种方法: 把变量的地址赋给它:
把 i 的地址赋给 p, 让 p 指向 i:
也可以在声明时就初始化:
i的声明甚至可以与p的声明合并:
printf("%d\n", *p);
j = *&i; /* 等同于 j = i; */
一旦指针变量指向一个对象, 就可以使用*(间接寻址)运算符来访问对象中存储的内容.
如果p指向i, 我们可以打印i的值如下:
对变量用 & 得到它的地址 (一个指针值); 把该值赋给指针变量后, 指针指向该变量
对指针用 * 则解引用: 顺着地址找到原变量, 读写它
只要p指向i, *p就是i的别名. *p与i具有相同的值. 更改*p的值会更改i的值. 下一张幻灯片上的示例说明了*p和i的等价性.
printf("%d\n", i); /* 打印 1 */
printf("%d\n", *p); /* 打印 1 */
*p = 2;
printf("%d\n", i); /* 打印 2 */
printf("%d\n", *p); /* 打印 2 */
p = &i;
i = 1;
int* p;
printf("%d", *p); /*** 错误的 ***/
int* p;
*p = 1; /*** 错误的 ***/
将间接寻址运算符应用于未初始化的指针变量会导致未定义的行为.
给*p赋值是特别危险的:
int i, j, *p, *q;
p = &i;
q = p;
*p = 1;
*q = 2;
C 允许使用赋值运算符来复制相同类型的指针. 假设有以下声明:
q现在指向与p相同的位置:
如果p和q都指向i, 就可以通过为*p或*q赋一个新值来更改i:
任意数量的指针变量都可以指向同一个对象.
注意不要混淆
q = p;
和
*q = *p;
第一条语句是指针赋值, 但第二条不是. 下面的示例显示了第二个语句的效果.
p = &i;
q = &j;
i = 1;
*q = *p;
任务: 令 int a = 3, *p = &a;, 打印 a、&a、p、*p; 再执行 *p = 7, 重新打印 a 和 *p
样例 (地址每次运行都不同):
a = 3, &a = 0x7ffd1a2b, p = 0x7ffd1a2b, *p = 3
改写 *p 之后: a = 7, *p = 7
提示:
打印地址用 %p, 打印整数用 %d——占位符要和参数类型对应
观察 &a 与 p 是否相等; 改写 *p 之后 a 是否跟着变
#include <stdio.h>
int main(void)
{
int a = 3;
int *p = &a; /* p 指向 a */
printf("a = %d, &a = %p, p = %p, *p = %d\n",
a, (void *)&a, (void *)p, *p);
*p = 7; /* 通过 p 改写 a */
printf("改写 *p 之后: a = %d, *p = %d\n", a, *p);
return 0;
}
p = &a: p 里存的是 a 的地址; 所以 &a 与 p 打印结果相同
*p 是 a 的别名: 读 *p 就是读 a, 写 *p = 7 就是写 a
%p 打印指针 (地址); 每次运行的地址不同, 不要硬编码比较
指针类型要匹配: int *p 只能指向 int 对象
int *p; 中 p 的类型是什么?
&x 和 *p 分别做什么?它们互为逆运算吗?
int i = 1, *p = &i; *p = 2; printf("%d", i); 输出什么?
int *p; *p = 1; 错在哪里?