欢迎光临
我们一直在努力

【20天学C语言】Day 11: 指针基础

【20天学C语言】Day 11: 指针基础

📅 学习时间:4-5小时
🎯 学习目标:理解指针的概念,掌握指针的基本操作
💡 难度:★★★★☆(核心难点开始)


📝 Day 10 练习题答案

练习1答案:递归求幂

#include <stdio.h>

long long power(int base, int exp) {
if (exp == 0) return 1;
if (exp == 1) return base;
return base * power(base, exp 1);
}

int main(void)
{
printf("2^10 = %lld\\n", power(2, 10)); // 1024
return 0;
}

练习2答案:递归逆序打印

#include <stdio.h>

void print_reverse(int arr[], int len) {
if (len <= 0) return;
printf("%d ", arr[len 1]);
print_reverse(arr, len 1);
}

int main(void)
{
int arr[] = {1, 2, 3, 4, 5};
print_reverse(arr, 5); // 5 4 3 2 1
printf("\\n");
return 0;
}

练习3答案:递归判断回文

#include <stdio.h>
#include <string.h>

int is_palindrome(char str[], int left, int right) {
if (left >= right) return 1;
if (str[left] != str[right]) return 0;
return is_palindrome(str, left + 1, right 1);
}

int main(void)
{
char str[] = "level";
int len = strlen(str);

if (is_palindrome(str, 0, len 1)) {
printf("'%s' 是回文\\n", str);
}
return 0;
}

练习4答案:归并排序

#include <stdio.h>

void merge(int arr[], int left, int mid, int right) {
int n1 = mid left + 1;
int n2 = right mid;
int L[n1], R[n2];

for (int i = 0; i < n1; i++) L[i] = arr[left + i];
for (int i = 0; i < n2; i++) R[i] = arr[mid + 1 + i];

int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
arr[k++] = (L[i] <= R[j]) ? L[i++] : R[j++];
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}

void merge_sort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right left) / 2;
merge_sort(arr, left, mid);
merge_sort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}

int main(void)
{
int arr[] = {64, 34, 25, 12, 22, 11, 90};
int len = 7;
merge_sort(arr, 0, len 1);

for (int i = 0; i < len; i++) printf("%d ", arr[i]);
printf("\\n");
return 0;
}


1. 知识点概述

今天我们将学习:

  • 内存与地址的概念
  • 指针变量的定义
  • 取地址运算符(&)
  • 解引用运算符(*)
  • 指针的初始化与赋值
  • 空指针与野指针
  • 指针的大小

2. 详细讲解

2.1 内存与地址

内存可以想象成一排连续的格子:

地址: 0x1000 0x1001 0x1002 0x1003 0x1004 …
+——-+——-+——-+——-+——-+
内存: | | | | | |
+——-+——-+——-+——-+——-+

当我们定义一个int变量时(假设int占4字节):
int num = 100;

地址: 0x1000 0x1001 0x1002 0x1003
+——-+——-+——-+——-+
内存: | 100 (占4个字节) |
+——-+——-+——-+——-+
^
num的地址是0x1000

2.2 什么是指针

#include <stdio.h>

int main(void)
{
int num = 100;

// &num 获取num的地址
printf("num的值: %d\\n", num);
printf("num的地址: %p\\n", (void*)&num);

// 指针就是存储地址的变量
int *p = &num; // p存储num的地址

printf("p的值(即num的地址): %p\\n", (void*)p);
printf("p指向的值(*p): %d\\n", *p);

return 0;
}

输出示例:

num的值: 100
num的地址: 0x7ffd12345678
p的值(即num的地址): 0x7ffd12345678
p指向的值(*p): 100

2.3 指针变量的定义

#include <stdio.h>

