C语言结构体与指针使用详解及避坑指南
掌握C语言结构体及其指针的正确用法是高效编程的基础。本文深入解析结构体的定义与成员访问,阐述为何在函数传参时推荐使用指针以避免数据拷贝开销,并详细介绍基于malloc的动态内存分配全流程,包括空间申请、有效性判断、字符串成员的独立管理及最终的内存释放与指针重置,帮助开发者规避常见的内存泄漏与野指针风险。
结构体的定义与基础操作
结构体(struct)是C语言中用于聚合不同类型数据的复合数据类型。在实际开发中,通常需要定义结构体类型并使用指针进行实例化或成员访问。
以下代码展示了如何定义一个名为 STAFF_T 的结构体,包含姓名、性别、年龄、薪资和日期等成员:
#include <stdio.h>
#include <stdlib.h> // 标准头文件替换了非标准的<malloc.h>
typedef struct Staff_t {
char* name;
char sex;
short age;
float salary;
char* date;
} STAFF_T;
函数传参:为什么推荐使用结构体指针?
当将结构体作为函数参数传递时,如果是按值传递(即直接传入结构体变量),系统会创建该结构体的完整副本。对于大型结构体,这种拷贝操作会消耗大量的时间与栈空间,导致性能下降。
最佳实践:
1. 优先使用结构体指针作为函数参数,仅传递地址,大幅降低开销。
2. 若函数仅需读取结构体数据而不修改,建议在指针前添加 const 限定符,防止意外修改原数据。
示例:使用常量指针接收结构体
以下函数演示了如何通过 const STAFF_T * 安全地读取结构体内容:
void print_msg(STAFF_T const *p) {
if (p == NULL) {
printf("print_msg: p is NULL\n");
return;
}
printf("Name:%s, Sex:%c, Age:%d, Salary:%0.2f, Date:%s\n",
p->name, p->sex, p->age, p->salary, p->date);
}
指针定义、初始化与动态内存管理
指针在使用前必须明确其状态。未初始化的指针指向随机地址,极其危险。同时,涉及内存分配时必须遵循严格的流程。
第1步:指针的定义与初始化
定义指针后应立即赋予确定值,通常为有效地址或 NULL。
// 错误示范:未初始化
int* p;
// 正确做法1:初始化为NULL
int* p = NULL;
// 正确做法2:指向现有变量
int i;
p = &i;
第2步:动态内存申请(malloc)
使用 malloc 在堆区申请内存,必须强制转换为目标结构体指针类型。需注意,malloc 失败时返回 NULL。
关键操作:
- 申请成功后方可使用。
- 必须进行判空检查,否则会导致程序崩溃。
STAFF_T* staff = NULL; // 定义并初始化为NULL
// 申请内存并转换类型
staff = (STAFF_T*)malloc(sizeof(STAFF_T));
// 严格检查是否成功
if (staff == NULL) {
printf("malloc(STAFF_T) ERROR\n");
return -1; // 或其他退出逻辑
}
第3步:处理嵌套指针成员
结构体中包含指针成员(如 char*)时,仅分配结构体本身的空间是不够的,还需为这些字符串成员单独分配内存。
// 为name字段分配20字节空间
staff->name = (char*)malloc(20 * sizeof(char));
// 可直接赋值字符串(编译器自动处理静态字符串驻留区引用)
staff->name = "duruofei";
// 或者使用strcpy复制到分配的内存块中
// strcpy(staff->name, "duruofei");
资源释放与防野指针
内存管理的最后一步同样重要。使用完动态分配的内存后,必须手动释放,并将指针置为空。
- 释放内存:调用
free(p)归还内存给系统。注意,释放结构体中的子指针(如staff->name)需根据分配情况单独释放,本例简化展示整体释放场景。 - 重置指针:
free仅释放内容,不改变指针本身的值。此时指针仍指向原地址(悬空/野指针)。若继续使用,可能导致不可预知的错误。 - 操作规范:执行
free(staff);后,务必紧跟staff = NULL;。
完整运行示例
以下为整合上述要点的完整代码,展示了从定义、申请、赋值、传参到释放的全生命周期管理:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Staff_t {
char* name;
char sex;
short age;
float salary;
char* date;
} STAFF_T;
void print_msg(STAFF_T const *p);
int main() {
int size;
STAFF_T* staff = NULL;
// 1. 申请结构体内存
staff = (STAFF_T*)malloc(sizeof(STAFF_T));
if (staff == NULL) {
printf("malloc(STAFF_T) ERROR\n");
return -1;
}
// 2. 打印内存占用大小(辅助调试)
size = sizeof(STAFF_T);
printf("STAFF_T size = %d\n", size);
printf("SIZEOF:\n char*:%lu, char:%lu, short:%lu, float:%lu \n",
sizeof(char*), sizeof(char), sizeof(short), sizeof(float));
// 3. 为内部指针成员分配内存并赋值
staff->name = (char*)malloc(20 * sizeof(char));
staff->name = "duruofei"; // 简单赋值演示
staff->sex = 'M';
staff->age = 29;
staff->salary = 15000;
staff->date = (char*)malloc(20 * sizeof(char));
strcpy(staff->date, "2021-12-09");
// 4. 通过指针传递(只读保护)
print_msg(staff);
// 5. 释放资源并清理指针
free(staff->name);
free(staff->date);
free(staff);
staff = NULL; // 防止野指针
printf("Hello World\n");
return 0;
}
void print_msg(STAFF_T const *p) {
if (p == NULL) {
printf("print_msg: p is NULL\n");
return;
}
printf("Name:%s, Sex:%c, Age:%d, Salary:%0.2f, Date:%s\n",
p->name, p->sex, p->age, p->salary, p->date);
}
以上就是C语言结构体与指针使用注意事项的详细内容,更多关于C/C++结构体内容的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









