CCF C++ 四级考级专项讲义
主题:指针类型的概念及基本应用
一、 考点概述与大纲要求
在CCF GESP C++ 四级考试中,指针(Pointer)是一个具有分水岭意义的核心考点。如果说前三级的学习重点是基础语法、控制结构和一维/二维数组,那么从四级开始,指针将带领大家进入C++内存管理的底层世界。
本讲核心知识点:
1. 内存与地址的概念(变量是如何在内存中存储的)
2. 指针的概念与定义
3. 取地址运算符(&)与解引用运算符(*)
4. 指针的算术运算与指针和数组的关系
5. 空指针(nullptr / NULL)与野指针防范
6. GESP考试高频考点与易错题型剖析
二、 内存与地址:指针的基石
要想理解指针,首先要理解计算机的内存。
* 内存就像是一栋巨大的高层公寓,公寓里的每一个房间都有一个唯一的编号,这个编号在计算机中被称为内存地址。
* 当我们在C++中定义一个变量时:
cpp
int a = 100;
系统会在内存中为变量 a 分配 4 个字节的“房间”,并将数值 100 存放在里面。同时,这个“房间”拥有一个内存地址(例如:0x7ffee3b4b88c)。
三、 指针的概念与基本语法
1. 什么是指针?
- 指针也是一个变量,但它存储的值不是普通的数字(如100、3.14),而是另一个变量的内存地址。
- 如果一个变量存储了另一个变量的地址,我们就称这个变量为指针变量。
2. 指针的定义方式
定义指针的语法格式为:
数据类型 *指针变量名;
- 例如:
cpp int *p; // 定义一个指向 int 类型变量的指针 char *cp; // 定义一个指向 char 类型变量的指针 double *dp;// 定义一个指向 double 类型变量的指针(注:int* p和int *p写法等价,在GESP考试中均常见,建议习惯int *p)
3. 核心运算符:& 与 *
&(取地址运算符):获取一个变量在内存中的地址。*(解引用/间接访问运算符):通过指针中存储的地址,去访问或修改那个地址所指向的内存空间里的数据。
四、 代码展示:指针的初体验
下面通过一段完整的C++代码,展示变量、地址、指针的赋值与解引用操作:
#include <iostream>
using namespace std;
int main() {
// 1. 定义普通整型变量
int a = 10;
// 2. 定义指针变量 p,并将变量 a 的地址赋给 p
// &a 表示获取 a 的内存地址
int *p = &a;
cout << "--- 基本信息打印 ---" << endl;
cout << "变量 a 的值: " << a << endl;
cout << "变量 a 的地址 (&a): " << &a << endl;
cout << "指针 p 存储的值 (即 a 的地址): " << p << endl;
cout << "指针变量 p 自身的地址 (&p): " << &p << endl;
cout << "\n--- 通过指针操作变量 ---" << endl;
// *p 表示通过指针 p 找到它指向的内存,并获取其中的值(解引用)
cout << "通过 *p 获取的值: " << *p << endl;
// 3. 通过指针修改原变量的值
*p = 200; // 这相当于把 a 的值改成了 200
cout << "\n执行 *p = 200; 之后:" << endl;
cout << "变量 a 的新值: " << a << endl;
return 0;
}
运行结果模拟:
--- 基本信息打印 ---
变量 a 的值: 10
变量 a 的地址 (&a): 0x61ff08
指针 p 存储的值 (即 a 的地址): 0x61ff08
指针变量 p 自身的地址 (&p): 0x61ff04
--- 通过指针操作变量 ---
通过 *p 获取的值: 10
执行 *p = 200; 之后:
变量 a 的新值: 200
(注:内存地址 0x... 每次运行可能会不同)
五、 指针与数组的亲密关系
在C++(以及C语言)中,指针和数组有着极其深厚的渊源。这也是GESP四级考试中最喜欢考查的考点之一。
- 数组名本质上是一个常量指针,它指向数组中第一个元素(下标为0的元素)的内存地址。
- 可以通过指针来遍历和访问数组元素。
代码展示:指针与一维数组
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
// 数组名 arr 就是首元素的地址
int *p = arr; // 等价于 int *p = &arr[0];
cout << "使用指针遍历数组元素:" << endl;
for (int i = 0; i < 5; i++) {
// 利用指针的算术运算 p + i
// *(p + i) 相当于 arr[i]
cout << "arr[" << i << "] = " << *(p + i) << " , 地址为: " << (p + i) << endl;
}
return 0;
}
- 指针的算术运算:
p + 1并不是简单的把地址数值加1,而是加上当前指针指向的数据类型所占的字节数。因为int占 4 个字节,所以如果p的地址是1000,p + 1的地址就是1004。
六、 指针的常见错误与防范(避坑指南)
在学习和考试中,指针由于操作底层内存,极易写出Bug。以下两类错误必须高度警惕:
1. 野指针(Wild Pointer)
- 概念:指针变量被定义了,但没有被初始化,或者指向了一个已被释放/未知的内存空间。
- 危险:对野指针进行解引用(如
*p = 100;)会导致程序崩溃(段错误 Segmentation Fault)或随机篡改系统数据。 - 规范:指针在定义时如果暂时不指向具体变量,必须初始化为
nullptr(C++11标准,推荐)或NULL。cpp int *p = nullptr; // 安全的空指针
七、 GESP 四级真题模拟演练
【单选题】 若有以下定义,则对表达式的正确理解是( )。
int a = 10;
int *p = &a;
A. p 的值是变量 a 的值,即 10
B. *p 的值是变量 a 的内存地址
C. &p 表示指针变量 p 自身的内存地址
D. *&a 等效于变量 p
【答案解析】
* A 选项错误:p 存储的是 a 的内存地址,而不是 a 的值。
* B 选项错误:*p 是解引用,其值是变量 a 的值(即 10)。
* C 选项正确:& 是取地址符,&p 表示获取指针变量 p 在内存中的存储位置。
* D 选项错误:&a 是地址,*&a 相当于 *(&a),即对 a 的地址取值,结果还是 a(数值 10),不等于 p。
* 正确答案:C
CCF C++ 四级考级专项讲义
主题:指针与数组、二维数组及字符串的深层应用
一、 考点概述与大纲要求
在上一讲中,我们初步认识了指针的概念以及它与一维数组的简单关系。在CCF GESP 四级考试中,指针的考察往往不会停留于表面,而是会深入到二维数组的内存布局以及字符指针与字符串的操作中。
本讲核心知识点:
1. 指针与一维数组的进阶(数组名本质、指针下标法)
2. 指针与二维数组(行指针、列指针、数组指针)
3. 指针与字符串(字符数组、char* 字符串常量、常见操作)
4. GESP 四级高频真题考点剖析
二、 指针与一维数组(温故知新)
在C++中,数组名在绝大多数表达式中都会被自动转换为指向该数组首元素的指针。
代码展示:一维数组的多种遍历方式
#include <iostream>
using namespace std;
int main() {
int arr[4] = {10, 20, 30, 40};
int *p = arr; // p 指向 arr[0]
cout << "方式一:常规下标法" << endl;
for (int i = 0; i < 4; i++) {
cout << arr[i] << " ";
}
cout << endl;
cout << "方式二:指针偏移法 (*(p + i))" << endl;
for (int i = 0; i < 4; i++) {
cout << *(p + i) << " ";
}
cout << endl;
cout << "方式三:指针变量自增法 (p++)" << endl;
// 注意:修改了 p 的指向,循环结束后 p 不再指向首元素
for (int i = 0; i < 4; i++) {
cout << *p << " ";
p++;
}
cout << endl;
return 0;
}
三、 指针与二维数组
二维数组在内存中其实是按行连续存储的“一维数组”(线性连续空间)。理解二维数组的指针,关键在于分清“行指针(数组指针)”。
1. 二维数组的内存本质
对于定义 int a[2][3] = {{1, 2, 3}, {4, 5, 6}};:
* a 是整个二维数组的数组名,代表第0行的地址(类型为 int(*)[3])。
* a[0] 是第0行一维数组的数组名,代表第0行第0个元素的地址。
* a + i 表示第 i 行的地址。
* *(a + i) 或 a[i] 表示第 i 行首元素的地址。
* *(a + i) + j 或 &a[i][j] 表示第 i 行第 j 列元素的地址。
* *(*(a + i) + j) 或 a[i][j] 表示第 i 行第 j 列元素的值。
2. 代码展示:二维数组与指针
#include <iostream>
using namespace std;
int main() {
int a[2][3] = {
{10, 20, 30},
{40, 50, 60}
};
cout << "--- 二维数组元素访问方式对比 ---" << endl;
cout << "1. 常规下标法 a[1][2] = " << a[1][2] << endl;
// 2. 指针形式:*(*(a + 行) + 列)
cout << "2. 指针等效形式 *(*(a + 1) + 2) = " << *(*(a + 1) + 2) << endl;
cout << "\n--- 使用行指针(数组指针)遍历二维数组 ---" << endl;
// 定义一个行指针 p,它指向一个包含 3 个 int 元素的数组
int (*p)[3] = a;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
// p[i][j] 或 *(*(p + i) + j)
cout << p[i][j] << " ";
}
}
cout << endl;
return 0;
}
四、 指针与字符串
在C++中,字符串可以通过字符数组存储,也可以通过字符指针 char* 来操作。这是GESP四级考试中经常设“陷阱”的地方。
1. 字符数组 vs 字符指针
- 字符数组:数据存储在栈区,内容可以修改。
- 字符指针(字符串常量):
char *str = "Hello";,“Hello”作为字符串常量存储在系统的只读数据段中,str只是指向它的首地址。绝对不能尝试通过str[0] = 'H';去修改只读常量,这会导致程序崩溃。
2. 代码展示:字符指针遍历字符串
#include <iostream>
using namespace std;
int main() {
// 字符数组:内容可改
char str1[] = "GESP Level 4";
// 字符指针指向字符串常量
const char *str2 = "Hello Pointer"; // 建议加 const 防止误修改
cout << "通过字符指针遍历 str1:" << endl;
char *p = str1;
// 利用字符串结尾的空字符 '\0' 作为循环终止条件
while (*p != '\0') {
cout << *p;
p++; // 指针后移
}
cout << endl;
cout << "直接输出字符指针 str2: " << str2 << endl; // cout 对 char* 有特殊重载,会直接输出整个字符串
return 0;
}
五、 指针作为函数参数(传址调用)
在函数调用中,如果希望在函数内部修改外部变量的值,必须使用指针(传址调用),而不是普通的值传递(传值调用)。
代码展示:通过指针交换两个变量的值
#include <iostream>
using namespace std;
// 定义交换函数,接收两个整型指针
void swap(int *a, int *b) {
int temp = *a; // temp 保存 a 指向的内容
*a = *b; // 把 b 指向的内容赋给 a 指向的空间
*b = temp; // 把 temp 的值赋给 b 指向的空间
}
int main() {
int x = 5, y = 10;
cout << "交换前: x = " << x << ", y = " << y << endl;
// 传入变量的地址
swap(&x, &y);
cout << "交换后: x = " << x << ", y = " << y << endl;
return 0;
}
六、 GESP 四级高频真题演练
【单选题】 若有以下定义和语句,则输出结果是( )。
char str[] = "Computer";
char *p = str;
p += 3;
cout << p << endl;
A. Computer
B. Com
C. puter
D. uter
【答案解析】
1. char str[] = "Computer"; 定义了一个字符数组,p 指向首字符 'C'(即下标 0)。
2. p += 3; 让指针向后移动 3 个位置,此时 p 指向了字符串中下标为 3 的字符,即 'p'(从 'p' 开始往后的字串是 "puter")。
3. 在 C++ 中,cout << p 如果接收的是一个 char* 指针,它会从该指针当前指向的地址开始输出字符,直到遇到 '\0' 结束。
4. 因此输出结果为 puter。
* 正确答案:C
七、 考点总结
- 一维数组名
arr是常量指针,*(arr + i)等价于arr[i]。 - 二维数组
a[i][j]等价于*(*(a + i) + j),其行指针类型为int(*)[3]。 - 字符串输出:
cout << char_ptr;会直接输出整串字符,直到\0为止,这与整型指针int*输出地址表现不同,需特别注意。
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com