Что такое «статическая» функция? - PullRequest
443 голосов
/ 17 февраля 2009

Вопрос касался простых функций, а не 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. Просто временное, быстрое решение.

Ответы [ 10 ]

613 голосов
/ 17 февраля 2009

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

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

185 голосов
/ 17 февраля 2009

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

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

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

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

73 голосов
/ 17 февраля 2009

Существует два варианта использования ключевого слова 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».

43 голосов

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

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

a.c

#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 upstream .

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

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 в качестве первого аргумента «метода», что в основном и делает 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 зачем вообще хранить статические функции в таблице символов, когда нет оптимизации? Могут ли они быть использованы для чего-либо?

См. Также

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

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

19 голосов
/ 26 апреля 2013

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

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

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

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

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

16 голосов
/ 17 февраля 2009

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

Во-вторых:

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

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

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

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

14 голосов
/ 17 февраля 2009

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

8 голосов
/ 17 февраля 2009

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

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

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

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

Person* tom = Person::createNewPerson();
7 голосов
/ 06 марта 2016

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

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

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

6 голосов
/ 17 февраля 2009

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

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

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

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

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

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

...