typedef와 #defines


32

우리 모두는 확실히 사용한 typedef들과 #define한 번에 또는 다른이야. 오늘 저는 그들과 함께 일하면서 일을 고민하기 시작했습니다.

int다른 이름으로 데이터 유형 을 사용하려면 아래 두 가지 상황을 고려하십시오 .

typedef int MYINTEGER

과

#define MYINTEGER int

위의 상황과 마찬가지로 많은 상황에서 #define을 사용하여 일을 잘 수행 할 수 있으며 typedef를 사용하여 동일한 작업을 수행 할 수 있지만 동일한 방식은 상당히 다를 수 있습니다. #define은 typedef가 할 수없는 MACRO 작업도 수행 할 수 있습니다.

그것들을 사용하는 기본 이유는 다르지만 그들의 작업은 얼마나 다른가? 둘 다 사용할 수있는 경우 어느 것이 다른 것보다 선호되어야합니까? 또한 어떤 상황에서 다른 것보다 빠를 것이 보장됩니까? (예 : #define은 전 처리기 지시문이므로 모든 것이 컴파일 또는 런타임보다 일찍 완료됩니다).


8
#define을 사용하십시오. 코드를 작성할 때 알 수없는 속성 (OS / 컴파일러 등)을 기반으로하는 조건부 컴파일. 그렇지 않으면 언어 구성을 사용하십시오.
— Martin York

답변:


67

typedef매크로가 특별히 필요한 이상한 이유가없는 한 A 가 일반적으로 선호됩니다.

매크로는 텍스트 대체를 수행하므로 코드의 의미에 상당한 폭력을 줄 수 있습니다. 예를 들면 다음과 같습니다.

#define MYINTEGER int

합법적으로 작성할 수 있습니다.

short MYINTEGER x = 42;

로 short MYINTEGER확장 되기 때문 short int입니다.

반면에 typedef를 사용하면 다음과 같습니다.

typedef int MYINTEGER:

name MYINTEGER은 키워드 "int"의 텍스트 대체가 아닌 type의 다른 이름입니다 int.

복잡한 유형 일수록 상황이 더욱 악화됩니다. 예를 들면 다음과 같습니다.

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a, b, 및 c모든 포인터가 있지만 dA는 char마지막 줄로 확장하기 때문에 :

char* c, d;

어느 것이

char *c;
char d;

(포인터 타입의 타입 정의는 일반적으로 좋은 생각은 아니지만 요점을 보여줍니다.)

또 다른 이상한 경우 :

#define DWORD long
DWORD double x;     /* Huh? */

1
다시 비난하는 포인터! :) 그러한 매크로를 사용하는 것이 현명하지 않은 포인터 이외의 다른 예는 무엇입니까?
— c0da

2
필자는 대신 매크로를 사용하지 typedef않았지만 다음과 같은 작업을 수행하는 매크로를 작성하는 올바른 방법은 인수를 사용하는 것 #define CHAR_PTR(x) char *x입니다. 이로 인해 컴파일러를 잘못 사용하면 최소한 kvetch가 발생합니다.
— Blrfl

1
잊지 마세요 :const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
— Jon Purdy

3
또한 오류 메시지를 이해할 수 없도록 정의
— Thomas Bonini

매크로를 잘못 사용하여 발생할 수있는 고통의 예 (매크로와 typedef는 직접 관련이 없지만) : github.com/Keith-S-Thompson/42
— Keith Thompson

15

지금까지 매크로의 가장 큰 문제는 범위가 지정되지 않았다는 것입니다. 이것만으로 typedef의 사용을 보증합니다. 또한 의미 적으로 더 명확합니다. 코드를 읽는 사람이 정의를 볼 때 코드를 읽고 전체 매크로를 이해할 때까지 코드의 내용을 알 수 없습니다. typedef는 형식 이름이 정의 될 것을 독자에게 알려줍니다. (C ++에 대해 이야기하고 있다고 언급해야합니다. C에 대한 typedef 범위 지정에 대해서는 확실하지 않지만 비슷합니다.)


