Вопрос

В чем разница между статической функцией - членом и внешней функцией привязки "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, ...);

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

Это было полезно?

Решение

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

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

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

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

void(*)(void)

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Область применения:

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

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

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

Внешний класс хранения:

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top