C 연산자 · 비트 · 제어문 E04

우선순위, 비트 연산, 삼항, break 없는 switch, static 지역변수, 진법. 짧은 코드에 함정 하나씩이다

왜 이 장을 하나
이 장의 코드는 10줄 안팎이고 답은 숫자 하나가 대부분이다. 그런데도 틀리는 이유는 연산자 하나의 규칙을 잘못 알아서다. << 는 곱셈, &&& 는 다른 것, switch 는 break 없으면 흘러내리고, static 은 남는다. 규칙 10개를 표로 굳히고 기출 11문항에 대 본다.
다루는 것우선순위 · 비트 · 삼항 · switch · static · 진법 · 반복 누적
문항기출 11 · 예상 3 · 훈련 10
끝나면연산자 식을 2진수 표로 계산한다

01왜 이 장인가

왜 이걸 하나 — C 42문항 중 11문항이 이 장이고, 짧아서 시간이 덜 드는 5점이다. 규칙만 맞으면 30초짜리다.

11회분 220문항 중 이 장의 기출은 11문항이다. 11회 중 8회에 나왔고 2023년 2회 · 2025년 3회에 2문항씩이었다. 훈련 C 10문항(5 · 6 · 11 · 14~16 · 20~22 · 27 · 30)이 붙는다.

몇 문항무엇을 적나보기
연산자 식 계산4시프트 · 삼항 · 비트 AND · 논리 · unsigned · XOR2024-1회 2번 · 2025-1회 19번 · 2025-3회 15번 · 17번
switch 흘러내림2break 없는 case 의 출력 · 누적2023-2회 10번 · 2024-2회 10번
반복 누적 · 판별2배수 세기 · 완전수 합2023-2회 4번 · 2023-3회 9번
빈칸 — 식2진법 변환의 % 와 자리값, 나눗셈 · 나머지 식 4개2023-1회 9번 · 2023-2회 13번
static 지역변수1호출마다 누적된 값의 합2024-3회 6번

→ 11문항 중 답이 숫자 하나인 것이 8문항이다. 계산이 짧은 만큼 규칙을 하나만 잘못 알아도 통째로 틀리고, 부분 점수도 없다. 2023년 2회 13번(식 4개)만 소문항이 있다.

E01 진단에 이 장 문항은 없었다. 그러나 2025년 3회에 2문항(비트 · unsigned)이 나왔고 2026년에는 안 나왔으니, 3회에 다시 나올 자리로 본다. 03절의 규칙 표를 외우면 이 장은 끝난다.

문항 수는 이 노트의 문제 데이터에서 ch=E04 인 것을 센 값이다(2026-09-14). 2023-2회 13번은 grandlife 복원이 Java 코드라 그대로 실었다(식 자체는 C 와 같다). 11문항 전부 실행하거나 빈칸을 채워 실행해 정답을 확인했다.

02한눈에

왜 이걸 하나 — 시험 전날 규칙 10개를 다시 본다.

우선순위괄호 → 단항(++ -- ! ~ * & 주소) → * / %+ - → 시프트 << >> → 비교 < > <= >=== != → 비트 &^| → 논리 &&|| → 삼항 ?: → 대입. 비트 &== 보다 낮다.
시프트x << n = x × 2ⁿ, x >> n = x ÷ 2ⁿ(내림). 37 << 2 = 148.
비트 연산& 둘 다 1 · | 하나라도 1 · ^ 다르면 1 · ~ 뒤집기. 2진수 8자리로 펴서 자리마다 계산. 12 & 10 = 8, 12 | 10 = 14, 12 ^ 10 = 6.
논리 연산결과는 1 또는 0. && 는 둘 다 참, || 는 하나라도. 0 이 아니면 참. &(비트)와 &&(논리)는 다른 연산자.
삼항 · 증감조건 ? A : B. x++ 는 쓰고 나서 증가, ++x 는 증가하고 나서 씀. i++ + ++i 같은 식은 왼쪽부터 값을 적으며 간다.
switch 흘러내림맞는 case 부터 break 를 만날 때까지 아래 case 와 default 를 전부 실행한다. case 값이 겹치면 첫 것에서 시작.
static 지역변수함수가 끝나도 값이 남는다. 초기화는 첫 호출 때 한 번. 같은 이름의 main 지역변수와는 다른 변수.
정수 나눗셈 · 나머지7 / 2 = 3, 7 % 2 = 1. 금액 분해는 m / 1000, (m % 1000) / 500 …. 자릿수 분리는 n % 10n /= 10.
진법2진수 → 10진수: 아래 자리부터 % 10 으로 떼어 place(1 · 2 · 4 …)를 곱해 더한다. 10진수 → 2진수: % 2 로 떼어 거꾸로. 16진수 A~F = 10~15, 0xA5 = 1010 0101.
unsigned음수가 없다. 0 아래로 내려가면 최댓값으로 되감긴다. %u 로 찍는다. 5u · 7u 는 unsigned 상수.

03밑바닥

왜 이걸 하나 — 규칙 10개는 2진수 한 줄과 흐름 한 줄이면 전부 그려진다.

3-1. 비트 식은 8자리 2진수로 펴서 자리마다 계산한다

비트 연산은 숫자를 2진수로 펴 놓고 같은 자리끼리 계산한다. 12 는 1100, 10 은 1010 이라 AND 는 둘 다 1 인 자리만 → 1000 = 8, OR 는 하나라도 1 → 1110 = 14, XOR 는 다르면 1 → 0110 = 6(예상 1회 6번). 시프트는 자리를 옮기는 것이라 왼쪽 n 칸은 2ⁿ 배, 오른쪽 n 칸은 2ⁿ 으로 나눈 몫이다.

2024년 1회 2번은 삼항과 시프트다. x = 37 은 20 보다 크니 x << 2 = 148, 거기에 3 을 더해 151. 2025년 1회 19번은 value & 0xA5 로 점수마다 1010 0101 자리만 남긴다 — 0xDB(1101 1011) & 0xA5 = 1000 0001 = 129 처럼 한 값씩 표로 계산해 합하면 908 이다. 16진수 한 자리가 2진수 네 자리라는 것만 알면 된다.

