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

Когда статические определения функций в файлах заголовков в C?

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

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

спасибо

4b9b3361

Ответ 1

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

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

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

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

С этим было сказано...

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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