이 코드가 64 비트 아키텍처에서는 segfault이지만 32 비트에서는 잘 작동하는 이유는 무엇입니까?


112

다음 C 퍼즐을 발견했습니다.

Q : 다음 프로그램이 IA-64에서는 segfault를 실행하지만 IA-32에서는 제대로 작동하는 이유는 무엇입니까?

  int main()
  {
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
  }

int64 비트 컴퓨터에서의 크기가 포인터의 크기와 같지 않을 수 있다는 것을 알고 int있습니다 (32 비트가 될 수 있고 포인터가 64 비트가 될 수 있음). 그러나 이것이 위의 프로그램과 어떤 관련이 있는지 잘 모르겠습니다. 어떤 아이디어?


50
stdlib.h포함되지 않는 것과 같은 어리석은 일 입니까?
user786653 2011 년

3
이 코드는 내 64 비트 컴퓨터에서 잘 실행됩니다. #include stdlib.h(malloc의 경우) 경고없이 컴파일됩니다
mpenkov 2011 년

1
오! @ user786653이 중요한 부분을 정했습니다. 를 사용하면 #include <stdlib.h>완벽하게 찾을 수 있지만 문제가 아닙니다.

8
@delnan-그렇게 작동 할 필요는 없지만, sizeof(int) == sizeof(int*)예를 들어 int사용 된 호출 규칙에서 s에 대한 다른 레지스터를 통해 포인터가 반환 된 경우 플랫폼에서 합법적으로 실패 할 수 있습니다.
Flexo

7
C99 환경에서 컴파일러는 암시 적 선언에 대해 최소한 경고를 제공해야합니다 malloc(). GCC는 이렇게 말합니다 warning: incompatible implicit declaration of built-in function 'malloc'.
Jonathan Leffler 2011 년

답변:


130

캐스트 int*는 적절한 #include반환 유형 malloc이없는 것으로 간주 된다는 사실 을 마스킹합니다 int. IA-64 sizeof(int) < sizeof(int*)는이 문제를 분명하게 만드는 일이 발생합니다.

( sizeof(int)==sizeof(int*)예를 들어 호출 규칙이 정수가 아닌 포인터를 반환하는 데 다른 레지스터를 사용하는 경우 와 같이 정의되지 않은 동작으로 인해 true를 유지 하는 플랫폼에서도 여전히 실패 할 수 있습니다. )

때 comp.lang.c 자주 묻는 질문은 토론 항목이 의 반환 캐스팅 이유를 malloc필요로하지 잠재적으로 나쁜 적이된다 .


5
적절한 #include가 없으면 왜 malloc의 반환 유형이 int로 간주됩니까?
user7 2011 년

11
@WTP-항상 newC ++에서 사용 하고 항상 C ++ 컴파일러가 아닌 C 컴파일러로 C를 컴파일 해야하는 좋은 이유 입니다.
Flexo

6
@ user7-그것이 규칙입니다. 모든 반환 유형은 알 수 int없는 경우 로 간주됩니다
Flexo

2
@vlad-더 나은 아이디어는 정확히 이런 이유로 암시 적 선언에 의존하는 것보다 항상 함수를 선언하는 것입니다. (그리고에서 반환하지 않습니다 malloc)
Flexo

16
@ user7 : "우리는 32 비트 메모리를 가리키는 p (크기 64)의 포인터를 가지고 있습니다."-잘못되었습니다. malloc에 ​​의해 할당 된 블록의 주소는에 대한 호출 규칙에 따라 반환되었습니다 void*. 그러나 호출 코드는 함수가 반환한다고 생각하므로 int(달리 알리지 않기로 선택했기 때문에) int. 따라서 반드시 할당 된 메모리를 가리키는 p것은 아닙니다 . an int과 a void*가 같은 크기이고 같은 방식으로 반환 되기 때문에 IA32에서도 작동합니다 . IA64에서는 잘못된 값을 얻습니다.
Steve Jessop 2011 년

33

and에 대한 헤더 파일을 포함하지 않았기 때문에 malloc컴파일러가 일반적으로 이에 대해 경고하지만 반환 값을 명시 적으로 캐스팅한다는 사실은 수행중인 작업을 알고 있음을 의미합니다.

즉, 컴파일러는를 int반환 malloc한 다음 포인터로 캐스트 할 것으로 예상합니다 . 크기가 다르면 슬픔이 생길 것입니다.

이것이 C 에서 반환 을 절대 캐스팅 하지 않는 이유 malloc입니다. 반환되는 void*것은 올바른 유형의 포인터로 암시 적으로 변환됩니다 (헤더를 포함하지 않은 경우 잠재적으로 안전하지 않은 정수에 대해 경고했을 것입니다. 포인터로 변환).


순진하게 들려서 미안하지만 항상 malloc이 적절한 유형으로 캐스팅 될 수있는 void 포인터를 반환한다고 가정했습니다. 저는 C 프로그래머가 아니므로 조금 더 자세히 감사하겠습니다.
user7 2011 년

5
@ user7 : #include <stdlib.h>가 없으면 C 컴파일러는 malloc의 반환 값이 int라고 가정합니다.
sashang 2011 년

4
@ user7 : void 포인터 캐스팅 수 있지만 void *암시 적으로 다른 포인터 유형으로 변환 할 수 있으므로 C에서는 필요하지 않습니다 . int *p = malloc(sizeof(int))적절한 프로토 타입이 범위 내에 있으면 작동하고 그렇지 않으면 실패합니다 (결과는라고 가정하기 때문입니다 int). 캐스트를 사용하면 둘 다 컴파일되고 후자는 sizeof(int) != sizeof(void *).

2
@ user7 그러나 포함 stdlib.h하지 않으면 컴파일러는 malloc반환 유형을 알지 못합니다 . 따라서 int기본으로 가정합니다 .
Christian Rau 2011 년

10

이것이 프로토 타입 누락에 대한 경고없이 컴파일하지 않는 이유입니다.

이것이 C에서 malloc 리턴을 결코 캐스팅하지 않는 이유입니다.

C ++ 호환성을 위해 캐스트가 필요합니다. 그것을 생략 할 이유가 거의 없습니다 (여기에 이유 없음).

C ++ 호환성이 항상 필요한 것은 아니며 일부 경우에는 전혀 불가능하지만 대부분의 경우 매우 쉽게 얻을 수 있습니다.


22
내 C 코드가 C ++와 "호환"되어 있는지 도대체 왜 신경 쓰겠습니까? 이 펄이나 자바 또는 에펠 이상과 호환 있다면 난 ... 상관 없어
스티븐 캐논

4
누군가가 당신의 C 코드를 보지 않을 것이라고 확신한다면, C ++ 컴파일러로 컴파일 할 것입니다.
Steven Lu

3
이것이 대부분의 C 코드가 간단 하게 C ++와 호환되도록 만들 수있는 이유 입니다.
curiousguy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.