Краткий ответ: инкапсуляция.
static
описывает время жизни и видимость переменной, и ее значение изменяется в зависимости от контекста.Я считаю, что это одна из наиболее полезных и важных функций языка для инкапсуляции в c.Игнорируя сложное отношение к extern
, вот упрощенное описание:
static
переменные, определенные на уровне файлов, имеют время жизни программы и единица компиляции видимость.Это означает, что все функции в файле .c могут обращаться к переменной или изменять ее, но другие файлы .c не будут знать о переменной.Это super полезно для обеспечения того, чтобы переменные, используемые в функциях с модулем компиляции, случайно не связывались с переменными в других модулях компиляции.Лично я настоятельно рекомендую, чтобы все файловые переменные были статическими по умолчанию.Удаляйте спецификатор static
только в том случае, если вы действительно хотите, чтобы другой модуль компиляции имел к нему доступ (хотя функция получения может быть более безопасной)
Переменные, объявленные static
в пределах области видимости блока (наиболее важная область действия): время жизни программы и видимость области действия.Это означает, что он функционирует так, как если бы вы объявили переменную глобально в файле, но только код внутри этой области блока может ее увидеть.Это также означает, что от одного вызова к другому, переменная не уничтожается, и состояние может быть передано от вызова к вызову.
Одно действительно важное отличие со статическими переменными состоит в том, что они являются значениями по умолчаниюинициализируется в ноль.Это отличается от всех других переменных в c и является причиной того, что ваша программа печатает значение 0. Часто в тривиальных программах мы не замечаем разницы, потому что стек еще не был загрязнен переменными,но это становится критичным для любой программы размером.
Наиболее распространенное использование для этого, которое я видел, состоит в том, чтобы включить однократную инициализацию в пределах области.Они также чрезвычайно полезны для примитивов синхронизации, таких как pthread_mutex_t
.Однажды я даже реализовал конечный автомат со статической переменной области действия.
пример:
int started;//oops, anybody in the entire program can change this value, especially with such a common name!
static int lastCall;
int callCount(void)
{
// This is default-initialized to 0
static int functionStaticVariable;
//Increment each time I'm called
++functionStaticVariable;
//tell the outside world that I'm the one who was called last
lastCall = 1;
//return (a copy of) my internal state.
return functionStaticVariable;
}
char *getSharedMemory(unsigned int bytes)
{
// Here I cannot see functionStaticVariable, but I can see globalVariable
//functionStaticVariable++; // this would cause a compilation failure
// static pointer is default-initialized to zero (i.e. NULL)
static char *sharedMemory;
if(sharedMemory == 0)
{
// This block only executes once, the first time the function is called.
// Actually this is a nice side-effect because it means if the function is never called we don't clutter the stack with unused memory
// Although we will probably never free this memory
sharedMemory = (char *)malloc(bytes);
}
// tell the outside world that this function has been called
lastCall = 2;//valid
//Woah, this is such a bad idea, but actually does _not_ return memory that gets invalidated
return sharedMemory;
}
Надеюсь, вы можете увидеть с помощью этого шаблона вы можете защитить переменную, поместив ее внутрьфункция и выполнение дополнительных действий, таких как получение блокировки мьютекса для выделения памяти.Вы можете даже реализовать шаблон двойной блокировки таким образом.
Я тайно желаю, чтобы все программисты на C ++ узнали хорошую инкапсуляцию c, потому что на самом деле язык действительно поощряет это.Вы можете сделать невероятное количество, поместив только функции, которые должны общаться друг с другом, в модуле компиляции.На языке, отличном от ООП, это может быть очень мощным.
Полная информация о static и extern описана в https://en.cppreference.com/w/c/language/storage_duration.