безопасно ли передавать указатели функций и сравнивать их так же, как указатели обычных объектов?

Вот что, у меня есть несколько функций,

void foo() {} void bar() {} 

И я хочу передать эти функции так же, как указатели обычных объектов,

 int main() { void (*fptr1)() = foo; void (*fptr2)() = fptr1; void (*fptr3)() = bar; if (fptr1 == foo) printf("foo function\n"); if (fptr2 == foo) printf("foo function\n"); if (fptr3 == foo) printf("foo function\n") } 

Могу ли я использовать эти указатели функций таким образом? И я написал программу для тестирования, похоже, хорошо. Кроме того, я думаю, что, как обычные объекты, которые могут находиться в stack или heap , функции находятся в text segment (справа?), Поэтому, когда я обращаюсь к foo , он дает мне физический адрес, по которому функция foo лежит в текстовом сегменте ?

СЛЕДОВАТЬ ЗА

Если я действительно работаю с DLL, рассмотрим это: во-первых, функции ptr fptr назначается функция,

 ReturnType (*fptr)(ArgType) = beautiful_func; 

Два сценария здесь,

1) если beautiful_func не находится в DLL, тогда безопасно использовать этот fptr .

2) если это в DLL, то позже, я думаю, было бы небезопасно использовать fptr , потому что теперь он может ссылаться на совершенно другую функцию, для которой не родился fptr , правильно?

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

Однако, когда вы говорите «сравните», проверьте, что у вас на уме:

  • вас интересует обнаружение того, что вам дается другая «вещь»,
  • или вы заинтересованы в обнаружении того, что вам дают разные функции?

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

Очевидно, что если вы создадите массив с номерами 1,2,3,4, а затем выделите другой массив и скопируйте его там, вы получите два разных указателя, верно? Но массив может быть SAME для вас, в зависимости от того, для чего вам это нужно.

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

Особенно это может произойти при работе с большими отдельными «подпроектами». Представьте, что вы написали функцию сортировки, затем включите ее с подпроектом A и подпроектом B, скомпилируйте / постройте все, затем соедините и запустите. Вы закончите с помощью одной функции сортировки или двух? Трудный вопрос, пока вы на самом деле не проверите и не адаптируете параметры связи.

Это немного сложнее, чем с массивами. С массивами у вас есть другой указатель, если массив был другим. Здесь одна и та же функция может иметь много разных адресов. Это может быть особенно заметно при работе с шаблонами на C ++, но это опять же зависит от того, насколько хорошо компоновщик выполнил свою работу. О, отличный пример: DLL. С тремя DLL, основанными на аналогичном коде, они почти гарантировали наличие трех копий всего, с чем они были связаны статически.

И когда говоришь о DLL … Вы знаете, что они могут загружать / выгружать дополнительный код в вашу память, не так ли? Это означает, что когда вы загружаете DLL, на каком-то адресе XYZ появляется функция. Затем вы выгружаете его, и он уходит. Но когда вы теперь загружаете разные DLL? Конечно, ОС разрешено повторно использовать пространство, и ему разрешено отображать недавно загруженную DLL в ту же область, что и предыдущая. Большую часть времени вы его не заметите, так как недавно загруженная DLL будет отображаться в другой регион, но это может произойти .

Это означает, что, хотя вы можете сравнить указатели, единственный ответ, который вы получаете, это: указатели одинаковые или нет?

  • если они не совпадают, то вы ПРОСТО НЕ ЗНАЕТЕ ; другой указатель функции не означает, что функция отличается. Это может быть так, это будет так в 99% случаев, но не обязательно должно быть разным

  • если они такие же:

    • если вы НЕ загружаете / выгружаете различные динамические библиотеки много раз, вы можете предположить, что ничего не меняется, и вы можете быть уверены, что получили тот же самый объект / объект / массив, что и раньше

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

ИЗМЕНИТЬ ПОСЛЕДУЮЩИЕ:

Если вы (или какая-либо библиотека, которую используете), никогда не выгружаете DLL, то ваш указатель к функции-that-target-a-DLL безопасен в использовании.

Как только DLL загружается, единственное злое дело, которое может изменить смысл адреса, который была выбрана этой DLL, – это выгрузка динамического модуля .

Если вы уверены, что:

  • (1) либо ваш указатель на функцию не нацелен на функцию из динамического модуля (указывает только на статически связанный код)
  • (2) или он нацелен на динамический модуль, но этот динамический модуль никогда не выгружается (ok: пока программа не завершится или не завершится сбой)
  • (3) или он нацелен на динамический модуль, и вы точно знаете, какой из них, и что динамический модуль иногда выгружается во время выполнения, но ваш код получает некоторое «предварительное уведомление» об этом факте

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

  • для (1) никаких мер безопасности не требуется: функции не будут заменены
  • для (2) никаких мер безопасности не требуется: функции не получат, пока программа не завершится
  • для (3) необходимы меры безопасности: вы должны прослушивать эти уведомления, и как только вы получите уведомление о выгрузке DLL, вы должны немедленно забыть все указатели, предназначенные для DLL. Вы все еще в безопасности, чтобы помнить других. Вы по-прежнему в безопасности, чтобы снова запомнить его, когда он снова загружается.

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

  • вы действительно не знаете, какая DLL указана этим указателем
  • или что DLL будет выгружена в любой момент без какого-либо уведомления

то ваш указатель на функцию небезопасно использовать вообще . И вообще я имею в виду ВСЕ ВСЕ. Не храните его, так как он может мгновенно испариться немедленно.

это дает мне физический адрес, в котором функция foo лежит в текстовом сегменте?

Если вы не работаете с примитивной или какой-либо другой ОС, НЕТ!

Адреса не являются физическими, это виртуальные адреса !

В основном существует механизм, используемый операционными системами, который позволяет программам, которые даже больше, чем физическая память. Таким образом, ОС выполняет работу по обработке отображения за кулисами.

Извините, если я вас смутил. Ваше понимание правильное (вполне нормально использовать указатели функций в том виде, в котором вы их используете), но адреса не являются физическими адресами (которые относятся к номерам, с помощью которых ваша основная память фактически адресована).

Да, стандарт C позволяет сравнивать указатели функций с операторами == и! =, Например, из C11 6.5.9:

Два указателя сравнивают равные, если и только если оба являются нулевыми указателями, оба являются указателями на один и тот же объект (включая указатель на объект и подобъект в начале) или функцию,

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

Учитывая тот факт, что указатель хранит, чем адрес памяти, который сводится к числу, да, вы можете сравнить tham таким образом. Что касается другого вопроса, взятого здесь , то сегмент текста будет определен как «один из разделов программы в объектном файле или в памяти, который содержит исполняемые инструкции». Это означает, что указатель должен содержать адрес где-то в текстовый сегмент.

Да, вы можете так использовать. И вы понимаете правильно.