int (*) (int *) = 5 (또는 임의의 정수 값)의 의미


88

나는 이것을 이해할 수 없다.

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

위의 할당은 g ++ c ++ 11로 컴파일됩니다. 나는 그것이 as 인수 int (*) (int *)를 받아들이고 (int *)int를 반환하는 함수에 대한 포인터 라는 것을 알고 있지만 그것을 5와 동일시 할 수있는 방법을 이해하지 못합니다. 처음에는 이것이 지속적으로 5를 반환하는 함수라고 생각했습니다. F #, 아마도, haha), 잠시 함수 포인터가 메모리 위치 5를 가리키고 있다고 생각했지만 명확하게 작동하지 않으며 16 진수 값도 마찬가지입니다.

함수가 int를 반환하고 int를 할당하는 것이 (어쨌든) 괜찮 기 때문일 수 있다고 생각하면서 다음을 시도했습니다.

int * (*) (int *) = my_ptr

여기서 my_ptr형이고 int *, int 형인 제 경우와 같이이 2 함수 포인터와 같은 형태. 이것은 컴파일되지 않습니다. 대신 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
흠 ... 정확하지 않은 것 같고 clang은 그것을 받아들이지 않습니다. gcc 확장 (또는 버그) 일 수 있습니다.
Wintermute 2015

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(*);
Johannes Schaub-litb

답변:


60

g ++의 버그입니다.

 int (*) (int *) 

유형 이름입니다.

C ++에서는 식별자없이 형식 이름이있는 선언을 가질 수 없습니다.

그래서 이것은 g ++로 컴파일됩니다.

 int (*) (int *) = 5;

그리고 이것은 또한 컴파일됩니다.

 int (*) (int *);

그러나 둘 다 잘못된 선언입니다.

수정 :

TC 는 유사한 테스트 케이스와 함께 bugzilla bug 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;' 작업?
Konrad Kapp 2015

1
GCC 버그질라의 경우 "NEW"는 확인 된 버그입니다. (확인되지 ​​않은 버그는 "UNCONFIRMED"입니다).
TC

4
@KonradKapp : int (*int_func)(int *); 라는 함수 포인터를 선언하는 경우 잘 작동합니다 int_func.
Edward

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것으로 예상하는 함수에 대한 포인터 입니다.5int*int

일부 마이크로 컨트롤러 / 마이크로 프로세서 5에서 유효한 코드 주소가있을 수 있으며 여기에서 그러한 기능을 찾을 수 있습니다.

대부분의 범용 컴퓨터에서 첫 번째 메모리 페이지 ( 0-10234K 페이지의 주소 )는 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

}
...

이것은 정확히 무슨 일이 일어나는지에 대한 답은 아니지만 올바른 방향으로 나아가 야합니다.


흥미 롭군. 이 중간 파일의 목적은 무엇입니까?
Konrad Kapp 2015

@KonradKapp 인간 코드에서 기계어 코드를 생성하는 것은 매우 복잡한 프로세스입니다 (특히 컴파일러가 출력을 최적화하도록하려는 경우). 컴파일은 매우 복잡하기 때문에 한 단계로 수행되지 않으며 대부분의 컴파일러에는 IR (중간 표현) 형식이 있습니다.
11,684

2
IR이있는 또 다른 이유는 잘 정의 된 IR이있는 경우 컴파일러의 프런트 엔드와 백 엔드를 분리 할 수 ​​있다는 것입니다. (예를 들어 프런트 엔드는 C를 IR로 컴파일하고 백엔드는 IR을 인텔 기계어 코드로 컴파일합니다. 이제 ARM 지원을 추가하려면 두 번째 백엔드 만 필요합니다. Go를 컴파일하려면 두 번째 프런트 엔드이며 그
외에도

@ 11684 알겠습니다. 매우 흥미로운. 나는 그것이 조립 C.와 혼합의 일종처럼 보이는 ... 롤랜드는하지만이 답변에 준 어떤 언어 판별 할 수 없습니다
콘라드 캡

IR은 인쇄 할 필요가 없습니다. gcc가 무엇을 사용하는지 전혀 모르겠습니다. 이것은 단순히 인쇄 가능한 표현 일 수 있습니다 @KonradKapp
11684
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.