C언어로 코딩테스트를 풀기 위한 기본기
코테에서 C와 C++의 가장 큰 차이는 STL 사용 가능 여부다. C++은 vector, map, set, queue, stack 등을 바로 쓸 수 있지만, C는 모든 자료구조를 직접 구현해야 한다.
| 항목 | C | C++ |
|---|---|---|
| 자료구조 | 직접 구현 | STL (vector, map 등) |
| 정렬 | qsort (비교함수 필요) | sort (람다/비교함수) |
| 입출력 | scanf/printf | cin/cout 또는 scanf/printf |
| 메모리 | malloc/free | new/delete 또는 STL |
| 문자열 | char 배열 + string.h | string 클래스 |
| bool | stdbool.h 또는 int | 기본 지원 |
1#include <stdio.h>2#include <stdlib.h>3#include <string.h>45#define MAX 1000016#define INF 0x3f3f3f3f78int main(void) {9 int n;10 scanf("%d", &n);1112 // 풀이 코드1314 return 0;15}
| 포맷 | 자료형 | 설명 |
|---|---|---|
| %d | int | 정수 |
| %ld | long | long 정수 |
| %lld | long long | 큰 정수 (최대 9.2 × 10¹⁸) |
| %u | unsigned int | 부호 없는 정수 |
| %f | float / double(printf) | 실수 (printf에서 float, double 모두) |
| %lf | double(scanf) | scanf에서 double 입력 시 필수 |
| %c | char | 문자 1개 (공백, 개행도 읽음!) |
| %s | char* | 공백 전까지의 문자열 |
| %x | unsigned int | 16진수 |
| %o | unsigned int | 8진수 |
%c는 공백/개행도 읽는다! 앞에 공백을 넣어서 scanf(" %c", &ch)로 사용하자.1// 1. 기본 정수/실수 입력2int n, m;3scanf("%d %d", &n, &m);45double x;6scanf("%lf", &x); // scanf에서 double은 %lf78// 2. 문자열 입력 (공백 없는 경우)9char str[101];10scanf("%s", str); // & 붙이지 않음!1112// 3. 공백 포함 문자열 입력13char line[101];14fgets(line, sizeof(line), stdin);15// fgets는 개행문자(\n)도 포함하므로 제거 필요16line[strcspn(line, "\n")] = '\0';1718// 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}2728// 5. EOF까지 입력 (입력 개수 모를 때)29int x;30while (scanf("%d", &x) != EOF) {31 // 처리32}3334// 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]);3940// 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.143printf("%.6f\n", 1.0 / 3.0); // 0.33333345// 자릿수 맞춤 (패딩)6printf("%5d\n", 42); // " 42" (우측 정렬)7printf("%-5d|\n", 42); // "42 |" (좌측 정렬)8printf("%05d\n", 42); // "00042" (0 패딩)910// 여러 값 한 줄 출력11for (int i = 0; i < n; i++)12 printf("%d%c", arr[i], i < n-1 ? ' ' : '\n');
| 자료형 | 크기 | 범위 | 사용처 |
|---|---|---|---|
| int | 4B | ±2.1 × 10⁹ | 대부분의 경우 |
| long long | 8B | ±9.2 × 10¹⁸ | 큰 수 계산, 누적합 |
| unsigned int | 4B | 0 ~ 4.2 × 10⁹ | 비트 연산 |
| char | 1B | -128 ~ 127 | 문자, visited 배열 |
| double | 8B | ±1.7 × 10³⁰⁸ | 실수 계산 |
(long long)a * b1// 오버플로 방지 패턴2int a = 100000, b = 100000;34// 잘못된 코드: int * int = int (오버플로!)5long long wrong = a * b; // 이미 오버플로 발생 후 대입67// 올바른 코드: 먼저 캐스팅8long long correct = (long long)a * b; // 10000000000910// 자주 실수하는 곳: 누적합, 곱셈, 조합11// n이 10만이면 n*(n-1)/2는 약 50억 → long long 필요12long long sum = (long long)n * (n - 1) / 2;1314// INF 값 설정15#define INF 0x3f3f3f3f // int용 (약 10억)16#define LLINF 0x3f3f3f3f3f3f3f3fLL // long long용
코테에서 매크로를 잘 쓰면 타이핑을 줄이고 실수를 방지할 수 있다.
1// ===== 필수 매크로 모음 =====23// 최대/최소4#define MAX(a, b) ((a) > (b) ? (a) : (b))5#define MIN(a, b) ((a) < (b) ? (a) : (b))67// 절대값8#define ABS(x) ((x) < 0 ? -(x) : (x))910// 두 값 교환11#define SWAP(a, b, type) do { type _t = (a); (a) = (b); (b) = _t; } while(0)1213// 배열 크기14#define ARRLEN(arr) (sizeof(arr) / sizeof(arr[0]))1516// 범위 체크 (2D 그리드)17#define IN_RANGE(x, y, n, m) ((x) >= 0 && (x) < (n) && (y) >= 0 && (y) < (m))1819// 상수20#define MAX_N 10000121#define INF 0x3f3f3f3f22#define MOD 10000000072324// 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 * x3// SQUARE(a + 1) → a + 1 * a + 1 (의도와 다름!)45// 올바른 매크로6#define SQUARE(x) ((x) * (x))7// SQUARE(a + 1) → ((a + 1) * (a + 1)) ✓89// 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문 안에서도 안전하게 사용 가능
코테에서 큰 배열은 반드시 전역으로 선언해야 한다. 지역변수는 스택에 할당되는데, 스택 크기는 보통 1~8MB로 제한된다.
1#include <stdio.h>23// ✅ 전역: BSS/Data 영역에 할당 (수백 MB 가능)4// 전역 변수는 자동으로 0으로 초기화됨5int arr[10000001]; // 약 40MB - 전역이면 OK6int visited[1001][1001]; // 약 4MB - 전역이면 OK7char grid[1001][1001]; // 약 1MB89int main(void) {10 // ❌ 지역: 스택에 할당 (1~8MB 제한)11 // int big[10000001]; → 스택 오버플로!1213 // ✅ 지역으로 써도 되는 크기14 int small[10001]; // 약 40KB - OK1516 return 0;17}
1// 전역변수 활용 패턴2#include <stdio.h>3#include <string.h>45#define MAX_N 10000167// 그래프 인접 리스트 (배열 기반)8int head[MAX_N];9int to[MAX_N * 2], nxt[MAX_N * 2];10int edge_cnt;1112// BFS/DFS용13int visited[MAX_N];14int dist[MAX_N];15int queue[MAX_N];1617// 여러 테스트 케이스에서 초기화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}2425int 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 | 전역 필수 |