Когда помещать статические определения функций в заголовочные файлы в C? - PullRequest
25 голосов
/ 18 октября 2010

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

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

спасибо

Ответы [ 6 ]

25 голосов
/ 18 октября 2010

Некоторые идеи:

  • Одно из возможных законных применений, о которых я могу подумать, - это когда вы хотите сделать функцию доступной, не создавая символ с внешней связью и не загрязняя внешнее пространство имен.(Но тогда вы могли бы просто использовать неясное префиксное имя, например mylib123__foobar и #define foobar mylib123__foobar в заголовочном файле, так что это кажется немного сомнительным.)
  • Вы хотите, чтобы определенные функции были доступны исключительно череззаголовочный файл, не требующий от пользователя ссылки на библиотеку / объектные файлы.Я мог видеть это как реальную мотивацию, когда предоставлял «библиотеку», которая почти ничего, кроме структур данных и нескольких тривиальных кусочков кода для манипулирования ими.Фактически, если структуры данных не являются непрозрачными и предназначены для непосредственного доступа к приложению, размещение функций для использования с ними в одном и том же заголовочном файле (по сравнению с библиотекой) значительно снижает риск поломки, если / когда вы меняете данныеструктуры.
  • Возможно, функция является просто оболочкой для внешней функции, и способ ее работы может зависеть от параметров времени компиляции в вызывающем модуле компиляции.Например:

    static int foobar(int x)
    {
        return real_foobar(COMPILETIME_PARAMETER, x);
    }
    

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

С учетом сказанного ...

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

Эта критика особенно применила пример OP "больших" статических функций в заголовочных файлах.Практически невозможно, чтобы большая функция могла извлечь выгоду из встраивания, если только постоянное значение аргумента не позволяет компилятору удалять 90% кода или что-то еще.(Для реального примера этого крайнего случая, посмотрите некоторые сумасшедшие определения встроенных функций / макросов, используемые в libavcodec.: -)

9 голосов
/ 18 октября 2010

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

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

Короче говоря, если вы не знаете, вне всяких сомнений, что вам нужна статическая функция в заголовочном файле ... вам не нужна статическая функция в заголовочном файле; вам нужна нестатическая функция в файле .c с заголовком в .h.

8 голосов
/ 18 октября 2010

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

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

2 голосов
/ 18 октября 2010

Modern C для этой задачи принял ключевое слово inline из C ++.Но если ваш компилятор не имеет этого (пока?) static в заголовочных файлах, это способ подражать этому.inline не означает, что функция обязательно указана для любого вызывающего, но просто то, что в конечном исполняемом файле обычно будет не более одной копии.(Технически соответствующие символы компоновщика являются «слабыми» символами.) Напротив, если просто объявлено static, то каждая единица компиляции будет сохранять копию.

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

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

2 голосов
/ 18 октября 2010

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

(Обратите внимание, что «правильное» решение состоит в замене strtok () нафункция без сохранения состояния и делает вызывающих абонентов ответственными за хранение контекста и информации о состоянии, точно так же, как fopen () и друзья заставляют вызывающего удерживать FILE для каждого контекста.)

0 голосов
/ 18 октября 2010

Если функция имеет внешнюю связь, она должна быть объявлена ​​в файле .h.

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

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

...