! 본 내용은 검토 전 포스트 입니다. 부정확한 내용이 있을 수 있으니 양해바랍니다.
1. 전처리
우리가 알고 있는 그 전처리 맞음
#define 뿐만 아니라 #include, #param, #ifdef 등 앞에 #붙인 모든게 전처리 대상임
#include <stdio.h>
#define PI 3.14
int main(void) {
printf("%.2f\n", PI); // PI == 3.14
return 0;
}
gcc -E preprocess_before.c -o preprocess_after.c
아래에는 PI에 3.14가 들어갔을 뿐만 아니라 주석도 지워짐
int main(void) {
printf("%.2f\n", 3.14);
return 0;
}
실제로는 헤더파일 내용이 위에 추가로 들어가 있음
stdio.h 위치 = /usr/include/stdio.h
1.1. 전처리 테크닉
전처리 구문은 단순히 #define으로 헤더파일들을 불러오거나 값을 대치할 뿐만 아니라 디버깅/실행시 옵션에 따라 일부 기능을 비활성화 시키거나 다른 흐름의 코드를 실행시키기 위함도 있다.
예) 물리 GPU가 있는 경우에는 직접 GPU와 통신하고, 물리 GPU가 없는 경우 에뮬레이트된 GPU와 통신하게끔 함
1.1.1. 값을 비교해서 확인
아래는 만약 DEBUG가 1이면 1을 출력하고 아니면 2를 출력하는 코드이다. #endif로 #if문이 끝남을 명시한다.
DEBUG의 값을 1 혹은 1이 아닌 값으로 바꾸는 방식으로 값을 비교하여 일부 기능을 무시하거나 흐름을 제어할 수 있다.
#include <stdio.h>
#define DEBUG 1
int main(void) {
#if DEBUG == 1
printf("1\n");
#else
printf("2\n");
#endif
return 0;
}
1.1.2. define이 존재하는지 확인
값을 비교할 필요 없이 매크로가 선언되어있는지만 확인하는 경우가 있다. 아래 코드는 DEBUG 메크로가 존재하는지 여부만 확인한다. DEBUG 메크로가 있으니 1과 2를 모두 출력한다. #define DEBUG
를 포함하거나 포함하지 않는 식으로 일부 기능을 비활성화 시킬 수 있다.
#include <stdio.h>
#define DEBUG
int main(void) {
#ifdef DEBUG
printf("1\n");
#endif
printf("2\n");
return 0;
}
2. 목적 파일 vs 실행 파일
컴파일된 코드가 링킹(다른 목적파일, 라이브러리와 결합)되지 않은 상태
gcc -c -o object_after.o object_before.c
실제 함수에 대한 동작과 결합되지 않은 상태이기 때문에 이후 설명할 실행파일에 비해 사이즈도 적음
gcc -o object_compile object_before.c
object파일은 c코드 보다는 크고 컴파일된 파일보다는 작음
텍스트파일이라고 출력되는 test.c와 달리 test.o는 실행파일 이라고 뜨긴 함(ELF 64-bits)하지만 실행이 안됨
연결된 라이브러리를 찾는 ldd 명령어를 쳤을때 obejct파일은 라이브러리 정보가 없는 반면 실행파일 라이브러리가 보임
즉 목적 파일은 라이브러리가 연결되어있지 않은 상태, 컴파일된 파일은 라이브러리가 연결되어있는 상태
2.1. 정적 링킹, 동적 링킹
자주 사용하는 함수 printf 등은 이미 .so(Shared object) 파일로 만들어져있고 #include <stdio.h>
를 통해 불러오게된다. so 파일을 사용하는 방법은 정적 링킹과 동적 링킹이 있다.
정적(Static) 링킹 : so 파일에서 사용할 함수들을 실행 파일 내부에 두는 것
동적(Dynamic) 링킹 : 실행 파일은 실행파일 대로 만들고, 실행파일에서 so 파일을 따로 첨부하는 것
so 파일을 따로 첨부하는데는 이유가 있다. so파일은 애초에 자주 사용되는 함수를 모아두기 때문에 다른 실행파일에서도 사용할 함수들이다. 예를 들어 실행파일이 100개이며(개당 1MB)이고 so파일 역시(1MB)라고 가정하자. 만약 동적 연결 한다면 (1MB + 1MB) * 100 = 200MB이고, 정적연결 한다면 100 * 1MB + 1MB = 101MB이다.
용량 뿐만 아니라 관리 측면에서도 있다. 만약 scanf 함수가 취약하여 업데이트를 했는데 동적 연결이 되어있다면 scanf 함수가 포함되어있기 때문에 업데이트를 받지 못해 업데이트에 취약할 것이다. 반면 정적 연결이 되어있다면 별도의 조치없이 업데이트된 scanf 함수를 사용할 수 있다.
정적 연결의 장점도 있는데 외부에서 함수를 가져오는것이 아니므로 속도가 빠르다.
'초보 해커를 위한 C언어와 동작 원리 > Ch4. Programming advanced' 카테고리의 다른 글
헤더 파일 (0) | 2025.04.23 |
---|