玄武纪8 月 · CSP-J 初赛打卡DAY 18 / 20

Day 18 内存、地址与指针

建议用时:22~28 分钟
今日目标:分清变量的值、变量的地址和指针中保存的地址,掌握 &*、指针赋值、空指针、数组与链式节点中的基本用法。
大纲定位:入门级指针与基本动态数据结构阅读;不展开复杂指针、函数指针和手工内存管理技巧。

一、一个变量要同时看“内容”和“位置”

程序运行时,变量会占用内存中的一块空间。这块空间存放变量的值,同时有一个用于定位它的地址。

int a = 10;

可以把 a 想成一个贴着门牌号的盒子:盒子里的内容是 10,门牌号是 a 的地址。地址的具体数字由运行环境决定,初赛程序阅读一般只关心“它是谁的地址”,不需要猜真实十六进制值。

cout << a;   // 变量的值
cout << &a;  // 变量 a 的地址

这里的 &取地址运算符。它写在变量前面,得到变量在内存中的地址。

二、指针变量专门保存地址

int a = 10;
int *p = &a;

p 是一个“指向 int 的指针”,它保存 a 的地址。此时:

*p = 20;
cout << a;   // 20

*p 赋值是在修改“所指向的变量”,不是修改指针本身保存的地址。

变量、地址和指针知识卡

三、p = q*p = *q 完全不同

int x = 101, y = 201;
int *p = &x;
int *q = &y;

若执行 p = q;,是把 q 中保存的地址复制给 p。执行后 pq 都指向 yxy 的数值都没有因此改变。

若执行 *p = *q;,是把 q 所指向的值复制到 p 所指向的位置。执行前 p 指向 xq 指向 y,因此这句会把 201 写入 x,而两个指针的指向不变。

语句 改变什么 结果
p = q 指针 p 保存的地址 p 改为与 q 指向同一对象
*p = *q p 所指对象的值 目标变量被赋值,指针指向不变
p = &x 指针 p 保存的地址 p 指向 x
*p = 7 p 所指对象的值 目标变量变为 7

这正是 CSP-J 指针选择题最常见的设问方式。

指针赋值与间接赋值知识卡

四、空指针、野指针和悬空指针

int *p = nullptr;

nullptr 明确表示“当前不指向有效对象”。可以判断 p == nullptr,但不能执行 *pp->member,因为没有有效目标可访问。

安全的基本习惯是:定义时初始化;解引用前确保目标有效;释放动态内存后及时把不再使用的指针设为 nullptr。初赛题若让空指针或无效指针被解引用,通常应判断为错误或未定义行为,不能继续按普通数值计算。

五、数组名与指针的基本关系

int a[4] = {10, 20, 30, 40};
int *p = a;

在多数表达式中,数组名 a 会转换为指向首元素 a[0] 的指针。因此:

*p       // a[0],值为 10
*(p + 1) // a[1],值为 20
p[2]     // a[2],值为 30

指针加 1 表示移动到“下一个同类型元素”,不是把地址的数值简单加 1 字节。若 p 指向 int 数组元素,p+1 就指向下一个 int

“数组名就是一个可以随意赋值的普通指针变量”并不准确。数组名能在许多表达式中转换为首元素指针,但不能写 a = p; 来改变数组的位置。

六、链式节点:指针把节点连起来

struct Node {
    int data;
    Node *next;
};

每个节点保存一份数据和下一个节点的地址。head 指向第一个节点,最后一个节点的 next 通常为 nullptr

在表头插入值为 42 的新节点,顺序是:

Node *newNode = new Node;
newNode->data = 42;
newNode->next = head;
head = newNode;

先让新节点接住原来的表头,再更新 head。如果先把 head 改为新节点,却没有保存原表头地址,原来的链表可能就接不上了。

数组指针与链表头插知识卡

七、指针程序的草稿纸画法

不要在脑中追地址,画两种符号即可:

x = 101      y = 201
p ───────→ x
q ───────→ y

执行 p=q 后,只改箭头:

x = 101      y = 201
              ↑
          p ──┤
          q ──┘

执行 *p=300 后,再改箭头终点盒子里的值。坚持“赋值号左边是谁就改谁”:左边是 p,改指针;左边是 *p,改目标。

今日选择题

第 1、2、4 题为近五年 CSP-J 第一轮单选原题;第 3、5 题为专题练习;第 6~8 题围绕同一段指针程序阅读。

  1. 【CSP-J 2022·第 3 题】 运行以下代码片段的行为是( )。

    int x = 101;
    int y = 201;
    int *p = &x;
    int *q = &y;
    p = q;
    

    A. 将 x 的值赋为 201
    B. 将 y 的值赋为 101
    C. 将 q 指向 x 的地址
    D. 将 p 指向 y 的地址

  2. 【CSP-J 2023·第 4 题】 已知链表节点定义如下,并有指向链表头部的指针 Node *head。要插入一个 data 为 42 的新节点,并使新节点成为第一个节点,正确操作是( )。

    struct Node {
        int data;
        Node *next;
    };
    

    A. Node* newNode = new Node; newNode->data = 42; newNode->next = head; head = newNode;
    B. Node* newNode = new Node; head->data = 42; newNode->next = head; head = newNode;
    C. Node* newNode = new Node; newNode->data = 42; head->next = newNode;
    D. Node* newNode = new Node; newNode->data = 42; newNode->next = head;

  3. 执行下面的代码后,a 的值是( )。

    int a = 7;
    int *p = &a;
    *p = 12;
    

    A. 7  B. 12  C. a 的地址  D. 无法确定

  4. 【CSP-J 2022·第 4 题】 链表和数组的区别包括( )。

    A. 数组不能排序,链表可以
    B. 链表比数组能存储更多的信息
    C. 数组大小固定,链表大小可动态调整
    D. 以上均正确

  5. 关于 int *p = nullptr;,下列说法正确的是( )。

    A. p 当前不指向有效的 int 对象
    B. *p 的值一定为 0
    C. 可以安全执行 *p = 5
    D. p 是一个普通整数变量

主题程序阅读

阅读下面的程序:

#include <iostream>
using namespace std;

int main() {
    int a[4] = {2, 4, 6, 8};
    int *p = a;
    int *q = p + 2;
    *p = *q;
    q = p + 3;
    *q += a[1];
    cout << a[0] << ' ' << a[2] << ' ' << a[3] << endl;
    return 0;
}
  1. 执行 int *q = p + 2; 后,q 指向( )。

    A. a[0]  B. a[1]  C. a[2]  D. a[3]

  2. 执行 *p = *q; 后,a[0] 的值是( )。

    A. 2  B. 4  C. 6  D. 8

  3. 程序最终输出( )。

    A. 6 6 8  B. 6 6 12  C. 2 6 12  D. 6 4 12

暂停 · 先完成并提交

画箭头,不猜地址

请先独立完成全部题目,并到玄武 OJ 提交今日答案。

  • `p=q` 只改指向,`*p=*q` 才改目标值;
  • 数组指针每移动 1 格,就跨过一个同类型元素;
  • 提交后再向下订正并重画错题箭头。
继续向下:每道题的完整题目、答案与解析
本页用于 CSP-J 第一轮自主复习。请先看知识卡并独立完成练习,提交玄武 OJ 后再查看解析。
← 上一天 返回学习中心 下一天 →