🚀个人主页:BabyZZの秘密日记
📖收入专栏:C语言
很多初学者第一次写结构体时都会遇到一个问题:
“为什么我写的 print 函数有时候用struct book b
,有时候又用struct book *b
?
它们到底差在哪?”
今天,我们就通过一个最小可复现实验——打印两本书的信息——把这件事彻底讲透。
1. 先上代码:两种打印风格
#include <stdio.h>
struct book
{
char name[20];
char id[20];
char author[20];
float price;
};
/* 方式一:按值传递 */
void print1(struct book b1)
{
printf("%s %s %s %.1f\n",
b1.name, b1.id, b1.author, b1.price);
}
/* 方式二:按地址传递(指针) */
void print2(struct book *b2)
{
printf("%s %s %s %.1f\n",
b2->name, b2->id, b2->author, b2->price);
}
int main(void)
{
struct book b1 = { .name = "兄弟", .id = "A101",
.author = "余华", .price = 29.8f };
struct book b2 = { .name = "白夜行", .id = "B102",
.author = "东野圭吾", .price = 25.8f };
print1(b1); /* 传值:把整本书复制一份 */
print2(&b2); /* 传址:只送过去门牌号 */
return 0;
}
2. 现象:输出完全一致
兄弟 A101 余华 29.8
白夜行 B102 东野圭吾 25.8
既然结果一样,那两种方式还有区别吗?
当然有,区别藏在“内存”和“性能”里。
3. 原理:一次复制 vs. 一个指针
调用方式 | 背后发生了什么 | 时间开销 | 空间开销 | 能否修改原对象 |
---|---|---|---|---|
print1(b1) | 在栈上新建一个 完整的 struct book 副本 | 复制 20+20+20+4 字节 | 较大 | ❌(修改的是副本) |
print2(&b2) | 只传递 8 字节左右的指针 | 几乎可忽略 | 极小 | ✅(可通过 b2->price = 30 修改原对象) |
注:结构体越大,按值传递的复制成本越高;而指针始终是固定大小(32 位 4 字节、64 位 8 字节)。
4. 何时该用谁?
-
只读场景
- 结构体很小(像本例 64 字节左右):两种方式都行,按值更直观。
- 结构体很大(几百字节以上):优先
const struct book *
,避免无意义拷贝。
-
需要修改原对象
- 必须传指针。
- 若不想被误改,可加
const
修饰:
void print2(const struct book *b)
。
-
C++ 用户请注意
- C++ 中推荐用引用
const book&
,语法更简洁,效果与const book*
类似。
- C++ 中推荐用引用
5. 动手实验:验证复制行为
在 print1
里偷偷改一下:
void print1(struct book b1)
{
b1.price = 0; // 改副本
printf("print1 内 price = %.1f\n", b1.price);
}
再在 main
里打印原对象:
print1(b1);
printf("main 里 price = %.1f\n", b1.price);
结果:
print1 内 price = 0.0
main 里 price = 29.8
证实:print1
拿到的是副本,对原书毫无影响。
6. 小结
- 按值传递:简单、安全,但有复制成本。
- 按址传递:高效、可写,但要自己保证指针合法,必要时加
const
防误改。
记住一句话:
“小数据传值,大数据传址;只读加
const
,想改就指针。”