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

431

Вопрос был о простой 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. Просто временное, быстрое решение.

Теги:
function
static

11 ответов

624
Лучший ответ

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

EDIT. Для тех, кто думал, что автор вопросов означал "метод класса": поскольку вопрос помечен C, он означает обычную старую функцию C. Для методов класса (С++/Java/...) static означает, что этот метод можно вызвать в самом классе, нет экземпляра этого класса.

  • 1
    На самом деле я не помечал это c ++, некоторые администраторы, вероятно, делали, но это было о C ++, так в чем же разница в C ++?
  • 13
    Методы C ++ часто называют «функциями-членами», поэтому я согласен, что C ++ вносит некоторую двусмысленность. Это не ваша вина - язык просто использует ключевое слово для двух разных вещей.
Показать ещё 6 комментариев
189

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

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

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

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

  • 37
    C ++ также имеет файловую статичность. Не нужно вводить С в это.
  • 12
    В C ++ статическая функция является статической функцией. Статическая функция-член - это статическая функция-член, также известная как метод. Тот факт, что C не имеет членов, не означает, что функции являются «C».
Показать ещё 4 комментария
72

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

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

// inside some .cpp file:

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

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

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

  • 1
    Вопрос о статике в ц.
  • 6
    @ Задание вопроса было изначально помечено как C ++, и автор говорит об использовании файлов ".cpp".
40

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

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

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

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * https://stackoverflow.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;
}

GitHub вверх по течению.

Скомпилируйте и запустите:

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

Выход:

main f
main sf
main f
a sf

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

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

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

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

Обычный C-шаблон - передавать this структуру в качестве первого аргумента "method", что в основном и делает то, что 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 оптимизацию с помощью -O3, символ sf полностью удаляется из таблицы символов: его нельзя использовать извне. TODO зачем вообще хранить статические функции в таблице символов, когда нет оптимизации? Могут ли они быть использованы для чего-либо?

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

C++ анонимные пространства имен

В C++ вы можете захотеть использовать анонимные пространства имен вместо статических, что дает аналогичный эффект, но дополнительно скрывает определения типов: Безымянный/анонимный пространства имен против статических функций

  • 3
    примечание: void f() { puts("sf"); } (то есть два определения функции f() ) вызывают неопределенное поведение без диагностики. Это проблема качества компоновщика, чтобы увидеть сообщение об ошибке.
18

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

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

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

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

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

15

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

Во-вторых:

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

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

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

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

  • 0
    Ну, я делаю это только для того, чтобы ВРЕМЕННО освободить место в main.cpp, пока не решу, как организовать файл в библиотеки вместе с соответствующими .hpp. Есть ли лучшая идея, как это сделать?
  • 1
    Правильная терминология в C ++ - функция-член, а не метод. В легальном языке С ++ нет "методов". Метод является общим термином ОО. C ++ реализует их через функции-члены.
Показать ещё 1 комментарий
14

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

8

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

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

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

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

Person* tom = Person::createNewPerson();
  • 1
    Мне кажется, что вы говорите о статическом «методе», а не «функции» ??
  • 0
    Я предположил, что вы имеете в виду статические функции в классе.
Показать ещё 4 комментария
7

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

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

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

6

Минус nit: статические функции видны для единицы перевода, которая для большинства практических случаев - это файл, в котором функция определена. Ошибка, которую вы получаете, обычно называется нарушением правила One Definition.

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

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

Это способ взглянуть на статические функции. Однако это устарело на С++.

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

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

-5

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню