打印

不同模块用到的变量定义问题,这样做有无问题?

[复制链接]
4736|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
kexd2004|  楼主 | 2010-4-2 21:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近读到一些资料:永远不要在.h文件中定义变量!
正确的做法是:
/*m1.h*/
extern int a;
/*m1.c*/
#include "m1.h"
int a=5;
/*m2.c*/
#include "m1.h"//模块2中包含模块1的.h文件
/*m3.c*/
#include "m1.h"//模块3中包含模块1的.h文件

我想问一下,我这样做是否可以:
/*m1.h*/
int a;
/*m1.c*/
#include "m1.h"
int foo()
{
   a=5;//直接对该变量进行(赋值、运算、判断等)操作
   ...
}
/*m2.c*/
#include "m1.h"//模块2中包含模块1的.h文件
int fff()
{
   a=5;//直接对该变量进行(赋值、运算、判断等)操作
   ...
}
/*m3.c*/
#include "m1.h"//模块3中包含模块1的.h文件
沙发
kexd2004|  楼主 | 2010-4-2 22:02 | 只看该作者
疑问是,.h文件不能定义变量,而是申明变量(不会分配内存)。在我的用法里并没有在任何一个.c文件里定义了变量,这样使用编译是没有报错,不过不知道是否是可以的

使用特权

评论回复
板凳
kexd2004|  楼主 | 2010-4-2 22:09 | 只看该作者
同样在一个.c文件里定义了一个函数,比如是
/*m1.c*/
int foo(int a)
{
   ;
}
我要在另一个函数里调用它,我在.文件里这样声明
/*m1.h*/
int foo(int a);

问:int foo(int a);前面需要加extern?

使用特权

评论回复
地板
有意思| | 2010-4-2 23:50 | 只看该作者
这样的问题,自己写个TEST就知道了啊,用Cygwin很方便的啊

使用特权

评论回复
5
香水城| | 2010-4-3 09:52 | 只看该作者
永远不要在.h文件中定义变量!

这是一个基本常识,因为.h文件可能会被多个.c文件引用,在.h文件中定义变量,就意味着某个变量会被多次定义,编译链接时不出错才怪呢!

使用特权

评论回复
6
mentlely| | 2010-4-3 17:56 | 只看该作者
LS说得对,永远不要在.h文件中定义变量!
不过可以想UCOS那样在.h文件中定义全局变量
在.h文件中的全部登记变量用OS_EXT定义
#ifdef   OS_GLOBALS
#define  OS_EXT
#else
#define  OS_EXT  extern
#end
然后在其中任意一个引用的.c文件最前面中定义
#define  OS_GLOBALS

使用特权

评论回复
7
xiaorunyi| | 2010-4-3 19:52 | 只看该作者
楼上这种做法有什么好处?
好像麻烦了更

使用特权

评论回复
8
戒指320| | 2010-4-3 21:13 | 只看该作者
6楼的这段 是uc/os_II 的刚开始里有叙述,楼主的问题不知道是不是看了《C语言嵌入式系统编程修炼之道》产生的疑问?  确实这个 修炼之道 讲的很有道理,当楼主写个程序测试后,应该会 理解。

使用特权

评论回复
9
kexd2004|  楼主 | 2010-4-4 13:39 | 只看该作者
本帖最后由 kexd2004 于 2010-4-4 13:45 编辑

先谢谢楼上几位。是的,在《嵌入式c精华》。就如6楼说的,在.h文件里加入
#ifndef _MAIN_H_
#define _MAIN_H_
.......//在这里定义变量
#endif
以上这段是防止变量被重复定义,然后可以在不同的.c文件里引用该.h文件了。
这么做,并没有出错。如果这种使用方法是可以的话,为什么“永远不在在.h文件里定义变量”呢?

使用特权

评论回复
10
ShakaLeo| | 2010-4-4 16:02 | 只看该作者
9楼:
加入
#ifndef _MAIN_H_
#define _MAIN_H_
.......//在这里定义变量
#endif
这个貌似只能防止某一个C文件重复包含该头文件引起的变量重定义,却没法防止两个C文件同时包含该头文件引起的重定义,就是说,如果一个C文件#include<main.h>了两次,编译不会报错,但是两个C文件都#include<main.h>就要报错了,变量重复定义。
而且“永远不在在.h文件里定义变量”这个并不绝对,像六楼的UCOS的那种写法就是在h文件中定义变量,只要在一个C文件中定义OS_GLOBALS,其它的C文件不定义,所有的C文件就都可以包含同一个h文件了。

使用特权

评论回复
11
kexd2004|  楼主 | 2010-4-4 17:27 | 只看该作者
本帖最后由 kexd2004 于 2010-4-4 22:17 编辑

