# C언어 (1)

## 1\. C언어의 기본 구조

C언어는 **절차지향 프로그래밍 언어**로, 위에서 아래로 순차적으로 코드를 실행한다. 모든 C 프로그램은 `main()` 함수에서 시작된다.

```c
#include <stdio.h>

int main(void) {
    printf("Hello, World!\n");
    return 0;
}
```

`#include <stdio.h>`는 표준 입출력 라이브러리를 포함시키는 전처리 지시문이다. `printf`같은 함수를 사용하려면 반드시 필요하다.

### 1-1. 컴파일

C언어는 **컴파일 언어**다. JavaScript처럼 한 줄씩 해석하며 실행하는 인터프리터 방식이 아니라, 소스 코드 전체를 기계어로 번역한 뒤 실행한다.

컴파일 과정은 크게 4단계를 거친다.

먼저 **전처리(Preprocessing)** 단계에서 `#include`, `#define` 등의 전처리 지시문을 처리한다. 그 다음 **컴파일(Compilation)** 단계에서 전처리된 소스 코드를 어셈블리어로 변환한다. 이어서 **어셈블(Assembly)** 단계에서 어셈블리어를 기계어(오브젝트 파일, `.o`)로 변환하고, 마지막으로 **링킹(Linking)** 단계에서 여러 오브젝트 파일과 라이브러리를 합쳐 하나의 실행 파일을 만든다.

```plaintext
소스코드(.c) → 전처리 → 컴파일 → 어셈블(.o) → 링킹 → 실행파일(.exe / a.out)
```

### 1-2. 실행

컴파일이 완료되면 생성된 실행 파일을 OS 위에서 직접 실행한다. 컴파일 시점에 이미 기계어로 번역되어 있기 때문에, 인터프리터 언어보다 **실행 속도가 빠르다.**

```bash
gcc main.c -o main   # 컴파일
./main                # 실행
```

* * *

## 2\. 변수

### 2-1. 변수를 사용하는 이유

프로그램은 결국 **데이터를 처리하는 것**이다. 데이터를 저장하고, 읽고, 수정하려면 메모리 공간이 필요한데, 그 메모리 공간에 이름을 붙인 것이 **변수**다.

변수가 없다면 메모리 주소를 직접 다뤄야 하는데, `0x7ffeefbff4ac` 같은 주소를 사람이 기억하며 코드를 짜는 것은 비현실적이다. 변수는 이 문제를 해결해준다.

### 2-2. 자료형에 따라 메모리 크기가 다르다

모든 데이터가 같은 크기의 메모리를 쓰는 건 아니다. 저장할 값의 종류와 범위에 따라 필요한 공간이 달라진다.

`char`는 1바이트로 -128에서 127까지, `short`는 2바이트로 약 -3만에서 3만까지, `int`는 4바이트로 약 -21억에서 21억까지 저장할 수 있다. 실수형인 `float`는 4바이트로 소수점 약 6~7자리 정밀도를, `double`은 8바이트로 약 15~16자리 정밀도를 제공한다.

작은 숫자를 저장할 때 `double`(8바이트)을 쓰면 메모리 낭비다. 반대로 큰 숫자를 `char`(1바이트)에 넣으면 **오버플로우**가 발생한다. 적절한 자료형 선택이 중요한 이유다.

### 2-3. 메모리에 할당된 변수를 어떻게 참조하는가?

변수를 선언하면 컴파일러가 해당 자료형 크기만큼 메모리를 확보한다. 이후 변수 이름을 사용하면, 컴파일러가 그 이름을 실제 메모리 주소로 변환해준다.

```c
int x = 10;
printf("값: %d\n", x);       // 변수 이름으로 값 접근
printf("주소: %p\n", &x);    // & 연산자로 메모리 주소 확인
```

즉 프로그래머는 이름으로 접근하고, 컴파일러가 주소로 바꿔주는 구조다.

### 2-4. 메모리 주소값은 OS가 할당한다

변수를 선언하면 실제로 어떤 메모리 주소에 배치될지는 **운영체제(OS)**가 결정한다. 프로그래머가 "이 변수를 주소 0x1000에 넣어라"라고 지정하는 것이 아니다.

OS는 프로그램이 실행될 때 가상 메모리 공간을 할당하고, 그 안에서 변수들의 위치를 정한다. 같은 프로그램을 두 번 실행하면 변수의 주소가 달라질 수 있는 이유가 이것이다.

### 2-5. 메모리에 변수 이름 부여

결국 변수 선언이란 다음 세 가지를 한 번에 하는 것이다.

**메모리 공간 확보** — 자료형 크기만큼 공간을 잡는다. **이름 부여** — 그 공간에 사람이 읽을 수 있는 이름을 붙인다. **타입 지정** — 그 공간에 어떤 종류의 데이터가 들어갈지 정한다.

```c
int age = 25;
```

이 한 줄로 4바이트 메모리가 확보되고, `age`라는 이름이 붙고, 정수형 데이터가 저장된다.

### 2-6. 변수의 종류

#### 기본형 (Primitive Type)

언어가 기본으로 제공하는 자료형이다. 정수형으로는 `char`, `short`, `int`, `long`, `long long`이 있고, 실수형으로는 `float`, `double`, `long double`이 있다. 그리고 반환값이 없음을 나타내는 `void`도 있다.

#### 유도형 (Derived Type)

기본형을 조합하거나 확장해서 만든 자료형이다.

