Указатели функций и адрес функции
Итак, я понял, что при создании указателей функций вам не нужно operator &
, чтобы получить адрес начальной функции:
#include <stdio.h>
double foo (double x){
return x*x;
}
int main () {
double (*fun1)(double) = &foo;
double (*fun2)(double) = foo;
printf("%f\n",fun1(10));
printf("%f\n",fun2(10));
printf("fun1 = %p \t &foo = %p\n",fun1, &foo);
printf("fun2 = %p \t foo = %p\n",fun2, foo);
int a[10];
printf(" a = %p \n &a = %p \n",a,&a);
return 0;
}
выход:
>./a.out
100.000000
100.000000
fun1 = 0x4004f4 &foo = 0x4004f4
fun2 = 0x4004f4 foo = 0x4004f4
a = 0x7fff26804470
&a = 0x7fff26804470
Тогда я понял, что это также верно для массивов, что означает, что если у вас есть int a[10]
, то оба a
и &a
указывают на одно и то же местоположение. Почему это с массивами и функциями? Является ли адрес сохраненным в ячейке памяти с тем же адресом, что и значение (адрес), сохраненное в нем?
Ответы
Ответ 1
Учитывая int a[10]
, оба a
и &a
дают один и тот же адрес, да, но их типы отличаются.
a
имеет тип int[10]
. Когда он неявно преобразован в тип указателя, указатель имеет тип int*
и указывает на начальный элемент массива. &a
имеет тип int (*)[10]
(то есть указатель на массив из десяти целых чисел). Поскольку в массиве не может быть заполнения, они оба дают указатели с одинаковым значением, но указатели имеют разные типы.
Функции похожи на массивы, но не полностью одинаковы. Ваша функция foo
имеет тип double(double)
. Всякий раз, когда foo
используется в выражении и не является операндом унарного оператора &
, он неявно преобразуется в указатель на себя, который имеет тип double(*)(double)
.
Таким образом, для всех практических целей имя функции и указатель на одну и ту же функцию взаимозаменяемы. Есть некоторые тонкости, все из которых я обсуждаю в ответ на Почему все эти сумасшедшие определения указателей функций все работают? Что действительно происходит? (Этот вопрос был задан вопрос о С++, но правила для несимвольных функций в С++ такие же, как для функций из C.)
Ответ 2
Нет, нет дополнительного хранилища, предназначенного для указания на функцию/массив.
С большинством переменных variable_name
имеет значение, отличное от адреса адреса этой переменной, поэтому вам нужно использовать &variable
для получения адреса.
С помощью функции или массива function_name
(сам по себе, не сопровождаемый скобками) не имеет никакого другого значения, поэтому не было проблемы с интерпретацией его как адреса адреса функции.
Аналогично обратное: нормальный указатель должен быть разыменован явно, но указатель на функцию не (опять же, потому что нет другой разумной интерпретации), поэтому задан указатель на такую функцию, как:
int (*func)(param_list);
Следующие эквиваленты друг другу - оба вызова любой функции func
указывают на:
(*func)(params);
func(params);
Ответ 3
В принципе, поскольку имя функции "известно" как функция, оно и не является строго необходимым. Такое поведение одинаково для массивов. Напомним, что сама функция не является переменной, поэтому она ведет себя несколько иначе, чем вы могли бы ожидать иногда. Если у вас есть второе издание K & R, вы можете проверить раздел 5.11 на указатели на функции или справочное руководство в конце,
Раздел A7.1 Генерация указателя: если тип выражения или subexpression является "массивом T" для некоторого типа T, тогда значение выражение является указателем на первый объект в массиве, а тип выражения изменяется на "указатель на T". Это преобразование не имеет место выражение - операнд унарного & оператор,... Аналогично, выражение типа "функция, возвращающая T", кроме случаев использования в качестве операнда оператора и, преобразуется в "указатель на функцию, возвращающую T."
Раздел A7.4.2 Адрес Оператор: Унарный и оператор принимает адрес его операнда.... В результате указатель на объект или функцию обозначается lvalue. Если тип операнда равен T, тип результата "указатель на T".
Насколько я знаю, это то же самое для C99.
Ответ 4
printf ( "fun1 =% p\t & foo =% p\n", fun1, foo);
Здесь вы вызываете foo
, передавая указатель функции с pass by value
и
printf ( "fun2 =% p\t foo =% p\n", fun2, & foo)
Здесь вы вызываете &foo
, передавая функцию Pointer с pass by reference
в обоих случаях, когда вы вызываете printf
только с указателем на функцию.
Помните foo
сам function pointer value
и `не переменная.
То же самое происходит с массивом.
int arr[10]
преобразуется в непрерывный непрерывный блок из 10 целых чисел, а адрес первого элемента сохраняется в arr. поэтому arr также является указателем.
Ответ 5
fun
и &fun
являются точно такими же (за исключением того, что sizeof (f) является незаконным).
a
и &a
совпадают с точностью до арифметики указателя: a + 10 == &a + 1
, потому что 10*sizeof(*a) == sizeof(a)
(где sizeof(*a) == sizeof(int)
).