继承要解决什么问题先看一个实际场景项目里有三种I2C传感器每种都需要I2C读写、设备地址、初始化状态。如果每个传感器都独立定义一遍I2C相关代码重复三份改一个地方要改三处。继承的本质是 把共性抽到基类个性留在派生类 。基础做法C语言实现继承的核心手段就一个——把基类结构体作为派生类的第一个成员。typedefstruct { uint8_t i2c_addr; uint8_t bus_id; int initialized; } i2c_device_t; typedefstruct { i2c_device_t base; // 基类必须放第一个 int32_t t_fine; // BME280特有 uint8_t calib[42]; // BME280特有 } bme280_t; typedefstruct { i2c_device_t base; // 基类必须放第一个 uint8_t crc_enabled; // SHT30特有 uint8_t repeatability; } sht30_t;为什么必须放第一个C标准C99 6.7.2.1保证结构体首成员的偏移量为0即首成员的地址等于结构体本身的地址。这意味着bme280_t bme; i2c_device_t *dev (i2c_device_t *)bme // 安全地址相同 i2c_device_t *dev2 bme.base // 等价写法两种写法指向同一个地址。这是C语言实现向上转型的基础——把派生类指针当成基类指针用不需要偏移计算。如果base不是第一个成员强转后地址就不对了读出来的字段全是错的在MCU上大概率HardFault。基类函数复用基类定义共性接口派生类直接调用inti2c_device_init(i2c_device_t *dev, uint8_t bus_id, uint8_t addr) { dev-i2c_addr addr; dev-bus_id bus_id; dev-initialized 1; return i2c_bus_check(bus_id); } inti2c_device_read_reg(i2c_device_t *dev, uint8_t reg, uint8_t *buf, uint16_t len) { return i2c_transfer(dev-bus_id, dev-i2c_addr, reg, buf, len); } inti2c_device_write_reg(i2c_device_t *dev, uint8_t reg, constuint8_t *buf, uint16_t len) { return i2c_transfer_write(dev-bus_id, dev-i2c_addr, reg, buf, len); }派生类构造时调用基类构造函数就像C的初始化列表intbme280_init(bme280_t *dev, uint8_t bus_id, uint8_t addr) { // 先初始化基类 int ret i2c_device_init(dev-base, bus_id, addr); if (ret ! 0) return ret; // 再初始化派生类自有属性 ret i2c_device_read_reg(dev-base, 0x88, dev-calib, 24); if (ret ! 0) return ret; ret i2c_device_read_reg(dev-base, 0xA1, dev-calib 24, 1); if (ret ! 0) return ret; ret i2c_device_read_reg(dev-base, 0xE1, dev-calib 25, 7); if (ret ! 0) return ret; dev-t_fine 0; return0; }读取传感器数据时基类函数直接复用intbme280_read_raw(bme280_t *dev, uint8_t *raw_data) { // 复用基类的i2c读函数 return i2c_device_read_reg(dev-base, 0xF7, raw_data, 8); }向上转型有了结构体嵌套就可以用一个基类指针数组管理所有派生类对象#define MAX_DEVICES 8 statici2c_device_t *g_devices[MAX_DEVICES]; staticint g_device_count 0; voiddevice_register(i2c_device_t *dev) { if (g_device_count MAX_DEVICES) { g_devices[g_device_count] dev; } } voiddevices_check_all(void) { for (int i 0; i g_device_count; i) { if (!g_devices[i]-initialized) { printf(Device 0xX not initialized\n, g_devices[i]-i2c_addr); } } }注册时传基类指针staticbme280_t g_bme280; staticsht30_t g_sht30; bme280_init(g_bme280, 0, 0x76); sht30_init(g_sht30, 0, 0x44); device_register(g_bme280.base); // 向上转型 device_register(g_sht30.base); // 向上转型device_register 只知道 i2c_device_t 不关心具体是BME280还是SHT30。新增传感器类型时注册逻辑不用改。向下转型向上转型是安全的地址相同向下转型需要额外信息——你得知道这个基类指针实际指向的是哪个派生类。Linux内核用 container_of 宏从成员指针反推出容器结构体指针#define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member)))原理已知成员指针和它在结构体中的偏移量用指针减去偏移量就得到结构体首地址。voidhandle_device_event(i2c_device_t *dev) { // 假设通过地址判断具体类型 if (dev-i2c_addr 0x76) { bme280_t *bme container_of(dev, bme280_t, base); float temp, humi; bme280_read_compensated(bme, temp, humi); } elseif (dev-i2c_addr 0x44) { sht30_t *sht container_of(dev, sht30_t, base); float temp, humi; sht30_read(sht, temp, humi); } }因为 base 是第一个成员 offsetof(bme280_t, base) 等于0所以 container_of 的结果和直接强转 (bme280_t *)dev 一样。但 container_of 更安全——如果有人把base挪到了其他位置直接强转会出bug container_of 不会。多层继承C语言也可以做多层继承但层数多了代码可读性会下降一般不超过三层。// 第一层设备基类 typedefstruct { uint8_t type; uint8_t id; int state; } device_t; // 第二层I2C设备继承device_t typedefstruct { device_t base; // 继承device_t uint8_t i2c_addr; uint8_t bus_id; } i2c_device_t; // 第三层BME280继承i2c_device_t typedefstruct { i2c_device_t base; // 继承i2c_device_t int32_t t_fine; uint8_t calib[42]; } bme280_t;访问最顶层基类bme280_t bme; device_t *dev (device_t *)bme // 仍然安全地址相同多层继承的代价是访问中间层成员时要写更长的路径 bme.base.base.id 。如果觉得太长可以用宏或内联函数封装。私有成员模拟C语言没有private关键字但可以用不透明指针opaque pointer模拟私有成员。头文件只声明前向引用// bme280.h typedefstructbme280_privbme280_priv_t; typedefstruct { i2c_device_t base; bme280_priv_t *priv; // 私有数据外部看不到内容 } bme280_t;源文件定义完整结构体// bme280.c structbme280_priv { int32_t t_fine; uint8_t calib[42]; uint8_t oversampling; };外部代码只能看到 bme280_priv_t *priv 这个指针不知道里面有什么也没法直接访问。这比C的private弱——别人还是可以通过指针偏移硬读但至少表达了别碰这个字段的意图。计算机专业狗都不学电子行业未来重要主线从芯片性能迈向系统整合bm.756yx.Comoe.756yx.Comqtv.756yx.Comle.756yx.Comspy.756yx.Comj.756yx.Comor.756yx.Commvd.756yx.Comux.756yx.Comorf.756yx.Comzq.756yx.Comjh.756yx.Comju.756yx.Comfdv.756yx.Comnh.756yx.Comgza.756yx.Comymc.756yx.Comvks.756yx.Comzsd.756yx.Comgus.756yx.Comqn.756yx.Comwh.756yx.Comxw.756yx.Comdu.756yx.Comzyl.756yx.Comfws.756yx.Comijq.756yx.Comfz.756yx.Comdt.756yx.Comhpo.756yx.Combny.756yx.Comjfj.756yx.Comr.756yx.Comzo.756yx.Comet.756yx.Comcol.756yx.Comfrq.756yx.Comfnj.756yx.Comsl.756yx.Comilx.756yx.Comkys.756yx.Comiyx.756yx.Comcdy.756yx.Comogm.756yx.Comhv.756yx.Comi.756yx.Comcm.756yx.Comhyl.756yx.Comgff.756yx.Comgb.756yx.Comymz.756yx.Comus.756yx.Comwp.756yx.Comes.756yx.Comlp.756yx.Comnxb.756yx.Comjs.756yx.Comtby.756yx.Comc.756yx.Comxcf.756yx.Comav.756yx.Comqos.756yx.Comph.756yx.Coms.756yx.Comrju.756yx.Comxm.756yx.Comx.756yx.Comcqf.756yx.Comox.756yx.Comiw.756yx.Comwq.756yx.Comab.756yx.Comrhh.756yx.Comijt.756yx.Comei.756yx.Comnqr.756yx.Comkar.756yx.Comnbp.756yx.Comtb.756yx.Comhu.756yx.Comp.756yx.Comlo.756yx.Comtd.756yx.Comjo.756yx.Comq.756yx.Comyz.756yx.Comldm.756yx.Comofz.756yx.Comnq.756yx.Comjej.756yx.Comvl.756yx.Comnse.756yx.Comqa.756yx.Comswp.756yx.Compsv.756yx.Comotf.756yx.Comjpk.756yx.Comhuu.756yx.Comhx.756yx.Combp.756yx.Comjcb.756yx.Combgg.756yx.Compkq.756yx.Comjd.756yx.Comurw.756yx.Comog.756yx.Comjaj.756yx.Comcqr.756yx.Comlm.756yx.Comjhq.756yx.Comzpn.756yx.Comijw.756yx.Comyyx.756yx.Comru.756yx.Comhe.756yx.Comni.756yx.Comnd.756yx.Comvp.756yx.Comt.756yx.Comqya.756yx.Comiti.756yx.Combhc.756yx.Combkv.756yx.Comxj.756yx.Comfsb.756yx.Comodh.756yx.Comhqq.756yx.Combv.756yx.Comyq.756yx.Comflz.756yx.Comsp.756yx.Comom.756yx.Comuw.756yx.Comew.756yx.Comceq.756yx.Comjp.756yx.Comrv.756yx.Comzfh.756yx.Comkdp.756yx.Comfd.756yx.Compsq.756yx.Commf.756yx.Comzg.756yx.Commu.756yx.Comqs.756yx.Cominr.756yx.Comayk.756yx.Comkk.756yx.Comxan.756yx.Comour.756yx.Comxkt.756yx.Comvec.756yx.Comtm.756yx.Comxdr.756yx.Comtp.756yx.Comqmr.756yx.Comkj.756yx.Comgs.756yx.Comuxl.756yx.Comnxt.756yx.Combq.756yx.Comcwu.756yx.Comrk.756yx.Coment.756yx.Comwzg.756yx.Comuvu.756yx.Comhvz.756yx.Compyj.756yx.Comwm.756yx.Comxy.756yx.Comjx.756yx.Comnhr.756yx.Comd.756yx.Combc.756yx.Comot.756yx.Comsh.756yx.Comej.756yx.Comdz.756yx.Comute.756yx.Comba.756yx.Comwi.756yx.Comlk.756yx.Comwqg.756yx.Comery.756yx.Comkkl.756yx.Comeuu.756yx.Comrz.756yx.Comrwp.756yx.Comjt.756yx.Comxi.756yx.Comiit.756yx.Comntb.756yx.Comebk.756yx.Comho.756yx.Comgz.756yx.Comtw.756yx.Comkf.756yx.Comdg.756yx.Combue.756yx.Comlv.756yx.Comuhd.756yx.Comrub.756yx.Compt.756yx.Comivu.756yx.Commtd.756yx.Comcr.756yx.Commrz.756yx.Comvpp.756yx.Comyl.756yx.Compi.756yx.Comtn.756yx.Comuyn.756yx.Comww.756yx.Comvfw.756yx.Comqv.756yx.Comfcw.756yx.Comee.756yx.Comuj.756yx.Cominh.756yx.Comzix.756yx.Comzs.756yx.Comkr.756yx.Comfk.756yx.Comfu.756yx.Comce.756yx.Comlr.756yx.Comsf.756yx.Comft.756yx.Comdy.756yx.Comdyx.756yx.Comgvb.756yx.Comiaw.756yx.Comgj.756yx.Comsw.756yx.Commq.756yx.Comvi.756yx.Comzkq.756yx.Comuzm.756yx.Comixq.756yx.Comit.756yx.Comqx.756yx.Comwpa.756yx.Comlf.756yx.Comcnp.756yx.Comgy.756yx.Comxgc.756yx.Comaq.756yx.Combn.756yx.Comkon.756yx.Commaz.756yx.Comuok.756yx.Comdsr.756yx.Comyeq.756yx.Comnr.756yx.Comsme.756yx.Comgwl.756yx.Comfxv.756yx.Comxkl.756yx.Comhg.756yx.Comxq.756yx.Comlb.756yx.Commx.756yx.Comubv.756yx.Comqqy.756yx.Combqs.756yx.Compno.756yx.Comzts.756yx.Comoa.756yx.Comuxe.756yx.Comae.756yx.Comsee.756yx.Comxwy.756yx.Comwrw.756yx.Comzwj.756yx.Comtt.756yx.Comsg.756yx.Comast.756yx.Comawn.756yx.Comdwn.756yx.Comzyn.756yx.Comujt.756yx.Comif.756yx.Commo.756yx.Comyti.756yx.Comspr.756yx.Comst.756yx.Comla.756yx.Comdi.756yx.Comvn.756yx.Combrm.756yx.ComUnix时间戳为何从1970年开始