[原创] 【旧帖浮沉 之二】一个.c一个.h?

辛昕   2018-9-12 00:05 楼主
不知道大家对于 .c和.h有什么看法?这其实是个很有意思的话题。

老规矩,这个问题在我自己身上虽然经过了很长时间的积淀才形成了看法。
但我今天突然写这个帖子,自然也是受一个老帖子启发。

AVR c语言优秀编程风格

这个帖子内容挺多,我只提取其中一句话,作为这个帖子的发端,因为这个观点,实际上很流行,包括曾经的我,也是这么干的。

引用: 我写的成型的程序的文件个数基本上都是偶数,因为每一个结构化的函数定义.c文件都会对应一个.h文件。main.c对应config.h。


此内容由EEWORLD论坛网友辛昕原创,如需转载或用于商业用途需征得作者同意并注明出处


强者为尊,弱者,死无葬身之地

回复评论 (13)

事实上,另一种更普遍的观点是

引用: 模块化......一个模块一个.c和一个配套的.h
比如说,有uart.c就该有uart.h,有gpio.c就该有gpio.h


这里,先不讨论所谓模块化如何区分和界定——因为每个人观点都不太一样。
但是有一点就是,假如真的有两个模块,一个是uart(.c)一个是gpio(.c),是不是就一定地分别有
uart.h和gpio.h呢?

这次,时间很晚了,我也不再写一大堆,也想做成一个开放性话题,我想听听你们对这个问题的看法。
我可以给一点我自己看法的小提示——不妨看看一些第三方库是怎么做的,比如说FreeRTOS,uCGui(v3.90)
之类的。
强者为尊,弱者,死无葬身之地
点赞  2018-9-12 00:06
1) 我觉得两种说法没有严格的区分,
2) 首先,一般一个源文件即是一个模块,比如logic.c与logic.h,可以说是一个模块,逻辑处理单元,也可以说只是一个源文件,如果不按模块分的源文件,我觉得不是好的源文件
3) 到于说的.c与.h主要是否看方便调用与声明,我的建议是配对出现,一个源文件配一个.h
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-9-12 08:28
一般成对出现,不同模块再使用文件夹区分。
点赞  2018-9-12 08:44
引用: 懒猫爱飞 发表于 2018-9-12 08:28
1) 我觉得两种说法没有严格的区分,
2) 首先,一般一个源文件即是一个模块,比如logic.c与logic.h,可以 ...

我很同意你的看法
尤其第一点: 这两种说法没有严格的区分。

但是关于你说的

  1. 如果不按模块分的源文件,我觉得不是好的源文件


我不是很认同,因为我觉得。

  1. 类似于函数长短只是外在表象,划分函数从来不是因为长短,而是因为功能归属。

  1. 对于我来说,我把 .c视为 实现部分,把.h视为 声明,或者说对外开放的接口说明文档。

强者为尊,弱者,死无葬身之地
点赞  2018-9-14 00:49
@懒猫爱飞

在这种认知之下,我的.c和.h可以完全不一一对应。

比如说,.h是对外的开放接口,这个接口,绝大多数情况下指的是 函数 或者 宏。
但是,从接口最少信息开放的原则,实现中肯定会有很多无需对外开放,被外部引用的 过程中函数,宏。
一个很特别的例子是,很可能,在这些过程中,出现了一些  可以划分为一个 更小整体的 函数群。

但这个函数群显然是不需要对外开放,而它在这整个完整的实现中,有可能担任两种角色:
1.它是一个相对独立的特殊功能,只需要用一次,但它和代码中的其他部分,不存在平行的关系。
举个例子说人话吧。
我是潮汕人,对于大多数非潮汕人特别是非广东人,我这样解释就可以了,我不用多说一句我是潮阳人,第一,我说了,它们不一定知道,第二,对于他们来说,潮汕人就是潮汕人,不需要区分什么潮阳人潮南人潮州人。

