Что такое "статическая" функция?

Вопрос был о простых функциях c, а не C++ static методы, как пояснено в комментариях.

Хорошо я понимаю что static переменная есть, но что такое static функционировать?

И почему, если я объявляю функцию, скажем void print_matrixв скажем a.c (БЕЗ a.h) и включить "a.c" - Я получил "print_matrix@@....) already defined in a.obj"НО если я объявлю это как static void print_matrix тогда это компилируется?

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

14 ответов

Решение

static функции - это функции, которые видны только другим функциям в том же файле (точнее, той же единице перевода).

РЕДАКТИРОВАТЬ: Для тех, кто думал, что автор вопросов означает "метод класса": как вопрос помечен C он имеет в виду простую старую функцию C. Для (C++/Java/...) методов класса, static означает, что этот метод может быть вызван для самого класса, экземпляр этого класса не требуется.

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

В C++ "static" также может применяться к функциям-членам и данным-членам классов. Статический член данных также называется "переменной класса", в то время как нестатический член данных является "переменной экземпляра". Это терминология Smalltalk. Это означает, что существует только одна копия статического члена данных, совместно используемого всеми объектами класса, в то время как каждый объект имеет свою собственную копию нестатического члена данных. Таким образом, статический член данных по сути является глобальной переменной, то есть членом класса.

Нестатические функции-члены могут получить доступ ко всем данным-членам класса: статическим и нестатическим. Статические функции-члены могут работать только со статическими данными-членами.

Один из способов думать об этом состоит в том, что в C++ статические члены-данные и статические функции-члены принадлежат не какому-либо объекту, а всему классу.

Существует два варианта использования ключевого слова static в отношении функций в C++.

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

// inside some .cpp file:

static void foo();    // old "C" way of having internal linkage

// C++ way:
namespace
{
   void this_function_has_internal_linkage()
   {
      // ...
   }
}

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

Пример минимальной работоспособной многофайловой области

Здесь я иллюстрирую, как static влияет на область определения функций в нескольких файлах.

переменный ток

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * https://stackru.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* OK: only declared, not defined. Will use the one in main. */
void f(void);

/* OK: only visible to this file. */
static void sf() { puts("a sf"); }

void a() {
    f();
    sf();
}

main.c

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

компилировать

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

Выход

main f
main sf
main f
a sf

интерпретация

  • есть две отдельные функции sf по одному на каждый файл
  • есть одна общая функция f

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

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

Распространенный шаблон C - передать this структурировать в качестве первого аргумента "метода", что в основном и делает C++ под капотом.

Что говорят об этом стандарты

C99 N1256 черновик 6.7.1 " Спецификаторы класса хранения" говорит, что static является "спецификатором класса хранения".

6.2.2 / 3 "Связи идентификаторов" говорит static подразумевает internal linkage:

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

и 6.2.2/2 говорит, что internal linkage ведет себя как в нашем примере:

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

где "единица перевода" - это исходный файл после предварительной обработки.

Как GCC реализует это для ELF (Linux)?

С STB_LOCAL связывание.

Если мы скомпилируем:

int f() { return 0; }
static int sf() { return 0; }

и разберите таблицу символов с помощью:

readelf -s main.o

вывод содержит:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

таким образом, связывание - единственная существенная разница между ними Value это просто их смещение в .bss раздел, поэтому мы ожидаем, что он будет отличаться.

STB_LOCAL задокументировано в спецификации ELF по адресу http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html:

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

что делает его идеальным выбором для представления static,

Функции без статики есть STB_GLOBAL и спецификация говорит:

Когда редактор ссылок объединяет несколько перемещаемых объектных файлов, он не позволяет использовать несколько определений символов STB_GLOBAL с одним и тем же именем.

что согласуется с ошибками ссылок в нескольких нестатических определениях.

Если мы проверим оптимизацию с -O3, sf Символ полностью удаляется из таблицы символов: его нельзя использовать извне. TODO зачем вообще хранить статические функции в таблице символов, когда нет оптимизации? Могут ли они быть использованы для чего-либо?

Смотрите также

Попробуй сам

Пример на GitHub для вас, чтобы играть с.

Ниже приведено описание простых функций C - в классе C++ модификатор "static" имеет другое значение.

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

В C каждый "модуль" (комбинация sample.c и sample.h) компилируется независимо, а затем каждый из этих скомпилированных объектных файлов (sample.o) связывается компоновщиком с исполняемым файлом.

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

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

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

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

Во-вторых:

C++ имеет здесь некоторую запутанную терминологию - я не знал об этом, пока не было указано в комментариях.

а) static functions - унаследовано от C, и о чем вы здесь говорите. Вне любого класса. Статическая функция означает, что она не видна вне текущего модуля компиляции - поэтому в вашем случае a.obj имеет копию, а ваш другой код имеет независимую копию. (Вздутие конечного исполняемого файла с несколькими копиями кода).

б) static member function - что объектная ориентация называет статическим методом. Живет внутри класса. Вы вызываете это с помощью класса, а не через экземпляр объекта.

Эти два разных определения статических функций совершенно разные. Будьте осторожны - здесь будут драконы.

