c언어 개념 정리와 문제 풀이를 통해 알고리즘 수업을 준비하는 과정입니다. 그동안 수강했던 c언어 수업 자료와 윤성우의 열혈 C 프로그래밍의 내용을 포함해 저에게 기록할 가치가 있는 내용이 담겨 있습니다.
소스파일 -> 실행파일
소스파일에서 실행파일이 생성되는 과정은 다음과 같다.
- 최초의 소스파일 (.c)
- 선행처리기 (선행처리)
- 선행처리 거친 소스파일
- 컴파일러 (컴파일)
- 오브젝트 파일 (.o)
- 링커 (링크)
- 실행파일 (.exe)
선행처리
컴파일 이전에 '선행처리'의 과정을 거친다. 선행처리는 컴파일 이전의 처리를 말한다.
컴파일 과정을 거치게 되면 바이너리 데이터로 이루어진 오브젝트 파일이 생성되는데,
컴파일 이전에 진행되는 선행처리의 과정을 거치게 되면 어떠한 데이터로 채워진 파일이 생성될까?
선행처리의 과정을 거쳐서 생성되는 파일도 그냥 소스파일이다.
소스파일의 형태가 그대로 유지되기 때문이다. 선행처리기가 하는 일은 매우 단순하다.
프로그래머가 삽입해 놓은 선행처리 명령문대로 소스코드의 일부를 수정할 뿐인데,
여기서 말한 '수정'은 '단순 치환'의 경우가 대부분이다.
#define PI 3.14 와 같은 명령문은 선행처리기에게 "PI를 만나면 3.14로 치환하여라."라는 메시지를 전달한다.
선행처리 명령문은 # 문자로 시작하며, 컴파일러가 아닌 선행처리기에 의해서 처리되는 문장이기 때문에 명령문의 끝에 세미콜론을 붙이지 않는다.
#include <stdio.h> 선언도 # 문자로 시작하는 선행처리 명령문이다.
"stdio.h 파일의 내용을 이곳에 가져다 놓으세요."라는 메시지이다.
#define: Object-like macro
#define PI 3.14 에서 #define은 '지시자', PI는 '매크로', 3.14는 '매크로 몸체'라고 한다.
명령문의 결과 PI라는 이름의 매크로는 그 자체로 상수 3.14가 된 셈이다.
PI와 같은 매크로를 가리켜 '오브젝트와 유사한 매크로', '매크로 상수'라고 한다.
오브젝트 유사 매크로
오브젝트는 그 자체로 '완전한 의미를 갖는 대상이나 사물'을 의미한다.
위에서 정의한 매크로 PI는 그 자체로 3.14라는 상수를 의미하기 때문에 '오브젝트와 유사한 매크로'라고 하는 것이다.
매크로 이름은 대문자로 정의하는 것이 일반적이다.
대문자로 정의함으로써 해당 식별자가 매크로라는 사실을 부각시킬 수 있기 때문이다.
#define: Function-like macro
매개변수가 존재하는 매크로를 정의할 수도 있다.
동작방식이 마치 함수와 유사하여 '함수와 유사한 매크로'라 하고 줄여서 '매크로 함수'라고 부르기도 한다.
#define SQUARE(X) X*X
매크로에 괄호가 등장함으로써 "SQUARE(X) 패턴 등장 시 X*X 유형으로 바꿔라" 라는 뜻이다.
괄호 안에 존재하는 이름이 중요한 게 아니라 괄호 안에 존재한다는 사실이 중요하다.
해당 명령문의 동작방식은 함수 호출과 유사하다.
이렇게 선행처리기에 의해 변환되는 과정 자체를 '매크로 확장'이라고 한다.
매크로 몸체에 괄호를 마구마구 쳐야 한다.
매크로 함수를 정의할 때는 매크로 몸체 부분을 구성하는 X와 같은 전달인자 하나하나에 괄호를 해야 하고
반드시 전체를 괄호로 한번 더 묶어줘야 한다.
매크로는 한 줄에 정의하는 것이 원칙이기 때문에
매크로를 두 줄 이상에 걸쳐서 정의할 때는 \ 문자로 줄바꿈을 명시해야 한다.
먼저 정의된 매크로를 사용해서 매크로를 정의할 수도 있다.
매크로 함수의 장점
매크로 함수를 정의하는 것은 일반 함수를 정의하는 것보다 복잡한데 분명 장점이 있기 때문에 사용한다.
다음은 함수를 매크로로 정의할 때 얻게 되는 장점들이다.
- 매크로 함수는 일반 함수에 비해 실행속도가 빠르다.
함수가 호출되면, '호출된 함수를 위한 스택 메모리의 할당 / 실행위치의 이동과 매개변수로의 인자 전달 / return 문에 의한 값의 반환'이 동반된다. 따라서 함수의 빈번한 호출은 실행속도 저하로 이어진다.
반면 매크로 함수는 선행처리기에 의해 매크로 몸체부분이 매크로 함수의 호출 문장을 대신하기 때문에 위의 사항들을 동반하지 않는다.
- 자료형에 따라서 별도로 함수를 정의하지 않아도 된다.
전달인자의 자료형에 상관없이 제대로 치환된다. 매크로 함수의 호출문장이 매크로 함수의 몸체부분으로 단순 치환되기 때문에 가능한 일이다.
매크로 함수의 단점
- 정의하기가 정말 까다롭다.
- 디버깅하기 쉽지 않다.
이와 같은 장단점을 고려했을 때 '작은 크기의 함수, 호출 빈도수가 높은 함수' 를 매크로의 형태로 정의하는 것이 옳다.
함수의 크기가 작아야 매크로의 형태로 정의하기가 편하고 에러 발생 확률이 낮아서 디버깅에 대한 염려를 덜 수 있다. 그리고 호출 빈도수가 높아야 매크로의 함수가 가져다 주는 성능 향상의 이점도 최대한 누릴 수 있다.
매크로 지시자 중에는 특정 조건에 따라 소스코드의 일부를 삽입하거나 삭제할 수 있도록 디자인 된 지시자가 있다.
#if... #endif: 참이라면
if문이 조건부 실행을 위한 것이라면 #if... #endif는 조건부 코드 삽입을 위한 지시자이다.
#ifdef... #endif: 정의되었다면
#if는 매크로가 참이냐 거짓이냐를 기준으로 동작한다면,
#ifdef는 매크로가 정의되었느냐 정의되지 않았느냐를 기준으로 동작한다.
#ifndef... #endif: 정의되지 않았다면
n이 not의 의미이다.
#else의 삽입: #if, #ifdef, #ifndef에 해당
if문에 else를 추가할 수 있듯이 #if, #ifdef, #ifndef문에도 #else문을 추가할 수 있다.
#elif의 삽입: #if에만 해당
if문에 else if를 여러 번 추가할 수 있듯이, #if문에도 #elif를 여러 번 추가할 수 있다.
else if의 끝을 else로 마무리할 수 있듯이, #elif의 끝을 #else로 마무리할 수 있다.
매개변수의 결합과 문자열화
문자열 내에서 매크로의 매개변수 치환이 발생하게 하려면 # 연산자를 사용해야 한다.
#define STR(ABC) #ABC
'매개변수 ABC에 전달되는 인자를 문자열 "ABC"로 치환해라'라는 뜻이다.
그리고 문자열을 나란히 선언하면, 하나의 문자열로 간주된다.
전달인자들이 단순히 이어지게 하고 싶으면 매크로 연산자 ##를 사용하면 된다.
'Algorithm > C' 카테고리의 다른 글
[c언어 개념] #17 파일의 분할과 헤더파일 디자인 (0) | 2022.08.13 |
---|---|
[c언어 개념] #15 메모리 구조, 메모리의 동적 할당 (malloc, free, calloc, realloc) (0) | 2022.08.02 |
[c언어 개념] #14 파일의 개방 모드, 파일 입출력 함수, 파일 위치 지시자 (0) | 2022.07.27 |
[c언어 개념] #13 파일 입출력 (0) | 2022.07.27 |
[c언어 개념] #12 구조체, 구조체 배열, typedef, 공용체, 열거형 (0) | 2022.07.25 |