static vs extern “C” / “C ++”

В чем разница между статической функцией-членом и внешней связью «C»? Например, при использовании «makecontext» в C ++ мне нужно передать указатель на функцию. Google рекомендует использовать внешнюю ссылку «C» для него, потому что «makecontext» – C. Но я обнаружил, что использование статических работ также. Мне просто повезло или …

class X { public: static void proxy(int i) {} } makecontext(..., (void (*)(void)) X::proxy, ...); 

против

 extern "C" void proxy(int i) {} makecontext(..., (void (*)(void)) proxy, ...); 

EDIT: Можете ли вы показать компилятор или архитектуру, где статическая версия члена не работает (и это не ошибка в компиляторе)?

Да, вам просто повезло 🙂 Экстерн «C» – это одна языковая связь для языка C, которую должен поддерживать каждый компилятор на C ++, кроме экстерна «C ++», который является значением по умолчанию. Компиляторы могут поддерживать другие языковые связи. GCC, например, поддерживает внешнюю «Java», которая позволяет взаимодействовать с Java-кодом (хотя это довольно громоздко).

extern «C» сообщает компилятору, что ваша функция может быть вызвана кодом C. Это может, но не обязательно, включать в себя соответствующее соглашение о вызове и соответствующее кодирование имени языка C (иногда называемое «украшением»), в частности, в зависимости от реализации. Если у вас есть статическая функция-член, соглашение о вызове для нее – это ваш компилятор на C ++. Часто они такие же, как для компилятора C этой платформы, поэтому я сказал, что вам просто повезло. Если у вас есть API C и вы передаете указатель на функцию, лучше всегда ставьте его в функцию, объявленную с помощью extern «C», например

 extern "C" void foo() { ... } 

Несмотря на то, что тип указателя функции не содержит спецификации связи, а скорее выглядит

 void(*)(void) 

Связь является неотъемлемой частью типа – вы просто не можете выразить ее напрямую без typedef:

 extern "C" typedef void(*extern_c_funptr_t)(); 

Компилятор Comeau C ++ в строгом режиме выдает ошибку, например, если вы попытаетесь присвоить адрес внешней функции «C» выше (void(*)()) , потому что это указатель на функцию с связью C ++.

Обратите внимание, что extern C является рекомендуемым способом совместимости C / C ++. Вот об этом говорит мастер. Чтобы добавить к ответу eduffy: обратите внимание, что статические функции и переменные в глобальном пространстве имен устарели. По крайней мере используйте анонимное пространство имен.

Назад к extern C : если вы не используете extern C, вам нужно будет узнать точное искаженное имя и использовать его. Это гораздо больнее.

extern "C" отключает управление компилятором C ++ (что необходимо для перегрузки).

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

Большая часть того, что делает extern "C" , в значительной степени зависит от компилятора. Многие платформы меняют соглашение об изъятии имени и вызова, основанное на объявлении, но ни один из них не указан стандартом. На самом деле единственное, что требуется стандарту, это то, что код в блоке можно вызвать из C-функций. Что касается вашего конкретного вопроса, стандарт говорит:

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

Это означает, что extern "C" void proxy(int i) {} и /*extern "C++"*/void proxy(int i) {} имеет разные типы, и в результате указатели на эти функции будут иметь разные типы. Компилятор не подводит ваш код по той же причине, что и не провалил бы большую работу:

 int *foo = (int*)50; makecontext(..., (void (*)(void)) foo, ...); 

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

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

Вообще говоря

Классы хранения:

classы хранения используются для указания продолжительности и объема переменной или идентификатора.

Продолжительность:

Продолжительность указывает продолжительность жизни переменной.

Объем:

Область видимости указывает на видимость переменной.

Статический class хранения:

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

Внешний class хранения:

Класс extern storage используется для объявления глобальной переменной, которая будет известна функциям в файле и может быть известна всем функциям программы. Этот class хранения имеет постоянную продолжительность. Любая переменная этого classа сохраняет свое значение до тех пор, пока не будет изменено другим назначением. Сфера охвата является глобальной. Переменная может быть известна или видима всеми функциями внутри программы.

  • Как hash и сравнить функцию-указатель-член?
  • Как работают указатели функций в C?
  • Тип указателя возвращаемой функции
  • Давайте будем гением компьютера.