[c] 특히 malloc의 결과를 캐스팅하는 것이 위험한 것은 무엇입니까?

이제 사람들이 이것을 복제로 표시하기 전에 다음을 모두 읽었으며 그중 어느 것도 내가 찾고있는 답을 제공하지 않습니다.

  1. C FAQ : malloc의 반환 값을 캐스팅하는 데 어떤 문제가 있습니까?
  2. SO : malloc ()의 반환 값을 명시 적으로 캐스팅해야합니까?
  3. SO : C의 불필요한 포인터 캐스트
  4. SO : malloc의 결과를 캐스팅합니까?

C FAQ와 위의 질문에 대한 많은 답변은 캐스팅 malloc의 반환 값이 숨길 수 있다는 신비한 오류를 인용합니다 . 그러나 그들 중 어느 것도 실제로 그러한 오류의 구체적인 예를 제공하지 않습니다. 이제 경고 아니라 오류 라고 말한 것에주의하십시오 .

이제 다음 코드가 제공됩니다.

#include <string.h>
#include <stdio.h>
// #include <stdlib.h>

int main(int argc, char** argv) {

    char * p = /*(char*)*/malloc(10);
    strcpy(p, "hello");
    printf("%s\n", p);

    return 0;
}

캐스트를 사용하거나 사용하지 않고 gcc 4.2로 위 코드를 컴파일하면 동일한 경고가 표시되고 프로그램이 올바르게 실행되고 두 경우 모두 동일한 결과를 제공합니다.

anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc
hello

그렇다면 누구든지 캐스트 malloc의 반환 값으로 인해 발생할 수있는 컴파일 또는 런타임 오류의 특정 코드 예제를 제공 할 수 있습니까? 아니면 이것은 도시의 전설일까요?

편집 나는이 문제에 대해 잘 쓰여진 두 가지 주장을 발견했습니다.

  1. 캐스팅에 찬성 : CERT 권고 : 메모리 할당 함수 호출의 결과를 할당 된 유형에 대한 포인터로 즉시 캐스팅합니다.
  2. Casting에 대해 (2012-02-14 기준 404 오류 : 2010-01-27 의 Internet Archive Wayback Machine 사본을 사용합니다 . {2016-03-18 : “robots.txt로 인해 페이지를 크롤링하거나 표시 할 수 없습니다.”})



답변

컴파일러 오류가 발생 하지 않지만 컴파일러 경고가 발생 합니다. 인용 한 소스 (특히 첫 번째 소스 )에서 . 를 포함하지 않고 캐스트 사용할 때 예측할 수없는 런타임 오류가 발생할 수 있습니다 .stdlib.h

따라서 당신 측의 오류는 캐스트가 아니라 stdlib.h. 컴파일러는 가정 할 수 malloc함수가 반환 int따라서 변환 void*사실에 의해 반환 된 포인터 mallocint다음 포인터 타입에 의한 명시 적 캐스트에 있습니다. 일부 플랫폼에서는 int포인터가 다른 바이트 수를 차지할 수 있으므로 유형 변환으로 인해 데이터가 손상 될 수 있습니다.

다행히 최신 컴파일러는 실제 오류를 가리키는 경고를 제공합니다. gcc제공 한 출력을 참조하십시오. 암시 적 선언 ( int malloc(int))이 내장 malloc. 그래서 없이도 gcc알고있는 것 같습니다 .mallocstdlib.h

이 오류를 방지하기 위해 캐스트를 남기는 것은 대부분 글을 쓰는 것과 같은 이유입니다.

if (0 == my_var)

대신에

if (my_var == 0)

후자는 =및 을 혼동하면 심각한 버그로 이어질 수 ==있지만 첫 번째는 컴파일 오류로 이어질 수 있기 때문입니다. 저는 개인적으로 후자의 스타일이 제 의도를 더 잘 반영하고이 실수를하지 않기 때문에 선호합니다.

에서 반환 된 값을 캐스팅 할 때도 마찬가지입니다 malloc. 저는 프로그래밍에서 명시적인 것을 선호하며 일반적으로 사용하는 모든 함수에 대한 헤더 파일을 포함하도록 두 번 확인합니다.


