Krafton Jungle/3. TIL

[WEEK05] 파이썬과 다른 C언어의 특성들

munsik22 2025. 4. 10. 15:44

지난 4주간은 고급 언어인 Python 언어로 가변 리스트, 우선순위 큐와 같은 추상화된 데이터 타입을 사용하여 컴퓨터를 다루는 방법을 익혔다. 앞으로의 4주간은 Assembly 언어와 매우 가까운 C를 사용하여 좀 더 컴퓨터의 본질에 가까이 다가가야 한다.


📁 선언(Declaration)과 정의(Definition)

  • 선언: 변수나 함수가 어디에 있고, 어떤 타입인지 컴파일러에게 알려주는 것.
  • 정의: 실제로 메모리를 할당하거나 함수의 구현부를 작성하는 것.
// header.h
int add(int, int); // 선언

// main.c
#include "header.h"
int add(int a, int b) { return a + b; } // 정의

보통 .h 파일에 선언을, .c 파일에 정의를 한다. 파이썬은 선언 없이 바로 정의하니 이 개념 자체가 생소할 수 있다.

🔁 전방 선언 (Forward Declaration)

C에서는 아래처럼 순서가 중요할 수 있어서, 아직 정의되지 않은 함수나 구조체를 먼저 알려줘야 할 때가 있다.

void hello(); // 전방선언

int main() {
    hello();
}

void hello() {
    printf("Hi\n");
}

🔒 static과 extern

  • static: 파일 내 한정 사용 (internal linkage). 외부에서 접근 불가.
  • extern: 다른 파일에 정의된 변수나 함수가 외부에서 사용 가능함을 명시.
// a.c
static int count = 0;  // 다른 파일에서는 못 씀
extern int total;      // 다른 파일에서 정의된 total 사용
키워드 의미 용도
static 파일 내부에서만 사용 전역 변수나 함수 숨김
extern 다른 파일에 정의된 변수/함수 사용 연결을 위해 선언만 할 때 사용

🔘 enum과 union

  • enum: 상수에 이름 붙이기. 가독성과 유지보수에 좋음.
  • union: 여러 멤버 중 하나만 메모리 차지. 같은 메모리를 여러 타입으로 해석 가능.
enum Color { RED, GREEN, BLUE };
union Data {
    int i;
    float f;
};

🏗️ 컴파일과 링크

  1. 컴파일 (compile): .c.o (기계어 번역)
  2. 링크 (link): 여러 .o 파일 → 실행 파일 (.exe, .out)로 결합

즉, C는 코드를 나눠서 작성하고 나중에 조립해서 실행 파일을 만든다. 파이썬처럼 인터프리터가 직접 실행하는 방식과는 다름.

📌 포인터

void** ptr = 0x00000000FFFF8392;
char* name = (char*) *ptr;
printf("%s", name);

void** ptr2 = 0x00000000FFFF3821;
int64_t* number = (int64_t*) *ptr2;
printf("%p", *number);
  • void** ptr: 이중 포인터. 주소를 가리키는 주소.
  • *ptr: void* → char* 혹은 int64_t*로 캐스팅 후 역참조
  • C는 메모리 주소를 직접 다룰 수 있기 때문에, 복잡한 구조를 효율적으로 처리 가능하지만 디버깅도 어렵다.

📦 malloc과 free

  • malloc(size): size만큼 메모리 할당. 실패하면 NULL 반환.
  • free(ptr): 할당 해제.
int* arr = malloc(sizeof(int) * 10);
// ... 사용
free(arr);

🔢 배열과 포인터

C의 배열은 사실상 포인터와 유사하게 동작한다.

int arr[3] = {1, 2, 3};
int* p = arr;

printf("%d", *(p + 1)); // 2

🧭 Call by Value vs Call by Reference

  • C는 기본적으로 Call by Value (값 복사)
  • 참조처럼 쓰려면 포인터를 인자로 넘겨야 한다.
void swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

🔄 가변 인자

#include <stdarg.h>

int sum(int count, ...) {
    va_list args;
    va_start(args, count);
    int total = 0;
    for (int i = 0; i < count; ++i)
        total += va_arg(args, int);
    va_end(args);
    return total;
}
  • stdarg.h 사용
  • va_list로 여러 인자 처리
  • va_start, va_arg, va_end 순서 지켜야 함

⚙️ 전처리기 명령어

  • #define: 상수/매크로
  • #include: 다른 파일 포함
  • #ifdef/#ifndef: 조건부 컴파일
  • typedef: 새로운 타입 이름 정의
#define PI 3.14
typedef unsigned int uint;

🔁 리스트 순회 (C++ STL과 유사하게)

Pintos 등에서는 C에서도 리스트 구조체를 정의해 순회 가능하게 구현:

struct list_elem* e;
for (e = list_begin(&mylist); e != list_end(&mylist); e = list_next(e)) {
    struct my_struct* s = list_entry(e, struct my_struct, elem);
}

⚡ 비트 연산자

  • &, |, ^, ~, <<, >>
  • 하드웨어 제어나 플래그 처리에 필수
int flags = 0b0101;
flags |= 0b1000; // 특정 비트 ON

🧠 정리

처음에는 C언어의 저수준 특성이 낯설었지만, 메모리와 하드웨어에 가까운 언어인 만큼 운영체제나 시스템 프로그래밍에 강력한 장점이 있다. 이 개념들만 이해해도 Pintos나 커널 공부할 때 훨씬 수월할 듯 하다.