그러나 질문에 제공된 예제에서 보았 듯이 typedef와 #define은 같은 수의 단어를 사용합니다! 또한 매크로의이 3 개의 단어는 단어가 동일하지만 재 배열되기 때문에 혼동을 일으키지 않습니다. :)
— c0da

2
예, 그러나 그것은 부족에 관한 것이 아닙니다. 매크로는 typedefing 외에도 다른 일을 할 수 있습니다. 그러나 개인적으로 나는 범위 지정이 가장 중요하다고 생각합니다.
— Tamás Szelei

2
@ c0da-typedef에는 매크로를 사용하지 말고 typedef에는 typedef를 사용해야합니다. 매크로의 실제 효과는 다양한 응답으로 표시된 것처럼 매우 다를 수 있습니다.
— Joris Timmermans

15

소스 코드는 주로 동료 개발자를 위해 작성되었습니다. 컴퓨터는 컴파일 된 버전을 사용합니다.

이 관점 typedef에서 #define그렇지 않은 의미가 있습니다.


6

Keith Thompson의 답변 은 매우 훌륭하며 범위 지정에 대한 Tamás Szelei의 추가 요점 과 함께 필요한 모든 배경을 제공해야합니다.

항상 매크로를 절망의 최후의 수단으로 생각해야합니다. 매크로로만 할 수있는 일이 있습니다. 그럼에도 불구하고, 정말로하고 싶다면 길고 열심히 생각해야합니다. 미묘하게 손상된 매크로로 인해 발생할 수있는 디버깅의 어려움은 상당하며 사전 처리 된 파일을 살펴볼 수 있습니다. C ++에서 문제의 범위에 대한 느낌을 얻으려면 한 번만 수행하는 것이 좋습니다. 전처리 된 파일의 크기 만 눈에 띄는 것일 수 있습니다.


5

#define은 이미 제공된 범위 및 텍스트 대체에 대한 모든 유효한 설명 외에도 typedef와 완전히 호환되지 않습니다! 즉, 함수 포인터의 경우에 관한 것입니다.

typedef void(*fptr_t)(void);

이것은 type fptr_t의 함수에 대한 포인터 인 type 을 선언합니다 void func(void).

이 유형은 매크로로 선언 할 수 없습니다. #define fptr_t void(*)(void)분명히 작동하지 않습니다. #define fptr_t(name) void(*name)(void)C 언어에서는 의미가 없으므로 생성자가없는 모호한 것을 작성해야합니다 .

#define으로 배열 포인터를 선언 할 수 없습니다. typedef int(*arr_ptr)[10];


C는 언급 할 가치가있는 유형 안전에 대한 언어 지원을 제공하지 않지만 typedef와 #define이 호환되지 않는 또 다른 경우는 의심스러운 유형 변환을 수행하는 경우입니다. typedef를 사용하는 경우 컴파일러 및 / 또는 정적 분석기 도구에서 이러한 변환에 대한 경고를 표시 할 수 있습니다.


1
함수와 배열 포인터의 조합이 더 좋습니다. char *(*(*foo())[])();( foo포인터를 반환하는 함수에 대한 포인터 배열에 대한 포인터를 반환하는 함수입니다 char).
— John Bode 2012

4

작업을 수행하는 데 필요한 전원이 가장 적고 경고가 가장 많은 도구를 사용하십시오. #define은 전 처리기에서 평가되며 대부분 자체적으로 있습니다. typedef는 컴파일러에 의해 평가됩니다. 이름이 확인 하듯이 검사가 이루어지고 typedef는 유형 만 정의 할 수 있습니다. 따라서 귀하의 예에서 분명히 typedef로 이동하십시오.


2

define에 대한 일반적인 논쟁 외에도 Macros를 사용하여이 함수를 어떻게 작성할 것입니까?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

이것은 명백한 예이지만, 그 함수는 더하기를 구현하는 형식의 순방향 반복 가능한 시퀀스를 합할 수 있습니다 *. 이와 같이 사용 된 Typedef는 Generic Programming 의 중요한 요소입니다 .

* 나는 이것을 여기에 썼다. 나는 그것을 독자를위한 운동으로 컴파일했다 :-)

편집하다:

