4. 结构体指针:定义、访问成员与作为函数参数
结构体指针,说白了就是指向结构体变量的指针。很多初学者觉得它绕,其实它就是个普通指针,只不过它指向的对象是一个结构体。我个人习惯把结构体指针看作是「遥控器」——你不需要把整个结构体搬来搬去,拿着遥控器就能操作它。
4.1 结构体指针的定义
定义结构体指针的语法很简单,和定义其他类型指针一样:
struct 结构体类型名 *指针变量名;
举个例子,假设我们有一个学生结构体:
struct Student {
char name[32];
int age;
float score;
};
// 定义结构体变量和指针
struct Student stu1 = {"张三", 20, 89.5};
struct Student *pStu;
// 让指针指向结构体变量
pStu = &stu1;
嗯,这里要注意:&stu1 取的是整个结构体的首地址,不是某个成员的地址。我在项目中见过有人写成 pStu = stu1,编译直接报错——结构体变量名不是地址,别跟数组搞混了。
4.2 通过指针访问成员
通过指针访问结构体成员有两种方式,我个人强烈推荐第二种:
| 方式 | 语法 | 说明 |
|---|---|---|
| 解引用 + 点操作符 | (*pStu).age |
先解引用得到结构体,再访问成员。括号不能省 |
| 箭头操作符 | pStu->age |
直接通过指针访问成员,更直观 |
看个完整例子:
#include <stdio.h>
#include <string.h>
struct Student {
char name[32];
int age;
float score;
};
int main() {
struct Student stu1;
struct Student *p = &stu1;
// 通过指针给成员赋值
strcpy(p->name, "李四");
p->age = 22;
p->score = 95.0;
// 两种方式访问成员
printf("姓名:%s\n", p->name);
printf("年龄:%d\n", (*p).age);
printf("成绩:%.1f\n", p->score);
return 0;
}
⚠ 避坑指南
我曾经在代码里写过
我曾经在代码里写过
*pStu.age,以为先解引用再取成员。结果编译器告诉我 . 的优先级比 * 高,实际被解析成了 *(pStu.age),直接报错。记住:(*p).age 括号不能丢,或者直接用 ->。
4.3 指针作为函数参数
为什么要把结构体指针传给函数?说白了就两个原因:
- 效率:传指针只复制4或8字节(地址),传整个结构体可能复制几十上百字节
- 修改:传指针可以在函数内部修改结构体的内容,传值的话改的是副本
来看一个实际例子:
#include <stdio.h>
#include <string.h>
struct Student {
char name[32];
int age;
float score;
};
// 通过指针修改结构体内容
void updateScore(struct Student *p, float newScore) {
if (p != NULL) {
p->score = newScore;
}
}
// 通过指针读取结构体内容(加const保护)
void printStudent(const struct Student *p) {
if (p == NULL) return;
printf("姓名:%s,年龄:%d,成绩:%.1f\n",
p->name, p->age, p->score);
}
int main() {
struct Student stu = {"王五", 21, 78.5};
printStudent(&stu);
updateScore(&stu, 88.0);
printStudent(&stu);
return 0;
}
💡 我的习惯
写函数参数时,如果只读不写,我习惯加
写函数参数时,如果只读不写,我习惯加
const 修饰。这样既避免了意外修改,又让调用者一眼就知道这个函数不会动数据。你想想看,这算不算一种「文档即代码」?
4.4 结构体指针与动态内存分配
实际项目中,结构体指针最常用的场景是配合 malloc 动态分配内存。我记得有一次做嵌入式数据采集系统,传感器数量不确定,用静态数组根本没法搞——这时候动态分配就派上用场了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Sensor {
int id;
float value;
char unit[8];
};
int main() {
// 动态分配一个结构体
struct Sensor *pSensor = (struct Sensor *)malloc(sizeof(struct Sensor));
if (pSensor == NULL) {
printf("内存分配失败\n");
return -1;
}
// 使用指针操作
pSensor->id = 1;
pSensor->value = 25.6;
strcpy(pSensor->unit, "°C");
printf("传感器%d:%.1f %s\n", pSensor->id, pSensor->value, pSensor->unit);
// 用完释放
free(pSensor);
pSensor = NULL; // 避免野指针
return 0;
}
⚠ 重要提醒
动态分配的结构体,用完后一定要
动态分配的结构体,用完后一定要
free,并且把指针置为 NULL。我曾经在一个项目中漏了 free,跑了三天后系统内存耗尽直接死机——排查了一整天才找到原因。嵌入式环境内存本来就紧张,千万别犯这种错。
4.5 结构体指针数组
有时候我们需要管理多个结构体,用指针数组是个好办法。说白了就是数组里每个元素都是一个指针,指向一个结构体。
#include <stdio.h>
#include <stdlib.h>
struct Point {
int x;
int y;
};
int main() {
struct Point *points[3]; // 指针数组,每个元素指向一个Point结构体
// 动态分配并初始化
for (int i = 0; i < 3; i++) {
points[i] = (struct Point *)malloc(sizeof(struct Point));
if (points[i] != NULL) {
points[i]->x = i * 10;
points[i]->y = i * 10 + 5;
}
}
// 遍历输出
for (int i = 0; i < 3; i++) {
printf("Point%d:(%d, %d)\n", i, points[i]->x, points[i]->y);
}
// 释放内存
for (int i = 0; i < 3; i++) {
free(points[i]);
points[i] = NULL;
}
return 0;
}
📌 核心要点总结
- 结构体指针用
->访问成员,比(*p).更直观 - 函数参数传指针:省内存、能修改原数据
- 只读参数加
const保护,这是好习惯 - 动态分配的结构体必须配对
free,防止内存泄漏 - 指针数组适合管理多个结构体,灵活且高效
公众号:蓝海资料掘金营,微信 deep3321