2025년 3회 15번은 &&& 가 한 식에 있다. y % 3 < 3 은 1 < 3 참이라 z = 2, z & z >> 1 은 시프트가 먼저라 2 & 1 = 0, 마지막 삼항에서 x > 5 && z <= 3 이 참이라 z × x = 0. 비트 & 는 시프트보다 늦고 논리 && 는 비교보다 늦다는 우선순위가 답을 정한다.

3-2. switch 는 break 가 없으면 흘러내린다

switch 는 맞는 case 로 뛰어 들어간 뒤 break 를 만날 때까지 아래로 계속 실행한다. 2023년 2회 10번은 total 240 을 30 으로 나눈 8 이라 case 8 부터 시작해 B · C · D 를 전부 찍는다(A 는 위라 안 찍힘). 2024년 2회 10번은 7 % 3 = 1 이라 case 1 부터 −7 − 4 − 2 = −13 이다.

case 값이 겹쳐 적힌 것(case 10: case 9:)은 둘 중 어느 값이든 같은 자리에서 시작한다는 뜻이다. break 가 하나도 없는 switch 는 「시작점만 정하고 아래는 전부」라고 읽으면 틀리지 않는다.

3-3. static 은 남고, 지역변수는 함수마다 따로다

함수 안의 static int value = 0; 은 첫 호출 때 한 번만 0 이 되고 그 뒤로는 이전 호출의 값이 남는다. 2024년 3회 6번의 next_value 는 부를 때마다 2 · 4 · 6 · 8 을 돌려주고 합이 20 이다. main 에도 value 라는 변수가 있지만 이름만 같은 다른 변수라 서로 건드리지 않는다(훈련 11 · 27번).

반복 누적 문제는 조건이 전부다. 2023년 2회 4번은 1~2023 에서 4 의 배수를 세면 2023 / 4 = 505 이고, 2023년 3회 9번은 1~100 의 완전수(약수 합이 자기 자신: 6 · 28)를 더해 34 다. 반복 변수의 시작 · 끝 · 증가폭과 조건식을 표에 적으면 손으로도 센다.

3-4. 진법과 금액 분해는 나머지와 몫이다

2진수 101110 을 10진수로 바꾸는 코드(2023년 1회 9번)는 아래 자리부터 input % 10 으로 한 자리를 떼고, 자리값 place 를 1 · 2 · 4 · 8 로 키우며 곱해 더한다. 빈칸은 %2(place *= 2)이고, 채워 돌리면 46 이 나온다. 거꾸로 10진수 → 2진수는 % 2 로 떼어 뒤에서부터 적는다(훈련 30번).

금액 4620 을 1000 · 500 · 100 · 10 으로 나누는 식(2023년 2회 13번)은 큰 단위부터 몫을 취하고 나머지를 다음 단위로 넘긴다 — money / 1000 = 4, (money % 1000) / 500 = 1, (money % 500) / 100 = 1, (money % 100) / 10 = 2. 정수 나눗셈이 몫만 남긴다는 것이 이 식의 근거다.

unsigned(2025년 3회 17번)는 음수가 없는 정수다. 이 문제는 되감김이 없고 리스트 값을 sum × 3 + value 로 모아 125 를 만든 뒤 42 와 XOR 해 87, 100 을 더해 187 이다. XOR 도 2진수로 펴서 자리마다 다르면 1 로 계산한다.

통과 기준은 넷이다. 첫째, 12 와 10 의 AND · OR · XOR · 시프트를 2진수로 펴서 30초 안에 계산한다. 둘째, 우선순위 표에서 시프트 · 비트 · 비교 · 논리 · 삼항의 순서를 안 보고 적는다. 셋째, break 없는 switch 의 시작점과 흘러내림을 손으로 따라간다. 넷째, static 지역변수 · 진법 변환 · 금액 분해의 규칙을 말한다. 기출 11문항이 이 넷 안에 있다.
& 와 && 는 같은 AND 다.& 는 비트끼리, && 는 참 · 거짓끼리다. 2 & 1 은 0(0010 과 0001 은 겹치는 자리가 없다)이지만 2 && 1 은 1(둘 다 0 이 아니라 참). 2025년 3회 15번이 이 둘을 한 식에 넣었다.
switch 는 맞는 case 하나만 실행한다.break 가 있을 때만 그렇다. 없으면 아래 case 와 default 까지 흘러내린다. 2023년 2회 10번이 B 하나가 아니라 BCD 인 이유다.

04기출

왜 이걸 하나 — 규칙 10개를 기출 11문항에 하나씩 대 본다. 어느 규칙이 빠졌는지 정답을 열면 보인다.

비트 식은 2진수 8자리를 종이에 적고, switch 는 시작 case 에 표시를 하고 아래로 선을 긋고, 반복문은 변수 표를 그린다. 답이 숫자 하나라도 과정을 답 칸에 적어 두면 틀린 자리가 남는다.

2023-1회 · 9번C

다음 C 프로그램에서 ①과 ②에 들어갈 연산자와 값을 순서대로 쓰시오.

#include <stdio.h> int main(void) { int input = 101110; int place = 1; int sum = 0; while (input != 0) { sum += (input ① 10) * place; place *= ②; input /= 10; } printf("%d", sum); return 0; }
정답① %, ② 2

— ① % ② 2. 2진수로 적힌 수 101110 을 10진수 46 으로 바꾸는 코드다.

원리 — 2진수 101110 은 자리마다 값이 다르다. 오른쪽 끝 자리는 1, 그다음은 2, 4, 8, 16, 32 다. 그러니 10진수로 바꾸려면 "각 자리의 숫자 × 그 자리의 값"을 전부 더하면 된다. 코드는 input 을 10 으로 나눈 나머지(input % 10)로 맨 오른쪽 자리를 떼어 내고, input /= 10 으로 그 자리를 버린다. 자리 값 place 는 1 에서 시작해 자리를 옮길 때마다 2배(place *= 2)가 된다. 그래서 ① 은 한 자리를 떼는 나머지 연산, ② 는 자리 값을 키우는 2 다.

