2013-07-31 3 views
20

이 클레임이 발생한 경우 multipletimes이 무엇을 의미하는지 알 수 없습니다. 결과 코드는 일반 C 컴파일러를 사용하여 컴파일되므로 결국 다른 코드와 마찬가지로 (또는 거의) 유형을 검사하지 않게됩니다.C 매크로는 유형이 안전하지 않은 이유는 무엇입니까?

매크로가 왜 형식이 안전하지 않은 이유는 무엇입니까? 그것은 그들이 악으로 간주되어야하는 주된 이유 중 하나 인 것으로 보인다.

+2

요점은 C 컴파일러가 매크로 정의를 다루지 않는다는 점입니다. 전 처리기가 그것들을 처리합니다. –

+3

그래, 그렇지만 생성 된 코드가 실제로 유형 시스템을 위반한다면 컴파일러는이를 잡아낼 것입니다. – Sarien

+2

C++ FAQ는 왜 매크로가 악의적 인 이유에 대해 확실하게 다루고 있습니다. http://www.parashift.com/c++-faq-lite/inline-vs-macros.html –

답변

11

자들이 직접 형태 보증 ... 내가 특정 시나리오에서 가정하지 않는/당신은 그들이 간접적으로 (즉, 결과 코드) 할 수 있습니다 주장 입력 안전 할 수 용도. 하지만 정수를위한 매크로를 만들 수 있고 문자열을 전달할 수 있습니다 ... 매크로를 처리하는 전처리 기는 확실히 신경 쓰지 않습니다. 컴파일러는

+5

이것은 좋은 것처럼 들리지만 누군가는 세상에 이것이 어떤 식 으로든 매크로를 만들어 내지 않으면 생성 될 수 있다는 것을 지적해야합니다. 그렇지 않으면 그렇지 않으면 복제 될 수 있습니다. :) – Sarien

+3

한때 공동 작업자가 printf를 코어 헤더 파일 중 하나의 다른 것으로 정의한 프로젝트에서 한 번 작업했습니다. –

1

매크로가 실행되면 소스 파일을 통해 텍스트가 일치합니다. 이는 컴파일 전이므로 변경된 데이터 유형을 인식하지 못합니다.

+1

컴파일이 완료되기 전에 유형이 검사되는 곳에서 완료되었다고 했으므로 그 점이 저를 혼란에 빠지게합니다. – Sarien

+1

일반적인 형식 시스템을 사용하여 컴파일러를 통해 코드가 모두 컴파일된다는 것은 사실입니다. 그러나 매크로 확장 단계 자체는 데이터 형식을 고려하지 않습니다. 컴파일이 시작되기 전에 발생하기 때문에 –

+0

특히 MSDN 링크에서 함수 매크로에 대해 이야기합니다 (예 : http://stackoverflow.com/questions/163365/). make-ac-macro-behaviour-like-a-function)은 두 입력을 인식하고 함께 추가하지만 컴파일러가 수행하는 방식을주의 깊게 검사하지 않고 예를 들어 –

36

전형적인 "최대"매크로를 고려 ... 용도에 따라, 거기에 질식 할 수 있습니다, 대 기능 : 여기

#define MAX(a,b) a < b ? a : b 
int max(int a, int b) {return a < b ? a : b;} 

은 매크로 입력 - 안전하지 않는에서 말할 때 사람들이 무엇을 의미하는지의 방법은 함수는 다음 함수의 호출자가

char *foo = max("abc","def"); 

컴파일러가 경고합니다 기록

합니다.

는 반면 매크로의 호출은 쓰면 :

char *foo = MAX("abc", "def"); 

전처리와 그 대체됩니다 : 아무런 문제없이 컴파일하지만, 거의 확실하게 결과를 당신에게 제공하지 않습니다

char *foo = "abc" < "def" ? "abc" : "def"; 

원했어.

또한 부작용이 다른 물론 함수 케이스를 고려해

int x = 1, y = 2; 
int a = max(x++,y++); 
최대() 함수 이후에 적용 할 x 및 y 및 사후 증분의 원래 값 작동

함수가 반환됩니다. 매크로 경우

: 두 번째 라인이 제공하는 전처리

int x = 1, y = 2; 
int b = MAX(x++,y++); 

것을 :

int b = x++ < y++ ? x++ : y++; 

가 다시, 어떤 컴파일러 경고 또는 오류 만이 예상 행동이있을 수 없습니다.

+10

이 예에서 템플릿은 형식에 안전하지 않습니다. 템플릿 화 된'max' 함수는 정확히 같은 포인터 비교 동작을 허용합니다. 템플릿은 물론 다중 평가 문제에 빠지지 않습니다. – Joel

+2

@Joel :이 답변의 요점은 함수 유형 (템플릿)이 특정 인수 유형에 대해 오버로드되거나 특수화 될 수 있다는 것입니다. 반면 매크로는 인수 유형에 관계없이 구현이 하나만 있습니다. – willj

+2

이것은 매크로에 관한 좋은 생각이지만 질문에 대한 답변이 아닙니다. – alk

11

매크로는 형식을 이해하지 못하기 때문에 형식이 안전하지 않습니다.

매크로에만 정수를 사용할 수는 없습니다. 전 처리기는 매크로 사용을 인식하고 토큰 시퀀스 (인수가있는 매크로)를 다른 토큰 세트로 대체합니다. 이 기능은 올바르게 사용하면 강력한 기능이지만 잘못 사용하는 것은 쉽습니다.함수와

당신은 기능 void f(int, int)를 정의 할 수 있습니다 컴파일러 플래그는 F의 반환 값을 사용하거나 문자열 통과하려고한다면.

매크로가 없습니다. 유일한 수표는 올바른 수의 인수가 주어진 것입니다. 토큰을 적절하게 바꾸고 컴파일러로 전달합니다.

#define F(A, B) 

은 ... 당신이 F(1, 2), 또는 F("A", 2) 또는 F(1, (2, 3, 4)) 또는 전화를 할 수

당신은 컴파일러에서 오류를 얻을 수 있습니다, 또는 당신은 수도 있지, 매크로 내에서 어떤 유형의 일종을 필요로하는 경우 안전. 하지만 전처리 기가 아닙니다. 숫자 기대 매크로에 문자열을 전달할 때 기회가 컴파일러에서 찍찍없이 숫자로 문자열 주소를 사용하게 될 겁니다만큼

당신은, 매우 이상한 결과를 얻을 수 있습니다. 매크로 처리기에 의해 처리되며, 전처리 유형을 이해하지 않기 때문에

+0

C++ 템플릿에 대해서도 마찬가지입니다. 템플리트는 유형 안전하지 않습니다. –

4

, 그것은 행복하게 잘못된 형식의 변수를 받아 들일 것입니다.

이 보통 기능과 같은 매크로에 대해서만 관심, 그리고 어떤 종류의 오류는 종종 전처리하지 않는 경우에도 컴파일러에 의해 잡힐 것입니다,하지만이 보장되지 않습니다. 당신이 편집 컨트롤에 풍선 팁을 보여주고 싶었다 경우

윈도우 API에서, 당신은 Edit_ShowBalloonTip을 사용하십시오. Edit_ShowBalloonTip은 편집 컨트롤에 대한 핸들과 EDITBALLOONTIP 구조에 대한 포인터의 두 가지 매개 변수를 취하는 것으로 정의됩니다. 그러나 Edit_ShowBalloonTip(hwnd, peditballoontip);는 일반적으로 그들에게 메시지를 전송하여 이루어집니다 컨트롤을 구성하기 때문에

SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)(peditballoontip)); 

