Рефакторинг от глобального к локальному. Должны ли они быть статичными или нет? - PullRequest
2 голосов
/ 25 сентября 2008

Я выполняю рефакторинг модуля C "спагетти-кода" для работы в многозадачной среде (RTOS).

Теперь есть очень длинные функции и много ненужных глобальных переменных.

Когда я пытаюсь заменить глобальные переменные, которые существуют только в одной функции, локальными, я попадаю в дилемму. Каждая глобальная переменная ведет себя как локальная «статическая» - например, сохраняйте его значение даже после выхода и повторного входа в функцию.

Для многозадачности «статические» локальные переменные хуже всего из глобальных. Они делают функции невосстановленными.

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

Ответы [ 6 ]

8 голосов
/ 25 сентября 2008

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

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

При условии, что глобальное значение действительно использовалось только в той области (которую компилятор / компоновщик должен подтвердить при удалении глобального), поведение должно быть примерно таким же. Могут возникнуть или не возникнуть проблемы при инициализации, я не могу вспомнить, что говорит стандарт: если статическая инициализация происходит в C в то же время, что и в C ++, когда выполнение впервые достигает объявления, тогда вы могли изменить безопасная для параллелизма функция в небезопасную.

Чтобы определить, является ли функция безопасной для повторного входа, также необходимо рассмотреть логику. Если в стандарте не указано иное (я не проверял), функция автоматически не возвращается повторно только потому, что объявляет статическую переменную. Но если он использует глобальный или статический каким-либо существенным образом, вы можете предположить, что он не реентерабельный. Если синхронизация отсутствует, предположим, что она также небезопасна.

Наконец, удачи. Похоже, этот код далеко от того места, где вы хотите его видеть ...

1 голос
/ 26 сентября 2008

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

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

Правило таково: если переменная используется в функции перед ее установкой, оставьте ее статической.

Пример переменной, которая может быть автоматически сделана локальной (вы должны поместить "int nplus4;" внутри функции (вам не нужно устанавливать ее на ноль, так как она установлена ​​перед использованием, и это должно выдать предупреждение, если вы на самом деле используйте его перед установкой, полезная проверка):

int nplus4 = 0;         // used only in add5
int add5 (int n) {
    nplus4 = n + 4;     // set
    return nplus4 + 1;  // use
}

nplus4 var устанавливается перед использованием. Ниже приведен пример, который следует оставить статическим, поместив «static int nextn = 0;» внутри функции:

int nextn = 0;             // used only in getn
int getn (void) {
    int n = nextn++;       // use, then use, then set
    return n;
}

Обратите внимание, что это может быть сложно, "nextn++" не устанавливается, он использует и настраивает, поскольку он эквивалентен "nextn = nextn + 1".

Еще одна вещь, на которую следует обратить внимание: в среде RTOS пространство стека может быть более ограниченным, чем глобальная память, поэтому будьте осторожны, перемещая большие глобальные переменные, такие как char buffer[10000], в функции.

1 голос
/ 25 сентября 2008

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

Но то, что вы хотите сделать, это поместить определение переменной в максимальную область, в которой она используется как локальная, а затем передать ее в качестве аргумента всему, что в ней нуждается. Это, очевидно, требует много работы потенциально (так как он имеет каскадный эффект). Вы можете сгруппировать аналогично необходимые переменные в «контекстные» объекты, а затем передать их.

См. Шаблон проектирования Контекст инкапсуляции

1 голос
/ 25 сентября 2008

Если ваш компилятор предупредит вас, если перед инициализацией используется переменная, сделайте подозреваемую переменную локальной, не назначая ей значение в объявлении.

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

0 голосов
/ 25 сентября 2008

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

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

0 голосов
/ 25 сентября 2008

Пожалуйста, приведите примеры того, что вы называете «глобальными» и «локальными» переменными

int global_c; // can be used by any other file with 'extern int global_c;'

static int static_c; // cannot be seen or used outside of this file.

int foo(...)
{
   int local_c; // cannot be seen or used outside of this function.
}

Если вы предоставите несколько примеров кода того, что у вас есть и что вы изменили, мы могли бы лучше ответить на вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...