따라가기 — input = 101110, place = 1, sum = 0.

  1. 0 × 1 = 0, place 2, input 10111
  2. 1 × 2 = 2, place 4, input 1011
  3. 1 × 4 = 4, place 8, input 101
  4. 1 × 8 = 8, place 16, input 10
  5. 0 × 16 = 0, place 32, input 1
  6. 1 × 32 = 32, input 0 → 끝. sum = 0 + 2 + 4 + 8 + 0 + 32 = 46

헷갈리는 자리 — ② 에 10 을 적는 것. 10 을 곱하면 101110 을 그대로 다시 만드는 셈이라 아무 변환도 안 된다. 2진 → 10진은 자리 값이 2배씩이라 2 다. ① 에 / 를 적으면 자리를 떼는 게 아니라 버리는 것이라 값이 커진다. 답은 기호 하나와 숫자 하나다.

빈칸에 답을 넣어 컴파일 · 실행해 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-2회 · 4번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int main(void) { int count = 0; for (int number = 1; number <= 2023; number++) { if (number % 4 == 0) { count++; } } printf("%d", count); return 0; }
정답505

— 505. 1 부터 2023 까지 4 의 배수가 몇 개인지 세는 코드이고, 2023 ÷ 4 = 505.75 이므로 505 개다.

원리number % 4 == 0 은 "4 로 나눠 나머지가 없다", 즉 4 의 배수라는 뜻이다. 1 부터 N 까지 k 의 배수 개수는 N 을 k 로 나눈 몫이다 — 4, 8, 12, … 처럼 k 칸마다 하나씩 있기 때문이다. 코드는 2023 번 반복하며 하나씩 세지만 손으로는 나눗셈 한 번이면 된다. 시험에서는 반복문을 실제로 돌리지 말고 "무엇을 세는가"를 읽어 수식으로 바꾸는 것이 시간을 아끼는 길이다.

따라가기

  1. 4 의 배수: 4, 8, 12, …, 2020 (2020 = 4 × 505)
  2. 2024 는 범위 밖(number <= 2023)
  3. 개수 = 2023 ÷ 4 의 몫 = 505

헷갈리는 자리 — 505.75 를 반올림해 506 으로 적는 것. 마지막 배수 2020 뒤에 2024 는 없으니 몫만 센다. 범위가 < 2023 이어도 2023 은 배수가 아니라 답이 같지만, 2024 까지였다면 506 이다 — 끝 값이 배수인지 확인하는 습관을 둔다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-2회 · 10번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int main(void) { int score[3] = { 76, 86, 78 }; int total = 0; for (int i = 0; i < 3; i++) { total += score[i]; } switch (total / 30) { case 10: case 9: printf("A"); case 8: printf("B"); case 7: case 6: printf("C"); default: printf("D"); } return 0; }
정답BCD

— BCD. 합 240 을 30 으로 나눈 몫 8 에서 시작해, break 가 없어 그 아래 case 를 전부 지나간다.

원리 — switch 는 값이 맞는 case 로 뛰어든 뒤 break 를 만날 때까지 아래로 계속 흘러내린다. 이 코드에는 break 가 하나도 없으므로 case 8 에 들어가면 그 아래의 case 7 · 6 · default 까지 전부 실행된다. 이름표(case)는 "여기서부터 시작"을 정할 뿐 "여기까지"를 정하지 않는다. 정수 나눗셈 total / 30 은 소수점을 버리므로 240 / 30 = 8 이다.

따라가기

  1. total = 76 + 86 + 78 = 240, 240 / 30 = 8
  2. case 8 진입 → B
  3. break 없음 → case 7, case 6 의 printf("C") → C
  4. default → D. 화면 BCD

헷갈리는 자리 — B 만 적는 것(break 가 있을 때의 답). 그리고 A 부터 적는 것 — 8 은 case 10 · 9 를 거치지 않고 case 8 로 바로 들어간다. 위쪽은 안 지나가고 아래쪽만 흘러내린다는 방향을 기억한다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-2회 · 13번C

금액 4,620원을 1,000원, 500원, 100원, 10원 단위로 나눌 때 각 개수를 구하는 ①부터 ④의 식을 쓰시오.

public class Main { public static void main(String[] args) { int money = 4620; int a = ( ① ); int b = ( ② ); int c = ( ③ ); int d = ( ④ ); System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); } }
정답① money / 1000, ② (money % 1000) / 500, ③ (money % 500) / 100, ④ (money % 100) / 10

— ① money / 1000(money % 1000) / 500(money % 500) / 100(money % 100) / 10. 큰 단위부터 몫을 세고, 나머지를 다음 단위로 넘긴다.

원리 — 4,620원을 지폐와 동전으로 바꾸는 절차 그대로다. 1,000원짜리는 4,620 을 1,000 으로 나눈 몫(4장). 남은 돈은 4,620 을 1,000 으로 나눈 나머지 620 이고, 그것을 500 으로 나눈 몫이 500원짜리(1개). 다시 나머지 120 을 100 으로 나눈 몫이 100원(1개), 나머지 20 을 10 으로 나눈 몫이 10원(2개). 정수 나눗셈 / 은 "몇 개", 나머지 % 는 "남은 돈"이다. 앞 단위의 나머지가 다음 단위의 입력이 되는 사슬이라 식마다 % 앞 단위 가 붙는다.

따라가기

  1. a = 4620 / 1000 = 4 (남은 돈 4620 % 1000 = 620)
  2. b = 620 / 500 = 1 (남은 돈 620 % 500 = 120)
  3. c = 120 / 100 = 1 (남은 돈 120 % 100 = 20)
  4. d = 20 / 10 = 2 → 화면에 4, 1, 1, 2 가 한 줄씩

헷갈리는 자리 — ② 를 money / 500 으로 적으면 9 가 된다(1,000원짜리로 이미 낸 돈을 또 센다). 나머지를 먼저 취해야 한다. money % 500 / 100 처럼 괄호를 빼도 % 와 / 는 같은 순위라 왼쪽부터 계산돼 결과는 같지만, 답안에는 괄호를 넣는 편이 안전하다.