로 평가되는 매크로 실제로, Edit_ShowBalloonTip이 구현에서 배역을 할 수있다, 그러나 오히려 인라인 함수보다 매크로이기 때문에, 그것의 peditballoontip 매개 변수에서 어떤 타입 검사도 할 수 없습니다.

여담

는 흥미롭게도, 때때로 C++ 인라인 함수는 약간에게 있습니다 너무 형 안전합니다. 매크로 표준 C MAX

#define MAX(a, b) ((a) > (b) ? (a) : (b)) 

와 C++ 인라인 버전

template<typename T> 
inline T max(T a, T b) { return a > b ? a : b; } 

MAX (1, ​​2U) 예상대로 작동하지만 최대 (1, 2U)를 고려하지 않습니다.

이 정말 대부분의 경우 매크로를 사용하여의 인수 (그들은 악을 아직도)하지 (1 및 2U는 다른 유형이기 때문에, 최대는. 그들 모두 인스턴스화 할 수없는), 그러나 그것은이다 C 및 C++의 유형 안전성에 대한 흥미로운 결과입니다.

+0

Windows API 예제는 캐스트가 아닌 인수를 취하는 함수처럼 보이지만 실제로는 거의 모든 쓰레기를 전달할 수 있습니다. – Joel

+0

"* ... 잘못된 형식의 변수를 기꺼이 받아 들일 것입니다. *"매크로 매개 변수가 형식화되지 않았기 때문에이 문장이 의미가 없기 때문에 잘못되었거나 올바른 형식이 없습니다. – alk

+2

@alk : 요점이 아닌가요? * 확실한 것은 틀린 타입이다. 같은 의미에서 타입이없는 언어는 예기치 않은 형식의 데이터를 제공하는 함수를 가질 수있다. 유형 없음, 유형 안전 없음. 그것은 여기서의 질문에 대한 해답입니다. – Phoshi

2

매크로가 더 적은 유형의 안전 기능을보다 상황이 있습니다. 예 :

void printlog(int iter, double obj) 
{ 
    printf("%.3f at iteration %d\n", obj, iteration); 
} 

인수를 역순으로 지정하면 잘림과 잘못된 결과가 발생하지만 위험하지는 않습니다. 반대로

#define PRINTLOG(iter, obj) printf("%.3f at iteration %d\n", obj, iter) 

은 정의되지 않은 동작을 일으 킵니다. 공정하게보기 위해 GCC는 후자에 대해 경고하지만 이전에는 그렇지 않다고 경고합니다. 그러나 다른 varargs 함수에 대해서는 printf을 알고 있기 때문에 그 결과는 잠재적으로 재앙입니다.

1

매크로는 유형 안전이 보장되지 않았기 때문에 유형 안전하지 않습니다.

매크로가 확장 된 후 컴파일러에서 형식 검사를 수행합니다.

매크로 및 확장은 C 소스 코드의 "게으른"저자 (작가/독자라는 의미)의 도우미 역할을합니다. 그게 다야.

+0

C++ 템플릿에서도 마찬가지입니다. 컴파일러는 * after * 템플릿 만 확장 (인스턴스화) 된 형식 검사를 수행합니다. 따라서 매크로는 C++ 템플릿보다 덜 형식 안전합니다. 컴파일러 *는 결국 최종 생성 된 코드에서 유형을 확인하기 때문에 둘 다 간접적으로 유형에 안전합니다. 어느 쪽도 C++ 템플릿이 아닌 * 타입 *이 타입 체크 된 직접 타입 안전하지 않습니다. –

관련 문제