一、背景:

  自从接触单片机编程以来,由于工作上的需要,不可避免的时常会接手别人的代码,但常常由于上一位同事的编码随意性有点大,导致可读性非常的差,有时候不得不完全舍弃原有代码,推倒重来,无形中增加了工作量,浪费了宝贵的开发时间。因而也越来越觉着规范的代码才是提高工作效率的重要保证,不仅仅是为了别人,也为自己的日后维护带来十足便利。

  因此本文即挑某一个方面:下位机多个".c, .h"文件的相互包含及排版做一个记录,提示自己,也为新加入的朋友们做个参考。

二、正文:

  以我现在正在开发的电源管理项目为例,项目需求既是通过I2C与某电脑主板通信并按要求对主板的供电进行管理。上一位同事也许由于时间仓促,只使用了一个"main.c"文件来实现了部分功能,造成的后果既是删改功能很难定位到相应位置并难以厘清相关联函数间的关系。

  此项目共有几大模块——主循环模块,与主板通信的IIC/UART模块,读取拨码开关/button按键模块,存储flash模块,电池管理模块。以及电源管理模块。每个模块既是一个".c,.h"文件,现在着重记录多个".c.h"相互关联、相互包含的解决办法。

  为了便于管理,该项目文件夹内,我分了以下几个文件夹:"PROJ"存放工程文件,"APP"存放"main.c"、"aplication.c"文件,"BASEDRIVE"存放的"Uart.c"文件,"EXTIDRIVE"存放的是"Flash.c","DOC"存放的是"Readme"文件,对应".h"文件存放在对应文件夹。

  包含头文件时可写绝对路径:譬如"#include  XXX/EXTIDRIVE/'Flash.h'",但过于麻烦,也可将头文件所在的目录设置进编译软件,

  a)IAR的设置方法:

  在工程文件名右键,选择"Option"-->"C/C++complier"-->"Proprocessor"-->"Additonal Include directories"。

  b)KEIL的设置方法:

  “Project”-->”Option for Target XXX”-->”C/C++”-->”Include Paths”。

  这样直接写相对路径:"#include 'Flash.h'",头文件也可被编译器找到。

  前期铺垫这么多,现在开始进入正题:“多个.c文件相互包含.h文件该做如何处理”?

  在每个”.c”文件内声明变量与函数,某些变量/函数如果会被其它”.c”文件调用,则在对应的”.h”文件extern 该变量/函数,若有文件要使用这些变量/函数,只需包含其头文件即可。宏定义可直接在头文件内定义。

  以该电源管理项目为例:在”main.c”文件中,我会声明一些基本上会被许多模块使用或者更改的变量,譬如主板的状态标志位,是开机,关机还是休眠,此状态由拨码开关/button,IIC/UART等模块共同更改,所以需要在”main.h”中extern该变量/函数,然后让所有文件包含该头文件;”main.h”的宏定义一般也是基本上所有模块均会使用的宏定义。又譬如”Uart.c”这个文件内,有一个标志位”uartdone_flag”,一个函数”myprintf(*string);前者不会有其它文件调用,我只在”Uart.c”文件声明即可,但函数”myprintf(*string)”一定会被其它文件调用,所以这个函数不仅仅在”Uart.c”声明,还需要在”Uart.h”中external出来,若有文件需要调用该函数,只需包含”Uart.h”即可。

  再来说说为何不直接在头文件内声明,而是需要extern关键词,若是直接在头文件内声明,两个”.c”文件同时包含该头文件,在编译时会出现重复定义的错误,加了”#ifndef”之类的条件编译也没有效果,所以为了避免该错误的发生,我使用了extern的关键词。至于为什么,还得待有时间去深究编译器原理。