빈칸에 답을 넣어 컴파일 · 실행해 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2023-3회 · 9번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int isPerfect(int number) { int divisor; int total = 0; for (divisor = 1; divisor <= number / 2; divisor++) { if (number % divisor == 0) { total += divisor; } } return number == total; } int main(void) { int number; int result = 0; for (number = 2; number <= 100; number++) { if (isPerfect(number)) { result += number; } } printf("%d", result); return 0; }
정답34

— 34. 2 부터 100 사이의 완전수는 6 과 28 뿐이고 그 합이 34 다.

원리 — 완전수는 자기 자신을 뺀 약수를 전부 더하면 자기 자신이 되는 수다. 6 의 약수 1 · 2 · 3 은 더하면 6 이다. isPerfect 는 1 부터 number / 2 까지 나눠떨어지는 수(약수)를 더해 number == total 인지 돌려준다. 자기 자신보다 큰 약수는 없고 자기 자신의 절반보다 큰 약수도 자기 자신뿐이라 절반까지만 본다. main 은 2 부터 100 까지 그 검사를 돌려 참인 수를 result 에 더한다. C 에서 비교식은 참이면 1, 거짓이면 0 이라 return number == total 이 그대로 조건이 된다.

따라가기

  1. 6: 약수 1 + 2 + 3 = 6 → 완전수, result = 6
  2. 28: 1 + 2 + 4 + 7 + 14 = 28 → 완전수, result = 34
  3. 다음 완전수 496 은 범위 밖 → 34

헷갈리는 자리 — 1 을 완전수로 세는 것. 1 의 약수 합은 0 이라 아니고, 어차피 반복이 2 부터 시작한다. 2 부터 100 을 손으로 다 돌리지 말고 "완전수 6 · 28 · 496 · 8128" 네 개를 외워 두면 이 유형은 즉답이다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-1회 · 2번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int main(void) { int x = 37; int y = (x > 20) ? (x << 2) : (x >> 1); printf("%d", y + 3); return 0; }
정답151

— 151. 37 은 20 보다 크므로 왼쪽 시프트 37 << 2 = 148 이 y 가 되고, 3 을 더한다.

원리 — 삼항 연산자 조건 ? A : B 는 조건이 참이면 A, 거짓이면 B 를 고르는 한 줄짜리 if 다. 비트 시프트 << n 은 2진수 자릿수를 왼쪽으로 n 칸 미는 것이고, 그 결과는 값이 2ⁿ 배가 된다. 오른쪽 >> n 은 2ⁿ 으로 나눈 몫이다. 그래서 x << 2 는 × 4, x >> 1 은 ÷ 2 로 읽으면 2진수를 안 적어도 된다.

따라가기

  1. x = 37, 조건 37 > 20 참 → x << 2 선택
  2. 37 × 4 = 148 (2진수로 100101 → 10010100)
  3. y + 3 = 148 + 3 = 151

헷갈리는 자리 — 조건이 거짓인 쪽(37 >> 1 = 18)까지 계산해 섞는 것. 삼항은 한쪽만 계산한다. 시프트를 "2를 더한다"로 착각해 39 를 적는 것도 흔하다 — 시프트는 곱하기 · 나누기다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-2회 · 10번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int calculate(int value) { int result = -value; switch (value % 3) { case 0: result += 3; case 1: result -= 4; default: result -= 2; } return result; } int main(void) { printf("%d", calculate(7)); return 0; }
정답-13

— −13. 7 % 3 = 1 이라 case 1 로 들어가고, break 가 없어 default 까지 흘러내려 −7 − 4 − 2 다.

원리int result = -value 로 출발점이 음수(−7)다. switch 는 맞는 case 로 뛰어든 뒤 break 가 없으면 아래 case 를 전부 실행한다. 이 코드는 case 0 · 1 · default 어디에도 break 가 없으니 진입한 자리부터 끝까지 다 더하고 뺀다. 7 을 3 으로 나눈 나머지는 1 이므로 case 0 의 += 3 은 건너뛰고 case 1 부터다. 음수에서 빼면 더 작아진다는 것만 조심하면 된다.

따라가기

  1. result = −7, 7 % 3 = 1 → case 1 진입
  2. result −= 4 → −11
  3. break 없음 → default: result −= 2 → −13

헷갈리는 자리 — case 0 의 +3 까지 넣어 −10 으로 적는 것(위쪽 case 는 안 지나간다). break 가 있다고 넘겨짚어 −11 로 적는 것. 답의 음수 기호를 빼먹는 것. 세 함정이 한 문제에 있다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2024-3회 · 6번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int next_value(void) { static int value = 0; value += 2; return value; } int main(void) { int value = 1; int sum = 0; for (int i = 0; i < 4; i++) { value++; sum += next_value(); } printf("%d", sum); return 0; }
정답20

— 20. static 변수는 함수가 끝나도 값을 기억하므로 next_value 는 2 · 4 · 6 · 8 을 차례로 돌려주고, 그 합이 20 이다.

원리 — 보통의 지역 변수는 함수가 불릴 때 새로 만들어지고 끝나면 사라진다. 그런데 static int value = 0 은 프로그램이 시작할 때 한 번만 0 으로 만들어지고, 함수가 끝나도 지워지지 않고 다음 호출 때 이어서 쓴다. 그래서 부를 때마다 2 씩 쌓인다. main 에도 value 라는 변수가 있지만 이름만 같을 뿐 다른 칸이고, main 의 value 는 어디에도 쓰이지 않는 미끼다.

따라가기

  1. 1회: value 0 → 2, 돌려줌 2, sum 2
  2. 2회: 2 → 4, sum 6
  3. 3회: 4 → 6, sum 12
  4. 4회: 6 → 8, sum 20

