문제

이 스레드에 대한 내 답변에 대한 의견이 있습니다.

함수 호출 내부의 Malloc이 반환 시 해제되는 것처럼 보입니까?

간단히 말해서 다음과 같은 코드가 있었습니다.

int * somefunc (void)
{
  int * temp = (int*) malloc (sizeof (int));
  temp[0] = 0;
  return temp;
}

이런 댓글을 받았습니다:

Malloc의 반환 가치를 캐스트하지 마십시오.필요하지 않으며 오류를 숨길 수 있습니다.

C에서는 캐스트가 필요하지 않다는 데 동의합니다.C++에서는 필수이므로 언젠가 C++로 코드를 이식해야 할 경우를 대비해 보통 추가합니다.

그러나 이와 같은 캐스트가 어떻게 오류를 숨길 수 있는지 궁금합니다.어떤 아이디어가 있나요?

편집하다:

양쪽 모두 매우 훌륭하고 타당한 주장이 있는 것 같습니다.게시해 주셔서 감사합니다.

도움이 되었습니까?

해결책

댓글을 남겼으니 답변을 게시하는 것이 맞는 것 같습니다 :P

기본적으로 포함하는 것을 잊어버린 경우 stdlib.h 컴파일러는 가정합니다 malloc 반환합니다 int.전송하지 않으면 경고가 표시됩니다.캐스팅을 사용하면 그렇지 않습니다.

따라서 캐스팅해도 아무 것도 얻지 못하고 합법적인 경고가 표시되지 않을 위험이 있습니다.

이에 대해 많은 글이 쓰여져 있으며, 빠른 구글 검색을 통해 더 자세한 설명을 찾아볼 수 있습니다.

편집하다

다음과 같이 주장되었습니다.

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));

실수로 충분한 메모리를 할당하지 않았을 때 명백해집니다. p ~였다 TYPe ~ 아니다 TYPE, 따라서 이 방법의 장점은 실수로 컴파일러 경고를 억제하는 데 드는 비용이 더 적기 때문에 malloc을 캐스팅해야 합니다.

나는 2가지를 지적하고 싶다:

  1. 너는 써야 해 p = malloc(sizeof(*p)*n); 항상 적절한 양의 공간을 malloc하도록 보장합니다.
  2. 위의 접근 방식을 사용하면 유형을 변경하는 경우 3곳을 변경해야 합니다. p:선언에서 한 번, 선언에서 한 번 malloc, 그리고 출연진에 한 번.

간단히 말해서, 나는 여전히 개인적으로 반환 값을 캐스팅할 필요가 없다고 믿습니다. malloc 확실히 모범 사례는 아닙니다.

다른 팁

이 질문은 C와 C++ 모두에 대해 태그가 지정되어 있으므로 IMHO라는 답변이 두 개 이상 있습니다.

에헴...너가 원하는 것을해라.

"stdlib"를 포함하지 않으면 경고가 표시되지 않습니다"라는 위에 주어진 이유는 헤더를 포함하는 것을 잊지 않기 위해 이러한 종류의 해킹에 의존해서는 안 되기 때문에 유효한 이유가 아니라고 생각합니다.

당신을 만들 수 있는 진짜 이유 ~ 아니다 캐스트 작성은 C 컴파일러가 이미 자동으로 캐스트를 캐스트했다는 것입니다. void * 원하는 포인터 유형으로 변환하므로 직접 수행하는 것은 과도하고 쓸모가 없습니다.

유형 안전성을 확보하려면 C++로 전환하거나 다음과 같이 자체 래퍼 함수를 ​​작성할 수 있습니다.

int * malloc_Int(size_t p_iSize) /* number of ints wanted */
{
   return malloc(sizeof(int) * p_iSize) ;
}

C++

때로는 C++에서도 malloc/realloc/free 유틸리티를 활용하여 이익을 얻어야 하는 경우가 있습니다.그런 다음 캐스팅해야합니다.하지만 당신은 이미 그것을 알고 있었습니다.언제나 그렇듯이 C 스타일 캐스트보다 static_cast<>()를 사용하는 것이 더 좋습니다.

그리고 C에서는 유형 안전성을 달성하기 위해 템플릿을 통해 malloc(및 realloc 등)을 재정의할 수 있습니다.

template <typename T>
T * myMalloc(const size_t p_iSize)
{
 return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ;
}

다음과 같이 사용됩니다.

int * p = myMalloc<int>(25) ;
free(p) ;

MyStruct * p2 = myMalloc<MyStruct>(12) ;
free(p2) ;

그리고 다음 코드:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization
short * p = myMalloc<int>(25) ;
free(p) ;

컴파일되지 않으므로 문제 없어.

전체적으로, 순수 C++에서는 누군가가 코드 내에서 두 개 이상의 C malloc을 발견하더라도 변명의 여지가 없습니다.:-)

C + C++ 크로스오버