/* 此处更改一点新发现。 2016年5月14日
* 在跟读keil的官方例程发现其头文件并没有使用“extern”关键字,却也能让多个“.c”包含它
* 而不出现重复定义的错误。仔细观察才发现,其头文件内并没有对变量进行 声明,而只是
* 单纯的声明函数。若是在“.h”文件内声明一个变量,那么就会出现重复定义的错误。
* 所以,按照这个现象,可以得出,在keil以及IAR的编译器内,头文件出现了变量的声明即
* 不可被多个“.c”文件包含。若是需要被多个“.c”文件包含,则变量请在.c文件内声明,变量
* 需要被外部文件调用,则使用“extern”关键字即可。
*
* 为什么会出现这种情况?以下是我的猜想,猜想而已,有错即欢迎留言指正。
* 当编译器在头文件内碰到变量声明时,则为该变量分配了一个实体地址;由于第二个“.c”
* 文件又包含了该头文件,编译器再次链接到该头文件(#ifndef 真的没起作用 -_-! )
* 并再次碰到该变量,正要为其分配实体地址时,才发现该变量已经分配了地址了,也许是
* 该编译器不够智能,不知将其分配到已分配到的地址,而是直接报错。(在linux系统下,
* 已经证实,gcc会智能将其分配到原先地址不报错。)
* 而若是函数声明呢?函数声明而已,不用暂实体地址,函数的实体地址在“.c”文件内才会
* 去分配实体地址,所以函数声明不会造成重复定义的错误。
*
* 另外一个需要注意的既是:要被多个“.c”文件包含的头文件,一定不能在该头文件内对变
* 量进行赋值,无论keil/IAR/gcc,均会产生重复定义的错误
*/
/* 更新一次关于结构体的声明方式      2016年5月26日
* 昨天在“.c”文件内,typedef了一个结构体,XXX_Struct。接着在“.c”文件定义了一个
* 结构体实体 XXX_Struct Test_Struct; 然后由于其它文件会调用该结构体,因此
* 我在“.h”文件“extern”了该结构体,但是在编译时却出现了Test_Struct undefined.
* 意味着,未认出该结构体。将“.h”文件内的extern删除,就没在报错了,可没有
* 了“extern”,结构体该如何被其它文件引用呢。
* 之后,通过朋友提示,将typedef放到“.h”文件内,然后加入extern,编译通过,也可
* 被其它文件调用。
* 为什么会这样呢?其实仔细想想,是自己没有弄明白typedef的真实含义罢了。
* typedef相当于声明了一种叫XXX_Struct的结构体的类型,本身并不会占用内存空间,
* 在“.h”文件内定义,即便被多个“.c”文件包含,也就不会报重复定义的错。所以其可在
* “.h”文件内声明定义。
* 但是,XXX_Struct Test_Struct; 就是在声明一个实体结构体变量了,是需要分配
* 空间的,因此,其不能在“.h”文件内定义,而应该是extern,在“.c”文件内定义。
*/
/* 更新一次关于 “extern”的问题     2017年2月7日
* 感谢杨旭礼(http://www.cnblogs.com/yangxuli/p/6438144.html)提供了这个思想。
* 即为了防止包含头文件过于繁杂,那么将所有其他文件需要用的变量在一个头文件
* 内“extern” 出来,那么要用外部变量的文件只需要包含这一个头文件即可,宏定义
* 也可以单独用一个头文件来定义。
*/

以下为实际效果对比图:

记录地点:深圳WZ

记录时间:2016年5月6日