헷갈리는 자리 — static 을 무시해 매번 0 에서 시작한다고 보면 2 × 4 = 8 이 된다. main 의 value++ 를 함수 안 value 와 섞어 계산하는 것 — 이름이 같아도 범위가 다르면 남남이다. 함수 안의 value 는 밖에서 볼 수도 없다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-1회 · 19번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> typedef struct { const char *name; int score[3]; } Student; int decode(int value) { return value & 0xA5; } int total(const Student *student) { return decode(student->score[0]) + decode(student->score[1]) + decode(student->score[2]); } int main(void) { Student students[2] = { { "Kim", { 0xA0, 0xA5, 0xDB } }, { "Lee", { 0xA0, 0xED, 0x81 } } }; int result = 0; for (int i = 0; i < 2; i++) result += total(&students[i]); printf("%d", result); return 0; }
정답908

— 908. 점수 여섯 개를 각각 0xA5 와 비트 AND 한 값의 합이고, 두 학생 모두 454 다.

원리 — 비트 AND(&)는 두 수를 2진수로 나란히 놓고 자리마다 둘 다 1 일 때만 1 을 남긴다. 0xA5 는 2진수 1010 0101 이라 "그 자리들만 남기는 거름망"이다. 16진수 한 자리는 2진수 네 자리이므로 A = 1010, 5 = 0101, D = 1101, B = 1011, E = 1110, 8 = 1000, 1 = 0001, 0 = 0000 으로 펴서 자리별로 맞춘다. 구조체 배열 두 개를 돌며 각 학생의 세 점수를 거른 뒤 전부 더한다.

따라가기 — 거름망 1010 0101.

  1. 0xA0 = 1010 0000 → 1010 0000 = 0xA0 = 160
  2. 0xA5 = 1010 0101 → 그대로 165
  3. 0xDB = 1101 1011 → 1000 0001 = 0x81 = 129 → Kim 합 454
  4. Lee: 0xA0 → 160, 0xED = 1110 1101 → 1010 0101 = 165, 0x81 = 1000 0001 → 129 → 454
  5. 454 + 454 = 908

헷갈리는 자리 — AND 를 "둘 중 하나라도 1"(OR)로 계산하는 것과, 16진수를 10진수로 먼저 바꾼 뒤 논리 AND(&&)처럼 다루는 것. 한 자리씩 2진수로 펴서 겹치는 1 만 남기는 것이 전부이고, 마지막에 10진수로 돌린다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-3회 · 15번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> int main(void) { int x = 7, y = 4, z; z = y % 3 < 3 ? 2 : 1; z = z & z >> 1; z = x > 5 && z <= 3 ? z * x : z / x; printf("%d", z); return 0; }
정답0

— 0. 둘째 줄에서 z & z >> 1 이 2 & 1 = 0 이 되고, 그 뒤로는 0 에 무엇을 곱해도 0 이다.

원리 — 한 줄에 연산자가 여럿이면 우선순위가 계산 순서를 정한다. 산술(%) → 시프트(>>) → 비교(<, >) → 비트 AND(&) → 논리 AND(&&) → 삼항(?:) 순이다. 둘째 줄 z & z >> 1 은 시프트가 먼저라 z & (z >> 1) 이다. 셋째 줄은 && 가 삼항보다 먼저 묶여 (x > 5 && z <= 3) ? z * x : z / x 로 읽는다.

따라가기 — x = 7, y = 4.

  1. z = (4 % 3 < 3) ? 2 : 1 → 1 < 3 참 → z = 2
  2. z = 2 & (2 >> 1) = 2 & 1 = 10 & 01 = 0
  3. 조건 7 > 5 참, 0 ≤ 3 참 → z * x = 0 × 7 = 0

헷갈리는 자리 — 둘째 줄을 (z & z) >> 1 로 읽어 1 을 얻는 것. 비트 AND 는 시프트보다 나중이다. 셋째 줄에서 조건이 거짓이라고 보고 z / x 로 가도 0 / 7 = 0 이라 답은 같지만, 이유가 다르면 다음 문제에서 틀린다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

2025-3회 · 17번C

다음 C 프로그램의 실행 결과를 쓰시오.

#include <stdio.h> struct Node { struct Node *next; unsigned int value; }; int main(void) { struct Node n1 = { 0, 5u }; struct Node n2 = { 0, 7u }; struct Node n3 = { 0, 11u }; n3.next = &n2; n2.next = &n1; struct Node *cur = &n3; unsigned int sum = 0; while (cur) { sum = sum * 3 + cur->value; cur = cur->next; } sum = (sum ^ 42u) + 100u; printf("%u", sum); return 0; }
정답187

— 187. 리스트를 11 → 7 → 5 순서로 돌며 sum = sum × 3 + 값 으로 125 를 만들고, 42 와 XOR 한 87 에 100 을 더한다.

원리sum = sum * 3 + value 는 "지금까지의 결과를 3배 하고 새 값을 더한다"로, 값을 3진수 자릿수처럼 쌓는 식이다. 사슬은 n3 → n2 → n1(next 대입 두 줄)이라 값 순서는 11 · 7 · 5 다. XOR(^)는 두 수를 2진수로 놓고 자리가 다르면 1, 같으면 0 이다. unsigned 는 음수를 안 쓰는 정수라는 뜻일 뿐 여기선 값에 영향이 없고, %u 로 그대로 찍는다.

따라가기

  1. cur = n3(11): sum = 0 × 3 + 11 = 11
  2. cur = n2(7): sum = 11 × 3 + 7 = 40
  3. cur = n1(5): sum = 40 × 3 + 5 = 125, next 가 0 → 끝
  4. 125 ^ 42: 0111 1101 ^ 0010 1010 = 0101 0111 = 87
  5. 87 + 100 = 187

헷갈리는 자리 — 사슬을 n1 → n2 → n3(5 · 7 · 11)로 돌려 sum 을 5 → 22 → 77 로 만드는 것. cur 의 시작이 n3 이고 n3.next = n2, n2.next = n1 이다. XOR 를 OR(127)나 AND(40)로 계산하는 것도 흔하다 — "다르면 1"이다.

복원 코드를 gcc(도커 alpine)로 실행해 같은 출력을 확인했다. 복원 출처 grandlife.co.kr(2026-09-14) — 공식 원문이 아니라 문장 · 변수명이 실제와 다를 수 있다.

