Куда именно указывают указатели на функции?

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

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

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

Ответов (5)

Решение

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

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

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

Для компилятора наиболее простой способ реализовать указатель функции - это указать адрес функции в памяти, и большинство компиляторов делают это таким образом, но в стандарте языка это не указано. Некоторые системы (я думаю, что IBM AS / 400 является примером) хранят дополнительную информацию в указателях функций. Компилятор может реализовать указатели на функции в виде целочисленных индексов в таблице дескрипторов, если требуемая семантика реализована должным образом.

Разыменовать указатель функции невозможно. Для вызова функции в качестве префиксного выражения требуется указатель, а не имя функции; если вы используете имя функции, это имя неявно преобразуется в указатель (что касается языка; сгенерированный машинный код может быть другим). Таким образом, у программы нет переносимого способа определить, действительно ли указатели функций содержат адреса памяти или нет.

Непереносимо, учитывая функцию func, вы можете сделать что-то вроде:

printf("Address of func is %p\n", (void*)func);

и вы, вероятно, увидите что-то похожее на удобочитаемое представление адреса памяти. Но, строго говоря, поведение преобразования указателя функции в void* не определяется языком, и это может не работать в некоторых системах.

Вы могли бы даже быть в состоянии преобразовать указатель на функцию unsigned char* и изучать машинный код функция, но выходит далеко за пределы ничего определенного в стандарте C.

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

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

Указатели функций указывают на адрес функции в памяти.

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

Что ж, я не уверен, но учитывая, что функции являются инструкциями (ADD, SUB, JMP) и что каждая из них имеет шестнадцатеричные значения, я считаю, что вы не будете изменять функцию, а только инструкцию JMP () ...