1. 빌드와 실행

더보기

1. 빌드와 실행

 

빌드와 실행

 

basiclike.tistory.com

 

 

 

 

2. 빌드 과정

각 단계별 파일을 만들고, 실제로 눈으로 확인하세요.


 

2. 간단한 gcc 명령어 살펴보기

더보기

1. 소스 코드

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    return 0;    
}

 

 

 

 

2. gcc 빌드 명령어

gcc test001.c  // test.c → a.out 생성
  • * 기호는 해당 파일이 "실행 가능한 파일(executable)"임을 나타내는 시각적 표시(리눅스 심볼) 
  • a.out의 .out 은 "기본 출력 실행 파일(auto output file)"을 의미, 확장자 .out이 아님

 

3. 빌드 과정과 gcc 컴파일 옵션

더보기

1. gcc 명령어 옵션별 역할과 생성된 파일 요약

 

옵션  설명  결과 생성되는 파일 예시 및 역할
-E 전처리 과정까지만 수행
E = Expand (전처리 하여 코드 확장)
test.i
전처리된 소스 코드 출력 (.i 파일)
-S 어셈블리 코드까지 생성
S = Assembly (UNIX 전통)
test.s 어셈블리 코드 파일 생성 (.s 파일)
-c 목적 파일 생성 (컴파일+어셈블리)
c = Compile only, no linking
test.o 목적 파일 생성 (.o 파일), 실행 파일은 생성하지 않음
-o <파일명> 출력 파일명 지정
o = Output file name
test.out 컴파일 결과물(전처리, 어셈블리, 목적 파일, 실행 파일 등)

 

 

 

 

2. -o 옵션 : 

  • Output file 출력 파일 이름 지정
  • 기본적으로 gcc는 출력 파일을 a.out으로 생성
  • -o 옵션을 사용하면 원하는 이름으로 실행파일명 설정 가능
  • 링크 포함 실행파일 생성

 

4. 실습 요약

더보기

[실습1] 전처리된 소스파일 만들기

 

 

 

 

[실습2] 어셈블리 파일 만들기

 

 

 

 

[실습3] 목적 파일 만들기

 

 

 

 

[실습4] 실행 파일 만들기


 

5. 전처리된 소스파일 만들기 실습

더보기

1. 소스코드 파일을 전처리만 실행

gcc -E test.c -o test.i    // test.c → test.i 생성
  • test.i: 전처리된 텍스트 파일 (매크로 확장, 헤더 포함 완료)

 

 

 

 

2. 전처리된 소스 파일 확인하기

  • 헤더 파일(예: <stdio.h>)에 정의된 수많은 선언과 매크로 정의가 모두 포함되어 있기 때문에 매우 방대합니다.
  • test.i는 사람이 읽을 수 있는 순수 텍스트 파일로, 함수 선언, 구조체 정의, 매크로 확장 코드 등 헤더 전체 내용을 포함합니다
  • 실제 실행에 필요한 최소한의 기계어 코드가 아니라, 소스코드 + 모든 포함 헤더 텍스트<stdio.h>를 전부 복사한 상태입니다. 따라서 크기가 더 크고, 컴파일 이후 생성된 바이너리(실행파일)보다 훨씬 용량이 큽니다.

 

 

 

3. printf() 함수의 선언부를 찾을 수 있습니다.


 

 

 

 