예상 1회 · 6번C빈출 원리: 비트 연산

다음 코드의 출력을 쓰시오.

int a = 12, b = 10; printf("%d %d %d", a & b, a | b, a ^ b);
정답8 14 6

— 8 14 6. 12 = 1100, 10 = 1010 을 비트 단위로 AND · OR · XOR 한 값이다.

원리 — 비트 연산은 두 수를 2진수로 놓고 같은 자리끼리 계산한다. AND(&)는 둘 다 1 일 때만 1, OR(|)는 하나라도 1 이면 1, XOR(^)는 서로 다를 때만 1 이다. 12 는 8+4 = 1100, 10 은 8+2 = 1010 이다. 자리별로 보면 8의 자리는 둘 다 1, 4의 자리는 12 만, 2의 자리는 10 만, 1의 자리는 둘 다 0 이다. AND 는 8의 자리만 남아 8, OR 는 8+4+2 = 14, XOR 는 4+2 = 6 이다. 2진수로 바꿔 세로로 놓고 푸는 것이 가장 안 틀린다.

따라가기

  1. 12 = 1100, 10 = 1010
  2. AND: 1000 = 8
  3. OR: 1110 = 14
  4. XOR: 0110 = 6

헷갈리는 자리 — & 와 && 를 섞는 것. && 는 논리 AND 라 12 && 10 은 1 이다. 한 글자면 비트, 두 글자면 논리다. 그리고 XOR 는 "같은 수를 두 번 XOR 하면 원래대로" 라는 성질(a ^ b ^ b = a)이 있어 값 교환 · 암호 문제에 나온다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 2회 · 3번C빈출 원리: 약수 판별

다음 코드의 출력을 쓰시오.

int n = 36, cnt = 0; for (int i = 1; i <= n; i++) if (n % i == 0) cnt++; printf("%d", cnt);
정답9

— 9. 36 의 약수 1 · 2 · 3 · 4 · 6 · 9 · 12 · 18 · 36 이 아홉 개다.

원리 — n % i == 0 은 "n 을 i 로 나눈 나머지가 0" 곧 i 가 n 의 약수라는 뜻이다. 1 부터 n 까지 전부 나눠 보며 약수를 세는 가장 단순한 방법이다. 36 = 2² × 3² 이라 약수 개수는 (2+1)(2+1) = 9 로 공식으로도 확인된다. 반복 범위가 i <= n 이라 36 자신도 포함된다 — 범위를 i < n 으로 바꾸면 8 이 된다. 시험은 이런 경계 하나를 바꿔서 낸다.

따라가기

  1. i = 1 · 2 · 3 · 4 · 6 → 5개
  2. i = 9 · 12 · 18 · 36 → 4개
  3. 9

헷갈리는 자리 — 약수를 세다가 6 을 두 번 세거나(6 × 6 = 36 이라 짝이 자기 자신) 1 · 36 을 빼먹는 것. 코드는 i 를 하나씩 올리니 그런 실수가 없다 — 손으로 셀 때는 짝(1·36, 2·18, 3·12, 4·9, 6)으로 세면 9 가 바로 나온다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

예상 2회 · 6번C빈출 원리: do-while

다음 코드의 출력을 쓰시오.

int i = 0, sum = 0; do { sum += i; i += 2; } while (i < 10); printf("%d %d", i, sum);
정답10 20

— 10 20. 0 + 2 + 4 + 6 + 8 = 20 을 더하고 i 는 10 에서 멈춘다.

원리 — do-while 은 본문을 먼저 한 번 실행하고 그다음에 조건을 검사한다. 조건이 처음부터 거짓이어도 본문이 한 번은 돈다는 것이 while 과의 차이다. 여기서는 sum += i 를 한 뒤 i 를 2 올리고 i < 10 인지 본다. i 가 8 일 때 sum 에 8 을 더하고 i 가 10 이 되어 조건이 거짓이라 끝난다. 그래서 마지막으로 더해진 값은 8 이고 i 의 최종값은 10 이다 — "더한 값"과 "멈춘 i" 가 다르다.

따라가기

  1. i=0 sum 0 → i 2 · i=2 sum 2 → i 4
  2. i=4 sum 6 → i 6 · i=6 sum 12 → i 8
  3. i=8 sum 20 → i 10 → 10 < 10 거짓 → 종료
  4. 출력 10 20

헷갈리는 자리 — 10 도 더해서 30 으로 적는 것. 10 은 조건 검사에서 걸러져 본문에 들어오지 못한다. 그리고 출력 순서 — printf 가 i 를 먼저, sum 을 나중에 찍는다. 변수 순서를 바꿔 읽으면 20 10 이 된다.

이전 자료의 예상 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

05틀리는 자리

왜 이걸 하나 — 짧은 코드는 규칙 하나에서 통째로 깎인다.

자리새는 꼴적는 법기출
우선순위z & z >> 1 을 왼쪽부터 계산한다시프트가 & 보다 먼저. 표를 외운다2025-3회 15번
& · &&비트를 논리로, 논리를 비트로기호 개수를 센다. 하나면 비트, 둘이면 논리2025-3회 15번
흘러내림맞는 case 하나만 적는다break 가 없으면 아래 전부2023-2회 10번 · 2024-2회 10번
static호출마다 0 으로 초기화한다초기화는 첫 호출 한 번. 값이 누적된다2024-3회 6번
같은 이름main 의 value 와 함수의 value 를 섞는다다른 변수. 함수 안 static 만 누적2024-3회 6번
16진수0xA5 를 10진수로 바꾸지 않고 AND 한다2진수 8자리로 펴서 자리마다2025-1회 19번
빈칸 식place *= 2 를 10 으로2진수 자리값은 2 의 거듭제곱2023-1회 9번
서식%u%d 로 읽어 음수를 적는다unsigned 는 음수가 없다2025-3회 17번

이 장의 답은 계산 한 번에 숫자 하나라 되돌아볼 자리가 없다. 정답을 적기 전에 규칙 10개 중 무엇을 썼는지 하나씩 표시하며 검산한다.

