Вопрос

Я работаю над проектом на 8051, где важен каждый байт.Поэтому я использую некоторые глобальные переменные там, где обычно не стал бы.Обычный метод передачи указателей в функцию добавляет здесь слишком много накладных расходов.

У меня есть ряд функций, которые используют однобитовые переменные (специфическое расширение компилятора для С), чтобы сигнализировать о результате функции в дополнение к обычному возвращаемому значению.

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() );

Есть еще идеи?

Это было полезно?

Решение

Ваши глобальные значения помечены для ясности, это хорошее начало.

В идеале вам нужно что-то, что не скомпилируется, если вы ошибетесь.Это означает, что макросы и комментарии не будут работать.

Я бы придерживался соглашения об именах функций — не обязательно венгерского, но что-то вроде A_Function_Returns_Flags, или менее многословно, если вы можете подумать об этом.

Другие советы

Использование условного обозначения, независимо от того, хотите ли вы назвать его «венгерским» или нет, — это лучший способ, на мой взгляд, отметить это навскидку.Стилистически какой-то префикс имени был бы предпочтительнее пустого #define, по крайней мере, для меня.

На самом деле, я думаю, это довольно распространенное явление.Я знаю, что среда программирования S60 использует множество обычных тегов для функций, например, для обозначения того, что они вызывают исключения.

Я защитил докторскую диссертацию.по аналогичной проблеме в Java.Я могу сказать вам одну вещь, которую вам не следует делать:не полагайтесь на документацию, потому что тогда вы будете зависеть от того, что кто-то действительно ее прочитает.Вам нужно добавить подсказку в имя метода, чтобы указать, что пользователю следует прочитать документацию, чтобы узнать о побочных эффектах.Если вы выберете что-то и будете с этим согласны, у вас, вероятно, будет больше шансов.

Если вы просто хотите упомянуть, что функция влияет на глобальные переменные, то может помочь простой (венгерский) префикс.

Но если вы хотите упомянуть каждый отдельный флаг(и), на которые он влияет, то, вероятно, лучше всего использовать заголовок функции.Как, например,

  /*************************************************************************
     * 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> 
  *************************************************************************/

На самом деле это вам не поможет, но у GCC есть способ сделать это. противоположный того, что вы хотите:для обозначения функций, которые имеют нет побочные эффекты.См. const и pure атрибуты.Это больше для оптимизации, чем для документации, подумал:если компилятор знает, что данная функция не проверяет никаких данных, кроме своих аргументов, он может выполнить более разумную оптимизацию, например движение кода, инвариантное к циклу.

Вы можете использовать макрос для имитации функции, чтобы иметь больше параметров:


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++;
}

Я не пробовал его скомпилировать, поэтому не могу сказать, что это сработает, но думаю, что так и должно быть.

Сначала я бы попытался закодировать его таким образом, чтобы для каждого из этих флагов существовал только один производитель и только один потребитель.Тогда я бы очищал/устанавливал флаг только при необходимости.Что касается указания побочного эффекта, то стандартного заголовка поверх функции в стиле 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 не работает с отдельными битами, поэтому использование целого байта для (различных) флагов не должно привести к значительному снижению производительности.

Если вы еще этого не сделали, возможно, вам также захочется ознакомиться с проект sdcc на sourceforge, это компилятор C, специально предназначенный для использования для разработки встроенных систем, который также нацелен на 8051, кроме того, компилятор поддерживает ряд пользовательских, специфичных для конкретных целей и нестандартных встроенных функций компилятора для различных вариантов использования, также я лично нашел разработку команде быть очень открытыми и оперативно реагировать на идеи по поводу новых улучшений и других запросов на соответствующие функции.

Если вам действительно нужно придерживаться этих глобальных переменных, вы можете сделать очевидным, что функция может изменять их, ожидая ссылок на них в качестве аргументов функции:

unsigned char A_Function (bit *p_error_flag, bit *p_data_ready_flag)
{
  ...
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top