先谢谢楼上。
6楼提到的UCOS的那种写法很好,(写错了一个地方,结尾不是#end,而是#endif)因为具有大量的变量时候,容易造成声明的头文件和变量定义的cpp文件中变量的不一致,该方法可以避免这样的情况。

“两个C文件都#include<main.h>就要报错了,变量重复定义”
这个在我的程序里并没有报错,程序运行也暂时正常。原因是什么呢?
我用vc60写了一个测试程序,当保存为一个main.c,一个test.c,一个global.h,编译没错,输出结果正常;
当保存为cpp格式,build时报错:
test.obj : error LNK2005: "int c" (?c@@3HA) already defined in main.obj
这个就是说重复定义了。
说明这种对重定义的支持是依据编译器的,c++标准下不允许这样做,c标准下似乎没问题?由于我一直是用c,
所以在我的程序里都“暂时”都很正常,究竟这么做会不会导致一些我未发现的错误呢?会不会c下的编译器会检测到第一个用到.h里的变量时候分配一个存储空间,后面(不管是不是多个c文件)用到的就不再分配了?

使用特权

评论回复
12
ShakaLeo| | 2010-4-4 17:47 | 只看该作者
“我用vc60写了一个测试程序,当保存为一个main.c,一个test.c,一个global.h,编译没错”
楼上说的这种情况不具备普遍性,即使是C编译器。可以试试keil和IAR。都会报错,变量重定义。

使用特权

评论回复
13
kexd2004|  楼主 | 2010-4-4 22:15 | 只看该作者
是的。所以这个方法不被鼓励,换到不同的编译环境下就会有问题了。我要好好修一修我的不良习惯了。。。。

使用特权

评论回复
14
xwj| | 2010-4-4 23:50 | 只看该作者
本帖最后由 xwj 于 2010-4-5 00:03 编辑

这个问题,说说老x的做法:
#ifndef __OSD_MAIN_H__
#define __OSD_MAIN_H__

#ifndef __OSD_MAIN_C__
        //  XWJ_OSD  公用函数
        /********************************************************/
        extern void OSD_Init(void);                // OSD_ 初始化
        extern void OSD_Cls(void);                // 清屏
           ....
        extern unsigned char xdata *SramAdr;
#else
        //  XWJ_OSD  公用函数
        /********************************************************/
        void OSD_Init(void);                        // OSD_ 初始化
        void OSD_Cls(void);                        // 清屏
           ....
        unsigned char xdata *SramAdr;
#endif

#endif
解释说明如下:
1、每个文件都有对应该文件名的标识定义:——这是为了避免多次重复包含这个头文件时出错,有了它就不管怎么重复引用这个头文件都没问题了。
#ifndef __OSD_MAIN_H__
#define __OSD_MAIN_H__
....
#endif

2、然后判断对应C文件的标识,没有定义__OSD_MAIN_C__的话则是外部引用,否则为真正的本地引用和变量声明——这样的话,在osd_main.c文件中先#define __OSD_MAIN_C__,然后再#include "osd_main.h",就可以确保只有唯一的一处会分配变量了,而别的程序中不定义#define __OSD_MAIN_C__,而只有#include "osd_main.h",所以执行的全都是#ifndef __OSD_MAIN_C__       ~    #else 之间的外部引用
#ifndef __OSD_MAIN_C__
        extern void OSD_Init(void);                // OSD_ 初
        extern unsigned char xdata *SramAdr;
....
#else
        void OSD_Init(void);                        // OSD_ 初
        unsigned char xdata *SramAdr;
#endif
注意:
在osd_main.c文件中这样写:
#define __OSD_MAIN_C__
#include "osd_main.h"
程序实体....

而在别的文件中却是这样写:
#include "osd_main.h"
程序实体....


修改:整理下格式

使用特权

评论回复
15
xwj| | 2010-4-4 23:58 | 只看该作者
其实,不管怎样做,都要有个统一的风格和规范,然后所以的程序都照着这样子套就可以了。

可千万不要这里这样做,哪里那样做哦~,那样的话当你想把你的不同模块放在一起用时,肯定就会打架啦:)

使用特权

评论回复
16
程序匠人| | 2010-4-5 00:38 | 只看该作者
俺喜欢把所有变量放在一个“common.h”里面,便于管理。如下:

//--------------------------------------------------------
//避免重复定义
//--------------------------------------------------------
#ifdef  root
  #define EXT_  
#else
  #define EXT_  extern
#endif

//--------------------------------------------------------
//定义变量
//--------------------------------------------------------
EXT_ u16 X_AVR;                                                            //X轴加速度采样滤波值
EXT_ u16 Y_AVR;                                                            //Y轴加速度采样滤波值
……
……

使用特权

评论回复
17
xwj| | 2010-4-5 00:55 | 只看该作者
老x的做法有点在于换个硬件、模块时,只要把.c文件加如工程并包含对应的.h文件即可,其他的不用任何变动即可调用模块的函数、存储器并且编译无误,既不会重复也不会浪费;

而你的做法,一旦工程变动则每次都要去“common.h”里面整理,时间稍微久一点根本不可能记住哪些用了,哪些不要用。

使用特权

评论回复
18
程序匠人| | 2010-4-5 14:21 | 只看该作者
老x的做法有点在于换个硬件、模块时,只要把.c文件加如工程并包含对应的.h文件即可,其他的不用任何变动即可调用模块的函数、存储器并且编译无误,既不会重复也不会浪费;

而你的做法,一旦工程变动则每次都要去“c ...
xwj 发表于 2010-4-5 00:55


如果是底层模块,具有通用性的,也是该这么做。呵呵。

使用特权

评论回复
19
hljzhiji| | 2010-4-7 14:27 | 只看该作者
:lol

使用特权

评论回复
20
hljzhiji| | 2010-4-7 14:28 | 只看该作者
:lol

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

65

帖子

1

粉丝