int main(void)
{
int *p1; // 指向int的指针
char *p2; // 指向char的指针
double *p3; // 指向double的指针
float *p4; // 指向float的指针

// *号靠近类型还是变量都可以
int* p5; // 风格1
int *p6; // 风格2(推荐)
int * p7; // 风格3

// 注意:同时定义多个指针
int *a, *b; // a和b都是指针
int *c, d; // c是指针,d是int

return 0;
}

2.4 取地址运算符 &

#include <stdio.h>

int main(void)
{
int num = 100;
int *p = &num; // &取地址

char ch = 'A';
char *pc = &ch;

double d = 3.14;
double *pd = &d;

printf("num地址: %p\\n", (void*)&num);
printf("ch地址: %p\\n", (void*)&ch);
printf("d地址: %p\\n", (void*)&d);

return 0;
}

2.5 解引用运算符 *

#include <stdio.h>

int main(void)
{
int num = 100;
int *p = &num;

// *p 访问p指向的内存
printf("*p = %d\\n", *p); // 读取:100

*p = 200; // 写入:修改num的值
printf("num = %d\\n", num); // 200

// 等价关系
// *(&num) == num
// &(*p) == p

return 0;
}

2.6 指针与变量的关系

int num = 100;
int *p = &num;

内存图:
+———-+
num (0x1000)| 100 |<–+
+———-+ |
|
+———-+ |
p (0x2000)| 0x1000 |—+ (p存储num的地址)
+———-+

关系:
– p 存储 num 的地址
– *p 和 num 是同一块内存
– 修改 *p 就是修改 num
– 修改 num 也会影响 *p 的读取结果

#include <stdio.h>

int main(void)
{
int num = 100;
int *p = &num;

printf("初始: num=%d, *p=%d\\n", num, *p);

*p = 200;
printf("修改*p后: num=%d, *p=%d\\n", num, *p);

num = 300;
printf("修改num后: num=%d, *p=%d\\n", num, *p);

return 0;
}

2.7 指针的初始化

#include <stdio.h>

int main(void)
{
// 方式1:定义时初始化
int num = 100;
int *p1 = &num;

// 方式2:先定义,后赋值
int *p2;
p2 = &num;

// 方式3:初始化为NULL
int *p3 = NULL; // 空指针

// ❌ 危险:未初始化的指针(野指针)
int *p4; // p4是随机值,指向未知地址
// *p4 = 100; // 危险!

return 0;
}

2.8 空指针 NULL

#include <stdio.h>

int main(void)
{
int *p = NULL; // 空指针,不指向任何有效地址

// 使用前检查
if (p != NULL) {
printf("*p = %d\\n", *p);
} else {
printf("p是空指针\\n");
}

// 简写形式
if (p) { // 非空
printf("p有效\\n");
}
if (!p) { // 为空
printf("p为空\\n");
}

return 0;
}

2.9 野指针

#include <stdio.h>

int main(void)
{
// 野指针的产生场景:

// 1. 未初始化
int *p1; // 野指针

// 2. 指向已释放的内存(后续动态内存章节)

// 3. 指向超出作用域的变量
int *p2;
{
int num = 100;
p2 = &num;
}
// 此时p2是野指针,num已经销毁

// 预防:使用后置空
p2 = NULL;

return 0;
}

2.10 指针的大小

#include <stdio.h>

int main(void)
{
printf("sizeof(char*) = %zu\\n", sizeof(char*));
printf("sizeof(short*) = %zu\\n", sizeof(short*));
printf("sizeof(int*) = %zu\\n", sizeof(int*));
printf("sizeof(long*) = %zu\\n", sizeof(long*));
printf("sizeof(float*) = %zu\\n", sizeof(float*));
printf("sizeof(double*) = %zu\\n", sizeof(double*));
printf("sizeof(void*) = %zu\\n", sizeof(void*));

// 所有指针大小相同:
// 32位系统:4字节
// 64位系统:8字节

return 0;
}


3. 代码示例

示例1:通过指针交换变量

#include <stdio.h>

void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

