C / C ++: статическая функция в заголовочном файле, что это значит? - PullRequest
48 голосов
/ 23 апреля 2009

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

Ответы [ 6 ]

61 голосов
/ 23 апреля 2009

Функция определена в заголовочном файле? Так что фактический код задается непосредственно в функции, например:

static int addTwo(int x)
{
  return x + 2;
}

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

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

ОБНОВЛЕНИЕ : Во многих случаях это хорошая идея сделать что-то подобное выше, и я понимаю, что мой ответ звучит очень черно-белым об этом, что немного упрощает вещи. Например, код, который моделирует (или просто использует) встроенные функции , может быть выражен, как указано выше, и с явным ключевым словом inline даже:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}

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

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

13 голосов
/ 23 апреля 2009

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

7 голосов
/ 24 апреля 2009

Как говорят другие, оно имеет то же значение, что и функция static в самом файле .c. Это связано с тем, что между .c и .h файлами нет семантической разницы; есть только модуль компиляции, составленный из файла, фактически переданного компилятору (обычно с именем .c) с содержимым любого и всех файлов с именами в #include строках (обычно с именем .h), вставленных в поток по мере их видны препроцессором.

Соглашение о том, что источник C находится в файле с именем .c, а публичные объявления в файлах с именем .h - это всего лишь соглашение. Но это, как правило, хороший. В соответствии с этим соглашением единственные вещи, которые должны появляться в .h файлах, - это объявления, так что вы обычно избегаете, чтобы один и тот же символ определялся более одного раза в одной программе.

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

Я не уверен, какие варианты использования вообще могли бы оправдать это в общедоступном общедоступном заголовке.

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

4 голосов
/ 05 апреля 2017

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

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

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

static int counter()
{
    static int ctr = 0;
    return ctr++;
}

Вместо:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}

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

То есть, единственное отличие - производительность и неправильный размер кода.

1 голос
/ 16 июля 2012

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

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

Библиотека векторной математики Apple в среде GLK использует такую ​​конструкцию (например, GLKMatrix4.h).

1 голос
/ 22 июля 2009

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

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

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

...