"Что такое â € œstaticâ € функция в C?"

Начнем с самого начала.

Все это основано на том, что называется "связью":

"Идентификатор, объявленный в разных областях или в одной области более одного раза, может быть использован для ссылки на один и тот же объект или функцию с помощью процесса, называемого связыванием. 29) Существует три вида связи: внешняя, внутренняя и никакая".

Источник: C18, 6.2.2 / 1


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

Источник: C18, 6.2.2/2


Если функция определена без спецификатора класса хранения, функция имеет externвся связь по умолчанию:

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

Источник: C18, 6.2.2/5

Это означает, что - если ваша программа состоит из нескольких единиц перевода / исходных файлов (.c или .cpp) - функция видна во всех единицах перевода / исходных файлах вашей программы.

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

В C и C++staticквалификатор класса-хранилища, примененный к функции в области видимости файла (не статическая функция-член класса в C++ или функция в другом блоке), теперь приходит на помощь и означает, что соответствующая функция видна только внутри единицы перевода / исходного файла он был определен в других TLU / файлах, а не в них.

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


30) Объявление функции может содержать спецификатор класса хранения, только если оно находится в области видимости файла; см. 6.7.1.

Источник: C18, 6.2.2/3


Таким образом, A static функция имеет смысл, только если:

  1. Ваша программа состоит из нескольких единиц перевода / исходных файлов (.c или .cpp).

    а также

  2. Вы хотите ограничить объем функции файлом, в котором определена конкретная функция.

Если оба эти требования не совпадают, вам не нужно ломать голову над квалификацией функции какstatic.


Боковые примечания:

Статическая функция - это функция, которая может быть вызвана в самом классе, в отличие от экземпляра класса.

Например, нестатический будет:

Person* tom = new Person();
tom->setName("Tom");

Этот метод работает с экземпляром класса, а не с самим классом. Однако у вас может быть статический метод, который может работать без экземпляра. Это иногда используется в шаблоне Factory:

Person* tom = Person::createNewPerson();

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

Стандарт, вероятно, говорит что-то вроде:

"Каждая программа должна содержать ровно одно определение каждой не встроенной функции или объекта, которая используется в этой программе; диагностика не требуется".

Это С-образный взгляд на статические функции. Однако в C++ это не рекомендуется.

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

Я бы добавил к примеру Parrot шаблон Singleton, который основан на статической функции-члене такого типа, чтобы получать / использовать один объект в течение всей жизни программы.

Ответ на статическую функцию зависит от языка:

1) В языках без OOPS, таких как C, это означает, что функция доступна только внутри файла, в котором она определена.

2) В языках с OOPS, таких как C++, это означает, что функция может быть вызвана непосредственно в классе, не создавая его экземпляр.

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

Вот простой пример.

main.c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

И скомпилировать с

gcc -o main main.c

Вы увидите, что это не удалось. Потому что вы даже не реализуете функцию ghost().

Но что, если мы воспользуемся следующей командой.

gcc -DTEST -O2 -o main main.c

Это успех, и эту программу можно нормально выполнять.

Почему? Есть 3 ключевых момента.

  1. -O2: Уровень оптимизации компилятора не ниже 2.
  2. -DTEST: определить ТЕСТ, поэтому test() не будет вызываться.
  3. Определен "статический" для test().

Только если все эти 3 условия выполнены, вы можете пройти компиляцию. Из-за этого "статического" объявления компилятор может подтвердить, что test() НИКОГДА не будет вызываться в другом файле. Ваш компилятор может удалить test() при компиляции. Поскольку нам не нужен test(), не имеет значения, определен или реализован ghost().

Статическая функция в C — это функция, область действия которой ограничена объектным файлом. Это означает, что статическая функция видна только в своем объектном файле. Функцию можно выразить как статическую функцию, поместив ключевое слово static перед именем функции.

Пример, демонстрирующий это, приведен ниже:

Есть два файла и . Содержимое этих файлов дается следующим образом:

Содержание first.c,

      static void staticFunction(void)
{
   printf("Static function staticFunction() ");
}

Содержание second.c,

      int main()
{
   staticFunction();
   return 0;
}

Теперь, если приведенный выше код скомпилируется, произойдет ошибка, что означает "undefined reference to staticFunction ()."Это происходит потому, что функция staticFunction() является статической функцией, которая видна только в своем объектном файле.

Программа, демонстрирующая статические функции на C, выглядит следующим образом:

      #include <stdio.h>

static void staticFunction(void){
   printf("Static function staticFunction() ");
}

int main()
{
   staticFunction();
   return 0;
}

Вывод вышеуказанной программы выглядит следующим образом:

      Static function staticFunc()

В приведенной выше программе функция является статической функцией, которая печатает "Static function staticFunction()". В main()функция называется staticFunction(). Эта программа выполняется правильно, потому что статическая функция вызывается только ее объектным файлом.

Поскольку статическая функция в "c" компилятор не будет создавать свои внутренние переменные в стеке, поэтому вызов статической функции происходит быстрее, и, как следствие, вы не можете использовать инициализаторы, такие как: char c='A'.

Другие вопросы по тегам