C ++에서`void *`가 암시 적으로 캐스트되지 않는 이유는 무엇입니까?


30

C에서는 void *다른 포인터 유형으로 a 를 캐스트 할 필요가 없으며 항상 안전하게 승격됩니다. 그러나 C ++에서는 그렇지 않습니다. 예 :

int *a = malloc(sizeof(int));

C에서는 작동하지만 C ++에서는 작동하지 않습니다. (참고 : mallocC ++ 또는 그 문제 new를 사용해서는 안되며 대신 스마트 포인터 및 / 또는 STL을 선호해야한다는 것을 알고 있습니다. 이것은 순전히 호기심에서 비롯됩니다.) C ++ 표준 이이 암시 적 캐스트를 허용하지 않는 이유는 무엇입니까? C 표준이하는 동안?


3
long *a = malloc(sizeof(int));죄송합니다. 누군가 한 가지 유형 만 변경하는 것을 잊었습니다!
Doval

4
@ Doval : sizeof(*a)대신 여전히 사용하여 쉽게 고칠 수 있습니다 .
wolfPack88

3
@ratchetfreak의 요점은 C 가이 암시 적 변환을 수행하는 이유는 malloc할당 된 유형에 대한 포인터를 반환 할 수 없기 때문이라고 생각합니다. newC ++은 할당 된 유형에 대한 포인터를 반환하므로 올바르게 작성된 C ++ 코드는 void *캐스트 하지 않습니다 .
로봇

3
제쳐두고, 그것은 다른 포인터 유형이 아닙니다. 데이터 포인터 만 적용하면됩니다.
중복 제거기

3
@ wolfPack88 당신은 역사가 잘못되었습니다. C ++에는 voidC가 없었습니다 . 해당 키워드 / 아이디어는 C에 추가되었을 때, 그들은 C의 필요에 맞게 변경되었습니다. 포인터 유형이 전혀 확인되지 않은 직후였습니다 . 온라인으로 K & R C 설명 팜플렛 또는 Waite Group의 C Primer 와 같은 C 프로그래밍 텍스트의 빈티지 사본을 찾을 수 있는지 확인하십시오 . ANSI C는 C ++에서 백 포트되거나 영감을 얻은 기능으로 가득 차 있었고 K & R C는 훨씬 간단했습니다. 따라서 C ++은 당시 존재했던대로 C를 확장하고 알고있는 C가 C ++에서 제거 된 것이 더 정확합니다.
JDługosz

답변:


39

암시 적 유형 변환은 일반적으로 안전하지 않기 때문에 C ++는 C보다 입력에 더 안전한 자세를 취합니다.

대부분의 경우 변환에 오류가있는 경우에도 C는 일반적으로 암시 적 변환을 허용합니다. 왜냐하면 C는 프로그래머가 자신이하는 일을 정확히 알고 있다고 가정하기 때문입니다. 그렇지 않은 경우 컴파일러의 문제가 아니라 프로그래머의 문제입니다.

C ++은 일반적으로 오류가 될 수있는 것을 허용하지 않으며 타입 캐스트로 의도를 명시 적으로 명시해야합니다. C ++은 프로그래머 친화적이려고하기 때문입니다.

실제로 더 많이 입력해야 할 때 얼마나 친숙한 지 묻습니다.

글쎄, 당신은 어떤 프로그램, 어떤 프로그래밍 언어로든 주어진 코드 줄은 일반적으로 쓰여질 것보다 많은 시간을 읽을 것입니다 (*). 따라서 읽기 쉬움은 쓰기 쉬움보다 훨씬 중요합니다. 그리고 읽을 때 잠재적으로 안전하지 않은 변환이 명시 적 유형 캐스트를 통해 눈에 띄면 진행 상황을 이해하고 진행 상황이 실제로 무슨 일이 일어나고 있는지 확실하게 알 수 있습니다.

또한, 명시 적 캐스트를 입력해야하는 불편 함은 경고를 받았지만 결코 본 적이없는 잘못된 할당으로 인해 발생하는 버그를 찾기 위해 몇 시간의 문제 해결 시간의 불편함에 비해 사소한 것입니다.

(*) 이상적으로는 한 번만 작성되지만 재사용에 대한 적합성을 결정하기 위해 누군가가 그것을 검토해야 할 때마다, 그리고 문제 해결이 진행될 때마다 그리고 누군가가 근처에 코드를 추가해야 할 때마다 읽습니다. 그런 다음 근처 코드에 대한 문제 해결이 발생할 때마다. 이것은 "한 번만 쓰고 실행 한 다음 폐기"스크립트를 제외한 모든 경우에 해당되므로 대부분의 스크립팅 언어는 읽기 쉬운 것을 완전히 무시하고 쉽게 작성하는 데 도움이되는 구문을 가지고 있습니다. 펄이 완전히 이해할 수 없다고 생각한 적이 있습니까? 당신은 혼자가 아닙니다. 이러한 언어를 "쓰기 전용"언어로 생각하십시오.


C는 본질적으로 머신 코드보다 한 단계 위입니다. 나는 이런 것들을 위해 C를 용서할 수있다.
Qix

8
프로그램 (언어가 무엇이든)을 읽어야합니다. 명백한 작업이 눈에.니다.
Matthieu M.

10
그것의 가치를 통해 캐스트가 있다는 지적 void*이다 때문에 특정 OOP 기능을 C에서 구현되는 방식으로, C ++에서 안전하지 않은 ++, 같은 개체에 대한 포인터 포인터 유형에 따라 다른 값을 가질 수 있습니다.
hyde