때로는 C와 C++ 모두에서 컴파일되는 코드를 생성하고 싶을 때가 있습니다(이유가 무엇이든...C++의 핵심은 아닌가요? extern "C" {} 차단하다?).이 경우 C++에서는 형변환을 요구하지만 C는 static_cast 키워드를 이해하지 못하므로 해결책은 C 스타일 형변환입니다(이런 종류의 이유로 C++에서는 여전히 적법합니다).

순수 C 코드를 작성하더라도 C++ 컴파일러로 컴파일하면 훨씬 더 많은 경고와 오류가 발생합니다. 예를 들어 함수를 먼저 선언하지 않고 사용하려고 하면 위에서 언급한 오류와 달리 컴파일되지 않습니다.

따라서 안전을 유지하려면 C++에서 깔끔하게 컴파일되는 코드를 작성하고 경고를 연구 및 수정한 다음 C 컴파일러를 사용하여 최종 바이너리를 생성하십시오.이는 다시 C 스타일 캐스트로 캐스트를 작성한다는 의미입니다.

발생할 수 있는 오류 중 하나는 C(C++ 아님)를 사용하여 64비트 시스템에서 컴파일하는 경우입니다.

기본적으로 포함하는 것을 잊어버린 경우 stdlib.h, 기본 int 규칙이 적용됩니다.따라서 컴파일러는 다음과 같이 행복하게 가정할 것입니다. malloc 의 프로토타입을 가지고 있다 int malloc(); 많은 64비트 시스템에서 int는 32비트이고 포인터는 64비트입니다.

아, 값이 잘리고 포인터의 하위 32비트만 얻을 수 있습니다!이제 반환 값을 캐스팅하면 malloc, 이 오류는 캐스트에 의해 숨겨집니다.그러나 그렇지 않으면 오류가 발생합니다("int를 T *로 변환할 수 없습니다"와 같은 특성).

물론 이는 두 가지 이유로 C++에는 적용되지 않습니다.첫째, 기본 int 규칙이 없으며 둘째, 캐스트가 필요합니다.

어쨌든 C++ 코드에서는 새로운 기능을 사용해야 합니다. :-P.

글쎄, 나는 그것이 정반대라고 생각합니다. 항상 필요한 유형에 직접 캐스팅하십시오. 여기를 읽어보세요!

"forgot stdlib.h" 인수는 허수아비입니다.최신 컴파일러는 문제를 감지하고 경고합니다(gcc -Wall).

항상 malloc 결과를 즉시 캐스팅해야 합니다.그렇게 하지 않으면 오류로 간주되어야 하며 단지 C++에서 실패하기 때문만은 아닙니다.예를 들어, 다양한 종류의 포인터를 사용하는 머신 아키텍처를 대상으로 하는 경우 캐스트를 넣지 않으면 매우 까다로운 버그가 발생할 수 있습니다.

편집하다:논평자 에반 테란 맞다.내 실수는 컴파일러가 어떤 상황에서든 void 포인터에 대해 작업을 수행할 필요가 없다고 생각한 것입니다.FAR 포인터 버그를 생각하면 겁이 나기 때문에 모든 것을 캐스팅해야 한다는 직감이 듭니다.고마워요 에반!

실제로 캐스트가 오류를 숨길 수 있는 유일한 방법은 한 데이터 유형에서 더 작은 데이터 유형으로 변환하여 데이터가 손실되거나 배를 사과로 변환하는 경우입니다.다음 예를 들어보세요.

int int_array[10];
/* initialize array */
int *p = &(int_array[3]);
short *sp = (short *)p;
short my_val = *sp;

이 경우 short로 변환하면 int에서 일부 데이터가 삭제됩니다.그리고 이 경우는 다음과 같습니다.

struct {
    /* something */
} my_struct[100];

int my_int_array[100];
/* initialize array */
struct my_struct *p = &(my_int_array[99]);

그러면 결국 잘못된 종류의 데이터를 가리키게 되거나 심지어 유효하지 않은 메모리를 가리키게 될 것입니다.

그러나 일반적으로 자신이 무엇을 하고 있는지 알고 있다면 캐스팅을 해도 괜찮습니다.더욱이 malloc에서 메모리를 가져올 때 캐스트하지 않으면 전혀 사용할 수 없는 void 포인터를 반환하고 대부분의 컴파일러는 lvalue(에 대한 값)로 캐스팅하는 경우 경고합니다. 과제 왼쪽)은 어차피 맡을 수 없습니다.

#if CPLUSPLUS
#define MALLOC_CAST(T) (T)
#else
#define MALLOC_CAST(T)
#endif
...
int * p;
p = MALLOC_CAST(int *) malloc(sizeof(int) * n);

또는 교대로

#if CPLUSPLUS
#define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N))
#else
#define MYMALLOC(T, N) malloc(sizeof(T) * N)
#endif
...
int * p;
p = MYMALLOC(int, n);