**배열(**`int arr[10]`**)**은 같은 타입의 데이터를 연속된 메모리에 저장한다. **포인터(**`int *p`**)**는 다른 변수의 메모리 주소를 저장한다. **구조체(**`struct`**)**는 서로 다른 타입의 데이터를 하나로 묶고, **공용체(**`union`**)**는 같은 메모리 공간을 여러 타입이 공유한다. **열거형(**`enum`**)**은 관련된 상수들에 이름을 부여한다.

### 2-7. 객체지향과의 관계

C언어 자체는 **절차지향 언어**이지만, 구조체와 함수 포인터를 조합하면 객체지향적인 설계를 흉내낼 수 있다. 하지만 본격적인 객체지향(클래스, 상속, 다형성, 캡슐화)은 C++에서 지원한다.

```c
typedef struct {
    char name[50];
    int age;
    void (*greet)(const char*);
} Person;

void sayHello(const char* name) {
    printf("안녕하세요, %s입니다.\n", name);
}
```

C++로 넘어가면 이런 패턴이 `class`로 깔끔하게 정리된다. C의 구조체를 잘 이해하면 C++의 클래스를 이해하는 데 큰 도움이 된다.

### 2-8. 변수 선언 방법

```c
// 선언과 동시에 초기화
int x = 10;
float pi = 3.14f;
char ch = 'A';

// 선언만 (초기화 안 하면 쓰레기값이 들어있음)
int y;

// 여러 변수 동시 선언
int a, b, c;
int d = 1, e = 2;

// 상수 선언 (값 변경 불가)
const int MAX = 100;
```

변수 이름은 영문자, 숫자, 언더스코어(`_`)만 사용할 수 있고, 숫자로 시작할 수 없다. `int`, `return` 같은 예약어도 사용 불가하며, 대소문자를 구분하므로 `age`와 `Age`는 서로 다른 변수다.

* * *

## 3\. 자료형과 서식 문자

### 3-1. 서식 문자의 종류

`printf`와 `scanf`에서 데이터를 입출력할 때 자료형에 맞는 서식 문자를 사용해야 한다.

정수를 출력할 때는 `%d`를 쓴다. `long`이면 `%ld`, `long long`이면 `%lld`를 사용한다. 부호 없는 정수는 `%u`다.

실수는 `printf`에서 `%f`를 쓰고, `scanf`에서 `double`을 입력받을 때는 `%lf`를 써야 한다. 지수 표기법으로 출력하고 싶으면 `%e`를 사용한다.

문자 1개는 `%c`, 문자열은 `%s`다. 포인터 주소를 출력할 때는 `%p`, 16진수는 `%x`, 8진수는 `%o`를 사용한다. `%` 문자 자체를 출력하려면 `%%`로 쓴다.

```c
int num = 42;
float pi = 3.14159f;
char grade = 'A';

printf("정수: %d\n", num);
printf("실수: %.2f\n", pi);     // 소수점 2자리까지
printf("문자: %c\n", grade);
printf("주소: %p\n", &num);
```

### 3-2. 자료형 상세

#### 정수형

`char`는 1바이트로, 문자를 저장하거나 작은 정수를 담을 때 사용한다. `unsigned char`로 선언하면 0부터 255까지 양수만 저장할 수 있다. `short`는 2바이트, `int`는 4바이트로 약 ±21억 범위를 가지며 가장 많이 사용하는 정수형이다. 매우 큰 정수가 필요하면 8바이트인 `long long`을 사용한다.

`signed`와 `unsigned` 키워드로 부호 유무를 지정할 수 있다. `unsigned int`는 음수를 포기하는 대신 양수 범위가 두 배로 늘어난다(0 ~ 약 42억).

#### 실수형

`float`는 4바이트로 소수점 약 6~7자리 정밀도를 제공하고, `double`은 8바이트로 약 15~16자리 정밀도를 제공한다. 일반적인 실수 연산에는 `double`을 사용하고, 메모리 절약이 필요할 때만 `float`를 쓴다.

실수는 컴퓨터에서 IEEE 754 표준에 따라 **부동소수점** 방식으로 저장된다. 이 때문에 `0.1 + 0.2 == 0.3`이 `false`가 될 수 있다. JavaScript에서도 동일한 현상이 발생하는데, 이유가 같다.

```c
printf("%.20f\n", 0.1 + 0.2);  // 0.30000000000000004441...
```

#### 문자형

`char`는 사실 1바이트 정수형이다. 문자를 저장하는 게 아니라 **문자에 대응하는 ASCII 코드 값(정수)**을 저장한다.

```c
char ch = 'A';       // 실제로 65가 저장됨
printf("%c\n", ch);  // A
printf("%d\n", ch);  // 65
```

### 3-3. 데이터베이스와의 연관성

C언어의 자료형 개념은 데이터베이스 설계와 직접적으로 연결된다. DB 테이블의 컬럼을 정의할 때도 적절한 데이터 타입을 선택해야 한다.

C의 `int`는 SQL의 `INT`나 `INTEGER`에, `float`이나 `double`은 `FLOAT`, `DOUBLE`, `DECIMAL`에 대응한다. `char`는 고정 길이 문자열인 `CHAR(n)`에, `char[]` 배열은 가변 길이 문자열인 `VARCHAR(n)`에 해당한다.

프로그래밍에서든 데이터베이스에서든 핵심은 같다 — **저장할 데이터의 성격에 맞는 타입을 고르는 것**이 메모리 효율성과 프로그램 안정성을 결정한다.

* * *

## 마무리

C언어의 핵심은 **메모리를 직접 다루는 것**이다. 변수 선언 하나에도 메모리 할당, 주소 지정, 타입 결정이 함께 일어난다. 이 기본기를 확실히 잡아두면 포인터, 동적 메모리 할당, 그리고 C++의 클래스까지 자연스럽게 이해할 수 있다.
