搜索
bottom↓
回复: 21

关于模块化编程问题

[复制链接]

出0入0汤圆

发表于 2013-8-4 11:44:02 | 显示全部楼层 |阅读模式
本帖最后由 and001 于 2013-8-4 11:55 编辑

为了使程序更加结构紧凑,具有更好的条理化,程序员往往将自己的程序按模块分成若干个原文件(*。c).然后用H头文件将函数相互关联起来,要调用就方便了!

现在我有个问题好象不是太明白,就是函数本身有时需要全局变量来参与运行如下:

KEY.C文件如:
void Key_SCan(void)
        {
               
        if(fkey_count)    //fkey_count是用来做标志,为“1”的时候才进入出来扫描
                {
//                P1=0xF4;
//        确认是自动扫描还是手动选择 ,手动时的自己输出  fhand_operation 1: 自动扫描 0: 手动
                uchar        Status_P_1_for_P1_0;
                fkey_count=0;
                Status_P_1_for_P1_0=P1;
                if(!(Status_P_1_for_P1_0&0x01)) {fhand_operation=0;}
                else{fhand_operation=1;}                                               
                old_status=new_status;
                new_status=P1;       
                }


KEY.H文件如:
         #ifndef _KEY_H_
         #define _KEY_H_
/*
void Key_SCan(void);  //在这里申明各函数
....
   
   上面是的fkey_count位变量应该怎么处理比较合适呢?是在头文件H中定义还是怎么样?一般好象是不定义变量的在其他原。C文件中不定义,请问有经验的朋友,你们是怎么处理的?


  另外,调用数是用带参还是尽量不用带参数好,如果带参的话,就好象不用在KEY.C文件中定义全局位变量(只要用形参传进来就好了)请大家各抒己见...

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2013-8-5 07:47:43 | 显示全部楼层
就是函数本身有时需要全局变量来参与运行如下:


Generally speaking, global variables are evil and should be reduced as much as you can.

   上面是的fkey_count位变量应该怎么处理比较合适呢?


Two approaches, in three expressions:

1) you can make the global variable globally accessbile, by declaring it extern in the .h file.
2) you can write a wrapper function that returns fkey_count's value - this is the more prudent but more expensive approach.
3) you can write a wrapper macro that returns fkey_count's value - this is fundamentally the #1 approach above. Sub-optimal but fast. You can further improve its safety by limiting your access to the variable via just the macro, and on some (good) compilers, you can declare that variable "const" in the .h file - this may not work if the compiler you are using is not exactly C-compliant (it will then put "const" variables in the flash).

出0入0汤圆

发表于 2013-8-5 08:23:33 | 显示全部楼层
对于模块以外使用的全局变量,不能通过
extern int var 的形式去调用,应该这样做:

static int var;

int get_var(void)
{
    return var;
}

在其他文件中需要用到这个变量,则不能extern int var,而要通过接口
get_var()  来访问。

从用户的角度来看,这个模块就是一个黑盒子,我不知道他里面什么变量,是什么,怎么得到的,
我只关心我能用什么,我通过调用API 就能拿到什么数据。(类似C++的类,成员函数等等思想)
这样可移植性就好了,你只需要在新平台上实现这些接口,其他程序都不需要改了。
这就是模块化,而不是说,分开几个文件来写程序就是模块化了。

出0入0汤圆

发表于 2013-8-5 08:36:04 | 显示全部楼层
一般的MCU,才多少空间,搞模块不是MCU必要的。


这模块化,是需要付出代价的。

出0入0汤圆

发表于 2013-8-5 08:50:24 | 显示全部楼层
两个头文件,一个给外部,一个给自己

出0入0汤圆

发表于 2013-8-5 11:11:55 | 显示全部楼层
对于全局变量
如果是C文件本身用的就定义在C文件中,并用static修饰
如果只是这个函数用的就定义在函数中,并用static修饰
如果是给别的文件用的则定义在C文件,不加修饰,并在h文件中声明成外部变量,用extern修饰

出0入0汤圆

发表于 2013-8-5 11:14:50 | 显示全部楼层
个人见解:
对于嵌入式系统不太建议使用类似C++的封装方式来调用全局变量
速度慢,而且每个BYTE的FLASH都是钱啊

出0入0汤圆

 楼主| 发表于 2013-8-5 12:42:32 | 显示全部楼层
本帖最后由 and001 于 2013-8-5 12:46 编辑
zhugean 发表于 2013-8-5 11:11
对于全局变量
如果是C文件本身用的就定义在C文件中,并用static修饰
如果只是这个函数用的就定义在函数中, ...


很精炼,很容易理解!赞一个呀!



还有一点就是:
如果是给别的文件和本身用的呢?定义在H文件中合适吗?

出0入0汤圆

发表于 2013-8-5 12:55:46 | 显示全部楼层
给别的文件和本身用的 同给别的文件
C文件要包含自己的H文件

出0入0汤圆

 楼主| 发表于 2013-8-5 13:53:10 | 显示全部楼层
badboy.tao 发表于 2013-8-5 08:50
两个头文件,一个给外部,一个给自己

这个不懂?能具体点吗?

出0入0汤圆

发表于 2013-8-5 14:12:50 | 显示全部楼层
一个C只能对应一个H文件

出0入0汤圆

发表于 2013-8-5 17:45:31 | 显示全部楼层
and001 发表于 2013-8-5 13:53
这个不懂?能具体点吗?

给外部的放给其它模块调用的函数声明和变量声明,注释尽量写详细,这样人家就只看你这个.H文件就知道怎么来调用你的函数了。

给内部用的.H,原本是放在.C文件开头定义的这个模块用的全局变量,和函数声明,我为了美观,单独用了一个.H,放在那里

出0入0汤圆

发表于 2013-8-5 17:57:45 | 显示全部楼层
devcang 发表于 2013-8-5 08:36
一般的MCU,才多少空间,搞模块不是MCU必要的。

其实占用空间是一样的,只是阅读修改起来不一样罢了

小程序当然不必要,当程序比较多的时候,一万行的程序写一个里面,那你翻看时要多么麻烦。

出0入0汤圆

 楼主| 发表于 2013-8-5 20:56:32 | 显示全部楼层
是的!感觉按功能模块来比较合适

出0入0汤圆

发表于 2013-8-6 08:36:59 | 显示全部楼层
qingyin2009 发表于 2013-8-5 17:57
其实占用空间是一样的,只是阅读修改起来不一样罢了

小程序当然不必要,当程序比较多的时候,一万行的程 ...

不错,  几K的程序,不必的了。  

出0入0汤圆

 楼主| 发表于 2013-8-8 11:21:26 | 显示全部楼层
我觉得这是一种编程风格,要严格规范要求自己,习惯养成了要面对ARM复杂程序就条理更清楚了!

出0入0汤圆

发表于 2013-8-8 19:03:55 | 显示全部楼层
Writing a modular code base is not just to make it more readable, but also to make it re-usable for your next job.

The following is an example:

  1. //#include <avr/io.h>  //we use gcc-avr
  2. #include <regx51.h>                                                        //we use keil c51
  3. #include "gpio.h"
  4. //#include "delay.h"
  5. #include "tmr0.h"                                                        //we use hardware tmr

  6. //hardware configuration
  7. #define P1_PORT                        P2
  8. #define P2_DDR                        P2
  9. #define P1_OUT                        (1<<0)                                //pulse 1 output pin
  10. #define P1_PR                        (TMR_20ms + TMR_5ms)        //p1's period
  11. #define P1_TMR                        TMR_5ms                                //tmr interrupts every 5ms
  12. #define P1_WIDTH()                {NOP();}                        //p1's pulse width
  13. //end hardware configuration

  14. //reset the pins
  15. void p1_init(void) {
  16.         IO_CLR(P1_PORT, P1_OUT);                                //idles low
  17.         IO_OUT(P1_DDR, P1_OUT);                                        //pin as output
  18. }

  19. //output a pulse
  20. void p1_out(void) {
  21.         static unsigned char p1_count=0;                //counter

  22.         p1_count+=1;
  23.         if (p1_count>=(P1_PR / P1_TMR)) {                //50ms has been reached
  24.                 p1_count -= (P1_PR / P1_TMR);                //update p1_count
  25.                 //output 12 pulses, 50% dc, with duration P1_WIDTH()
  26.                 //each pulse takes (2 + 1) * 2 us, at 12Mhz crystal, 12:1 prescaler
  27.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  28.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  29.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  30.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  31.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  32.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  33.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  34.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  35.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  36.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  37.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  38.                 IO_SET(P1_PORT, P1_OUT); P1_WIDTH(); IO_CLR(P1_PORT, P1_OUT); P1_WIDTH();
  39.         }
  40. }

  41. void mcu_init(void) {
  42. }

  43. int main(void) {
  44.         mcu_init();                                                                //reset the mcu
  45.         p1_init();                                                                //reset the pins
  46.         tmr0_init(1, TMR_5ms);                                        //5ms per interrupt
  47.         tmr0_act(p1_out);                                                //install handler
  48.         ei();
  49.         while (1) {
  50.         }
  51. }
复制代码
It is a piece of code that I wrote to generate a series of short pulses every 25ms. It was originally written for an avr, utilizing tmr0. I simply copy-and-paste the avr code, made a few minor changes, and then plug in the corresponding C51 timer modules and then code now runs on 89C51.

The way those modules are coded allows me to reuse the code base, with confidence that they would work for a new chip - because I have debug'd and tested them before and they share the same interface.

That is really the beauty of writing modular / portable code.

出0入0汤圆

发表于 2013-8-8 23:54:54 | 显示全部楼层
我的经验是头文件里面只声明函数,不要定义变量,可以定义枚举体什么的,但是不要定义变量,否则在重复包含头文件的时候会有变量重复定义的问题,全局变量就在.c文件中定义就可以了,如果需要在别的文件中进行引用,直接在需要引用的那个文件里定义外部变量,比如:extern int var;这样就可以在这个文件里面用了。

出0入0汤圆

发表于 2013-8-8 23:58:38 | 显示全部楼层
Etual 发表于 2013-8-5 08:23
对于模块以外使用的全局变量,不能通过
extern int var 的形式去调用,应该这样做:

这番话点醒了我,谢谢

出0入0汤圆

 楼主| 发表于 2013-8-9 11:00:13 | 显示全部楼层
wzd5230 发表于 2013-8-8 23:54
我的经验是头文件里面只声明函数,不要定义变量,可以定义枚举体什么的,但是不要定义变量,否则在重复包含 ...

你最后那句话很金典----很多书上好像没说!我很容易搞混‘EXTERN’的实际意思!总是弄糊涂!这算是懂了!

出0入0汤圆

 楼主| 发表于 2013-8-9 11:23:47 | 显示全部楼层
millwood0 发表于 2013-8-8 19:03
Writing a modular code base is not just to make it more readable, but also to make it re-usable for  ...

谢谢你!模块化编程的可读性和可移植性都应该比较好吧!

出0入0汤圆

发表于 2013-12-27 16:23:58 | 显示全部楼层
学习。。。。。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 05:17

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表