06훈련

왜 이걸 하나 — 규칙 10개를 짧은 코드 10개로 돌린다. 매일 5문항씩 곁들이는 자리다.

훈련 10문항은 전위 · 후위 증감, continue 와 break, static, switch 흘러내림, 비트 3종, 중첩 삼항, 초 변환, do-while, 중첩 반복, 전역 · 지역 가리기, 10진수 → 2진수다. 정답은 전부 gcc 로 실행해 확인했다.

훈련 C · 5번C전위/후위 증감
int x = 5; printf("%d ", x++); printf("%d ", ++x); printf("%d", x);
정답5 7 7

— 5 7 7. 후위 증가는 "쓰고 나서 올리고", 전위 증가는 "올리고 나서 쓴다".

원리 — x++ 는 식의 값이 "올리기 전 x" 이고 그 뒤에 x 가 1 커진다. ++x 는 먼저 1 올리고 식의 값이 "올린 뒤 x" 다. 첫 printf 는 x++ 라 5 를 찍고 x 는 6 이 된다. 둘째 printf 는 ++x 라 x 를 7 로 만들고 7 을 찍는다. 셋째는 그냥 x 라 7 이다. 결국 두 번 올라 7 이 되는 것은 같고, "언제 읽었나"만 다르다.

따라가기

  1. x++ → 찍기 5, 그 뒤 x = 6
  2. ++x → x = 7, 찍기 7
  3. x → 7. 출력 5 7 7

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 6번Ccontinue와 break의 순서
int s = 0; for (int i = 1; i <= 10; i++) { if (i % 2 == 0) continue; if (i > 7) break; s += i; } printf("%d", s);
정답16

— 16. 홀수 1 + 3 + 5 + 7 을 더하고 9 에서 멈춘다.

원리 — continue 는 "이번 회차의 나머지를 건너뛰고 다음 회차로", break 는 "반복문을 아예 나간다"이다. 짝수는 첫 if 에서 continue 로 걸러져 더해지지 않는다. 홀수 중 7 보다 크면 break 로 반복이 끝난다. 그래서 1 · 3 · 5 · 7 만 더해지고, 9 에서는 i > 7 이 참이라 더하지 못하고 나간다. 두 문장의 순서도 중요하다 — 8 은 짝수라 continue 로 넘어가 break 검사를 안 받는다.

따라가기

  1. 1 → s 1 · 2 continue · 3 → s 4 · 4 continue
  2. 5 → s 9 · 6 continue · 7 → s 16 · 8 continue
  3. 9 → 9 > 7 break → 출력 16

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 11번Cstatic 지역변수
int f() { static int c = 0; c += 2; return c; } int main() { f(); f(); printf("%d", f()); }
정답6

— 6. static 변수는 함수가 끝나도 값을 유지하므로 호출마다 2 씩 쌓여 세 번째 호출에서 6 이다.

원리 — 보통의 지역 변수는 함수가 불릴 때마다 새로 만들어지고 끝나면 사라진다. static 을 붙이면 프로그램이 시작할 때 한 번만 0 으로 초기화되고, 함수가 끝나도 메모리에 남아 다음 호출 때 이어서 쓴다. static int c = 0 은 "처음 한 번만" 실행되는 초기화다. 그래서 f() 를 부를 때마다 c 가 2 · 4 · 6 으로 커진다. static 이 없었다면 매번 0 에서 시작해 항상 2 다.

따라가기

  1. 첫 f() → c 2 · 둘째 f() → c 4
  2. 셋째 f() → c 6, 이 값을 printf
  3. 출력 6

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 14번Cswitch fall-through
int n = 3, r = 0; switch (n) { case 1: r += 1; case 3: r += 3; case 5: r += 5; break; case 7: r += 7; } printf("%d", r);
정답8

— 8. case 3 에 들어간 뒤 break 가 없어 case 5 까지 흘러내려(fall-through) 3 + 5 가 더해진다.

원리 — switch 는 값이 맞는 case 로 "점프"한 뒤, break 를 만날 때까지 아래로 계속 실행한다. case 는 출입구이지 칸막이가 아니다. n = 3 이라 case 3 부터 시작해 r += 3, 이어서 case 5 의 r += 5 를 실행하고 거기 break 가 있어 나간다. case 1 은 위에 있어 실행되지 않고, case 7 은 break 뒤라 닿지 않는다. break 를 빠뜨린 switch 가 시험의 단골이다.

따라가기

  1. n = 3 → case 3 진입 → r = 3
  2. break 없음 → case 5 → r = 8 → break
  3. 출력 8

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 15번C비트 연산 3종
int a = 9, b = 6; printf("%d %d %d", a & b, a | b, a >> 2);
정답0 15 2

— 0 15 2. 9 = 1001, 6 = 0110 이라 AND 는 겹치는 비트가 없고, OR 는 전부 채워지며, 9 를 오른쪽으로 2칸 밀면 2 다.

원리 — 9 와 6 은 2진수에서 1 의 자리가 하나도 안 겹친다(1001 · 0110). 그래서 AND 는 0000 = 0 이고 OR 는 1111 = 15 다. >> 는 비트를 오른쪽으로 미는 것이고 한 칸마다 2 로 나눈 몫이 된다 — 9 >> 2 는 9 ÷ 4 = 2(1001 → 10). 반대로 << 는 2 를 곱한다. 시프트는 밀려 나간 비트가 버려지므로 나머지는 사라진다.

따라가기

  1. 1001 & 0110 = 0000 → 0
  2. 1001 | 0110 = 1111 → 15
  3. 1001 >> 2 = 10 → 2. 출력 0 15 2

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 16번C중첩 삼항 연산자
int x = 8, y = 5; int r = x > y ? (x % y == 0 ? 1 : 2) : 3; printf("%d", r);
정답2

— 2. 바깥 조건 8 > 5 가 참이라 안쪽 삼항으로 들어가고, 8 % 5 = 3 이 0 이 아니라 2 다.