4. 전처리

  • 컴파일 전 처리할 작업 수행
  • #include "header.h"나 #include <stdio.h> 같은 헤더 파일 포함 지시문을 만나면,
    해당 헤더 파일의 내용을 텍스트 수준에서 소스 코드에 복사 붙여넣기 합니다.
  • 프로그래밍의 편의를 위해 작성된 매크로 변환 (e.g. #define)
  • 컴파일할 영역 명시 (e.g. #if, #ifdef, …)
  •  전처리 지시자
지시자 사용 예 기능
#include #include <stdio.h> include 디렉터리에서 stdio.h를 찾아 그
내용 복사
#include "myhdr.h" 소스 파일이 있는 디렉터리에서 myhdr.h를
찾아 그 내용 복사
#define #define PI 3.14 PI는 상수 3.14로 바뀜
#define SUM(x, y) ((x)+(y)) SUM(10, 20)은 ((10)+(20))으로 바뀜
#if ~ #endif #if (VER >= 6)
    max = 1;
#endif
VER이 6 이상이면 max = 1; 컴파일
#ifdef ~ #endif #ifdef DEBUG
    printf("%d", a);
#endif
DEBUG가 정의되어 있으면
printf 문장 컴파일
#ifndef ~ #endif
#ifndef _POINT H_
#define _POINT_H_
(코드영역)
#endif
_POINT H_ 매크로명이 정의되어 있지 않으면
_POINT H_ 매크로명을 정의하고
(코드영역)
ifndef 영역의 끝 표시
기타 #else, #elif, #undef,
#pragma, #error, #line 등 사용 가능
 

 

 

 

 

5. 실습

 

실제 소스코드를 작성하고,

#define 메크로 및 여러 파일로 분할된 소스코드를 전처리하여 어떻게 구성되는지 확인해보셔야 합니다.


 

6. 어셈블리 파일 만들기 실습

더보기

1. 소스코드 파일을 어셈블리 파일로 만들기 (전처리 + 컴파일)

gcc -S test.c -o test.s    // test.c → test.s 생성
  • test.s: 어셈블리어 코드 (사람이 읽을 수 있는 저수준 코드)

 

 

 

 

2. 전처리된 파일을 어셈블리 파일로 만들기

gcc -S test.i -o test.s    // test.i → test.s 생성
  • test.s: 어셈블리어 코드 (사람이 읽을 수 있는 저수준 코드)

 

 

 

 

3. 어셈블리 확인하기

  • 링커를 수행하지 않고 중간 결과인 어셈블리어 코드를 생성합니다.
  • test.s 파일에 사람이 읽을 수 있는 저수준 어셈블리 코드가 저장됩니다.
  • 어셈블리 코드는 CPU 명령어와 레지스터, 메모리 주소 등을 표현한 코드입니다.

 

 

 

 

4. 어셈블리 예시

	.file	"test001.c"      # 소스 파일 이름 표시 (디버깅 및 식별용)
	.text                    # 실행 코드가 위치하는 섹션 시작
	.section	.rodata  # 읽기 전용 데이터(Read-Only Data) 섹션 시작. 상수, 문자열 저장 공간
.LC0:                            # 라벨(Label). 상수 "Hello, World!" 문자열 위치 표시
	.string	"Hello, World!"  # 문자열 상수를 저장
	.text                    # 실행 코드 섹션 재시작
	.globl	main             # main 함수를 전역 심볼로 선언 (링커가 참조 가능)
	.type	main, @function  # main 심볼이 함수임을 표시
main:
.LFB0:
	.cfi_startproc
  • 어셈블리어는 기계어에 매우 가까운 저수준 언어로, 사람이 읽고 쓸 수 있는 형태
  • 플랫폼(예: x86, ARM)마다 어셈블리 명령어와 문법이 다름
  • 컴파일러가 고급언어를 어떻게 기계어 명령어로 변환하는지 감각 익히기

 

7. 목적 파일 만들기 실습

더보기

1. 목적 파일 만들기  (전처리 + 컴파일 + 어셈블링)

gcc -c test.c -o test.o     // test.c → test.o 생성
  • test.o: 목적 파일 (기계어, 바이너리, 이진수 코드가 담긴 중간 파일)

 

 

 


2. 어셈블리 파일을 목적(바이너리) 파일로 만들기

gcc -c test.s -o test.o    // test.s → test.o 생성
  • test.o: 목적 파일 (기계어, 바이너리, 이진수 코드가 담긴 중간 파일)

 

 

 


3. 바이너리 확인하기

VSCode에서 xxd처럼 파일의 이진(바이너리) 내용을 헥사 덤프(hexdump) 형태로 보려면

Hex Editor (by Microsoft) 확장 프로그램 설치

설치 후, 이진 파일을 우클릭 → Open With → Hex Editor 선택 가능

 

 

  • -c는 링크 단계는 생략했기에, 최종 실행 파일을 만들지 않고, 중간 산출물인 목적 파일(object file, .o)을 생성
  • .o 파일은 기계어(바이너리, 이진수) 코드가 담긴 목적 파일
  • 플랫폼 의존적: 해당 CPU와 운영체제 아키텍처용 바이너리 코드 포함
  • 대부분은 .s(텍스트 기반 어셈블리 코드) 크기가 훨씬 크고, .o(바이너리 목적 파일)는 용량이 작습니다.
  • .o 파일은 단순 기계어 코드뿐 아니라 심볼 테이블, 디버깅 정보, 재배치 정보(relocation info), 섹션 헤더 등 여러 메타데이터를 포함하기 때문에 순수 명령어 텍스트인 .s보다 용량이 커질 수 있음

 

8. 실행 파일 만들기 실습과 링크

더보기

1 . 소스코드 파일을 실행 파일로 변환 (전처리 + 컴파일 + 어셈블링 + 링크)

gcc test.c -o test.out    // test.c → test.out 생성
  • test.out: 실행 가능한 바이너리 파일 

 

 



2. 목적 파일을 실행 파일로 변환 실행

gcc test.o -o test.out    // test.o → test.out 생성
  • test.out: 실행 가능한 바이너리 파일 
  • * 기호는 해당 파일이 "실행 가능한 파일(executable)"임을 나타내는 시각적 표시 
  • a.out의 .out 은 "기본 출력 실행 파일(auto output file)"을 의미, 확장자 .out이 아님

 

 

 

 

3. 실행파일 확인하기

 

 

 

 

4. 링커의 역할

  • 링커는 여러 목적 파일과 라이브러리(예: C 표준 라이브러리)를 결합하여 하나의 실행 파일을 만듦
  • 링킹 과정에서 함수 참조를 해결하고, 필요한 라이브러리 코드도 포함시킴
  • 라이브러리는 보통 목적 파일(Object file)들의 집합으로 구성됩니다.

 

 

 

 

5. mysql이나 stdio 라이브러리 연결 시

  • 보통 libmysqlclient.a (정적) 혹은 libmysqlclient.so (동적) 형태로 제공됨
  • stdio 같은 C 표준 라이브러리는 시스템에 따라 libc.a (정적), libc.so (동적) 형태로 있음
  • 정적 라이브러리 .a는 목적 파일(.o) 묶음
  • 정적 라이브러리면 링커가 .o 단위로 함수 구현 필요한 부분을 찾아서 실행 파일에 포함