Chapter 1

C언어 코테 기초

C언어로 코딩테스트를 풀기 위한 기본기

C vs C++ 차이점 (코테 관점)

코테에서 C와 C++의 가장 큰 차이는 STL 사용 가능 여부다. C++은 vector, map, set, queue, stack 등을 바로 쓸 수 있지만, C는 모든 자료구조를 직접 구현해야 한다.

항목CC++
자료구조직접 구현STL (vector, map 등)
정렬qsort (비교함수 필요)sort (람다/비교함수)
입출력scanf/printfcin/cout 또는 scanf/printf
메모리malloc/freenew/delete 또는 STL
문자열char 배열 + string.hstring 클래스
boolstdbool.h 또는 int기본 지원
: C로 코테를 푸는 장점: 메모리와 속도에서 유리하고, 자료구조를 깊이 이해하게 된다. 단점: 구현량이 많아 시간이 더 걸릴 수 있다.

C 코테의 기본 뼈대

기본 템플릿
시작점
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#define MAX 100001
6#define INF 0x3f3f3f3f
7
8int main(void) {
9 int n;
10 scanf("%d", &n);
11
12 // 풀이 코드
13
14 return 0;
15}

입출력 패턴

scanf / printf 포맷 스트링 총정리

포맷자료형설명
%dint정수
%ldlonglong 정수
%lldlong long큰 정수 (최대 9.2 × 10¹⁸)
%uunsigned int부호 없는 정수
%ffloat / double(printf)실수 (printf에서 float, double 모두)
%lfdouble(scanf)scanf에서 double 입력 시 필수
%cchar문자 1개 (공백, 개행도 읽음!)
%schar*공백 전까지의 문자열
%xunsigned int16진수
%ounsigned int8진수
주의: scanf에서 %c는 공백/개행도 읽는다! 앞에 공백을 넣어서 scanf(" %c", &ch)로 사용하자.

다양한 입력 패턴

입력 패턴 모음
1// 1. 기본 정수/실수 입력
2int n, m;
3scanf("%d %d", &n, &m);
4
5double x;
6scanf("%lf", &x); // scanf에서 double은 %lf
7
8// 2. 문자열 입력 (공백 없는 경우)
9char str[101];
10scanf("%s", str); // & 붙이지 않음!
11
12// 3. 공백 포함 문자열 입력
13char line[101];
14fgets(line, sizeof(line), stdin);
15// fgets는 개행문자(\n)도 포함하므로 제거 필요
16line[strcspn(line, "\n")] = '\0';
17
18// 4. 여러 줄 입력
19int n;
20scanf("%d", &n);
21getchar(); // scanf 뒤의 개행문자 소비
22for (int i = 0; i < n; i++) {
23 char buf[101];
24 fgets(buf, sizeof(buf), stdin);
25 buf[strcspn(buf, "\n")] = '\0';
26}
27
28// 5. EOF까지 입력 (입력 개수 모를 때)
29int x;
30while (scanf("%d", &x) != EOF) {
31 // 처리
32}
33
34// 6. 2차원 배열 입력
35int grid[101][101];
36for (int i = 0; i < n; i++)
37 for (int j = 0; j < m; j++)
38 scanf("%d", &grid[i][j]);
39
40// 7. 문자 격자 입력 (붙어있는 경우: "01101")
41char grid2[101][101];
42for (int i = 0; i < n; i++)
43 scanf("%s", grid2[i]);
44// grid2[i][j]로 각 문자 접근

출력 패턴

출력 패턴
1// 소수점 자릿수 지정
2printf("%.2f\n", 3.14159); // 3.14
3printf("%.6f\n", 1.0 / 3.0); // 0.333333
4
5// 자릿수 맞춤 (패딩)
6printf("%5d\n", 42); // " 42" (우측 정렬)
7printf("%-5d|\n", 42); // "42 |" (좌측 정렬)
8printf("%05d\n", 42); // "00042" (0 패딩)
9
10// 여러 값 한 줄 출력
11for (int i = 0; i < n; i++)
12 printf("%d%c", arr[i], i < n-1 ? ' ' : '\n');

자료형 총정리

자료형크기범위사용처
int4B±2.1 × 10⁹대부분의 경우
long long8B±9.2 × 10¹⁸큰 수 계산, 누적합
unsigned int4B0 ~ 4.2 × 10⁹비트 연산
char1B-128 ~ 127문자, visited 배열
double8B±1.7 × 10³⁰⁸실수 계산
주의: 오버플로 주의! int × int는 int 범위를 넘을 수 있다. 캐스팅 필요: (long long)a * b
오버플로 방지
자주 출제
1// 오버플로 방지 패턴
2int a = 100000, b = 100000;
3
4// 잘못된 코드: int * int = int (오버플로!)
5long long wrong = a * b; // 이미 오버플로 발생 후 대입
6
7// 올바른 코드: 먼저 캐스팅
8long long correct = (long long)a * b; // 10000000000
9
10// 자주 실수하는 곳: 누적합, 곱셈, 조합
11// n이 10만이면 n*(n-1)/2는 약 50억 → long long 필요
12long long sum = (long long)n * (n - 1) / 2;
13
14// INF 값 설정
15#define INF 0x3f3f3f3f // int용 (약 10억)
16#define LLINF 0x3f3f3f3f3f3f3f3fLL // long long용