원리 — 삼항 연산자 조건 ? 참값 : 거짓값 은 if-else 를 식 하나로 쓴 것이다. 괄호 안에 삼항이 또 있으면 바깥 조건을 먼저 판정하고, 참이면 괄호 안 삼항을 다시 판정한다. x > y 는 8 > 5 참 → (x % y == 0 ? 1 : 2) 를 계산 → 8 % 5 = 3, 0 이 아니므로 거짓 → 2. 바깥이 거짓이었다면 안쪽은 아예 계산하지 않고 3 이다.

따라가기

  1. 8 > 5 참 → 안쪽 삼항으로
  2. 8 % 5 = 3 ≠ 0 → 2
  3. 출력 2

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 20번C초 → 시:분:초 변환
int t = 3750; printf("%d:%d:%d", t / 3600, (t % 3600) / 60, t % 60);
정답1:2:30

— 1:2:30. 3750초 = 1시간 2분 30초다.

원리 — 초를 시 · 분 · 초로 쪼개는 정석이다. 시는 3600 으로 나눈 몫(정수 나눗셈이라 소수점 버림). 시를 뺀 나머지 t % 3600 을 60 으로 나눈 몫이 분. 60 으로 나눈 나머지 t % 60 이 초다 — 3600 이 60 의 배수라 시를 뺄 필요 없이 바로 t % 60 이 초다. 서식 "%d:%d:%d" 라 콜론이 그대로 찍힌다.

따라가기

  1. 3750 / 3600 = 1
  2. 3750 % 3600 = 150 → 150 / 60 = 2
  3. 3750 % 60 = 30. 출력 1:2:30

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 21번Cdo-while 자릿수 세기
int n = 2026, c = 0; do { c++; n /= 10; } while (n > 0); printf("%d", c);
정답4

— 4. 2026 을 10 으로 계속 나누며 0 이 될 때까지 센 횟수 = 자릿수다.

원리 — 10 으로 나누면(정수 나눗셈) 마지막 자리가 하나 떨어진다. 2026 → 202 → 20 → 2 → 0 까지 네 번 나누므로 자릿수 4 다. do-while 이라 n 이 0 이어도 한 번은 돈다 — 그래서 n = 0 일 때 자릿수 1 이 정확히 나온다(while 이었다면 0 이 된다). 이 코드는 "자릿수 세기"의 표준형이라 통째로 알아 둔다.

따라가기

  1. c 1, n 202 · c 2, n 20
  2. c 3, n 2 · c 4, n 0 → 조건 거짓
  3. 출력 4

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 22번C중첩 반복 누적
int c = 0; for (int i = 2; i <= 4; i++) for (int j = 1; j <= i; j++) c += j; printf("%d", c);
정답19

— 19. i = 2 · 3 · 4 각각에 대해 1 부터 i 까지 더한 3 + 6 + 10 이다.

원리 — 안쪽 반복이 바깥 변수 i 에 묶여 있다(j <= i). 그래서 바깥이 한 바퀴 돌 때마다 안쪽 횟수가 달라진다 — 삼각형 모양의 이중 반복이다. i = 2 면 j 는 1 · 2 로 합 3, i = 3 이면 1 · 2 · 3 으로 합 6, i = 4 면 합 10. 안쪽 합은 1 부터 i 까지의 합 i(i+1)/2 이고 그것을 i = 2~4 에 대해 더한다.

따라가기

  1. i=2 → 1+2 = 3 → c 3
  2. i=3 → 1+2+3 = 6 → c 9
  3. i=4 → 1+2+3+4 = 10 → c 19. 출력 19

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 27번C전역 vs 지역 변수 가리기
int x = 1; void f() { int x = 10; x++; printf("%d ", x); } int main() { f(); x += 5; printf("%d", x); }
정답11 6

— 11 6. 함수 안의 x 는 전역 x 와 다른 변수라 서로 영향을 주지 않는다.

원리 — 전역 변수 x = 1 이 있어도 f() 안에서 int x = 10 으로 다시 선언하면 그 함수 안에서는 지역 x 가 전역 x 를 가린다(shadowing). f() 의 x++ 는 지역 x 를 11 로 만들고 찍는다. 함수가 끝나면 지역 x 는 사라지고 전역 x 는 여전히 1 이다. main 의 x += 5 는 전역 x 를 6 으로 만든다. "같은 이름 = 같은 변수"가 아니다 — 선언된 범위(스코프)를 본다.

따라가기

  1. f() → 지역 x 10 → 11 → 출력 "11 "
  2. main → 전역 x 1 + 5 = 6 → 출력 "6"
  3. 합쳐 11 6

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

훈련 C · 30번C10진수 → 2진수
int n = 13, b[8], i = 0; while (n > 0) { b[i++] = n % 2; n /= 2; } for (i = i - 1; i >= 0; i--) printf("%d", b[i]);
정답1101

— 1101. 13 을 2 로 나눈 나머지를 모아 거꾸로 찍은 2진수다.

원리 — 10진수를 2진수로 바꾸는 손 계산 그대로다. 2 로 나눈 나머지가 가장 낮은 자리부터 나오므로 b[0] 에 1의 자리, b[1] 에 2의 자리… 순으로 쌓인다. 13 → 나머지 1, 몫 6 → 나머지 0, 몫 3 → 나머지 1, 몫 1 → 나머지 1, 몫 0. b = [1 0 1 1], i 는 4. 출력은 높은 자리부터여야 하니 i − 1 = 3 부터 0 까지 거꾸로 찍어 1101 이 된다. 8 + 4 + 1 = 13 으로 검산된다.

따라가기

  1. 13 % 2 = 1, 6 % 2 = 0, 3 % 2 = 1, 1 % 2 = 1 → b = [1 0 1 1]
  2. i = 4 → 3 · 2 · 1 · 0 순으로 b[3] b[2] b[1] b[0]
  3. 출력 1101

이 노트에서 만든 변형 문제다. 코드를 손으로 따라가고 docker gcc(alpine 13.2) 실행 결과와 대조했다.

07백지 재현

왜 이걸 하나 — 아래를 안 보고 적을 수 있어야 이 장이 끝난다.