C 언어 기초 빠른 복습
C 언어 기초 빠른 복습¶
다른 프로그래밍 언어 경험이 있는 분을 위한 C 핵심 문법 정리
1. C 언어의 특징¶
다른 언어와의 비교¶
| 특징 | Python/JS | C |
|---|---|---|
| 메모리 관리 | 자동 (GC) | 수동 (malloc/free) |
| 타입 시스템 | 동적 타입 | 정적 타입 |
| 실행 방식 | 인터프리터 | 컴파일 |
| 추상화 수준 | 높음 | 낮음 (하드웨어 가까움) |
C 언어를 배워야 하는 이유¶
- 시스템 프로그래밍 (OS, 드라이버)
- 임베디드 시스템
- 성능이 중요한 애플리케이션
- 다른 언어의 기반 이해 (Python, Ruby는 C로 작성)
2. 기본 구조¶
#include <stdio.h> // 헤더 파일 포함 (전처리기 지시문)
// main 함수: 프로그램 시작점
int main(void) {
printf("Hello, C!\n");
return 0; // 0 = 정상 종료
}
Python과 비교¶
# Python
print("Hello, Python!")
// C
#include <stdio.h>
int main(void) {
printf("Hello, C!\n");
return 0;
}
C의 특징:
- 세미콜론 ; 필수
- 중괄호 {} 로 블록 구분
- 명시적인 main 함수
- 헤더 파일 include 필요
3. 자료형¶
기본 자료형¶
#include <stdio.h>
int main(void) {
// 정수형
char c = 'A'; // 1바이트 (-128 ~ 127)
short s = 100; // 2바이트
int i = 1000; // 4바이트 (보통)
long l = 100000L; // 4 또는 8바이트
long long ll = 100000000000LL; // 8바이트
// 부호 없는 정수
unsigned int ui = 4000000000U;
// 실수형
float f = 3.14f; // 4바이트
double d = 3.14159265; // 8바이트
// 출력
printf("char: %c (%d)\n", c, c); // A (65)
printf("int: %d\n", i);
printf("float: %f\n", f);
printf("double: %.8f\n", d);
return 0;
}
형식 지정자 (printf)¶
| 지정자 | 타입 | 예시 |
|---|---|---|
%d |
int | printf("%d", 42) |
%u |
unsigned int | printf("%u", 42) |
%ld |
long | printf("%ld", 42L) |
%f |
float/double | printf("%f", 3.14) |
%c |
char | printf("%c", 'A') |
%s |
문자열 | printf("%s", "hello") |
%p |
포인터 주소 | printf("%p", &x) |
%x |
16진수 | printf("%x", 255) → ff |
sizeof 연산자¶
printf("int 크기: %zu 바이트\n", sizeof(int));
printf("double 크기: %zu 바이트\n", sizeof(double));
printf("포인터 크기: %zu 바이트\n", sizeof(int*));
4. 포인터 (C의 핵심!)¶
포인터란?¶
메모리 주소를 저장하는 변수입니다.
메모리:
주소 값
0x1000 42 ← int x = 42;
0x1004 0x1000 ← int *p = &x; (x의 주소 저장)
기본 문법¶
#include <stdio.h>
int main(void) {
int x = 42;
int *p = &x; // p는 x의 주소를 저장
printf("x의 값: %d\n", x); // 42
printf("x의 주소: %p\n", &x); // 0x7fff...
printf("p의 값 (주소): %p\n", p); // 0x7fff... (같은 주소)
printf("p가 가리키는 값: %d\n", *p); // 42 (역참조)
// 포인터로 값 변경
*p = 100;
printf("x의 새 값: %d\n", x); // 100
return 0;
}
포인터 연산자¶
| 연산자 | 의미 | 예시 |
|---|---|---|
& |
주소 연산자 | &x → x의 주소 |
* |
역참조 연산자 | *p → p가 가리키는 값 |
왜 포인터가 필요한가?¶
// 문제: C에서 함수는 값을 복사해서 전달 (call by value)
void wrong_swap(int a, int b) {
int temp = a;
a = b;
b = temp;
// 원본은 변경되지 않음!
}
// 해결: 포인터로 주소 전달
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
// 원본이 변경됨!
}
int main(void) {
int x = 10, y = 20;
wrong_swap(x, y);
printf("wrong_swap 후: x=%d, y=%d\n", x, y); // 10, 20 (변화 없음)
swap(&x, &y);
printf("swap 후: x=%d, y=%d\n", x, y); // 20, 10
return 0;
}
5. 배열¶
기본 배열¶
#include <stdio.h>
int main(void) {
// 배열 선언 및 초기화
int numbers[5] = {10, 20, 30, 40, 50};
// 접근
printf("%d\n", numbers[0]); // 10
printf("%d\n", numbers[4]); // 50
// 크기
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("배열 크기: %d\n", size); // 5
// 순회
for (int i = 0; i < size; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]);
}
return 0;
}
배열과 포인터의 관계¶
int arr[5] = {1, 2, 3, 4, 5};
// 배열 이름은 첫 번째 요소의 주소
printf("%p\n", arr); // 첫 번째 요소 주소
printf("%p\n", &arr[0]); // 같은 주소
// 포인터 연산
int *p = arr;
printf("%d\n", *p); // 1 (arr[0])
printf("%d\n", *(p + 1)); // 2 (arr[1])
printf("%d\n", *(p + 2)); // 3 (arr[2])
// arr[i] == *(arr + i)
문자열 (char 배열)¶
#include <stdio.h>
#include <string.h> // 문자열 함수
int main(void) {
// 문자열은 char 배열 + 널 종료 문자 '\0'
char str1[] = "Hello"; // 자동으로 '\0' 추가
char str2[10] = "World";
char str3[] = {'H', 'i', '\0'};
printf("%s\n", str1); // Hello
printf("길이: %zu\n", strlen(str1)); // 5
// 문자열 복사
char dest[20];
strcpy(dest, str1); // dest = "Hello"
// 문자열 연결
strcat(dest, " ");
strcat(dest, str2); // dest = "Hello World"
printf("%s\n", dest);
// 문자열 비교
if (strcmp(str1, "Hello") == 0) {
printf("같음!\n");
}
return 0;
}
6. 함수¶
기본 함수¶
#include <stdio.h>
// 함수 선언 (프로토타입)
int add(int a, int b);
void greet(const char *name);
int main(void) {
int result = add(3, 5);
printf("3 + 5 = %d\n", result);
greet("Alice");
return 0;
}
// 함수 정의
int add(int a, int b) {
return a + b;
}
void greet(const char *name) {
printf("Hello, %s!\n", name);
}
배열을 함수에 전달¶
// 배열은 포인터로 전달됨 (크기 정보 없음)
void print_array(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 또는 이렇게 표기 (동일한 의미)
void print_array2(int arr[], int size) {
// ...
}
int main(void) {
int nums[] = {1, 2, 3, 4, 5};
print_array(nums, 5);
return 0;
}
7. 구조체¶
기본 구조체¶
#include <stdio.h>
#include <string.h>
// 구조체 정의
struct Person {
char name[50];
int age;
float height;
};
int main(void) {
// 구조체 변수 선언 및 초기화
struct Person p1 = {"홍길동", 25, 175.5};
// 멤버 접근 (. 연산자)
printf("이름: %s\n", p1.name);
printf("나이: %d\n", p1.age);
// 멤버 수정
p1.age = 26;
strcpy(p1.name, "김철수");
return 0;
}
typedef로 간단하게¶
typedef struct {
char name[50];
int age;
} Person; // 이제 'struct' 키워드 없이 사용
int main(void) {
Person p1 = {"홍길동", 25};
printf("%s\n", p1.name);
return 0;
}
포인터와 구조체¶
typedef struct {
char name[50];
int age;
} Person;
void birthday(Person *p) {
p->age++; // 포인터는 -> 연산자 사용
// (*p).age++; 와 동일
}
int main(void) {
Person p1 = {"홍길동", 25};
birthday(&p1);
printf("나이: %d\n", p1.age); // 26
// 포인터로 접근
Person *ptr = &p1;
printf("이름: %s\n", ptr->name);
return 0;
}
8. 동적 메모리 할당¶
malloc / free¶
#include <stdio.h>
#include <stdlib.h> // malloc, free
int main(void) {
// 정수 하나 동적 할당
int *p = (int *)malloc(sizeof(int));
if (p == NULL) {
printf("메모리 할당 실패\n");
return 1;
}
*p = 42;
printf("%d\n", *p);
free(p); // 메모리 해제 (필수!)
// 배열 동적 할당
int n = 5;
int *arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // 배열도 해제 필수!
return 0;
}
메모리 누수 주의¶
// 나쁜 예: 메모리 누수
void bad(void) {
int *p = malloc(sizeof(int));
*p = 42;
// free(p); 없음 → 메모리 누수!
}
// 좋은 예
void good(void) {
int *p = malloc(sizeof(int));
if (p == NULL) return;
*p = 42;
// 사용 후...
free(p);
p = NULL; // dangling pointer 방지
}
9. 헤더 파일¶
헤더 파일 구조¶
// utils.h
#ifndef UTILS_H // include guard
#define UTILS_H
// 함수 선언
int add(int a, int b);
int subtract(int a, int b);
// 구조체 정의
typedef struct {
int x, y;
} Point;
#endif
// utils.c
#include "utils.h"
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
// main.c
#include <stdio.h>
#include "utils.h"
int main(void) {
printf("%d\n", add(3, 5));
Point p = {10, 20};
printf("(%d, %d)\n", p.x, p.y);
return 0;
}
컴파일¶
gcc main.c utils.c -o program
10. 주요 차이점 요약 (Python → C)¶
| Python | C |
|---|---|
print("Hello") |
printf("Hello\n"); |
x = 10 |
int x = 10; |
if x > 5: |
if (x > 5) { |
for i in range(5): |
for (int i = 0; i < 5; i++) { |
def func(x): |
int func(int x) { |
class Person: |
struct Person { |
| 자동 메모리 관리 | malloc() / free() |
len(arr) |
sizeof(arr)/sizeof(arr[0]) |
다음 단계¶
이제 실제 프로젝트를 만들어보겠습니다!
03_Project_Calculator.md → 첫 번째 프로젝트 시작!