zoukankan      html  css  js  c++  java
  • 浅谈C工程中的.c与.h文件

    基于C语言的单片机、arm相关的工程开发时,C语言的模块化特点体现的非常明显。试想一下:你的一个工程中需要用到AD采样模块、液晶显示模块、串口发送模块、DA控制模块等。你肯定不会选择在一个.c文件中进行,必须是分模块的,这样才有利于团队开发,提高效率。

    那么模块化设计遵循着怎样的原则呢,应该怎么写.c,.h文件呢。

    1. .c和.h文件的区别

    通常意义上的说法,.c是源文件,.h是头文件。通常创作者为了保护其代码,而把.c文件封装起来,不公开,而将.h文件提供出来。此时.h相当于接口,供程序员调用。

    但是实际上我们自己编写.h,.c文件时会发现.c与.h文件里面的内容其实并没有什么区别。在.c中写的代码同样可以在.h文件写

    2.工程中使用.c,.h文件

    总的来说,.h文件有两种使用方法。

    方法一:

    正是由于1中蓝色部分的描述的原因。我们会发现好多工程中某个模块只有.h文件,而没有.c文件

     

    以上图的DSP开发【瑞泰提供的例程为例】其工程中ICETEK-VC5509-EDU.h文件中包含了对12864液晶屏的寄存器地址定义,相关操作函数定义。并没有写相关的.c文件。然后在lcd.c文件中调用ICETEK-VC5509-EDU.h。这样的做法是可行的,为什么可行,上面1中已经讲得很清楚。

    但是我们再来思考一个问题。如果这个工程中我又添加了一个ad.c。在ad.c中我们需要用到液晶显示,那么我们得调用ICETEK-VC5509-EDU.h 。如果我们这么做了,工程编译链接会通过吗?

    肯定不会,会报define duplicated //重复定义的错误,为什么呢? include "ICETEK-VC5509-EDU.h"相当于把这句话替换成ICETEK-VC5509-EDU.h中的内容,而ICETEK-VC5509-EDU.h中对液晶的寄存器都进行了定义,初始化,函数都已经定义了。你在两个文件中引用ICETEK-VC5509-EDU.h,肯定会报重复定义的错误。

    这种方法适用于单模块,或者某个模块不会被多个.c文件调用

     

    方法二:在.c文件中进行变量,数组,函数的定义,在.h文件中进行变量,数组,函数的声明。

    C语言支持的是一处定义,多处声明【这里所讲的声明指的是狭义上的声明,引用声明,即不会进行内存分配】

    现在将上述的ICETEK-VC5509-EDU.h分成.c和.h文件

    首先在.h文件中开头写上

    #ifndef __ICETEK-VC5509-EDU_H 
    #define __ICETEK-VC5509-EDU_H //防止重复定义,名字与.c相同
    #endif

     

    然后就要进行进一步的操作

    (1)宏定义,宏定义是遇到此变量即替换

    所以放在.h文件中

    如:

    // McBSP0  ------------------------------------------------------
    #define SPCR0 (*(unsigned int *)0x018c0008)
    #define PCR0 (*(unsigned int *)0x018c0024)
    #define SPCR01 (*(unsigned int *)0x01900008)
    #define SPCR02 (*(unsigned int *)0x0)

     

    (2)函数,函数的定义和声明比较清晰

    .h文件进行声明

    void InitInterrupt(void);   // 初始化中断
    void InitCTR();      // 初始化ICETEK-CTR
    void CloseCTR();     // 关闭ICETEK-CTR上各设备

    .c文件进行定义

    void CloseCTR()
    {
        CTRGR=0;     
        CTRLR=0; CTRLR=0x40;
        CTRLR=0x0c0;
     LCDCMD(LCDCMDTURNOFF); 
     dbClearKey=CTRCLKEY;
     LBDS=0;
    }

    这样就可以实现多个.c文件加.h头文件,而不会导致重复定义的问题。

     

    (3)数组等变量

    变量的声明和定义在单个文件时好像都不怎么区分的。

    变量的声明:指明变量名,变量类型。

    变量的定义:指名变量名,变量类型,分配内存空间。

    eg int  a; //定义

    extern int a; //声明

    如果进行了赋值,则肯定是定义

    extern int a=0; //定义

     

    所以在.h文件中:

    extern unsigned char ledbuf[8],ledx[8];
    extern unsigned char dbClearKey;
    extern unsigned char ledkey[10][8];
    extern unsigned int music[nMusicNumber][2];

    //变量声明

    .c文件:

    unsigned int pwm[8]={ 0x86,0x87,0x83,0x8b,0x89,0x8d,0x8c,0x8e };
    //变量必须要定义
    unsigned char dbClearKey; //必须要定义
    unsigned  char ledbuf[8];
    unsigned char ledx[8];

     

    在这地方还是有个疑问的,在.h文件中如果不加extern 编译也是可以通过的,那么unsigned char ledbuf[8]也能表示声明。【.h文件是先被加载进.c文件,所以.h中unsigned  char ledbuf[8]是最先执行的。为什么表示的是声明呢】

     

    关于上述疑问的说明:

    当时是在dsp开发环境ccs下测试的,现在在keil 环境下编译,发现按照上述做法是无法通过的,“重复定义的错误”这也验证了当时的疑问。
    通过查找资料:c语言中函数本质上是外部的,所以为了方便编程,c语言允许声明函数时省略写extern【c语言规定,如果定义函数省略extern,则隐含为外部函数】
    而对于变量,默认类型是auto,是局部变量,动态存储的。所以声明时不可以省略。
    至于为什么CCS环境下可以通过,这个没有进一步研究。
  • 相关阅读:
    2020-06-20 助教一周小结(第十七周)
    2020-06-14 助教一周小结(第十六周)
    2020-06-7助教一周小结(第14周)
    2020本科校招-从小白到拿到30k offer的学习经历
    2020-05-24助教一周小结(第13周)
    2020-05-17 助教一周小结(第十二周)
    2020-05-10 助教一周小结(第十一周)
    2020-05-04 助教一周小结(第十周)
    2020-04-26 助教一周小结(第九周)
    Ngnix搭建静态网页和安装wordpress
  • 原文地址:https://www.cnblogs.com/stoneFang/p/6715336.html
Copyright © 2011-2022 走看看