4

我正在开发一个 8051 上的项目,其中每个字节都很重要。因此,我使用了一些我通常不会使用的全局变量。将指针传递给函数的常规方法在这里增加了太多开销。

除了正常的返回值之外,我有许多函数使用单比特变量(编译器特定的C扩展)来表示函数的结果。

bit global_error_flag = 0;
bit global_data_ready_flag = 0;

unsigned char A_Function (void) {
    // Do some stuff

    if ( badness ) {
        global_error_flag = 0;
        global_data_ready_flag = 1;

        return 0;
    }

    if ( data_is_ready_use ) {
        global_data_ready_flag = 1;
    }

    return a_value;    
}

void Other_Function (void) {
    unsigned char c;

    c = A_Function();

    if( global_error_flag) {
        // Do error stuff here.
    }
    else
    if( global_data_ready_flag ) {
        // Do data processing here.
    }
    global_error_flag = 0;
    global_data_ready_flag = 0;

}

鉴于该技术是邪恶的,有什么方法可以使代码更清晰吗?

我应该如何最好地指示哪些函数调用会产生设置这些标志的副作用?评论够吗?我应该命名函数以指示它们的 API(准匈牙利风格)吗?我是否应该使用宏来标记此类调用:

#define FUNCTION_SETS_FLAGS(code) (code)

FUNCTION_SETS_FLAGS( c = A_Function() );

还有其他想法吗?

4

9 回答 9

6

使用约定,无论您是否想称其为“匈牙利语”,都是我能想到的最好的临时标记方式。从风格上讲,某种命名前缀比空的#define 更可取,至少对我而言。

我认为这实际上很常见。例如,我知道 S60 编程环境在函数上使用了很多常规标记来指示它们抛出异常。

于 2009-04-13T22:13:30.977 回答
3

为了清楚起见,您的全局变量被标记,这是一个好的开始。

理想情况下,如果你弄错了,你想要一些不会编译的东西。这意味着宏和注释将不起作用。

我会坚持函数的命名约定 - 不一定是匈牙利语,而是类似的东西A_Function_Returns_Flags,或者如果你能想到的话,那么冗长。

于 2009-04-13T22:13:57.513 回答
3

我读了博士学位。在Java中的类似问题上。我可以告诉你一件你不应该做的事情:不要依赖文档,因为那样你就依赖于实际阅读它的人。您需要在方法名称中添加一些提示,以指示用户应该阅读文档以了解副作用。如果您选择某件事并与之保持一致,那么您可能最有机会。

于 2009-04-13T23:11:22.663 回答
3

如果您只想提及一个函数会影响全局变量,那么一个简单的(匈牙利语)前缀可能会有所帮助。

但是,如果您想提及它影响的每个标志,那么使用函数头可能是要走的路。例如,

  /*************************************************************************
     * FUNCTION    : <function_name>
     * DESCRIPTION : <function description> 
     * PARAMETERS  : 
     *  Param1  - <Parameter-1 explanation>
     *  Param2  - <Parameter-2 explanation>
     *  Param3  - <Parameter-3 explanation>
     * RETURN      : <Return value and type>
     * GLOBAL VARIABLES USED: 
     *  Global1 - <Global-1 explanation>
     *  Global2 - <Global-2 explanation>
     *  Global3 - <Global-3 explanation> 
  *************************************************************************/
于 2009-04-14T01:22:10.493 回答
2

这并不能真正帮助您,但是 GCC 有一种与您想要的相反的方法:标记没有副作用的函数。见constpure属性。这是为了优化而不是文档,认为:如果编译器知道给定函数不检查除参数以外的任何数据,它可以执行更智能的优化,例如循环不变的代码运动

于 2009-04-13T22:22:58.803 回答
2

您可以使用宏来模拟具有更多参数的函数:


unsigned char _a_function(void);

#define A_Function(ret_val) (*(ret_val) = _a_function(), !global_error_flag)

...
unsigned char var;
/* call the function */
if (!A_Function(&var))
{
    /* error! */
}
else
{
    /* use var */
    var++;
}

我没有尝试编译它,所以不能说这会起作用,但我认为它应该。

于 2009-04-14T08:50:10.790 回答
0

首先,我会尝试以一种方式对其进行编码,即每个标志只有一个生产者和一个消费者。然后我只会在需要时清除/设置一个标志。至于表明副作用,函数顶部的标准标题,doxygen 风格,应该足够了:

    // Function func
    // Does something
    // Consumes ready_flag and  sets error_flag on error.

    int func()
    {
        if (ready_flag)
        {
            //do something then clear the flag
            if (some_error)
                error_flag = x;
            ready_flag = 0;
        }
        //don't mess with the flags outside of their 'scope'
        return 0;
    }

另一方面,如果错误和就绪标志是互斥的,您可以使用一个字节(或字节/寄存器中的位)来指示就绪或错误状态。

0 表示错误,1 表示未准备好/没有错误,2 表示准备好/没有错误(或 -1、0、1 等)

IIRC,标准的 8051 指令集不能对单个位进行操作,因此将整个字节用于(各种)标志不应给您带来巨大的性能损失。

于 2009-04-13T23:04:01.573 回答
0

如果您还没有这样做,您可能还想查看sourceforge 上的 sdcc 项目,它是一个专门用于嵌入式开发的 C 编译器,它也针对 8051,此外编译器还支持许多自定义,针对各种用例的特定目标和非标准编译器内在函数,我个人也发现开发团队对新增强和其他相关功能请求的想法非常开放和响应。

于 2009-05-22T18:27:39.953 回答
-2

如果你真的必须坚持使用这些全局变量,你可以通过期望将它们作为函数参数的引用来明确一个函数可以修改它们:

unsigned char A_Function (bit *p_error_flag, bit *p_data_ready_flag)
{
  ...
}
于 2009-04-16T06:58:07.127 回答