2.这个例子在这个实现中,可以多处被复用,但是,它对于这个实现之外,却意义不大,因此没必要开放成一个独立的公用子模块。

在上述这两种情况之下,这个更内层的函数群,都完全具备理由,被视为一个单独的源文件实现存放代码(这有助于代码管理,也有助于测试)
但是,它本身作为一个模块的意义不大,也不需要和那些真正被外部调用的接口一起对外开放。

在这种情况下,@懒猫爱飞 有没考虑过,这个时候,就不需要一个.c对应一个.h成对出现了?
强者为尊,弱者,死无葬身之地
点赞  2018-9-14 00:57
引用: lcn1992 发表于 2018-9-12 08:44
一般成对出现,不同模块再使用文件夹区分。

见楼上,继续往下说点。
  1. 这个地方,我很想举个例子,哪怕是个伪代码也好,可惜一时想不出一个好例子。
  2. 这个回复超出了我昨晚对接下来要回复的内容的预期,所以,暂时只能先不写,过后希望继续讨论出点新话题
强者为尊,弱者,死无葬身之地
点赞  2018-9-14 01:00
关于我昨晚提到的小提示  FreeRTOS
由于我很久没用过了,加上之前电脑资料进行过大清洗,以前也没有好好传到 git服务器上,导致我下个freertos都十分卡顿。
我在github上看了一下别人 fork的 FreeRTOS
我必须承认我记忆有误,因为FreeRTOS的 Source/Include并非只有一个  FreeRTOS.h 和 heap.h
这的确是打脸了。

也不得不承认,在这个过程中,我也有不断地想一个问题,甚至想到屏幕后面有一个阴恻恻的得意的嘲笑。
但我还是认为,讨论一下自己对于代码如何分配,是一个有意义的问题。

因为我认为,小到函数怎么划分,大到源文件,模块如何定义。
都是通过一个外在形式显示化 程序结构 的 思想。

不管我们的做法是否和那些主流的著名的程序库,码农大神一样,我们都需要有一个好理由和一个好的做法。

今天晚了,明天继续写~~
欢迎你的回复
强者为尊,弱者,死无葬身之地
点赞  2018-9-14 01:34
1) 不管多复杂的东西,我觉得,至少有一点是管理方便,
     就像有人把.h, .c会分两个文件夹存储,而有人喜欢放在一块,
2)那么.c与.h也是有一样,有人喜欢成对出现,有人觉得没必要,这个与项目大小,个人喜好都有一定的联系,你非   要争个哪种方式好,哪种方式不好,我觉得没必要,适合自己的才是最这好的
3) 我没有开发过特大型项目,对我我开发的嵌入式,一般是按功能划分模块,一个模块一个源文件,一个源文件一个头文件
     当然,你会说,有的功能非常强大,几千行,几万行写不完,要划分多源文件,这个我不否人,
     同时,我有必要提醒一下,功能可分大小,比如同样是LCD显示的,你可以认为他是一个功能模块,那么我还可以细分 ,比如,LCD底层驱动可以划分一个模块,比如画线可以划分能一个功能模块,比如画圆可以划分成一个功能模块,只要你想分,怎么都能分开,
    我说这些,只是想阐明,我所谓的功能划分的方法
4) 就像我们设计硬件图纸时(只说功能简单的),有人喜欢画在一页图纸上,那怕把图纸设计成A3或A2也要画在一张图纸上,认为这样才完整,
      而我恰恰相反,只用A4纸,那怕这纸原理图上,只画一个LDO,那么我会把他命名为电源管理
     哪怕这个图纸上只有几个按键,我会把它命名为 输入接口
     除非是一张A4纸,真的很轻松画完所有元件,并很清晰,否则,我宁愿拆分多个模块
5) 回归程序,我也在做同样的事
6) 好吧,上班时间,还是搬砖吧
     。。。
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-9-14 08:47
引用: 辛昕 发表于 2018-9-14 00:57
@懒猫爱飞

