打印

实践中出现的一些烦人问题

[复制链接]
15114|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
XIANSir|  楼主 | 2011-3-14 17:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 XIANSir 于 2011-3-14 17:51 编辑

最近调试STM32的SD卡、FAT16程序遇到一些比较别扭的事情,虽然不是不知道怎么解决,但就是觉得感觉怪怪的,总感觉这样解决不舒服。

1、第一个问题是关于枚举类型定义的
程序编译时报错: error:  #101: "TRUE" has already been declared in the current scope
经过查找,发现问题产生的原因是:
在fatfs模块的integer.h中有如下定义:
typedef enum {FALSE = 0,TRUE}BOOL;
同时在STM32官方固件库的头文件stm32f10x.h中有如下定义:
typedef enum {FALSE = 0, TRUE = !FALSE} bool;
于是问题就来了:在同一个工程下,有两个地方定义了FALSE和TRUE,于是就发生了重复定义。

问题倒也不是太难解决,只要将两个地方的定义其中一个做一些稍微的改变,以区分开来就可以了,比如,将fatfs模块的integer.h中的定义改为:typedef enum {F_FALSE = 0,F_TRUE}BOOL;
然后再把所有用到这个枚举变量的地方都把FALSE和TRUE分别改为F_FALSE和F_TRUE就可以了。

可是总感觉这样不太好,首先,这两个模块都不是我自己写的,而我现在却要去修改别人模块化的代码,而且修改仅仅是因为重复定义的语法问题,而不是为了对代码进行针对性的优化

另外,这里仅仅涉及到两个模块,而且fatfs模块中使用TRUE和FALSE的地方并不多,假如我的代码中使用了七八个来自不同途径的模块,并且都为了方便进行了上面类似的定义,而且每个模块中又大量使用TRUE和FALSE的话,那就要进行大量的纯体力的替换工作,这实在不是一种好的编程习惯。

与之相似的还有typedef问题,为了代码更加的易读,一般比较规范的代码模块都会进行如下的定义:
typedef unsigned int  ui32;
如果程序中使用了来自不同途径的功能模块的话,很难保证不会产生类型重复定义而撞车,到时候大量的体力劳动恐怕再次不可避免。

2、第二个问题关于STM32官方片级支持库结构体变量定义的问题
GPIO_InitTypeDef GPIO_InitStructure;

比如上面的结构体变量定义,如果把这个定义放在函数中定义成局部变量,就会造成频繁分配大量临时变量并导致程序堆栈变大,另外如果多个函数中使用了此结构体变量,这个问题就会更加的突出,而且没办法对此结构体变量的部分取值进行保存和传递,导致频繁赋值和出栈入栈操作,降低系统性能。

如果将此结构体变量的定义放在函数外部,定义成全局变量,又有可能导致变量重复定义问题——因为不同的文件可能都要使用此结构体变量。

倒是可以通过加static关键字解决这个问题,比如:
static GPIO_InitTypeDef GPIO_InitStructure;

或者,可以把所有用到的结构体变量定义都统一放在一个.c文件中进行定义,然后在相应的.h文件中进行外部变量声明:
extern static GPIO_InitTypeDef GPIO_InitStructure;
然后通过包含此.h文件来使用此结构体变量。

这种方法好是好,但是,在多任务环境下(使用了RTOS)或者在中断和程序中同时使用此结构体变量又要特别小心。在这种情况下,看来使用static GPIO_InitTypeDef GPIO_InitStructure;定义更加方便了。

尽管这个问题显得很不值一提,但是这却是实实在在的问题,因为我发现,对于ARM CM3的芯片,厂家越来越倾向于提供片级支持库进行编程了,这好像是一种趋势。

3、第三个问题,我很想问一下,前辈以及大侠们,你们会在.h文件中使用#inclued来包含其他的.h文件吗这是一种好的编程习惯吗??

这个问题在有些人看来更加不是一个问题了——想怎么放怎么放,但是我却觉得这实在是一个有关最佳编程实践的问题,良好的习惯必将在未来的程序维护中显现它的益处。

因为.h文件其实是功能模块的接口,而#include实际上决定了模块的依赖关系,这肯定是一个关键的问题——只是一种感觉,因为我编写的代码毕竟还很少,没有更深的体会。

嗯,看来只好慢慢实践,从实践中进行比较,对这些烦人的问题找到最合自己习惯的编码方式。

相关帖子

沙发
airwill| | 2011-3-14 20:42 | 只看该作者
楼主 "编写的代码毕竟还很少", 却一上来, 就玩这么复杂的代码.
真是难为你了, 精神可嘉, 鼓励鼓励.
不过通常学习, 是需要循序渐进的. 否则也许在**上会有困难的.

使用特权

评论回复
板凳
XIANSir|  楼主 | 2011-3-14 21:26 | 只看该作者
2# airwill
只是告诉自己注意一下这些问题,真正编程的时候自己也不可能真的追求面面俱到,要是真那样的话,恐怕自己永远都没法开始编程了。
只能是平时多注意一下,希望以后程序看得多了、写得多了,自然而然能够越来越应用自如,左右逢源。

而且,这些东西,即使大师教给了,也不一定能够明白其中的苦心,只有自己经历的多了,摔的跟头多了,才能真正懂得其中道理。所以,这些东西,恐怕没人能教,只能自己慢慢领会。

使用特权

评论回复
地板
ejack| | 2011-3-14 22:46 | 只看该作者
第1个问题是否考虑其它包含较多文件的项目的通用方法,即用#ifndef条件来互斥重复定义。
第2个问题,
如果把这个定义放在函数中定义成局部变量,就会造成频繁分配大量临时变量并导致程序堆栈变大,另外如果多个函数中使用了此结构体变量,这个问题就会更加的突出

