Подтвердить что ты не робот

Внутренние статические переменные в C, вы бы использовали их?

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

Например:

#include <stdio.h>

void foo_bar( void )
{
        static counter = 0;
        printf("counter is %d\n", counter);
        counter++;
}
int main( void )
{
        foo_bar();
        foo_bar();
        foo_bar();
 return 0;
}

вывод будет

counter is 0
counter is 1
counter is 2

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

4b9b3361

Ответ 1

Эта путаница обычно возникает из-за того, что ключевое слово static выполняет две цели.

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

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

При использовании внутри функций он контролирует длительность, а не видимость. Видимость уже решена, поскольку она находится внутри функции - ее нельзя увидеть вне функции. Ключевое слово static в этом случае заставляет объект создаваться одновременно с объектами уровня файла.

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

Кроме того, пока я использую слово "объект", я не имею в виду это в смысле объектов С++ (так как это вопрос C). Это просто потому, что static может применяться к переменным или функциям на уровне файла, и мне нужно всеохватывающее слово, чтобы описать это.

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

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

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

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

Ответ 2

Они используются для реализации таких инструментов, как strtok, и они вызывают проблемы с повторной установкой...

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

Ответ 3

Например, в С++ он используется как один из способов получения одноэлементных значений

SingletonObject& getInstance()
{
  static SingletonObject o;
  return o;
}

который используется для решения проблемы порядка инициализации (хотя он не является потокобезопасным).

Объявление "не должно быть функцией в своем собственном файле"

Конечно, нет, это вздор. Значительная часть языков программирования - это облегчение изоляции и, следовательно, повторное использование кода (локальные переменные, процедуры, структуры и т.д.), И это просто еще один способ сделать это.

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

Ответ 4

Мне это удобно для одноразовой, отложенной, инициализации:

int GetMagic()
{
   static int magicV= -1;

   if(-1 == magicV)
   {
      //do expensive, one-time initialization
      magicV = {something here}
   }
   return magicV;
}

Как говорили другие, во время самого первого вызова это не является потокобезопасным, но иногда вы можете с ним справиться:)

Ответ 5

Я думаю, что люди вообще избегают внутренних статических переменных. Я знаю, что strtok() использует один или что-то вроде этого, и из-за этого, вероятно, самая ненавистная функция в библиотеке C.

Другие языки, такие как С#, даже не поддерживают его. Я думаю, что идея заключалась в том, что она должна была обеспечить некоторое подобие инкапсуляции (если можно так выразиться) до времени языков OO.

Ответ 6

Вероятно, не очень полезно в C, но они используются на С++, чтобы гарантировать инициализацию статической статистики пространства имен. В C и С++ существуют проблемы с их использованием в многопоточных приложениях.

Ответ 7

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

Ответ 8

Некоторые примеры использования статических переменных:

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

Ответ 9

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

Как и любая конструкция, он должен использоваться хорошо и ответственно. Вы должны знать последствия использования конструкции.

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

Например -

char *GetTempFileName()
{
  static int i;
  char *fileName = new char[1024];
  memset(fileName, 0x00, sizeof(char) * 1024);
  sprintf(fileName, "Temp%.05d.tmp\n", ++i);
  return fileName;
}

VB.NET поддерживает ту же конструкцию.

Public Function GetTempFileName() As String
  Static i As Integer = 0
  i += 1
  Return String.Format("Temp{0}", i.ToString("00000"))
End Function

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

Ответ 10

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

Ответ 11

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

Ответ 12

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

Ответ 13

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

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

void errorLog(...)
{
    static int reentrant = 0;
    if(reentrant)
    {
        // We somehow caused an error while logging a previous error.
        // Bail out immediately!
        hardwareReset();
    }

    // Leave ourselves a breadcrumb so we know we're already logging.
    reentrant = 1;

    // Format the error and put it in the log.
    ....

    // Error successfully logged, time to reset.
    hardwareReset();
}

Этот подход проверяет очень маловероятное событие, и он безопасен только потому, что прерывания отключены. Однако во встроенной цели правило "никогда не зависает". Этот подход гарантирует (в пределах разумного), что аппаратное обеспечение в конечном итоге получает reset, так или иначе.