C语言中指针、结构体与memset的使用疑问
关于C语言指针、结构体与memset的问题解答
一、memset在自定义函数中仅置零首个成员的原因
大概率是你在函数里用错了sizeof的计算对象。比如你写的是:
void fun(struct car* ptr) { memset(ptr, 0, sizeof(ptr)); // 错误:sizeof(ptr)取的是指针的大小,不是结构体的大小 }
ptr是指针变量,32位系统下占4字节,64位系统下占8字节。如果你的struct car第一个成员name是指针类型(刚好占4/8字节),那memset只会把这部分内存置零,后面的speed、price自然不会被修改。
正确写法应该指定结构体的内存大小:
void fun(struct car* ptr) { memset(ptr, 0, sizeof(struct car)); // 正确:直接指定结构体类型的大小 } // 更安全的写法(避免写错结构体名): void fun(struct car* ptr) { memset(ptr, 0, sizeof(*ptr)); // 取指针指向对象的大小 }
你在main函数里能成功置零所有成员,是因为你直接用了sizeof(struct car)或者sizeof(结构体变量),覆盖了整个结构体的内存空间。
二、C语言的传值机制
C语言里所有函数参数都是传值,也就是说函数拿到的是实参的一份独立副本:
- 如果传的是
struct car* p,函数里的参数ptr是另一个指针,它和p指向同一块内存,但ptr本身是独立变量。所以在函数里修改ptr本身(比如ptr = NULL),main里的p不会变化;但修改ptr指向的内容(比如ptr->speed = 0),会影响main里的结构体,因为它们指向同一块内存。 - 如果传的是结构体本身(不是指针),函数会拷贝整个结构体,修改副本不会影响原结构体。
三、void*指针的使用问题
void*是通用指针类型,C标准允许任何对象类型的指针转换为void*,也允许void*转换回原指针类型(写代码时加上强制转换会更清晰)。
比如struct car* p; void* s = &p;,这里&p的类型是struct car**(指向指针的指针),它可以赋值给void*,符合通用指针的规则。但这种写法的合理性要看场景:
- 写通用工具函数(比如内存分配、拷贝函数)时,用
void*可以处理任意类型的指针,这是合理的。 - 但在明确类型的业务代码里,不建议这么写——
void*丢失了类型信息,后续使用时如果转换错误,会导致难以排查的bug。比如把struct car**存在void*里,之后误转换成struct car*去用,直接就会出错。
四、手动逐个赋值结构体成员是否为良好实践
分场景判断:
- 如果结构体成员少,或者只需要初始化部分成员:手动赋值是很好的实践,比如
car.name = NULL; car.speed = 0; car.price = 0;,代码清晰可读性高,还能避免memset的潜在问题(比如结构体包含非POD类型、位域,或者有需要特殊初始化的成员时,memset置零可能不符合预期)。 - 如果结构体很大,或者需要全置零:用
memset或者结构体初始化器更高效。比如struct car c = {0};(C标准规定,未明确初始化的成员会自动置零),这种写法比memset更安全简洁。 - C99及以后支持指定初始化器,比如
struct car c = {.name = NULL, .speed = 120, .price = 150000};,可以精准初始化指定成员,剩下的自动置零,兼顾清晰和效率。
内容的提问来源于stack exchange,提问作者Tryint0learn
相关产品推荐
相关产品推荐

