Как на самом деле работает приведение функции в C? - PullRequest
6 голосов
/ 16 апреля 2009
int foo(char *c)  {...}

main() {
     int (*thud)(void *);

     thud = (int (*)(void *))(foo);
}

Что на самом деле происходит во время оценки задания?

Существует разница между типом заклинания и foo; тип приведения - указатель , а foo - функция . Итак, преобразовывает ли компилятор то, что в '(foo)', в указатель на foo и только затем выполняет приведение? Потому что больше ничего не имеет смысла; другой вариант заключается в том, что сама функция преобразуется в указатель на функцию, которая получает void* и возвращает int как я знаю, функция является меткой для фрагмента кода в памяти и, таким образом, не может стать указателем, который является переменной.

Ответы [ 5 ]

5 голосов
/ 16 апреля 2009

Имя функции является указателем, если используется как таковое. Это несколько похоже на то, как имя массива является указателем на его первый элемент.

При этом вызов функции через указатель с типом, отличным от фактического прототипа функции (как в вашем примере), является неопределенным поведением. Не делай этого.

Добавление

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

из , раздел 6.3.2.3 стандарта С.

5 голосов
/ 16 апреля 2009

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

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

Еще одно уточнение: Стандарт указывает (в 6.5.2.2), что поведение не определено, если вызывающая сторона использует несовместимые типы. Например, приведение функции, которая возвращает void, к функции, которая возвращает int, и затем вызов этой функции, возвращаемое значение не имеет смысла. Перед вызовом функции рекомендуется преобразовать функцию в совместимый тип, иначе вы можете получить неожиданные результаты.

2 голосов
/ 16 апреля 2009

Это будет комментарий к ответу Рика С. Петти, но он не умещается в 300 символов.

Стандарт C не очень ограничительный - указатели на объекты (и функции не являются объектами) могут быть преобразованы в указатель на void и обратно без проблем. POSIX требует, чтобы указатели на функции имели одинаковый размер и могли быть преобразованы в указатель на void.

POSIX 2008 - Общая информация - Среда компиляции

2.12.3 Типы указателей

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

Примечание: Стандарт ISO C не требует этого, но это требуется для соответствия POSIX.

2 голосов
/ 16 апреля 2009

Правильный термин распад . Функция foo затухает до указателя на foo перед приведением. Сам актерский состав будет неактивным на всех платформах, о которых я могу подумать.

Обратите внимание, однако, что поведение программы, содержащей такое преобразование, не определено стандартом C.

2 голосов
/ 16 апреля 2009

Указатель на C - это адрес, то есть число, хранящееся в каком-то месте. Функция в C - это адрес некоторого кода. Эти двое - одно и то же.

...