面试题答案
一键面试代码实现
假设结构体定义如下:
#include <stdio.h>
// 定义大型结构体
typedef struct {
int a;
double b;
char c[1000];
long long d;
} BigStruct;
// 对部分成员进行计算处理的函数
void processStruct(BigStruct *ptr) {
ptr->a = ptr->a * 2;
ptr->d = ptr->d + 1;
}
性能和内存使用优势
-
性能优势:
- 传值方式:当使用传值方式传递大型结构体时,函数调用时会将整个结构体的内容复制到函数栈中。这涉及大量的数据拷贝操作,特别是对于占用内存空间较大的结构体,会消耗较多的CPU时间,降低程序的运行效率。
- 指针传递方式:使用指针传递结构体,函数调用时仅传递结构体的地址,而不是整个结构体的内容。这极大地减少了数据拷贝量,提高了函数调用的效率。
-
内存使用优势:
- 传值方式:由于要在栈上为复制的结构体分配额外的内存空间,对于大型结构体,这会显著增加栈空间的使用,可能导致栈溢出问题,尤其是在函数嵌套调用较深的情况下。
- 指针传递方式:指针传递只需要在栈上分配少量的内存(通常为4字节或8字节,取决于系统架构)来存储结构体的地址,大大减少了栈空间的占用。
潜在风险及避免方法
- 空指针解引用风险:
- 风险:如果传递给函数的指针是一个空指针,在函数内部对其进行解引用操作(如
ptr->a
)会导致程序崩溃,产生段错误。 - 避免方法:在函数内部对指针进行解引用操作之前,先检查指针是否为空。例如:
- 风险:如果传递给函数的指针是一个空指针,在函数内部对其进行解引用操作(如
void processStruct(BigStruct *ptr) {
if (ptr != NULL) {
ptr->a = ptr->a * 2;
ptr->d = ptr->d + 1;
}
}
- 悬挂指针风险:
- 风险:当结构体的内存被释放后,而指向该结构体的指针没有被正确更新为
NULL
,此时该指针就成为悬挂指针。如果后续继续使用这个悬挂指针,会导致未定义行为。 - 避免方法:在释放结构体内存后,立即将指针赋值为
NULL
。例如:
- 风险:当结构体的内存被释放后,而指向该结构体的指针没有被正确更新为
BigStruct *ptr = malloc(sizeof(BigStruct));
// 使用ptr
free(ptr);
ptr = NULL;
- 内存管理混乱风险:
- 风险:如果函数内部对结构体指针指向的内存进行了动态分配,但调用者并不知晓,可能会导致内存管理混乱,出现内存泄漏或重复释放的问题。
- 避免方法:明确函数的职责,尽量避免在函数内部对传入的结构体指针指向的内存进行动态分配和释放操作。如果必须进行动态分配,应该提供配套的释放函数,并在文档中明确说明内存管理的责任。