对应于同一对象的变量应当只需定义一次,各个函数以指针形式传递对象的使用权不行吗?

使用特权

评论回复
5
ayb_ice| | 2011-3-15 08:32 | 只看该作者
这个时候预处理的作用就大了

使用特权

评论回复
6
XIANSir|  楼主 | 2011-3-15 10:09 | 只看该作者
4# ejack
第1个问题是否考虑其它包含较多文件的项目的通用方法,即用#ifndef条件来互斥重复定义。”
我猜前辈说的是:
#ifndef
#define
....
#endif


可惜这个方法对于第一个问题,基本无效。
因为这个方法对于避免多次包含同一个.h文件有效,但是第1个问题出现的原因是“不同的.h文件中包含相同的定义”。简单演示一下:
integer.h:
#ifndef  __INTEGER_H__
#define __INTEGER_H__
......
typedef enum {F_FALSE = 0,F_TRUE}BOOL;

......
#endif
-------------------------------------------------------------------------------
stm32f10x.h:
#ifndef  __STM32F10X_H__
#define __STM32F10X_H__
......
typedef enum {FALSE = 0, TRUE = !FALSE} bool;

......
#endif
--------------------------------------------------------------------------------------
diskio.c:
#include "integer.h"
#include "sdcard.h"


-----------------------------------------------------------------------------------------
sdcard.h:
#include "stm32f10x.h"


上面的代码并不是我虚构的,而是切切实实的实际代码的结构,而且,这种结构实在是无可非议,确实是非常的合理而且必要。


通过分析就会发现:在diskio.c中同时包含了integer.h和stm32f10x.h这两个头文件,所以枚举的重复定义就发生了——两个头文件中都定义了FALSE和TRUE这两个常量。
而且你会发现,这两个地方的定义并非完全相同:一个是bool,另一个是BOOL,所以你也无法通过简单的将其中一个定义注视掉来解决这个问题——这不是我在苦心为难大家,而是实际的代码就是这样子的,而且这两个功能模块(代码库)都是非常好并且被大量使用的模块,


看来,要解决这个办法,还是只能使用我前面提到的办法:修改定义,避免冲突。总之,要避免这种问题,没有一劳永逸的办法,只能是:遇到问题,解决问题,并且大多数是体力劳动。

使用特权

评论回复
7
XIANSir|  楼主 | 2011-3-15 10:30 | 只看该作者
4# ejack "对应于同一对象的变量应当只需定义一次,各个函数以指针形式传递对象的使用权不行吗?“


我觉得大侠这个方法既不简洁明了,也不易于使用。


既然只定义一次,那干脆定义成全局变量,整个程序中都使用这个这一个变量岂不简单易懂,岂不比又要定义指针,又要传递参数来的简洁方便。


另外,我的想法现在又有了一些改变:
既然用函数库进行STM32程序的编写,那么对于性能应该不是特别计较,那干脆就把那些结构体变量定义成函数内的局部变量。如果感觉需要优化,那么这个函数干脆直接操作寄存器,而不使用官方提供的函数库——即局部优化。


如果想在性能损失和编程方便性之间进行一个折中,那么还是定义文件域局部变量:
static GPIO_InitTypeDef GPIO_InitStructure;


当然,相对于整个程序只定义一个结构体变量的做法,这种做法显然会造成静态存储区的更多消耗。但是,这样做却也可使得程序更易维护——更好的模块化和自包含。


决定采用上面的两种方法相结合进行编程实践,在实践中找到更合适的操作方法。

使用特权

评论回复
8
XIANSir|  楼主 | 2011-3-15 10:56 | 只看该作者
唉,发现自己对第二个问题的担心和思考实属多余。因为
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
这样的结构体变量实际上只在对外设初始化的时候使用一次,之后就很少甚至基本上不再使用了,所以,它们的定义完全可以放在函数内,因为这个函数在整个程序运行过程中基本上只被调用一次,所以对程序性能的影响基本上可以忽略不计。

看来,第二个问题是真的解决掉了,结论就是:放在函数里定义

使用特权

评论回复
9
ejack| | 2011-3-15 22:47 | 只看该作者
既然只定义一次,那干脆定义成全局变量,整个程序中都使用这个这一个变量岂不简单易懂,岂不比又要定义指针,又要传递参数来的简洁方便。

这可就偏得厉害了……

使用特权

评论回复
10
XIANSir|  楼主 | 2011-3-16 12:03 | 只看该作者
做一下结贴前的总结:
1、第一个问题是C语言中很难避免的问题,只能是“遇山开路,逢水搭桥”,体力劳动不可避免
2、第二个问题彻底解决了,那些结构体变量只会使用1次或很少的几次,所以直接函数内定义成局部变量就可以了。
3、第三个问题,关于.h文件包含.h文件的问题,这个应该是尽量不使用.h文件保护.h文件,但是不可避免时也可以灵活处理。并且对于C语言的标准头文件,比如stdio.h,则不必有上面的顾虑。

立此贴为记,在以后编的程序多了,再来重新审视这些原则是否可行。

使用特权

评论回复
11
monsterhoho| | 2013-6-10 12:03 | 只看该作者
我曾经遇到过那个两个h文件中重复定义的问题,我当时用的是keil在我设置完 C/C++那个标签里面的路径之后问题就自己消失了,虽然不明白什么原理,希望对楼主有帮助吧。

使用特权

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

本版积分规则

个人签名:冷暖自知,泰然处之;持之以恒,必有所成!

0

主题

609

帖子

2

粉丝