사람들은 이미 내가 주로 트로트를 하는 이유를 언급했습니다.포함하지 않는 것에 대한 오래된 (대부분의 컴파일러에는 더 이상 적용되지 않음) 주장 stdlib.h 그리고 사용 sizeof *p 나중에 업데이트하더라도 유형과 크기가 항상 일치하는지 확인합니다.나는 캐스팅에 반대하는 또 다른 주장을 지적하고 싶습니다.작은 내용이지만 적용할 수 있을 것 같아요.

C는 상당히 약한 유형입니다.대부분의 안전한 유형 변환은 자동으로 발생하며 대부분의 안전하지 않은 유형 변환에는 캐스트가 필요합니다.고려하다:

int from_f(float f)
{
    return *(int *)&f;
}

위험한 코드입니다.이는 기술적으로 정의되지 않은 동작이지만 실제로는 실행하는 거의 모든 플랫폼에서 동일한 작업을 수행합니다.그리고 출연진이 당신에게 말하는데 도움을 줍니다 "이 코드는 끔찍한 해킹입니다."

고려하다:

int *p = (int *)malloc(sizeof(int) * 10);

캐스트를 보고 '이게 왜 필요한가?해킹은 어디 있지?" 뭔가 사악한 일이 벌어지고 있다는 생각에 목이 뻣뻣해지지만 실제로는 코드가 전혀 무해합니다.

우리가 C를 사용하는 한, 캐스트 (특히 포인터 캐스트)는 "여기서 악의적이고 쉽게 깨질 수있는 것이 있습니다."라고 말하는 방법입니다. 그들은 당신이 달성해야 할 것을 성취 할 수 있지만, 당신과 미래의 유지 관리자에게 아이들이 괜찮지 않다는 것을 나타냅니다.

모든 경우에 캐스트 사용 malloc 포인터 캐스팅의 "해킹" 표시가 줄어듭니다.다음과 같은 것을 보는 것이 덜 거슬립니다. *(int *)&f;.

메모:C와 C++은 다른 언어입니다.C는 약한 형식이고 C++는 더 강력한 형식입니다.출연진 ~이다 (내 생각으로는) 불필요하게 강력한 C++ 유형 시스템 때문에 전혀 해킹을 나타내지 않더라도 C++에서는 필요합니다.(실제로 C++ 유형 시스템이 "너무 강하다"고 생각하는 유일한 경우는 이 특별한 경우이지만 "너무 약하다"고 생각되는 곳은 없으며 전체적으로 내 취향에 비해 너무 강합니다.)

C++ 호환성이 걱정된다면 걱정하지 마세요.C를 작성하는 경우 C 컴파일러를 사용하십시오.모든 플랫폼에 사용할 수 있는 정말 좋은 것들이 많이 있습니다.만일 어떤 말도 안되는 이유로 당신이 가지다 C++로 깔끔하게 컴파일되는 C 코드를 작성하는 것은 실제로 C를 작성하는 것이 아닙니다.C를 C++로 포팅해야 하는 경우 C 코드를 보다 관용적인 C++로 만들기 위해 많은 변경을 수행해야 합니다.

그 중 아무것도 할 수 없다면, 무엇을 하든 코드가 예쁘지 않을 것이므로 그 시점에서 어떻게 캐스팅하기로 결정했는지는 별로 중요하지 않습니다.나는 올바른 유형을 반환하는 새로운 할당자를 만들기 위해 템플릿을 사용하는 아이디어를 좋아합니다. new 예어.

(void *)를 반환하는 함수를 (int *)로 변환하는 것은 무해합니다.한 유형의 포인터를 다른 유형으로 캐스팅하고 있습니다.

대신에 정수를 반환하는 함수를 포인터로 캐스팅하는 것은 올바르지 않을 가능성이 높습니다.명시적으로 캐스팅하지 않았다면 컴파일러에서 플래그를 지정했을 것입니다.

한 가지 가능한 오류는 (실제로 원하는지 여부에 따라 다름) 하나의 크기 척도를 사용하여 다른 유형의 포인터에 할당하는 것일 수 있습니다.예:

int *temp = (int *)malloc(sizeof(double));

이렇게 하고 싶은 경우가 있을 수 있지만 그런 경우는 거의 없을 것 같습니다.

캐스트를 넣어야 할 것 같아요.유형에는 세 가지 위치가 있습니다.

T1 *p;
p = (T2*) malloc(sizeof(T3));

두 줄의 코드는 크게 분리될 수 있습니다.그러므로 컴파일러가 그렇게 하는 것이 좋습니다. 억지로 시키다 T1 == T2입니다.T2 == T3임을 시각적으로 확인하는 것이 더 쉽습니다.

T2 캐스트를 놓치면 T1 == T3이기를 바랍니다.

반면에 stdlib.h 인수가 누락되었지만 문제가 될 가능성은 적다고 생각합니다.

반면에 코드를 C++로 이식해야 하는 경우 'new' 연산자를 사용하는 것이 훨씬 더 좋습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top