Причины использования статических функций и переменных в C

Меня интересует использование ключевого слова static в качестве ограничения области действия для переменных в файле в C.

Стандартный способ создания программы на C, как я ее вижу, заключается в следующем:

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

Я вижу две причины для объявления гобала как static, если переменная все равно не опубликована в файле h:

  • один для удобства чтения. Сообщите будущим читателям, включая меня, что к переменной нет доступа ни в одном другом файле.
  • во-вторых, предотвратить повторное объявление переменной как extern другим файлом c. Я предполагаю, что компоновщику не понравится, если переменная будет одновременно extern и static. (Мне не нравится идея файла, переопределяющего переменную, принадлежащую кому-то другому, как extern, это нормальная практика?)

Любая другая причина?

То же самое касается static функций. Если прототип не опубликован в файле h, другие файлы все равно могут не использовать эту функцию, так зачем вообще определять ее static? Я вижу те же две причины, но не более.


person Gauthier    schedule 04.06.2010    source источник
comment
Шесть лет спустя я теперь знаю, что static не ограничивает область действия, а дает внутреннюю связь. Аналогично, если рассматривать единицу перевода как область действия, а не как правильный термин.   -  person Gauthier    schedule 03.03.2016


Ответы (6)


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

Переопределить переменную static как extern невозможно, но компилятор (как обычно) даст вам достаточно веревки, чтобы повеситься.

Если я напишу static int foo; в одном файле и int foo; в другом, они будут считаться разными переменными, несмотря на то, что имеют одинаковое имя и тип - компилятор не будет жаловаться, но вы, вероятно, сильно запутаетесь позже, пытаясь прочитать и /или отладить код. (Если я напишу extern int foo; во втором случае, это не свяжется, если я не объявлю нестатический int foo; где-то еще.)

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

person crazyscot    schedule 04.06.2010
comment
Интересно. Я понимаю, что этот внешний глобальный файл в заголовочном файле все еще нуждается в определении в файле c, поскольку extern в файле h делает его простым объявлением. - person Gauthier; 04.06.2010

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

person qrdl    schedule 04.06.2010
comment
Это как раз одна из двух причин, о которых я упоминал: вторая — предотвратить использование другим c-файлом, казалось бы, ограниченного по области видимости глобального файла, переобъявив его как extern. И я задавался вопросом о качестве такой практики? - person Gauthier; 04.06.2010
comment
@Gaunthier Значит, в вашем посте есть внутренний конфликт. - person qrdl; 04.06.2010
comment
ИМО, static имеет одинаковое значение во всех трех известных случаях использования (файловая переменная, функциональная переменная, функция), а именно: это имеет ограниченную область действия и имеет постоянный адрес на протяжении всего выполнения программы. - person Gauthier; 04.06.2010
comment
Что ж, в этом смысле значение то же самое, но цель другая - static на уровне файла управляет видимостью, static на уровне функции контролирует постоянство. - person qrdl; 04.06.2010
comment
Если вы объявите что-то статическое в одной единице компиляции, вы не сможете повторно объявить это как внешнее в другой единице компиляции. Они будут восприниматься как разные вещи. static также служит для предотвращения загрязнения пространства имен - например. у вас может быть 2 единицы компиляции, содержащие функцию с именем sort , и они будут рассматриваться как разные вещи. - person nos; 04.06.2010
comment
@nos Точно, поэтому компоновщик будет жаловаться на отсутствующий символ - person qrdl; 04.06.2010
comment
@qrdl: надеюсь, мой пост теперь понятнее. @nos: о загрязнении пространства имен, если два файла содержат функции с одинаковыми именами, но они не опубликованы ни в одном файле h, будет работать отдельная компиляция файла c. Единственная проблема заключается в том, что третий файл не будет знать, какую из этих двух функций вызывать. Но это не проблема, поскольку ни один из них не публикуется в h-файле. Однако, если один из файлов, содержащих sort, опубликовал заголовок, а другой файл - нет, я понимаю вашу точку зрения. - person Gauthier; 04.06.2010
comment
@Gauthier Итак, я изменил свой пост соответственно. - person qrdl; 04.06.2010
comment
@qrdl: но ваш пост все тот же, что и вторая причина, о которой я упоминаю: предотвратить повторное объявление переменной в другом файле c как extern .. Я ищу другие причины (извините, если это было неясно). - person Gauthier; 04.06.2010
comment
@Gauthier Готье Я имел в виду статические функции - вам не нужно заботиться / беспокоиться о ком-то еще, назвав свою функцию sort, а также о той, которую вы хотите в своем блоке компиляции, является статической. Однако, если у вас есть 2 нестатические функции с одинаковыми имя, оно обычно не будет ссылаться, независимо от того, объявите ли вы его в файле .h или нет (и если ваш компоновщик все еще принимает его, вы получите неопределенное поведение) - person nos; 04.06.2010

Когда вы объявляете статическую функцию, вызов функции является "ближним вызовом" и теоретически он работает лучше, чем "дальний вызов". Вы можете Google для получения дополнительной информации. Это я нашел с помощью простого поиска в Google.

person INS    schedule 04.06.2010
comment
Также интересно, хотя и зависит от платформы (у моей цели есть только одна инструкция CALL для абсолютного и нет дальнего/ближнего). - person Gauthier; 04.06.2010
comment
например gcc может переключать соглашение о вызовах (на что-то более быстрое) для статических функций на многих платформах. - person nos; 04.06.2010

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

person Adam Rosenfield    schedule 04.06.2010

Если вы объявляете переменную foo в файле a.c, не делая ее статической, и переменную foo в файле b.c, не делая ее статической, обе они автоматически являются внешними, что означает, что компоновщик может пожаловаться, если вы инициализируете обе, и назначить одно и то же место в памяти, если это не так. Не жалуйся. Ожидайте удовольствия от отладки вашего кода.

Если вы напишите функцию foo() в файле a.c, не сделав ее статической, и функцию foo() в файле b.c, не сделав ее статической, компоновщик может пожаловаться, но если это не так, то все вызовы foo() будут вызывать та же самая функция. Ожидайте удовольствия от отладки вашего кода.

person gnasher729    schedule 01.04.2014

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

Мне нравится хранить папку для вспомогательных классов для большинства моих проектов, которые в основном состоят из статических методов, чтобы делать что-то быстро и эффективно на лету, никаких объектов не требуется!

person Jake Kalstad    schedule 04.06.2010
comment
Вопрос касается C, а не C++. В C нет классов, методов и закрытых членов. Более того, в C++ есть разница в статических функциях и статических методах. - person el.pescado; 04.06.2010