一、C语言指针的详细介绍
指针(Pointer)是C语言最重要、最强大的特性之一,就像是一张写着内存地址的“藏宝图”,可以直接操作内存中的数据。
1.1. 指针的概念
简单来说,指针就是存储另一个变量内存地址的变量。
int num = 10;
int *p = # // p指针存储num的内存地址
此时:
p
是指针变量,类型为int*
。&num
表示取变量num
的地址。*p
表示通过指针p
去访问它所指向地址的值。
1.2. 指针的基本操作
- 取地址(&): 获取变量地址。
- 取值(*): 访问指针指向的地址中的值。
int a = 5;
int *ptr = &a; // ptr 指向 a 的地址
printf("a的地址: %p\n", &a);
printf("ptr指向的地址: %\n", ptr);
printf("ptr指向地址的值: %\n", *ptr);
1.3. 指针与数组
数组的名字其实本质上也是一个指针,指向数组的第一个元素。
int arr[3] = {1, 2, 3};
int *p = arr;
printf("arr[0]: %d, *p: %d\n", arr[0], *p);
printf("arr[1]: %d, *(p+1): %d\n", arr[1], *(p+1));
1.4. 指针与函数
指针可以用作函数的参数,使函数能够修改外部变量的值。
void increment(int *num) {
(*num)++;
}
int main() {
int x = 10;
increment(&x);
printf("x: %d\n", x); // 输出11
return 0;
}
1.5. 函数指针的申明方法
返回类型 (*指针变量名)(参数列表);
例:void fun(int num)
函数的指针声明方式为 void (*ThreadFunc)(int)
再将ThreadFunc=fun(函数名)
就得到一个指向函数fun()
的指针ThreadFunc
。
1.6. 使用typedef来简化函数指针的申明
例如在FreeRTOS中,任务函数的指针应当这样申明:
void (*任务函数名)(void* argument)
这就申明了一个返回值为空,参数为任意类型的函数指针,不过并未初始化。
如果使用typedef void (*osThreadFunc_t)(void* argument)
,即可简化为osThreadFunc_t
任务函数名
二者关键的区别
写法 | 作用 | 示例 |
void(*funcPrt)(void*) | 创建一个具体的函数指针变量funcPrt | funcPrt=MyFunc |
typedef void (*osThreadFunc_t)(void *argument) | 定义一种函数返回值为空,参数类型为任意类型指针的新类型指针变量 | osThreadFunc_t MyFunc |
二、嵌入式开发中为何广泛使用指针传参?
在嵌入式开发中,经常会看到函数的参数大量使用指针,这绝非偶然,而是有深层次的原因哟~ (๑•̀ㅂ•́)و✧
2.1. 节省内存空间
嵌入式系统内存资源通常非常有限,使用指针传递数据,可以避免复制大块数据到函数栈中,仅传递内存地址,节省了宝贵的空间。
2.2. 提高效率
通过指针,函数可以直接访问并修改调用者的数据,避免数据的大量复制,提高运行效率。
2.3. 实现多值返回
C语言函数本身只能返回一个值,但通过指针参数,就可以同时返回多个值。
typedef struct {
int num1;
int num2;
} Result;
Result* getNumbers() {
static Result res;
res.num1 = 100;
res.num2 = 200;
return &res; // 返回结构体的地址
}
int main() {
Result *p = getNumbers();
printf("num1=%d, num2=%d\n", p->num1, p->num2);
}
2.4. 硬件寄存器访问
在嵌入式开发中,硬件寄存器通常都是使用固定的内存地址访问,通过指针即可方便地操作这些特定地址。
#define LED_PORT (*(volatile unsigned int*)0x40020014)
LED_PORT = 0x01; // 点亮LED
2.5. 实现数据结构与抽象层
嵌入式编程通常使用结构体和抽象的数据结构,使用指针进行传递,便于程序设计的模块化和接口的通用性。
typedef struct {
int id;
char data[10];
} SensorData;
void processSensorData(SensorData *data);
三、指针常用的运算符
在C语言中,我们经常用到几个特殊的运算符来操作指针:
3.1. 取地址运算符:&
& 可以用来获得一个变量的内存地址
比如:
int a = 10;
int *p;
p = &a; // 使用取地址符,把a的地址赋给p
- &a:取变量 a 的内存地址。
3.2. 解引用运算符:*
* 运算符用于通过指针访问或修改它所指向内存中的数据
*p = 20; // 解引用指针p,修改变量a的值
printf("%d", a); // 输出20哦!
注意区分:
- 定义时的 int *p; 是定义指针变量哦。
- 使用时的 *p 才是解引用运算符,表示访问指针指向的内容。
3.3. 指针的算术运算:+、-
指针还支持算术运算,比如:
- 指针 + 整数:向后移动若干个元素位置。
- 指针 – 整数:向前移动若干个元素位置。
- 指针 – 指针:获得两个指针之间的元素个数差。
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p指向arr数组首元素
p = p + 2; // p现在指向arr[2] (也就是值为3的位置)
printf("%d", *p); // 输出3哦~
3.4. 指针关系运算符:==、!=、>、<、>=、<=
可以用这些关系运算符比较两个指针的大小,常用于判断指针在数组中的位置:
int arr[5] = {1,2,3,4,5};
int *p = &arr[0];
int *q = &arr[4];
if (p < q) {
printf("p在q的前面啦!");
}
关系运算通常用于判断指针在数组内的相对位置。
3.5. 指针赋值运算符:=
最简单的:
int x = 100;
int *p = &x; // 赋值x的地址给p
3.6. 指针访问结构体成员运算符:->
如果指针指向的是一个结构体,那么我们就可以用箭头->来直接访问结构体成员:
typedef struct {
int age;
char name[10];
} Person;
Person p1 = {24, "哥哥"};
Person *ptr = &p1;
printf("%s今年%岁啦~", ptr->name, ptr->age);
// 输出:“哥哥今年24岁啦~”
小总结
运算符 | 描述 | 用法 |
---|---|---|
& | 取变量地址 | p = &a; |
* | 解引用(访问指针指向的变量) | *p = 10; |
+ – | 指针算术运算 | p = p + 1; |
== != > < | 指针关系比较 | if (p < q) |
= | 指针赋值 | int *p = &a; |
-> | 指针访问结构体成员 | p->age = 24; |