在这种认知之下,我的.c和.h可以完全不一一对应。

比如说,.h是对外的开放接口,这个接口 ...

我也用过你说的这种方式,当然这只是实现一个很简单不带逻辑的小模块。
目录结构:
AES.c
CRC16.c
util.h
然后放在util文件夹下。
这种方式个人感觉比较适合一些简单的,涉及内容/函数功能少的模块。
如果涉及到的内容多,比如最近在做一个需要和平台交互的项目。
单就网络连接的方式包含RJ45、ESP8266,与平台的交互命令。
RJ45中包含lwip,ESP8266包含串口。
显然这时候如果只实现rj45.c, esp8266.c, platform.c, platform.h,这样.h的文件将会非常庞大复杂。
点赞  2018-9-14 08:57
引用: 懒猫爱飞 发表于 2018-9-14 08:47
1) 不管多复杂的东西,我觉得,至少有一点是管理方便,
     就像有人把.h, .c会分两个文件夹存储,而有 ...

是的。
无所谓好坏。
昨晚我睡下后,突然想,每个项目就像每个人一样,都是独特的存在。
就好像我现在已经不那么介意代码写成怎样了。
就像这个世界一样,乱糟糟的,才是最真实的,哪里有到处都是金碧辉煌,瓷砖闪耀的。

而讨论这个,不是为了争辩谁对谁错,只是探讨一下看法。
强者为尊,弱者,死无葬身之地
点赞  2018-9-14 11:14
引用: lcn1992 发表于 2018-9-14 08:57
我也用过你说的这种方式,当然这只是实现一个很简单不带逻辑的小模块。
目录结构:
AES.c
CRC16.c
ut ...

嗯,所以我一直觉得
不管是源文件还是函数的划分,首先着眼于功能。
然后是功能分组。

到底是所有.c对外开放的函数都放在一个.h里呢还是一一成对出现呢?
取决于这些功能被使用的时候,是经常一起还是分开。

后来我还想到一点,就是,它们之间是否独立的。
比如C标准库的15个子库,都是独立的。
单独使用任何一个都不依赖于另外的,这个时候把它们分开才有意义,否则不管任何情况下使用一个都得调用另一个,就没必要分成15个.h了。

我回头去看看STM32。
我一度觉得,STM32库并没有完全遵照至少像C标准库这样的层次结构。
对于gpio uart这种东西,比如uart底层肯定的配置IO口,其实,单独分是不太好界定的,尽管stm32xxx_uart.c里即使要配置gpio口,也可以直接通过寄存器操作,实际上并非一定要调用gpio的接口。

对于STM32库让人不满的另一点,当然是个题外话,就是那个assert只是一个while是个很蠢的设计或者引导。
即使不能通过串口出来,实在不行,写在标准错误流里也挺好。
直接while死循环,鬼知道到底死在哪个assert里。
所以我一直觉得,stm32库有很强烈的模仿C标准库的感觉,但是某些实现,累赘而愚蠢。

但不论如何,我想很多人和我一样,起步于理解和模仿STM库
强者为尊,弱者,死无葬身之地
点赞  2018-9-14 11:20
引用: 辛昕 发表于 2018-9-14 11:20
嗯,所以我一直觉得
不管是源文件还是函数的划分,首先着眼于功能。
然后是功能分组。

到底是所有.c ...

理论上说,ST里里面这个ASSERT是留给开发者去填充的,它们给出的只是一个他们的思想模式
做开发,就是要开发
不能拿来就用
专注智能产品的研究与开发,专注于电子电路的生产与制造……QQ:2912615383,电子爱好者群: void
点赞  2018-9-14 11:41
引用: 懒猫爱飞 发表于 2018-9-14 11:41
理论上说,ST里里面这个ASSERT是留给开发者去填充的,它们给出的只是一个他们的思想模式
做开发,就是要 ...

u r right
强者为尊,弱者,死无葬身之地
点赞  2018-12-3 01:32
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复