답변

결과를 캐스팅하는 것에 대한 좋은 상위 수준의 주장 중 하나 malloc는 종종 언급되지 않은 채로 남아 있지만, 제 생각에는 잘 알려진 하위 수준 문제보다 더 중요합니다 (선언이 누락되었을 때 포인터를 자르는 것과 같은).

좋은 프로그래밍 방법은 가능한 한 유형과 무관 한 코드를 작성하는 것입니다. 이는 특히 코드에서 유형 이름을 가능한 한 적게 언급하거나 전혀 언급하지 않는 것이 가장 좋다는 것을 의미합니다. 이는 캐스트 (불필요한 캐스트 방지), 인수로서의 유형 sizeof(에서 유형 이름 사용 방지 sizeof) 및 일반적으로 유형 이름에 대한 다른 모든 참조에 적용됩니다.

유형 이름은 선언에 속합니다. 가능한 한 형식 이름은 선언과 선언으로 만 제한되어야합니다.

이 관점에서이 코드는 나쁘다

int *p;
...
p = (int*) malloc(n * sizeof(int));

그리고 이것은 훨씬 낫다

int *p;
...
p = malloc(n * sizeof *p);

단순히 “결과를 캐스트하지 않기 때문”이 malloc아니라 유형 독립적 (또는 선호하는 경우 유형에 무관)이기 때문 p입니다. 사용자.


답변

프로토 타입이 아닌 함수는를 반환하는 것으로 간주됩니다 int.

따라서를 int포인터로 캐스팅하고 있습니다. 포인터가 int플랫폼에서 s 보다 넓은 경우 이는 매우 위험한 동작입니다.

게다가, 물론, 어떤 사람들은 경고를 고려하는 것이 할 오류, 즉 코드는 그들없이 컴파일해야한다.

개인적 void *으로 다른 포인터 유형 으로 캐스트 할 필요가 없다는 사실이 C의 기능이라고 생각하며 깨질 코드를 고려합니다.


답변

64 비트 모드에서 컴파일 할 때 이렇게하면 반환 된 포인터가 32 비트로 잘립니다.

편집 : 너무 짧아서 죄송합니다. 다음은 토론을위한 예제 코드입니다.

본관()
{
   char * c = (char *) malloc (2);
   printf ( "% p", c);
}

반환 된 힙 포인터가 0xAB00000000과 같이 int에서 표현할 수있는 것보다 크다고 가정합니다.

malloc이 포인터를 반환하도록 프로토 타입이 아닌 경우 반환 된 int 값은 초기에 모든 중요한 비트가 설정된 일부 레지스터에 있습니다. 이제 컴파일러는 “좋아, 어떻게 포인터로 int를 변환합니까”라고 말합니다. 이는 프로토 타입을 생략하여 malloc이 “반환”한다고 말한 하위 32 비트의 부호 확장 또는 0 확장이 될 것입니다. int가 서명되었으므로 변환은 부호 확장이 될 것이라고 생각합니다.이 경우 값을 0으로 변환합니다. 0xABF0000000의 반환 값을 사용하면 역 참조를 시도 할 때 재미를 유발하는 0이 아닌 포인터를 얻게됩니다.


답변

재사용 가능한 소프트웨어 규칙 :

malloc ()을 사용하는 인라인 함수를 작성하는 경우 C ++ 코드에서도 재사용 할 수 있도록 명시 적 유형 캐스팅 (예 : (char *))을 수행하십시오. 그렇지 않으면 컴파일러가 불평합니다.


답변

C의 void 포인터는 명시 적 캐스트없이 모든 포인터에 할당 될 수 있습니다. 컴파일러는 경고를 제공하지만 해당 유형 으로 유형 캐스팅 하여 C ++에서 재사용 할 수 있습니다 malloc(). C는 엄격한 유형 검사가 아니기 때문에 out 유형 캐스팅을 사용하면 C 에서도 사용할 수 있습니다 . 그러나 C ++는 엄격하게 유형 검사 이므로 C ++에서 유형 캐스트가 필요합니다 .malloc()


답변