Skip to main content

Command Palette

Search for a command to run...

C언어 (1)

Updated
6 min read

1. C언어의 기본 구조

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

#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) 단계에서 여러 오브젝트 파일과 라이브러리를 합쳐 하나의 실행 파일을 만든다.

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

1-2. 실행

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

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바이트로 소수점 약 67자리 정밀도를, double은 8바이트로 약 1516자리 정밀도를 제공한다.

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

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

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

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

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

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

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

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

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

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

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

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++에서 지원한다.

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. 변수 선언 방법

// 선언과 동시에 초기화
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 같은 예약어도 사용 불가하며, 대소문자를 구분하므로 ageAge는 서로 다른 변수다.


3. 자료형과 서식 문자

3-1. 서식 문자의 종류

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

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

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

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

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을 사용한다.

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

실수형

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

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

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

문자형

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

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

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

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

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

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


마무리

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

5 views

More from this blog

C언어 (13)

1. STL의 개념 1-1. 배경 C++로 프로그래밍을 하다 보면 동적 배열, 연결 리스트, 정렬, 검색 같은 자료구조와 알고리즘을 반복적으로 구현하게 된다. 프로젝트마다 매번 새로 만들면 시간도 낭비되고, 버그가 생길 가능성도 높아진다. 이런 문제를 해결하기 위해 자주 사용되는 자료구조와 알고리즘을 미리 만들어서 표준 라이브러리에 포함 시킨 것이 STL이

Apr 1, 202610 min read3

chamdom's tech

16 posts