int main(void)
{
int x = 10, y = 20;
printf("交换前: x=%d, y=%d\\n", x, y);

swap(&x, &y); // 传递地址

printf("交换后: x=%d, y=%d\\n", x, y);

return 0;
}

示例2:指针与不同类型

#include <stdio.h>

int main(void)
{
char c = 'A';
int i = 100;
double d = 3.14;

char *pc = &c;
int *pi = &i;
double *pd = &d;

printf("c = %c, *pc = %c\\n", c, *pc);
printf("i = %d, *pi = %d\\n", i, *pi);
printf("d = %.2f, *pd = %.2f\\n", d, *pd);

// 指针类型决定了解引用时读取多少字节
printf("sizeof(char) = %zu, sizeof(*pc) = %zu\\n", sizeof(char), sizeof(*pc));
printf("sizeof(int) = %zu, sizeof(*pi) = %zu\\n", sizeof(int), sizeof(*pi));
printf("sizeof(double) = %zu, sizeof(*pd) = %zu\\n", sizeof(double), sizeof(*pd));

return 0;
}

示例3:多级指针

#include <stdio.h>

int main(void)
{
int num = 100;
int *p = &num; // 一级指针
int **pp = &p; // 二级指针
int ***ppp = &pp; // 三级指针

printf("num = %d\\n", num);
printf("*p = %d\\n", *p);
printf("**pp = %d\\n", **pp);
printf("***ppp = %d\\n", ***ppp);

// 它们都能访问到num的值

return 0;
}


4. 常见错误(踩坑指南)

错误1:未初始化就使用

// ❌ 危险代码
int *p;
*p = 100; // 野指针,可能崩溃

// ✅ 正确代码
int num;
int *p = &num;
*p = 100;

错误2:类型不匹配

// ❌ 错误代码
int num = 100;
double *p = &num; // 类型不匹配

// ✅ 正确代码
int num = 100;
int *p = &num;

错误3:混淆地址和值

// ❌ 混淆
int num = 100;
int *p;
p = num; // 错误:把值当地址
*p = &num; // 错误:把地址当值

// ✅ 正确
p = &num; // p存储地址
*p = 100; // *p存储值

错误4:解引用空指针

// ❌ 危险代码
int *p = NULL;
printf("%d\\n", *p); // 崩溃!

// ✅ 正确代码
int *p = NULL;
if (p != NULL) {
printf("%d\\n", *p);
}


5. 练习题目

练习1:指针基础(难度:★☆☆☆☆)

定义一个int变量和指向它的指针,通过指针修改变量的值。

练习2:交换函数(难度:★★☆☆☆)

实现swap函数,使用指针参数交换两个变量的值。

练习3:指针计算(难度:★★☆☆☆)

编写函数,通过指针参数返回两个整数的和与差。

void sum_diff(int a, int b, int *sum, int *diff);

练习4:找最大最小值(难度:★★☆☆☆)

编写函数,使用指针参数返回数组的最大值和最小值。


6. 小结

今日核心要点

[核心概念]
1. 指针是存储地址的变量
2. & 取地址
3. * 解引用(访问指向的内存)

[定义与初始化]
4. 类型 *指针名;
5. 初始化为有效地址或NULL

[关系]
6. *(&变量) == 变量
7. &(*指针) == 指针

[注意事项]
8. 避免野指针
9. 使用前检查NULL
10. 类型要匹配
11. 所有指针大小相同(4或8字节)

指针图示

变量: int num = 100;
指针: int *p = &num;

+——-+ +——-+
num | 100 | <——– |0x1000 | p
(0x1000) +——-+ points +——-+ (0x2000)
to

*p 就是 num
&num 就是 p 的值


下一篇预告: Day 12 – 指针与数组

我们将学习指针与数组的关系,以及如何用指针操作数组。


练习题答案将在 Day 12 开头公布

赞(0)
未经允许不得转载:171主机测评 » 【20天学C语言】Day 11: 指针基础
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址