Значення int (*) (int *) = 5 (або будь-яке ціле значення)


88

Я не можу зрозуміти це:

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

Вищевказане призначення компілюється з g ++ c ++ 11. Я знаю, що int (*) (int *)це вказівник на функцію, яка приймає (int *)аргумент як аргумент і повертає 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 або імені функції, яка повертає подвійну.

Редагувати 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).
nos

5
Скорочений тест: int(*) = 5;аint(*);
Йоханнес Шауб - літ

Відповіді:


60

Це помилка в g ++.

 int (*) (int *) 

- це назва типу.

У C ++ ви не можете мати декларацію з іменем типу без ідентифікатора.

Отже, це компілюється з g ++.

 int (*) (int *) = 5;

і це також компілює:

 int (*) (int *);

але обидва вони є недійсними деклараціями.

РЕДАГУВАТИ :

TC згадує в коментарях помилку bugzilla 60680 з подібним тестом, але вона ще не затверджена . Помилка підтверджена в bugzilla.

EDIT2 :

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

EDIT3 :

Я перевірив і можу відтворити проблему в останньому випуску 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;
М. М.

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*і повертає an int.

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

На більшості комп’ютерів загального призначення перша сторінка пам’яті (адреси 0-1023сторінок 4K) спеціально недійсна (не відображена) для того, щоб отримати 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 Виробництво машинного коду з людського коду є досить складним процесом (особливо якщо ви хочете, щоб ваш компілятор оптимізував його вихід). Оскільки компіляція настільки складна, це не робиться за один крок, більшість компіляторів мають певну форму проміжного представлення (ІР).
11684

2
Ще однією причиною наявності IR є те, що якщо у вас є чітко визначений IR, ви можете розділити інтерфейсний і задній кінці вашого компілятора. (Наприклад, інтерфейсний компілює C до вашого ІЧ, внутрішній компілює ІР до машинного коду Intel. Тепер, якщо ви хочете додати підтримку ARM, вам потрібен лише другий фоновий інтерфейс. А якщо ви хочете скомпілювати Go, вам потрібен лише другий інтерфейс, а крім того, компілятор Go відразу підтримує як Intel, так і ARM, оскільки ви можете повторно використовувати обидва
фони

@ 11684 Добре, є сенс. Дуже цікаво. Я не міг визначити, яку мову Роланд дав у цій відповіді, хоча ... це схоже на якусь асамблею, змішану з C.
Конрад Капп

ІЧ не потрібно друкувати; Я не уявляю, що використовує gcc, це може бути просто подання для друку @KonradKapp
11684
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.