本文件只干一件事:把第 08 课的每个语法点,写成能直接编译运行的完整程序,每行都带
//注释。
用法:先照着敲一遍 → 编译运行 → 再合上书自己默写 → 最后做第三区的题。
一、知识点代码
c
// ============ 1. 取地址运算符 & 与解引用运算符 * ============
#include <stdio.h> // 引入标准输入输出,才有 printf
int main() // 主函数,程序入口
{
int a = 100; // 建一间 int 房,住进 100,名牌叫 a
int *p = &a; // 造一张 int 便签 p,抄上 a 的房间号(& = 取地址)
printf("a = %d\n", a); // 直接访问:按名字 a 找过去,输出 100
printf("*p = %d\n", *p); // 间接访问:照便签上的号去敲门,还是 100
*p = 200; // 解引用后赋值:把 200 送进 p 所指的那间房(就是 a)
printf("a = %d\n", a); // 再按名字看,a 已经变成 200 了
return 0; // 程序正常结束
} // main 结束c
// ============ 2. 基类型:int * 与 char * 走一步各跨多少 ============
#include <stdio.h> // 引入 printf
int main()
{
int a = 65; // 一间 int 房,住 65
char c = 'A'; // 一间 char 房,住字符 'A'
int *pi = &a; // pi 的基类型是 int,只该指向 int 房
char *pc = &c; // pc 的基类型是 char,只该指向 char 房
printf("pi = %p\n", (void *)pi); // 用 %p 输出 pi 里存的地址
printf("pc = %p\n", (void *)pc); // 用 %p 输出 pc 里存的地址
printf("pi+1 = %p\n", (void *)(pi + 1)); // int 指针加 1:地址加 4 字节
printf("pc+1 = %p\n", (void *)(pc + 1)); // char 指针加 1:地址加 1 字节
printf("%d %c\n", *pi, *pc); // 解引用取回 65 和 'A'
return 0; // 结束
} // main 结束c
// ============ 3. 指针作函数参数:真正交换两个变量 ============
#include <stdio.h> // 引入 printf
void swap(int *p1, int *p2) // 形参是两个 int 指针,用来接收两个地址
{
int temp; // 中转站必须是普通 int:要中转的是"人"不是"号"
temp = *p1; // 把 p1 所指房里的值抄进 temp
*p1 = *p2; // 把 p2 所指房里的值搬进 p1 那间
*p2 = temp; // 把 temp 里的值搬进 p2 那间
} // swap 结束
int main()
{
int a = 3, b = 8; // 两个普通变量
printf("交换前:a = %d, b = %d\n", a, b); // 输出 3 和 8
swap(&a, &b); // 传进去的是地址,不是值
printf("交换后:a = %d, b = %d\n", a, b); // 输出 8 和 3
return 0; // 结束
} // main 结束c
// ============ 4. 数组名就是首元素的地址 ============
#include <stdio.h> // 引入 printf
int main()
{
int a[5] = {10, 20, 30, 40, 50}; // 五间连号的 int 房
int *p = a; // 等价于 p = &a[0];(数组名本身就是地址)
printf("a = %p\n", (void *)a); // 数组名的值就是一个地址
printf("&a[0] = %p\n", (void *)&a[0]); // 与上一行完全相同
printf("p = %p\n", (void *)p); // p 里存的也是这个地址
printf("%d %d %d\n", a[2], *(a + 2), *(p + 2)); // 三种写法指向同一个元素
return 0; // 结束
} // main 结束c
// ============ 5. 指针的加减:p+i 与 p2-p1 ============
#include <stdio.h> // 引入 printf
int main()
{
int a[6] = {10, 20, 30, 40, 50, 60}; // 六间连号的 int 房
int *p = &a[1]; // p 指向 1 号元素(20)
int *q = &a[4]; // q 指向 4 号元素(50)
printf("*(p + 2) = %d\n", *(p + 2)); // p 往后挪 2 个元素 → 40
printf("*(q - 1) = %d\n", *(q - 1)); // q 往前挪 1 个元素 → 40
printf("q - p = %d\n", (int)(q - p)); // 两指针相减 = 相隔几个元素 → 3
printf("p+1 跨了 %d 字节\n", // 用 char* 相减量出真实字节数
(int)((char *)(p + 1) - (char *)p)); // 一个 int = 4 字节
return 0; // 结束
} // main 结束c
// ============ 6. 遍历数组的三种写法 ============
#include <stdio.h> // 引入 printf
int main()
{
int a[5] = {1, 2, 3, 4, 5}; // 五个元素
int i, *p; // i 当循环变量,p 当便签
for (i = 0; i < 5; i++) // 写法一:下标法
printf("%d ", a[i]); // 直接写 a[i],最直观
printf("\n"); // 换行
for (i = 0; i < 5; i++) // 写法二:用数组名算地址
printf("%d ", *(a + i)); // a+i 是地址,交给 * 取值
printf("\n"); // 换行
for (p = a; p < a + 5; p++) // 写法三:便签从 a[0] 一路走到 a+5
printf("%d ", *p); // 取便签当前指着的值
printf("\n"); // 换行
return 0; // 结束
} // main 结束c
// ============ 7. 数组名作函数参数:函数里改,外面跟着变 ============
#include <stdio.h> // 引入 printf
void inv(int x[], int n) // 形参写成数组,编译时按指针处理
{
int temp, i, j, m = (n - 1) / 2; // m 是 i 的上界(n=6 时 m=2)
for (i = 0; i <= m; i++) // i 从 0 走到中间
{
j = n - 1 - i; // j 从末尾往中间走
temp = x[i]; // 三变量交换的第一步
x[i] = x[j]; // 把后面的搬到前面
x[j] = temp; // 把前面的搬到后面
} // for 结束
} // inv 结束
int main()
{
int i, a[6] = {1, 2, 3, 4, 5, 6}; // 待逆序的数组
inv(a, 6); // 传数组名 = 传首元素地址
for (i = 0; i < 6; i++) // 遍历输出
printf("%d ", a[i]); // 结果 6 5 4 3 2 1
printf("\n"); // 换行
return 0; // 结束
} // main 结束c
// ============ 8.(选学)行指针:int (*p)[4] ============
#include <stdio.h> // 引入 printf
int main()
{
int a[3][4] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23}; // 3 行 4 列
int (*p)[4] = a; // p 是行指针,一步跨一行(4 个 int = 16 字节)
int i, j; // i 号行,j 号列
for (i = 0; i < 3; i++) // 遍历 3 行
{
for (j = 0; j < 4; j++) // 遍历 4 列
printf("%3d ", *(*(p + i) + j)); // 先跨 i 行,再跨 j 列,最后取值
printf("\n"); // 一行输出完换行
} // 外层 for 结束
return 0; // 结束
} // main 结束二、课后题 · 代码题
本章书末习题统一放到第 09 课(讲完 8.4–8.7)后集中做,本区暂时留空。
原因是第 8 章习题会用到字符串指针、函数指针、指针数组,现在做等于硬背。
这一课先把第三区的随课练习吃透——它们和书末习题的题型一一对应。
三、我出的题 · 随课练习
🔮 猜输出
c
// ============ 猜输出 ① ============
#include <stdio.h> // 引入 printf
int main()
{
int a = 10, b = 20; // 两个整型变量
int *p = &a, *q = &b; // p 指向 a,q 指向 b
*p = *q; // 注意:这里左边带 *,改的是"人"
p = q; // 这里左边不带 *,改的是"号"
*p = 30; // 再用 * 给现在 p 指向的变量赋值
printf("%d %d %d %d\n", a, b, *p, *q); // 输出四个值
return 0; // 结束
} // main 结束c
// ============ 猜输出 ② ============
#include <stdio.h> // 引入 printf
void f(int x, int *y) // 第一个形参是普通值,第二个是地址
{
x = x + 1; // 改的是复印件(形参 x)
*y = *y + 1; // 改的是 y 指向的那个变量(原件)
} // f 结束
int main()
{
int a = 1, b = 1; // 两个都从 1 开始
f(a, &b); // 传 a 的值,传 b 的地址
printf("%d %d\n", a, b); // 看看各自变成了多少
return 0; // 结束
} // main 结束c
// ============ 猜输出 ③ ============
#include <stdio.h> // 引入 printf
int main()
{
int a[5] = {1, 2, 3, 4, 5}; // 五个元素
int *p = a; // p 指向 a[0]
printf("%d ", *p++); // 没有括号:先取还是先挪?
printf("%d ", *(++p)); // 括号里先算 ++
printf("%d ", ++(*p)); // 加的是 *p 还是 p?
printf("%d %d\n", *p, a[2]); // 最后看看 p 和 a[2]
return 0; // 结束
} // main 结束c
// ============ 猜输出 ④ ============
#include <stdio.h> // 引入 printf
int main()
{
int a[6] = {10, 20, 30, 40, 50, 60}; // 六个元素
int *p = &a[1]; // 指向 1 号元素
int *q = &a[4]; // 指向 4 号元素
printf("%d ", (int)(q - p)); // 两个指针相减,单位是"元素"不是字节
printf("%d ", *(p + 2)); // 从 p 往后数 2 个元素
printf("%d\n", *(q - 1)); // 从 q 往前数 1 个元素
return 0; // 结束
} // main 结束🛠 变式题
c
// ============ 变式题 1:把 swap 改成能交换两个 double ============
// 要求:仿照知识点代码 3,形参用 double *,函数里用 * 交换两个值
#include <stdio.h> // 引入 printf / scanf
void swap(double *p1, double *p2) // 形参是两个 double 指针
{
// 在这里写你的代码:
// 定义中转变量,交换 *p1 和 *p2
} // swap 结束
int main()
{
double x = 1.5, y = 2.5; // 两个小数
scanf("%lf,%lf", &x, &y); // 读入两个小数(用逗号分隔)
swap(&x, &y); // 传地址进去
printf("%.2f %.2f\n", x, y); // 输出交换后的值
return 0; // 结束
} // main 结束c
// ============ 变式题 2:一次带回最大值和最小值 ============
// 要求:补全 maxmin,用两个指针参数把结果带回 main(不许用 return 带值)
#include <stdio.h> // 引入 printf
void maxmin(int a[], int n, int *pmax, int *pmin) // 形参:数组 + 两个地址
{
// 在这里写你的代码:
// 1) 先用 a[0] 给 *pmax 和 *pmin 赋初值
// 2) 循环比较,遇到更大的就更新 *pmax,更小的就更新 *pmin
} // maxmin 结束
int main()
{
int a[8] = {12, 5, 78, 3, 90, 6, 41, 27}; // 待处理的数组
int max, min; // 用来接收结果的两个变量
maxmin(a, 8, &max, &min); // 传数组名 + 两个变量的地址
printf("max = %d, min = %d\n", max, min); // 目标:max = 90, min = 3
return 0; // 结束
} // main 结束c
// ============ 变式题 3:把下标法翻译成指针法 ============
// 要求:不许出现 [],只用指针,结果应输出 186
#include <stdio.h> // 引入 printf
int main()
{
int a[8] = {12, 5, 78, 3, 90, 6, 41, 27}; // 八个元素
int *p; // 一张便签
int sum = 0; // 累加器,从 0 开始
// 在这里写你的代码:
// 用 p 从 a 走到 a+8,判断每个元素是否为偶数,是就累加进 sum
printf("%d\n", sum); // 正确结果是 186
return 0; // 结束
} // main 结束c
// ============ 变式题 4:补全 reverse(两个指针对撞) ============
// 要求:形参用指针变量,循环用两个指针 i、j 从两头往中间撞,不许用 []
#include <stdio.h> // 引入 printf
void reverse(int *x, int n) // 形参是指针变量
{
// 在这里写你的代码:
// int *i = x; // i 指向头
// int *j = x + n - 1; // j 指向尾
// while (i < j) { 交换 *i 和 *j; i++; j--; }
} // reverse 结束
int main()
{
int a[6] = {1, 2, 3, 4, 5, 6}; // 待逆序的数组
int i; // 输出用的循环变量
reverse(a, 6); // 传数组名
for (i = 0; i < 6; i++) // 遍历输出
printf("%d ", a[i]); // 目标输出:6 5 4 3 2 1
printf("\n"); // 换行
return 0; // 结束
} // main 结束🐛 改错题
c
// ============ 改错题 1 ============
#include <stdio.h> // 引入 printf
int main()
{
// ❌ 错误示例(故意)
int *p; // 只造了便签,没写房号 → 野指针
*p = 100; // 照着空号去敲门,往未知单元写数据
printf("%d\n", *p); // 输出什么完全不可预料
return 0; // 结束
} // main 结束
// 【错在哪】p 没有确定值,*p 指向的存储单元不可预料,写它可能破坏别的数据。
// 【怎么改】先让 p 有确定指向:
// int a;
// int *p = &a;
// *p = 100;c
// ============ 改错题 2 ============
#include <stdio.h> // 引入 printf
int main()
{
// ❌ 错误示例(故意)
int a = 5; // 一间 int 房,住 5
int *p; // 一张空白便签
*p = &a; // 错:左边是"p 指向的变量",不是 p 本身
printf("%d\n", *p); // 程序行为不可预料
return 0; // 结束
} // main 结束
// 【错在哪】*p 的类型是 int,&a 的类型是 int *,两边类型对不上;
// 而且此时 p 还没有确定值,*p 指向哪里都说不清。
// 【怎么改】去掉左边的 *:p = &a;c
// ============ 改错题 3 ============
#include <stdio.h> // 引入 printf
void swap(int *p1, int *p2) // 两个指针形参
{
// ❌ 错误示例(故意)
int *t; // 想用指针当中转站
t = p1; // 这里换的是"号",不是"人"
p1 = p2; // p1 现在指向 b
p2 = t; // p2 现在指向 a
} // swap 结束
int main()
{
int a = 3, b = 8; // 两个变量
swap(&a, &b); // 传地址
printf("%d %d\n", a, b); // 期望 8 3,实际仍是 3 8
return 0; // 结束
} // main 结束
// 【错在哪】函数里只换了形参指针本身,实参 a、b 一个都没动。
// 指针形参也是"值传递",函数里改复印件,原件不变。
// 【怎么改】交换指针所指的值,中转变量用普通 int:
// int t;
// t = *p1; *p1 = *p2; *p2 = t;四、加练题
(暂无)