下位机多个".c, .h"文件的相互包含及排版的更多相关文章

  1. qt c++对象头文件如何相互包含

    今天在写qt时,遇到了两个类相互包含的问题,类A要用到类B,类B要用到类A. 类A:a.h #ifndef A_H #define A_H #include <b.h> class A { ...

  2. QT 头文件之间相互包含会报错:类名不存在

    "希望是一个美好的东西! 希望, 这能自己给自己,否则只有无尽的痛苦和迷茫!"---Frank 假设你写了两个类A和B,如果在A.h中有 #include<B.h>;  ...

  3. 51单片机C语言学习笔记7:关于.c文件和.h文件

    1)h文件作用 1 方便开发:包含一些文件需要的共同的常量,结构,类型定义,函数,变量申明: 2 提供接口:对一个软件包来说可以提供一个给外界的接口(例如: stdio.h). 2)h文件里应该有什么 ...

  4. linux下保存下位机输出的串口信息为文件

    linux下保存下位机输出的串口信息为文件 1.stty -F /dev/ttyUSB0 raw (转换成raw模式) 2.stty -F /dev/ttyUSB0 speed 115200 (设置波 ...

  5. ROS主题发布订阅控制真实的机器人下位机

    先模拟控制小乌龟 新建cmd_node.ccpp文件: #include"ros/ros.h" #include"geometry_msgs/Twist.h" ...

  6. "废物利用"也抄袭——“完全”DIY"绘图仪"<二、下位机程序设计>

    就不说怎么组装了吧,一把辛酸泪.说程序,因为这有两把辛酸泪……一把给下位机的C代码一把为了VB.NET的图像处理……不过就上上一篇说的,它们可以正确运行了,并且今天克服了Arduino上电过程中步进电 ...

  7. STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    这里我主要说一下如何做一个USB下位机,这里主要分3部分:1.建立工程:2.添加报文描述符:3.数据的传输.这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了. 建立工程:工程建立参考:h ...

  8. 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发

    STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发  本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...

  9. C#做上位机软件——绘图并传输给下位机

    拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...

随机推荐

  1. Unity赛车游戏之移动

    这个赛车游戏真是让我费劲脑汁啊.尤其是写这种系统化的东西. 目前漂移还没找到更好的算法,不过基本的移动还是可以做到的. 别看就光是个移动,其实也是很费事的. Unity给了个对于赛车系统很好的碰撞组件 ...

  2. 屠蛟之路_重伤的屠蛟俊_ThirdDay

    在屠蛟少年们重登数据库大山的途中,少年屠蛟俊(511)实力扛把子,一直坚持在队伍前头开路引领.披荆斩棘,却也因为一路与险水恶林.狂禽猛兽做战斗而精力受损,最终一不小心坠进beta怪蛟设置的陷阱深洞里, ...

  3. api get

    http://ibi.imim.es/befree/ http://disgenet.org/ http://rest.ensembl.org/ { "variantSetId": ...

  4. Docker入门教程(六)另外的15个Docker命令

    Docker入门教程(六)另外的15个Docker命令 [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第六篇,继续介绍Docker命令.之前的第二篇文章 ...

  5. Saltstack异步执行命令(十三)

    Saltstack异步执行命令 salt执行命令有时候会有超时的问题,就是命令下发下去了,部分主机没有返回信息,这时候就很难判断命令或任务是否执行成功.因此,salt提供异步执行的功能,发出命令后立即 ...

  6. Saltstack 介绍、安装、配置(一)

    Slatstack 介绍 官网:https://saltstack.com/ 官方源:http://repo.saltstack.com/  (介绍各操作系统安装方法) http://repo.sal ...

  7. css属性设置

    css在线编辑工具地址:http://tool.chinaz.com/Tools/CssDesigner.aspx 案例详情: http://dongtianee.sinaapp.com/index. ...

  8. 查看apt-get安装软件的版本

    apt-cache search name 查询 apt-get install name 安装 dpkg dpkg dpkg-checkbuilddeps dpkg-genchanges dpkg- ...

  9. CentOS只允许部分IP登陆ssh |ssh 允许指定IP

    在/etc/hosts.allow输入   (其中192.168.10.88是你要允许登陆ssh的ip,或者是一个网段192.168.10.0/24)   sshd:192.168.10.88:all ...

  10. wordpress后台404页面

    就在刚刚,boss需要看公司网站后台,网站是用wordpress搭的,发现全是404,蛋疼,于是google,下面是解决办法: location / { if (-f $request_filenam ...