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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

Здесь я иллюстрирую, как 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++ вы можете захотеть использовать анонимные пространства имен вместо статических, что дает аналогичный эффект, но дополнительно скрывает определения типов: Безымянный/анонимный пространства имен против статических функций

Ответ 5

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

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

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

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

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

Ответ 6

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

Во-вторых:

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

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

Person* tom = Person::createNewPerson();

Ответ 9

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

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

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

Ответ 10

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

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

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

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

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

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

Ответ 11

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