@MatthieuM. 매우 사실입니다. 추가해 주셔서 감사합니다. 답변의 일부가 될 가치가 있습니다.
Mike Nakis

1
@MatthieuM .: 아, 그러나 당신은 정말로 모든 것을 명시 적으로 할 필요는 없습니다. 읽을 거리가 더있어 가독성이 향상되지 않습니다. 이 시점에서 균형은 분명하게 드러나기위한 것이지만
중복 제거기

28

Stroustrup이 말한 내용은 다음과 같습니다. .

C에서는 void *를 T *로 암시 적으로 변환 할 수 있습니다. 이것은 안전하지 않다

그런 다음 void *가 어떻게 위험 할 수 있는지에 대한 예를 보여주고 다음과 같이 말합니다.

... 결과적으로 C ++에서 void *에서 T *를 얻으려면 명시 적 캐스트가 필요합니다. ...

마지막으로 그는 다음과 같이 지적합니다.

C에서이 안전하지 않은 변환의 가장 일반적인 용도 중 하나는 malloc ()의 결과를 적절한 포인터에 할당하는 것입니다. 예를 들면 다음과 같습니다.

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

C ++에서 typesafe new 연산자를 사용하십시오.

int * p = 새로운 int;

그는 C ++의 디자인과 진화 에서 이것에 대해 더 자세히 설명합니다 .

따라서 언어 디자이너는 이것이 안전하지 않은 패턴이라고 생각하여 불법적으로 만들었고 패턴이 일반적으로 사용되는 방식을 대체하는 다른 방법을 제공했습니다.


1
malloc예제 와 관련하여 malloc생성자를 호출하지 않을 것이므로 메모리를 할당하는 클래스 유형 인 경우 암시 적으로 클래스 유형으로 캐스트 할 수있는 것은 잘못된 것입니다.
chbaker0

이것은 아마도 내 것보다 훨씬 좋은 답변입니다. 나는 "권한의 주장"접근 방식을 약간 싫어한다.
Mike Nakis

"new int"-와우, C ++ 초보자로서, 기본 유형을 힙에 추가하는 방법을 생각하는 데 어려움을 겪고 있었으며, 그렇게 할 수 있다는 것도 몰랐습니다. malloc에 ​​-1을 사용합니다.
Katana314

3
@MikeNakisFWIW, 내 의도는 귀하의 답변을 보완하는 것이 었습니다. 이 경우 질문은 "그들이 왜 그렇게했는지"였습니다. 그래서 수석 디자이너의 의견이 정당하다고 생각했습니다.
로봇

11

C에서는 다른 포인터 유형으로 void *를 캐스트 할 필요가 없으며 항상 안전하게 승격됩니다.

항상 홍보되지만, 거의 안전 하지는 않습니다. .

C ++는 C보다 안전한 유형 시스템을 사용하려고하기 때문에이 동작을 정확하게 비활성화합니다 . 이 동작은 안전 하지 않습니다 .


일반적으로 유형 변환에 대한 다음 3 가지 접근 방식을 고려하십시오.

  1. 사용자가 모든 것을 쓰도록 강요하므로 모든 전환이 명시 적입니다.
  2. 사용자가 자신이하는 일을 알고 있다고 가정하고 모든 유형을 다른 유형으로 변환
  3. 형식이 안전한 제네릭 (템플릿, 새로운 식), 명시적인 사용자 정의 변환 연산자를 사용하여 컴파일러가 완벽하게 볼 수없는 것만 명시 적으로 변환하도록하는 완전한 형식 시스템을 구현합니다.

글쎄, 1은 못 생겼고 어떤 일을하는 데 실질적인 장애물이지만, 큰주의가 필요한 곳에 진정으로 사용될 수 있습니다. C는 구현하기가 더 쉬운 2와 3을 구현하기는 어렵지만 더 안전한 3을 선택했습니다.


나중에 예외를 추가하고 템플릿을 나중에 추가하여 3에 도달하는 것은 오랜 시간이 걸렸습니다.
JDługosz

음, 템플릿이되지 않습니다 정말 그런 완전한 안전 형 시스템에 필요한 : 힌들리 - 밀너 기반 언어는 암시 전혀 변환 및 명시 적으로 두 유형을 쓸 필요없이, 아주 잘 그들없이 지낼. (물론,이 언어들은 유형 삭제 / 가비지 수집 / 고종 다형성, C ++이 피하는 것들에 의존합니다.)
leftaroundabout

1

정의상, void 포인터는 무엇이든 가리킬 수 있습니다. 모든 포인터를 빈 포인터로 변환 할 수 있으므로 동일한 값에 도달하면 다시 변환 할 수 있습니다. 그러나 다른 유형에 대한 포인터에는 정렬 제한과 같은 제약 조건이있을 수 있습니다. 예를 들어, 문자가 메모리 주소를 점유 할 수 있지만 주소 경계에서 정수를 시작해야하는 아키텍처를 상상해보십시오. 특정 아키텍처에서 정수 포인터는 한 번에 16, 32 또는 64 비트를 계산하여 char *가 실제로 메모리의 동일한 위치를 가리키는 동안 int *의 배수를 가질 수 있습니다. 이 경우, void *에서 변환하면 실제로 복구 할 수없는 비트를 반올림하므로 되돌릴 수 없습니다.

간단히 말해서, void 포인터는 다른 포인터가 가리킬 수없는 것을 포함하여 무엇이든 가리킬 수 있습니다. 따라서 void 포인터로의 변환은 안전하지만 다른 방법은 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.