前言:
上一期我们分享了C语言中的程序结构,本篇我们来介绍C语言中指针的基础知识。
Part 1:理解内存与地址:
1. 什么是内存?
内存就像一栋巨大的写字楼,每个房间(内存单元)都有一个门牌号(地址),房间内可以存放数据(如数字、字符等)。
每个房间的大小固定(例如1字节),不同类型的数据需要不同数量的房间(如
int
占4字节,char
占1字节)。
2. 变量如何存储?
当声明变量
int a = 10;
,相当于:在写字楼中预订一个连续的房间(4个字节)。
给这些房间贴上标签“a”。
将值
10
存入这些房间。
变量的地址就是这些房间的“起始门牌号”。
Part 2:指针的本质
1. 指针是什么?
指针是一个变量,但它存储的不是普通数据,而是另一个变量的地址。
比喻:指针就像一张“小纸条”,上面写着某个房间的门牌号(地址),而不是房间里的物品(数据)。
2. 指针的声明与初始化
int a = 10; // 普通变量
int *p = &a; // 指针变量p,存储a的地址
复制
我们看上面的例子,int *p
:p
是一个指向int
类型的指针。其中&a是取地址运算符,是用来获取变量a
的地址。
3. 指针的“解引用”(*操作符)
printf("%d", *p); // 输出10(通过p找到a的值)
*p = 20; // 通过p修改a的值为20
复制
上面的例子中,*p的含义和作用是根据指针p中存储的地址找到其在内存对应的位置并进行操作。
Part 3:指针的常见用法
示例1:指针与变量:
为了更好的理解指针和内存地址对应的关系,我们可以用画图或者表格的形式来展示,方便我们理解。
int a = 10; // 假设a的地址是0x1000
int *p = &a; // p的值是0x1000
还用上述的示例,那再内存中,图示大概是这个样子:
示例2:修改指针指向的值
int a = 10, b = 20;
int *p = &a; // p指向a
p = &b; // 现在p指向b
*p = 30; // 修改b的值为30
复制
这个例子中,我们可以详细的看下内存的变化过程,我逐步拆解分析下,
初始状态:
a
的值为10
,地址假设为0x1000
。b
的值为20
,地址假设为0x2000
。指针
p
初始化为&a
(即0x1000
)。
内存地址图如下:
执行 p = &b
:
p
的值变为0x2000
(即b
的地址)。此时内存地址变化如下
执行 *p = 30
:
通过
p
修改b
的值为30
。
最终结果
a
的值保持为10
(未被修改)。b
的值变为30
(通过指针修改)。
Part 4:指针的常见用途(分场景详解)
场景1:函数参数传递(修改实参)
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 1, b = 2;
swap(&a, &b); // 传递地址
printf("a=%d, b=%d", a, b); // 输出a=2, b=1
return 0;
}
复制
- Tips:如果不传指针,函数内部无法修改外部的变量。
场景2:动态内存分配
int *arr = (int*)malloc(5 * sizeof(int)); // 申请5个int的空间
if (arr != NULL) {
for (int i = 0; i < 5; i++) {
arr[i] = i * 10; // 通过指针操作数组
}
free(arr); // 必须手动释放内存!
}
复制
- Tips:传统数组大小固定,动态分配允许程序运行时决定内存大小。
Part 5:指针的常见陷阱以及问题
1. 空指针(NULL Pointer)
int *p = NULL; // 明确初始化为空
if (p != NULL) {
*p = 10; // 安全操作
}
复制
2. 野指针(Dangling Pointer)
int *p;
{
int a = 10;
p = &a; // p指向局部变量a
} // a的作用域结束,内存被释放
*p = 20; // 危险!操作已释放的内存
复制
3. 指针类型不匹配
double d = 3.14;
int *p = &d; // 错误!类型不匹配
复制
4. 指针可以进行运算操作
a. 指针的加减
int arr[3] = {10, 20, 30};
int *p = arr;
p++; // p指向arr[1]
p--; // p回到arr[0]
p += 2; // p指向arr[2]
复制
Tips:p + n
的实际地址是 p + n * sizeof(数据类型)
b. 指针比较
int *p1 = &arr[0];
int *p2 = &arr[2];
if (p1 < p2) { // 比较地址大小
printf("p1在p2之前");
}
复制
示例3:指针的综合应用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
// 基础指针操作
int num = 42;
int *p = #
printf("num的值: %d, 地址: %p\n", num, &num);
printf("p存储的地址: %p, 通过p访问的值: %d\n", p, *p);
// 动态内存分配
int *dynamicArr = (int*)malloc(3 * sizeof(int));
if (dynamicArr != NULL) {
dynamicArr[0] = 10;
*(dynamicArr + 1) = 20; // 等价于dynamicArr[1] = 20
dynamicArr[2] = 30;
for (int i = 0; i < 3; i++) {
printf("dynamicArr[%d] = %d\n", i, dynamicArr[i]);
}
free(dynamicArr);
}
// 指针与函数
void addTen(int *x) {
*x += 10;
}
int value = 5;
addTen(&value);
printf("addTen后的值: %d\n", value); // 输出15
return 0;
}
复制
这个例子感兴趣的朋友可以自己玩一下试试,最后的输出结果如下:
num的值: 42, 地址: 0x7ffd1234abcd
p存储的地址: 0x7ffd1234abcd, 通过p访问的值: 42
dynamicArr[0] = 10
dynamicArr[1] = 20
dynamicArr[2] = 30
addTen后的值: 15
复制
最后:
虽然是慢工但是不是什么细活 我们下期见。