Подтвердить что ты не робот

Какой тип имени функции в C?

Я всегда понимал, что в C, func и &func эквивалентны. Я предполагаю, что оба они должны иметь указатель типа, который составляет 8 байтов в моей системе Win64. Однако я просто попробовал это:

#include <stdio.h>

int func(int x, int y)
{
    printf("hello\n");
}

int main()
{
    printf("%d, %d\n", sizeof(&func), sizeof(func));
    return 0;
}

И ожидая получить вывод 8, 8 был удивлен, получив вместо него 8, 1.

Почему это? Какой тип func? Кажется, это тип char или некоторый эквивалент.
Что здесь происходит?

Я скомпилировал это с помощью gcc -std=c99, если это имеет значение.

4b9b3361

Ответ 1

Какой тип является именем функции в C?

Имя функции или обозначение функции имеет тип функции. Когда он используется в выражении, кроме случаев, когда он является операндом оператора sizeof или &, он преобразуется из типа "возвращающий функцию функции", чтобы ввести "указатель на возвращаемый тип функции". (Это указано в C99, 6.3.2.1p4).

Теперь

sizeof(func)

недействителен C, поскольку sizeof не допускается с операндом типа функции. Это указано в ограничениях оператора sizeof:

(C99, 6.5.3.4p1 Ограничения) "Оператор sizeof не должен применяться к выражению, которое имеет тип функции или неполный тип, к имени в скобках такого типа или к выражению, которое обозначает бит- член поля."

Но

sizeof(func)

разрешен в GNU C.

В GNU C есть расширение GNU, которое разрешает его и в GNU C sizeof с операндом типа функции дает 1:

6.23 Арифметика на указателях void и Function

[...] sizeof также разрешен на void и на типах функций и возвращает 1.

http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html

Ответ 2

Дано:

int func(int x, int y) { /* ... */ }

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

В большинстве контекстов выражение типа функции неявно преобразуется в указатель на функцию; в этом случае указатель имеет тип int(*)(int, int).

Контексты, в которых это неявное преобразование не происходит, следующие:

  • Когда выражение является операндом унарного &; в этом случае &func дает адрес функции (как обычно, func); и

  • Когда выражение является операндом sizeof. Без этого исключения sizeof func даст размер указателя функции. Вместо этого это нарушение ограничения, требующее диагностики от компилятора.

(Примечание: это преобразование происходит, когда имя функции используется в вызове функции. Оператор () "(стандарт не называет его) требует префикса типа указателя на функцию.)

gcc имеет нестандартное расширение; он разрешает арифметику указателей на указатели функций и тип void*, действуя как арифметика указателя на указатели char* (т.е. работает в единицах байтов). К сожалению, IMHO, gcc сделал это через kludge, установив размер типов функций и типа void равным 1. Вот почему вы получаете sizeof func == 1; если вы включите один из стандартных режимов соответствия (например, gcc -std=c99 -pedantic), вы получите предупреждение.

Кстати, не используйте %d для печати результата sizeof. sizeof дает результат типа size_t. Если ваша реализация поддерживает его (C99 или новее), используйте %zu; Если нет, вам нужно использовать приведение, чтобы явно преобразовать значение size_t в то, что вы можете распечатать. Например:

printf("%lu\n", (unsigned long)sizeof &func);

Ответ 3

Какой тип является именем функции в C?

Это тип функции .

Я всегда понимал, что в C, func и &func эквивалентны

Ну, они не "эквивалентны". Однако функция распадается на указатель на функцию.

Я предполагаю, что оба они должны быть указателя типа

Это неправильное предположение.

И ожидая получить выход 8, 8 был удивлен, получив вместо него 8, 1. Почему это?

Потому что 1. он UB, если он компилируется, 2. он не должен даже компилироваться на первом месте, и как таковая ваша программа может ничего сделать.

Ответ 4

Имена не имеют типа C. Некоторые типы имен обозначают сущности, имеющие тип, такие как имена typedef, объекты или функции. Другие типы имен обозначают объекты, которые не имеют типа, например символы препроцессора или метки goto. Однако другие типы имен просто обозначают сами типы, а именно имена typedef.

Название функции обозначает объект, который имеет тип функции. Этот тип функции включает возвращаемый тип и (возможно, неполную) информацию о параметрах.

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

Концептуально даже при прямом вызове типа foo() происходит то, что foo, выражение, обозначающее функцию, при оценке неявно преобразуется в значение указателя на функцию. После этого оператор postfix оператора () вызывает эту функцию с помощью этого указателя.

Существует правило, что выражение, имеющее тип функции, создает значение указателя кроме, если это выражение является операндом & (адрес) или оператора sizeof. func и &func эквивалентны только в том смысле, что они дают одно и то же значение. func выводит значение указателя неявно. &func подавляет неявное генерирование указателя (func является операндом &, и поэтому преобразование подавляется), но затем & принимает адрес.

Итак, вы можете видеть, что sizeof &func и sizeof func отличаются. Первый принимает размер указателя, а последний пытается взять размер функции.

Принимая размер функции, это нарушение ограничений в C: для этого требуется диагностика из реализации, которая соответствует стандарту. Если программа все еще переводится, и при запуске создается значение 1, то есть "бонусное" поведение, характерное для вашей языковой реализации. Это не стандартный язык.