이 답변은 많은 혼란을 일으키는 것처럼 보이므로 더 자세히 설명하겠습니다. STL 벡터의 정의를 살펴보면 다음과 비슷한 것을 볼 수 있습니다.

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

표준 컨테이너 내에서 typedef를 사용하면 (위에서 만든 것과 같은) 일반 함수가 이러한 유형을 참조 할 수 있습니다. 함수 "합계"는 용기 (유형에 템플 레이팅 std::vector<int>되지 않은 용기 내에 유지 된 입력 (상) int). typedef가 없으면 해당 내부 유형을 참조 할 수 없습니다.

따라서 typedef는 Modern C ++의 중심이며 매크로에서는 불가능합니다.


이 질문은 typedef매크로 대 인라인 함수가 아니라 매크로 대 에 대해 질문했습니다 .
— 벤 Voigt

물론 이것은 typedefs에서만 가능합니다 ... value_type입니다.
— Chris Pitman

이것은 typedef가 아니며 템플릿 프로그래밍입니다. 함수 나 함수는 형식에 관계없이 유형이 아닙니다.

@Lundin 자세한 내용을 추가했습니다. Sum 함수는 표준 컨테이너가 typedef를 사용하여 템플릿 형식을 노출하기 때문에 가능합니다. 나는 하지 합계가 형식 정의라고 말하고. std :: vector <T> :: value_type 이 typedef 라고 말하고 있습니다. 표준 라이브러리 구현의 소스를 확인하여 확인하십시오.
— Chris Pitman

충분합니다. 두 번째 스 니펫 만 게시하는 것이 좋을 것입니다.

1

typedefC ++ 철학에 부합 : 컴파일 시간에 가능한 모든 확인 / 어설 션. #define컴파일러에 많은 의미를 숨기는 전 처리기 트릭입니다. 코드 정확성 이상의 컴파일 성능에 대해 걱정하지 않아도됩니다.

새 유형을 만들면 프로그램 도메인이 조작하는 새로운 "사물"을 정의하게됩니다. 따라서이 "사물"을 사용하여 함수와 클래스를 구성 할 수 있으며 컴파일러를 사용하여 정적 검사를 수행 할 수 있습니다. 어쨌든 C ++은 C와 호환 int되므로 경고를 생성하지 않는 int 기반 유형 간에 많은 암시 적 변환이 있습니다. 따라서이 경우 정적 검사 기능을 최대한 활용할 수 없습니다. 그러나, 당신은 당신을 대체 할 수 typedef로 enum암시 적 변환을 찾을 수 있습니다. 예 : 당신이 한 경우 typedef int Age;, 다음으로 대체 할 수있는 enum Age { };당신이 사이의 암시 적 변환에 의해 오류의 모든 종류를 얻을 것이다 Age하고 int.

또 다른 것은 : typedef안에있을 수 있습니다 namespace.


1

타입 정의 대신에 정의를 사용하는 것은 또 다른 중요한 측면, 즉 유형 특성의 개념에서 문제가됩니다. 특정 유형 정의를 정의하는 다양한 클래스 (표준 컨테이너 생각)를 고려하십시오. typedef를 참조하여 일반 코드를 작성할 수 있습니다. 예를 들면 다음과 같은 일반적인 컨테이너 요구 사항 (c ++ 표준 23.2.1 [container.requirements.general])이 있습니다.

X::value_type
X::reference
X::difference_type
X::size_type

이 모든 것은 범위가 없기 때문에 매크로를 사용하여 일반적인 방식으로 표현할 수 없습니다.


1

디버거에서 이것의 의미를 잊지 마십시오. 일부 디버거는 #define을 잘 처리하지 못합니다. 사용하는 디버거에서 모두 놀아보십시오. 그것을 쓰는 것보다 그것을 읽는 데 더 많은 시간을 소비한다는 것을 기억하십시오.


-2

"#define"은 작성한 내용을 대체하고 typedef는 유형을 생성합니다. 따라서 사용자 정의 유형을 원하면 typedef를 사용하십시오. 매크로가 필요한 경우 define을 사용하십시오.


나는 투표를 거부하고 의견을 쓰지 않는 사람들을 좋아합니다.
— 이니 우스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.