매크로와 전처리기

코테에서 매크로를 잘 쓰면 타이핑을 줄이고 실수를 방지할 수 있다.

자주 쓰는 매크로
1// ===== 필수 매크로 모음 =====
2
3// 최대/최소
4#define MAX(a, b) ((a) > (b) ? (a) : (b))
5#define MIN(a, b) ((a) < (b) ? (a) : (b))
6
7// 절대값
8#define ABS(x) ((x) < 0 ? -(x) : (x))
9
10// 두 값 교환
11#define SWAP(a, b, type) do { type _t = (a); (a) = (b); (b) = _t; } while(0)
12
13// 배열 크기
14#define ARRLEN(arr) (sizeof(arr) / sizeof(arr[0]))
15
16// 범위 체크 (2D 그리드)
17#define IN_RANGE(x, y, n, m) ((x) >= 0 && (x) < (n) && (y) >= 0 && (y) < (m))
18
19// 상수
20#define MAX_N 100001
21#define INF 0x3f3f3f3f
22#define MOD 1000000007
23
24// 4방향/8방향 이동
25int dx[] = {-1, 1, 0, 0};
26int dy[] = {0, 0, -1, 1};
27// 8방향: dx={-1,-1,-1,0,0,1,1,1}, dy={-1,0,1,-1,1,-1,0,1}
주의: 매크로 인자에 반드시 괄호를 씌워라! MAX(a+1, b)에서 괄호 없으면 연산 우선순위 문제 발생.
매크로 주의사항
1// 잘못된 매크로
2#define SQUARE(x) x * x
3// SQUARE(a + 1) → a + 1 * a + 1 (의도와 다름!)
4
5// 올바른 매크로
6#define SQUARE(x) ((x) * (x))
7// SQUARE(a + 1) → ((a + 1) * (a + 1)) ✓
8
9// do-while(0) 패턴: 여러 문장 매크로를 안전하게
10#define SWAP(a, b, type) do { \
11 type _t = (a); \
12 (a) = (b); \
13 (b) = _t; \
14} while(0)
15// if문 안에서도 안전하게 사용 가능

전역변수 vs 지역변수

코테에서 큰 배열은 반드시 전역으로 선언해야 한다. 지역변수는 스택에 할당되는데, 스택 크기는 보통 1~8MB로 제한된다.

전역 vs 지역 변수
1#include <stdio.h>
2
3// ✅ 전역: BSS/Data 영역에 할당 (수백 MB 가능)
4// 전역 변수는 자동으로 0으로 초기화됨
5int arr[10000001]; // 약 40MB - 전역이면 OK
6int visited[1001][1001]; // 약 4MB - 전역이면 OK
7char grid[1001][1001]; // 약 1MB
8
9int main(void) {
10 // ❌ 지역: 스택에 할당 (1~8MB 제한)
11 // int big[10000001]; → 스택 오버플로!
12
13 // ✅ 지역으로 써도 되는 크기
14 int small[10001]; // 약 40KB - OK
15
16 return 0;
17}
: 경험법칙: 배열 크기가 10만 이상이면 전역으로. 전역 배열은 자동으로 0으로 초기화되므로 memset이 불필요한 경우가 많다.
전역변수 활용 예시
실전 패턴
1// 전역변수 활용 패턴
2#include <stdio.h>
3#include <string.h>
4
5#define MAX_N 100001
6
7// 그래프 인접 리스트 (배열 기반)
8int head[MAX_N];
9int to[MAX_N * 2], nxt[MAX_N * 2];
10int edge_cnt;
11
12// BFS/DFS용
13int visited[MAX_N];
14int dist[MAX_N];
15int queue[MAX_N];
16
17// 여러 테스트 케이스에서 초기화
18void init(int n) {
19 edge_cnt = 0;
20 memset(head, -1, sizeof(int) * (n + 1));
21 memset(visited, 0, sizeof(int) * (n + 1));
22 memset(dist, 0, sizeof(int) * (n + 1));
23}
24
25int main(void) {
26 int T;
27 scanf("%d", &T);
28 while (T--) {
29 int n;
30 scanf("%d", &n);
31 init(n);
32 // 풀이
33 }
34 return 0;
35}

메모리 크기 계산법

배열의 메모리 크기를 빠르게 계산하는 방법: 요소 수 × 자료형 크기

선언크기전역/지역
int[100000]400KB지역 OK
int[1000000]4MB전역 권장
int[1000][1000]4MB전역 권장
long long[1000000]8MB전역 필수