下位机多个".c, .h"文件的相互包含及排版
一、背景:
自从接触单片机编程以来,由于工作上的需要,不可避免的时常会接手别人的代码,但常常由于上一位同事的编码随意性有点大,导致可读性非常的差,有时候不得不完全舍弃原有代码,推倒重来,无形中增加了工作量,浪费了宝贵的开发时间。因而也越来越觉着规范的代码才是提高工作效率的重要保证,不仅仅是为了别人,也为自己的日后维护带来十足便利。
因此本文即挑某一个方面:下位机多个".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"文件的相互包含及排版的更多相关文章
- qt c++对象头文件如何相互包含
今天在写qt时,遇到了两个类相互包含的问题,类A要用到类B,类B要用到类A. 类A:a.h #ifndef A_H #define A_H #include <b.h> class A { ...
- QT 头文件之间相互包含会报错:类名不存在
"希望是一个美好的东西! 希望, 这能自己给自己,否则只有无尽的痛苦和迷茫!"---Frank 假设你写了两个类A和B,如果在A.h中有 #include<B.h>; ...
- 51单片机C语言学习笔记7:关于.c文件和.h文件
1)h文件作用 1 方便开发:包含一些文件需要的共同的常量,结构,类型定义,函数,变量申明: 2 提供接口:对一个软件包来说可以提供一个给外界的接口(例如: stdio.h). 2)h文件里应该有什么 ...
- linux下保存下位机输出的串口信息为文件
linux下保存下位机输出的串口信息为文件 1.stty -F /dev/ttyUSB0 raw (转换成raw模式) 2.stty -F /dev/ttyUSB0 speed 115200 (设置波 ...
- ROS主题发布订阅控制真实的机器人下位机
先模拟控制小乌龟 新建cmd_node.ccpp文件: #include"ros/ros.h" #include"geometry_msgs/Twist.h" ...
- "废物利用"也抄袭——“完全”DIY"绘图仪"<二、下位机程序设计>
就不说怎么组装了吧,一把辛酸泪.说程序,因为这有两把辛酸泪……一把给下位机的C代码一把为了VB.NET的图像处理……不过就上上一篇说的,它们可以正确运行了,并且今天克服了Arduino上电过程中步进电 ...
- STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发
这里我主要说一下如何做一个USB下位机,这里主要分3部分:1.建立工程:2.添加报文描述符:3.数据的传输.这里就不讲USB的理论知识了,有想要了解的自行百度一下就可以了. 建立工程:工程建立参考:h ...
- 转载 STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发
STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发 本文转载自 https://www.cnblogs.com/xingboy/p/9913963.html 这里我主要说一 ...
- C#做上位机软件——绘图并传输给下位机
拿到任务之后首先分成了几个部分: 1.绘图.学习了GDI+ 2.图片保存. 3.将图片转换成byte[].由于使用Socket通信,只能传输byte[]数据,所以这一步是向下位机传输的关键. 相应地, ...
随机推荐
- div内容上下居中
今天无聊闲逛技术群,听一哥们说要在div里面居中span内容. 第一印象:vertical-align: middle; 结果失效.因为他只对属于inline的元素或是inline-block.tab ...
- oracle修改表字段名时报错:ORA-00054:资源正忙,但指定以NOWAIT方式获取资源,或者超时失效的问题
打开sql plus select session_id from v$locked_object;查询出oracle锁定的会话ID SELECT sid, serial#, username, os ...
- js常用函数、书写可读性的js、js变量声明...
1.Array类型函数 array.concat(item...) 函数功能:关联数组,实现数组相加功能,但并不影响原先数组,concat返回新数组. array.join(separator) 函数 ...
- 捉襟见肘之自定义自拍相机AVFoundation
因为上篇的问题的,我搜索到解决方法: http://stackoverflow.com/questions/5427656/ios-uiimagepickercontroller-result-ima ...
- SQL Server 2012 学习笔记2
1. 新建数据库 可以在对应目录下右键新建数据库,也可以用程序添加: 先打开程序编辑对话框"New Query" create database Library 2. 添加表格 可 ...
- Python2.7安装(win7)
Python可在官方网站直接下,或者百度一下Python2.7下载,这里推荐使用2.7而不是3.3,比较适合初学者 工具/原料 win7系统 python2.7安装包 方法/步骤 1.从官网下载最新的 ...
- CentOS6.5 安装Sphinx 配置MySQL数据源
前提安装完mysql,并创建测试表和数据 DROP TABLE IF EXISTS `documents`; CREATE TABLE IF NOT EXISTS `documents` ( `i ...
- 跟我一起玩转Sencha Touch 移动 WebApp 开发(一)
1.目录 移动框架简介,为什么选择Sencha Touch? 环境搭建 创建项目框架,框架文件简介 创建简单Tabpanel案例 自定义图标的方式 WebApp产品测试和发布 HTML5离线缓存 发布 ...
- Http常用状态码
HTTP状态码(HTTP Status Code) 一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务不可用 所有状态解释:点击查看 1xx(临时响应 ...
- AspNetUsers
public class CanDooDbContext : DbContextBase<CanDooDbContext> { protected override void OnMode ...