Значение int (*) (int *) = 5 (или любое целочисленное значение)

88

Я не могу понять этого:

int main() {
    int (*) (int *) = 5;
    return 0;
}

Приведенное выше назначение компилируется с g ++ c ++ 11. Я знаю, что int (*) (int *)это указатель на функцию, которая принимает (int *)аргумент as и возвращает int, но я не понимаю, как вы могли бы приравнять его к 5. Сначала я подумал, что это функция, которая постоянно возвращает 5 (из моего недавнего обучения в F #, наверное, ха-ха), тогда я подумал вкратце, что указатель функции указывает на ячейку памяти 5, но это явно не работает, как и шестнадцатеричные значения.

Думая, что это может быть потому, что функция возвращает int, и что назначение int нормально (как-то), я также попробовал это:

int * (*) (int *) = my_ptr

где my_ptrимеет тип int *того же типа, что и этот второй указатель на функцию, как и в первом случае с типом int. Это не компилируется. Назначение 5 или любого другого значения int вместо my_ptrэтого также не компилируется для этого указателя на функцию.

Итак, что означает задание?

Обновление 1

У нас есть подтверждение, что это ошибка, как показано в лучшем ответе. Однако до сих пор неизвестно, что на самом деле происходит со значением, которое вы присваиваете указателю на функцию, или что происходит с присвоением. Будем очень признательны за любые (хорошие) объяснения по этому поводу! Пожалуйста, обратитесь к приведенным ниже изменениям, чтобы прояснить проблему.

Редактировать 1

Я использую gcc версии 4.8.2 (в Ubuntu 4.8.2)

Редактировать 2

На самом деле, приравнивание его ко всему работает в моем компиляторе. Работает даже приравнивание ее к переменной std :: string или имени функции, возвращающей значение типа double.

Изменить 2.1

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

std::string (*) () = 5.6;

Но как только указатель функции находится на функции, которая возвращает какой-то указатель, он не компилируется, например, с

some_data_type ** (*) () = any_value;
Конрад Капп
источник
3
Хм ... выглядит не так, и лязг не принимает. Может быть расширение gcc (или ошибка).
Wintermute
4
g ++ компилируется, но gcc не работает:error: expected identifier or '(' before ')' token
tivn
3
@ 0x499602D Обратите внимание, что код не дает имени указателю. С int *x = 5твоим именем x. С int * (*x) (int *) = 5этим компилироваться не буду. (хотя он будет компилироваться как код C).
5
Сокращенный int(*) = 5;int(*);
тестовый пример

Ответы:

60

Это ошибка в g ++.

 int (*) (int *) 

это имя типа.

В C ++ у вас не может быть объявления с именем типа без идентификатора.

Итак, это компилируется с g ++.

 int (*) (int *) = 5;

и это также компилируется:

 int (*) (int *);

но оба они недействительны.

ИЗМЕНИТЬ :

TC упоминает в комментариях ошибку bugzilla 60680 с аналогичным тестовым примером, но она еще не утверждена . Ошибка подтверждена в bugzilla.

РЕДАКТИРОВАТЬ2 :

Когда два вышеуказанных объявления находятся в области видимости файла, g ++ правильно выдает диагностику (не может выполнить диагностику в области видимости блока).

РЕДАКТИРОВАТЬ3 :

Я проверил и могу воспроизвести проблему в последней версии g ++ версии 4 (4.9.2), последней предварительной версии 5 (5.0.1 20150412) и последней экспериментальной версии 6 (6.0.0 20150412).

ауа
источник
5
MSVC отклонил отредактированный опубликованный код с помощьюerror C2059: syntax error : ')'
Weather Vane
Если это имя типа, почему бы не 'int (*) (int *) int_func;' Работа?
Конрад Капп,
1
Для bugzilla GCC "NEW" - подтвержденная ошибка. (Неподтвержденные ошибки считаются «НЕ ПОДТВЕРЖДЕНЫ»).
TC
4
@KonradKapp: он отлично работает, если вы скажете, int (*int_func)(int *); что объявляет указатель на функцию с именем int_func.
Эдвард
3
@KonradKapp C ++ использует инфиксную нотацию для размещения идентификатора; по той же причине, что int x[5];и нетint[5] x;
MM
28

Это недопустимый C ++. Помните, что, поскольку ваш конкретный компилятор компилируется, это не делает его действительным. Компиляторы, как и все сложное программное обеспечение, иногда содержат ошибки, и это одна из них.

Напротив, clang++жалуется:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Это ожидаемое поведение, поскольку строка с нарушением не является допустимой для C ++. Он претендует на присвоение (из-за =), но не содержит идентификатора.

Эдвард
источник
9

Как указывали другие ответы, это ошибка, которая

int (*) (int *) = 5;

компилируется. Разумное приближение этого утверждения, которое, как можно было бы ожидать, будет иметь смысл:

int (*proc)(int*) = (int (*)(int*))(5);

Теперь procэто указатель на функцию, который ожидает, что адрес 5будет базовым адресом функции, которая принимает int*и возвращает int.

На некоторых микроконтроллерах / микропроцессорах 5может быть действительный кодовый адрес, и там может быть возможно найти такую ​​функцию.

На большинстве компьютеров общего назначения первая страница памяти (адреса 0-1023для страниц размером 4 КБ) намеренно недействительна (не отображается), чтобы улавливать nullобращения к указателям.

Таким образом, хотя поведение зависит от платформы, можно разумно ожидать, что произойдет сбой страницы при *procвызове (например, (*proc)(&v)). До момента *procвызова не происходит ничего необычного.

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

Атсби
источник
2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Эта командная строка генерирует множество промежуточных файлов. Первый из них so.cpp.170r.expand, говорит:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Это все еще не отвечает на вопрос, что именно происходит, но это должен быть шаг в правильном направлении.

Роланд Иллиг
источник
Интересно. Какова цель этих промежуточных файлов?
Конрад Капп,
@KonradKapp Создание машинного кода из человеческого кода - довольно сложный процесс (особенно если вы хотите, чтобы ваш компилятор оптимизировал свой вывод). Поскольку компиляция настолько сложна, она не выполняется за один шаг, большинство компиляторов имеют ту или иную форму промежуточного представления (IR).
11684
2
Еще одна причина наличия IR заключается в том, что, если у вас есть четко определенный IR, вы можете разделить интерфейсную часть и внутреннюю часть вашего компилятора. (Например, интерфейсная часть компилирует C в ваш IR, внутренняя часть компилирует IR в машинный код Intel. Теперь, если вы хотите добавить поддержку ARM, вам понадобится только второй сервер. А если вы хотите скомпилировать Go, вам понадобится только второй интерфейс, и, кроме того, компилятор Go сразу поддерживает как Intel, так и ARM, так как вы можете повторно использовать оба
серверных интерфейса
@ 11684 Хорошо, имеет смысл. Очень интересно. Я не мог определить, на каком языке Роланд дал этот ответ ... похоже, что это какая-то сборка, смешанная с C.
Конрад Капп
ИК-порт не обязательно должен быть доступен для печати; Я понятия не имею, что использует gcc, это может быть просто представление для печати @KonradKapp
11684