C 포인터 · 배열 E02

C 기출의 절반이 포인터와 배열이다. 주소를 그림으로 그리고 첨자를 손으로 세면 답이 하나로 좁혀진다

왜 이 장을 하나
C 문제의 함정은 문법이 아니라 지금 포인터가 어디를 가리키는지다. 배열 이름이 주소라는 것, *(p+1)*p+1 의 차이, 2차원 배열의 행 · 열 첨자, 매개변수로 넘어간 배열이 포인터가 되는 것 — 이 넷이 15문항의 전부다. 종이에 칸을 그리며 추적하는 절차를 세운다.
다루는 것포인터 산술 · 배열 첨자 · 2차원 배열 · 주소 전달 · 정렬 코드
문항기출 15 · 예상 3 · 훈련 10
끝나면포인터 식을 보고 칸을 그려 값을 읽는다

01왜 이 장인가

왜 이걸 하나 — 프로그래밍 91문항 중 C 가 42문항이고, 그중 15문항이 포인터 · 배열이다. 11회 중 9회에 나왔다.

11회분 220문항 중 이 장의 기출은 15문항이다. 2023년 2회에는 4문항(순환 출력 · 스택 · 공유 버퍼 · 선택 정렬)이 몰렸고, 2026년 2회 12번(sizeof)까지 이어진다. 코드 추적 훈련 10문항(훈련 C 1~4 · 23~26 · 28 · 29)이 이 장에 붙는다.

몇 문항무엇을 적나보기
문자열 포인터 출력4%s · %c 로 여러 줄2023-1회 2번 · 2023-3회 3번 · 2025-1회 10번 · 2025-3회 2번
배열 · 2차원 배열 계산5합 · 평균 · 특정 칸 값2024-2회 11번 · 15번 · 2025-1회 11번 · 2026-1회 2번 · 2026-2회 12번
빈칸 채우기3변수명 · 첨자 식 · 비교 연산자2023-1회 14번 · 2023-2회 9번 · 14번
포인터로 자료구조3스택 · 공유 버퍼 · 이중 포인터2023-2회 11번 · 12번 · 2024-3회 19번

→ 답이 여러 줄인 문항이 4개다(2023-1회 2번 5줄 · 2023-3회 3번 5줄 · 2025-1회 10번 2줄 · 2026-2회 12번 4줄). 한 줄이라도 빠지거나 순서가 바뀌면 그 문항이 통째로 나가므로, 줄 수를 먼저 세고 시작한다.

E01 진단의 C 2문항 중 12번(sizeof 와 배열 매개변수)이 이 장이다. 코드 추적은 머릿속으로 하면 첨자 하나가 밀린다 — 칸을 그리고 화살표를 옮기는 습관이 03절이다.

문항 수는 이 노트의 문제 데이터에서 ch=E02 인 것을 센 값이다(2026-09-14). 기출 코드는 grandlife.co.kr 복원에서 옮겼고, 15문항 전부 gcc(도커 alpine)로 실행해 정답을 확인했다. 빈칸 문제 3개는 답을 넣어 실행하거나 컴파일해 확인했다.

02한눈에

왜 이걸 하나 — 시험 전날 규칙 8개를 다시 본다. 전부 「주소냐 값이냐」의 변주다.

배열 이름 = 첫 칸 주소int a[5] 에서 a&a[0]. a[i]*(a + i) 와 같다. 문자열 char w[] = "Art" 는 A · r · t · \0 네 칸.
포인터 산술p + 1 은 한 칸(자료형 크기만큼) 옆의 주소. *(p + 1) 은 그 칸의 값, *p + 1 은 첫 칸 값에 1 을 더한 것. p++ 는 포인터를 옮기고, (*p)++ 는 값을 올린다.
%s 와 %c%s 에 주소를 주면 그 자리부터 \0 전까지 출력. %c 에 값을 주면 글자 하나. %c*p + 4 를 주면 코드가 4 큰 글자(K → O).
2차원 배열a[2][3] 은 행 2 열 3, 메모리에는 한 줄로 1 2 3 4 5 6. a[1][2] = *(*(a + 1) + 2). int *p = a[0] 이면 p[4] 는 5(한 줄로 센다).
함수에 넘기면배열을 넘기면 포인터가 된다 — int arr[] 매개변수의 sizeof(arr) 는 포인터 크기(8). 값을 넘기면 복사본, 주소를 넘기면 원본이 바뀐다. 이중 포인터 int **pp 는 포인터의 주소.
전역 배열 공유같은 전역 버퍼를 돌려주는 함수를 세 번 부르면 세 포인터가 같은 곳을 가리켜 마지막 값이 세 번 찍힌다.
정렬 · 스택 뼈대선택 정렬은 if (a[i] > a[j]) swap 이 오름차순. 버블은 이웃끼리 a[j] > a[j+1]. 스택은 top 을 올리며 넣고 내리며 뺀다. 순환 첨자는 (i + 1) % n.
sizeof배열 자체는 전체 바이트, 포인터는 8(64비트). 원소 수 = sizeof(a) / sizeof(a[0]). 배열 포인터 int (*p)[8]*p 는 배열 전체(32).

03밑바닥

왜 이걸 하나 — 규칙 8개는 메모리를 칸으로 그리면 전부 한 그림에서 나온다. 그림이 있으면 처음 보는 코드도 같은 절차다.

3-1. 배열은 칸의 줄이고 이름은 첫 칸의 번지다

char word[] = "Art"; 는 메모리에 A · r · t · \0 네 칸을 나란히 만들고, word 라는 이름은 그 첫 칸의 주소다. 그래서 printf("%s", word) 는 첫 칸부터 \0 전까지 Art 를 찍고, *word 는 첫 칸의 값 A 다. char *p = word; 로 포인터를 만들어도 같은 칸을 가리키니 *p 도 A 다(2023년 1회 2번).

포인터에 1 을 더하면 한 칸 옆의 주소가 된다. word + 1 은 r 의 주소라 %s 로 찍으면 rt 이고, *(word + 3) 은 넷째 칸 값이다. 2023년 3회 3번은 KOREA 로 이 다섯 가지를 한 문제에 넣었다 — %s word(KOREA), %s word+1(OREA), %c *word(K), %c *(word+3)(E), %c *word+4(K 의 코드 75에 4 를 더한 O).

마지막 것이 가장 자주 틀린다. *(word + 3) 은 「3칸 옆의 값」이고 *word + 3 은 「첫 칸 값에 3 을 더한 것」이다. 괄호가 있으면 주소를 먼저 옮기고, 없으면 값을 먼저 꺼낸다 — * 가 + 보다 먼저이기 때문이다. 2025년 3회 2번도 같은 갈림에서 C 가 답이었다.

3-2. 2차원 배열은 한 줄이다

int a[2][3] = {{1,2,3},{4,5,6}}; 는 그림으로는 2행 3열이지만 메모리에는 1 2 3 4 5 6 이 한 줄로 놓인다. a[1][2] 는 「둘째 행의 셋째 칸」이고 한 줄로 세면 1 × 3 + 2 = 5번째(0부터)라 6 이다. 그래서 int *p = a[0]; 처럼 첫 행의 주소를 한 줄 포인터에 담으면 p[4] 는 5, p[3] 은 4 다(예상 1회 9번).

포인터 식으로 쓰면 *(*(a + 1) + 2) 다. a + 1 은 둘째 행의 주소, *(a + 1) 은 그 행의 첫 칸 주소, 거기에 2 를 더해 셋째 칸, 마지막 * 로 값. 2024년 2회 15번과 훈련 3번이 이 식을 그대로 묻는다. 행 첨자에는 열 개수를 곱한다는 것만 기억하면 어느 칸이든 한 줄 번호로 바꿀 수 있다.

3-3. 함수에 넘기면 배열은 포인터가 된다

매개변수 자리의 int arr[] 는 배열이 아니라 포인터다. 그래서 함수 안에서 sizeof(arr) 는 배열 크기가 아니라 포인터 크기(64비트에서 8)이고, 2026년 2회 12번의 function3 이 8 / 4 = 2 를 찍는다. 배열 전체를 가리키는 int (*arr)[8] 로 받으면 *arr 가 배열 자체라 32 / 4 = 8 이 된다(function4).

같은 문제의 앞부분이 값 전달과 주소 전달이다. function1(&value) 는 주소를 받아 *p = 50 으로 원본을 바꾸고, function2(value) 는 복사본만 60 으로 바꿔 원본은 50 그대로다. 훈련 28번(값 전달 vs 주소 전달)이 같은 원리이고, 2024년 3회 19번은 포인터의 주소(int **)를 넘겨 원본 포인터를 바꾼다.

전역 버퍼도 같은 함정이다. 2023년 2회 12번은 scanf 로 읽어 전역 배열 buffer 의 주소를 돌려주는 함수를 세 번 부른다. 세 포인터가 같은 버퍼를 가리키므로 마지막에 읽은 gamma 가 세 번 찍힌다. 「포인터를 세 개 만들었으니 값도 세 개」라고 생각하면 틀린다.

3-4. 첨자 식과 정렬 뼈대는 손으로 한 바퀴 돌린다

빈칸 문제는 뼈대를 알아야 채운다. 첫 값을 맨 뒤로 보내는 순환 출력은 values[(i + 1) % 5] — i 가 4 일 때 5 % 5 = 0 으로 첫 칸에 돌아온다(2023년 2회 9번, 5 4 3 2 1 을 넣으면 4 3 2 1 5). 선택 정렬의 오름차순은 if (values[i] > values[j]) 에서 바꾸는 것이라 빈칸은 > 다(2023년 2회 14번). 버블 정렬의 swap 은 arr[idx2] = temp 로 끝나고 호출은 bubbleSort(values, size) 다(2023년 1회 14번).

스택은 top 하나로 돌아간다. push 는 top 을 올린 뒤 넣고, pop 은 값을 꺼낸 뒤 top 을 내린다. 2023년 2회 11번은 push 1 · 2, pop → 2, push 3, pop → 3, push 4 · 6, pop → 6, pop → 4, push 5, pop → 5, pop → 1 로 213465 다. pop 이 찍는 것은 가장 최근에 넣은 것이라 순서를 표로 적으면 틀리지 않는다.

통과 기준은 넷이다. 첫째, 문자열 포인터 식 5종(%s p · %s p+1 · %c *p · %c *(p+n) · %c *p+n)의 출력을 KOREA 로 안 보고 적는다. 둘째, 2차원 배열의 칸을 한 줄 번호로 바꾼다. 셋째, 값 전달 · 주소 전달 · 배열 매개변수의 sizeof 를 가른다. 넷째, 스택 · 순환 첨자 · 정렬 뼈대를 손으로 한 바퀴 돌린다. 기출 15문항이 이 넷 안에 있다.
*p + 1 과 *(p + 1) 은 같다.*+ 보다 먼저다. *p + 1 은 첫 칸 값에 1 을 더한 것(K → L), *(p + 1) 은 둘째 칸의 값(O). 2023년 3회 3번의 마지막 줄 O 는 *word + 4 라 K 에 4 를 더한 것이다.
함수 안에서도 sizeof(arr) 는 배열 크기다.매개변수 int arr[] 는 포인터라 sizeof 가 8 이다. 2026년 2회 12번의 3.2 가 그 결과다. 배열 크기를 함수 안에서 알려면 길이를 따로 넘기거나 배열 포인터 int (*arr)[8] 로 받는다.

04기출

왜 이걸 하나 — 코드를 종이에 칸으로 그리며 푸는 연습이다. 출력이 여러 줄이면 줄 수를 먼저 센다.

변수마다 칸을 그리고 포인터는 화살표로 그린다. printf 를 만날 때마다 답 칸에 한 줄씩 적고, 정답을 연 뒤 몇째 줄에서 어긋났는지를 보면 규칙 8개 중 무엇이 빠졌는지 나온다.

2023-1회 · 2번C

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

#include <stdio.h> int main(void) { char word[] = "Art"; char *p = word; printf("%s\n", word); printf("%c\n", *p); printf("%c\n", *word); printf("%s\n", p); for (int i = 0; word[i] != '\0'; i++) { printf("%c", word[i]); } return 0; }
정답Art
A
A
Art
Art
배열 이름 a와 포인터 p는 같은 곳(문자열 시작 주소)을 가리키는 쪽지입니다. %s는 \0 전까지 전부 출력, *p와 *a는 첫 글자 'A'. for문으로 한 글자씩 출력해도 결국 Art. 복원된 코드를 실행해 위 출력을 그대로 얻었다. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「ArtAAArtArt」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-1회 · 14번C

다음 C 프로그램에서 ①과 ②에 들어갈 변수명을 순서대로 쓰시오.

#include <stdio.h> void swap(int *arr, int idx1, int idx2) { int temp = arr[idx1]; arr[idx1] = arr[idx2]; arr[①] = temp; } void bubbleSort(int *arr, int length) { for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { swap(arr, j, j + 1); } } } } int main(void) { int values[] = { 85, 75, 50, 100, 95 }; int size = 5; bubbleSort(values, ②); return 0; }
정답① idx2, ② size
swap은 "두 컵의 물을 빈 컵(t) 하나로 맞바꾸기" — t에 A를 붓고, A에 B를 붓고, B에 t를 부으면 끝. 마지막 단계가 ①. 정렬 함수엔 배열과 크기를 전달하므로 ②는 nx. 확인 방법: 컴파일만 확인. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「① idx2, ② size」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-2회 · 9번C

정수 다섯 개를 배열에 입력한 뒤 첫 번째 값을 맨 뒤로 보내 출력하려고 한다. ①에 들어갈 식을 쓰시오.

#include <stdio.h> int main(void) { int values[5]; int i; for (i = 0; i < 5; i++) { scanf("%d", &values[i]); } for (i = 0; i < 5; i++) { printf("%d", values[( ① )]); } return 0; }
정답(i + 1) % 5
%(나머지)는 "회전판". i=0→1번 칸, i=3→4번 칸, i=4→(4+1)%5=0번 칸으로 되돌아옵니다. 원형 큐·순환 구조는 전부 (i+1)%크기 패턴. 빈칸에 답을 넣어 실행하면 출력이 「43215」 로 나오는 것을 확인했다. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「(i + 1) % 5」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-2회 · 11번C

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

#include <stdio.h> int stack[10]; int top = -1; void push(int value) { stack[++top] = value; } int pop(void) { return stack[top--]; } int main(void) { push(5); push(2); printf("%d", pop()); push(4); push(1); printf("%d", pop()); push(3); printf("%d", pop()); printf("%d", pop()); push(6); printf("%d", pop()); printf("%d", pop()); return 0; }
정답213465
스택은 "접시 쌓기" — 마지막에 올린 접시부터 꺼냅니다(LIFO). 종이에 스택 그림을 그리며 push/pop을 순서대로 추적하는 것이 가장 안전합니다. 복원된 코드를 실행해 위 출력을 그대로 얻었다. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「213465」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-2회 · 12번C

다음 C 프로그램에 alpha, beta, gamma를 차례대로 입력했을 때 출력 결과를 줄마다 쓰시오.

#include <stdio.h> char buffer[30]; char *readValue(void) { scanf("%29s", buffer); return buffer; } int main(void) { char *first = readValue(); char *second = readValue(); char *third = readValue(); printf("%s\n", first); printf("%s\n", second); printf("%s", third); return 0; }
정답gamma
gamma
gamma
세 포인터가 같은 배열(같은 집)을 가리키므로, 마지막 입력 "박영희"가 이전 내용을 덮어씁니다. 쪽지(포인터)가 3장이어도 집이 1채면 내용물은 하나! 복원된 코드를 실행해 위 출력을 그대로 얻었다. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「gammagammagamma」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-2회 · 14번C

선택 정렬로 배열을 오름차순 정렬하려고 한다. ①에 들어갈 비교 연산자를 쓰시오.

#include <stdio.h> int main(void) { int values[] = { 64, 25, 12, 22, 11 }; int size = sizeof(values) / sizeof(values[0]); for (int i = 0; i < size - 1; i++) { for (int j = i + 1; j < size; j++) { if (values[i] ( ① ) values[j]) { int temp = values[i]; values[i] = values[j]; values[j] = temp; } } } return 0; }
정답>
오름차순 = "앞이 뒤보다 크면 교환". 내림차순이면 <. 정렬 방향과 부등호 방향을 연결해 암기: "오름차순은 큰 놈을 뒤로 보낸다(>)". 확인 방법: > 를 채워 컴파일하고 배열을 찍어 오름차순 11 12 22 25 64 확인. > 를 넣어 컴파일한 뒤 배열을 찍어 보면 11 12 22 25 64 로 오름차순이 된다 (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「>」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2023-3회 · 3번C

다음 C 프로그램의 실행 결과를 줄바꿈까지 맞춰 쓰시오.

#include <stdio.h> int main(void) { char *word = "KOREA"; printf("%s\n", word); printf("%s\n", word + 1); printf("%c\n", *word); printf("%c\n", *(word + 3)); printf("%c", *word + 4); return 0; }
정답KOREA
OREA
K
E
O
p+1은 "한 칸 옆에서부터 끝까지"(OREA), *(p+3)은 "3칸 옆 글자"(E), *p+4는 'K'+4 = 문자 코드 연산 = 'O'. 괄호 유무가 완전히 다른 결과 — 최다 함정 포인트! 복원된 코드를 gcc(도커 alpine)로 실행해 위 다섯 줄을 그대로 얻었다. *word + 4 는 문자 코드 K(75)에 4 를 더한 O 다. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2024-2회 · 11번C

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

#include <stdio.h> #include <string.h> int main(void) { char text[10] = "abc"; strcpy(text + 3, "de"); printf("%d", (int)strlen(text) + (text[4] - 'a') + 1); return 0; }
정답10
strcpy(text + 3, "de") 는 "abc" 뒤(첨자 3)부터 "de" 를 복사해 "abcde" 가 된다. strlen 은 5, text[4] 는 e 라 e − a = 4, 거기에 1 을 더해 5 + 4 + 1 = 10. 포인터 + 3 은 셋째 칸 뒤에 이어 붙이는 것이고 글자는 코드값이라 뺄 수 있다. 복원된 코드를 gcc(도커 alpine)로 실행해 10 을 얻었다. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2024-2회 · 15번C

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

#include <stdio.h> int main(void) { int values[] = { 3, 5, 7, 9 }; int *p = values + 1; printf("%d", *p + *(p + 1) + values[3]); return 0; }
정답21
2차원 배열 a에서 *(*(a+i)+j) == a[i][j]. "a+i는 i행으로, +j는 j칸 옆으로" — 겹괄호를 행/열 이동으로 번역하면 쉽습니다. 복원된 코드를 gcc(도커 alpine) 로 실행해 위 출력을 그대로 얻었다. 그랜드라이프 가답안 표기는 「21」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2024-3회 · 19번C

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

#include <stdio.h> void update(int **values, int size) { for (int i = 0; i < size; i++) { *(*values + i) = (*(*values + i) + i) % size; } } int main(void) { int values[] = { 3, 1, 4, 1, 5 }; int *p = values; int **pp = &p; update(pp, 5); printf("%d", values[2]); return 0; }
정답1
이중 포인터 = "쪽지의 위치를 적은 쪽지". **pp는 두 번 찾아가서 값 읽기. 변수→포인터→이중 포인터를 3단 그림으로 그리면 절대 안 틀립니다. 복원된 코드를 gcc(도커 alpine) 로 실행해 위 출력을 그대로 얻었다. 그랜드라이프 가답안 표기는 「1」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2025-1회 · 10번C

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

#include <stdio.h> int main(void) { char letters[5] = { 'B', 'A', 'D', 'E', '\0' }; char target = 'C'; int pos; printf("%d\n", letters[3] - letters[1]); for (pos = 0; pos < 4; pos++) { if (letters[pos] > target) break; } for (int i = 4; i > pos; i--) { letters[i] = letters[i - 1]; } letters[pos] = target; for (int i = 0; i < 5; i++) printf("%c", letters[i]); return 0; }
정답4
BACDE
첫 줄은 letters[3] − letters[1] = E − A = 4(글자는 코드값). 그 뒤 C 를 정렬된 자리에 끼워 넣는다 — B · A · D · E 중 C 보다 큰 첫 글자는 D(pos = 2)이고, 그 뒤를 한 칸씩 밀어 B A C D E. 답은 두 줄이라 4 와 BACDE 를 줄을 나눠 적는다. 복원된 코드를 gcc(도커 alpine)로 실행해 두 줄을 얻었다. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2025-1회 · 11번C

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

#include <stdio.h> void place(int grid[3][3], const int values[9]) { for (int i = 0; i < 9; i++) { grid[((i + 1) / 3) % 3][(i + 1) % 3] = values[i]; } } int main(void) { int grid[3][3]; int values[9] = { 5, 2, 7, 4, 1, 8, 3, 6, 9 }; int total = 0; place(grid, values); for (int i = 0; i < 9; i++) { total += grid[i / 3][i % 3] * (i % 2 == 0 ? 1 : -1); } printf("%d", total); return 0; }
정답13
malloc = "필요할 때 방 빌리기". 할당 후에는 일반 배열과 똑같이 인덱스·포인터 연산으로 접근합니다. 2025년부터 동적 메모리 유형이 새로 등장 — 개념 1-1, 1-4 참고. 복원된 코드를 gcc(도커 alpine) 로 실행해 위 출력을 그대로 얻었다. 그랜드라이프 가답안 표기는 「13」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2025-3회 · 2번C

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

#include <stdio.h> struct Item { int offset; const char *text; }; int main(void) { struct Item items[] = { {1, "AB"}, {2, "DC"}, {3, "EB"} }; struct Item *p = &items[1]; printf("%s", p->text + (p->offset - 1)); return 0; }
정답C
p 는 items[1] 을 가리키므로 offset 2, text "DC". p->text + (2 − 1) 은 둘째 글자의 주소라 %s 로 찍으면 거기서부터 끝까지 C 다. 포인터 + n 은 n 칸 뒤의 주소이고 %s 는 그 자리부터 전까지 찍는다. 복원된 코드를 gcc(도커 alpine)로 실행해 C 를 얻었다. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2026-1회 · 2번C

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

#include <stdio.h> double arr1(int p[], int len) { double av = 0; int i; for (i = 0; i < len; i++) { av += (double)p[i]; } return av / len; } double arr2(int *p, int len) { double av = 0; int i; for (i = 0; i < len; i++) { av += (double)(*(p + i)); } return av / len; } int main(void) { int arr[10] = { 80, 20, 50, 55, 45, 95, 55, 10, 40, 80 }; int len = 10; printf("%.2f", arr1(arr, len) + arr2(arr, len)); return 0; }
정답106.00
arr[i]*(arr+i)는 완전히 같으므로 두 함수의 결과도 동일(53+53=106). %.2f는 소수점 둘째 자리까지 — 106.00으로 써야 정답, 106이라고 쓰면 오답! 복원된 코드를 실행해 위 출력을 그대로 얻었다. (gcc(도커 alpine)) 그랜드라이프 가답안 표기는 「106.00」. 복원 출처는 grandlife.co.kr 회차별 복원 페이지(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·값이 실제와 다를 수 있고, 문항 번호도 복원본마다 다르다.
2026-2회 · 12번C

다음 C 프로그램의 실행 결과를 줄 단위로 쓰시오. 단, sizeof(int)=4, sizeof(int*)=8이다.

#include <stdio.h> void function1(int *p) { *p = 50; } void function2(int p) { p = 60; } void function3(int arr[]) { printf("3.%zu\n", sizeof(arr) / sizeof(arr[0])); } void function4(int (*arr)[8]) { printf("4.%zu\n", sizeof(*arr) / sizeof((*arr)[0])); } int main(void) { int value = 10; int arr[8] = { 0 }; function1(&value); printf("1.%d\n", value); function2(value); printf("2.%d\n", value); function3(arr); function4(&arr); return 0; }
정답1.50
2.50
3.2
4.8
function1 은 주소를 받아 원본을 50 으로 바꾼다. function2 는 값의 복사본만 바꾸므로 원본은 50 그대로. function3 의 int arr[] 는 매개변수 자리에서 포인터로 바뀌므로 sizeof 가 8 → 8/4 = 2. function4 의 *arr 는 int 8개짜리 배열 자체라 32/4 = 8. 앞의 1. 2. 3. 4. 는 프로그램이 찍는 글자다. gcc(64비트, sizeof(int*)=8)로 실행해 4줄을 확인했다. 복원 출처는 grandlife.co.kr 2026-2회 복원 풀이(2026-09-14 대조). 공식 원문이 아니라 문장·변수명·수치가 실제와 다를 수 있다.
예상 1회 · 2번C빈출 원리: 포인터·문자 연산

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

char *s = "INFORMATION"; printf("%s\n", s + 6); printf("%c\n", *(s + 2)); printf("%c\n", *s + 2);
정답ATION / F / K
s+6은 6칸 옆부터 끝까지 "ATION". *(s+2)는 2칸 옆 글자 'F'. *s+2는 'I'(73)+2 = 75 = 'K' — 괄호가 없으면 글자 코드 덧셈! 2023 3회 KOREA·2025 3회 문제의 동일 함정.
예상 1회 · 9번C빈출 원리: 2차원 배열 + 포인터

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

int a[2][3] = {{1,2,3},{4,5,6}}; int *p = a[0]; printf("%d %d", *(p+4), a[1][0]);
정답5 4
2차원 배열은 메모리에 한 줄로 펼쳐져 있습니다: 1,2,3,4,5,6. p+4는 5번째(0부터) 값 5. a[1][0]은 2행 1열 = 4. 2024 2회 Q15와 동일 원리.
예상 2회 · 2번C빈출 원리: 배열 순회 + 조건 분리

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

int a[] = {3, 8, 5, 2, 7}; int s1 = 0, s2 = 0; for (int i = 0; i < 5; i++) { if (a[i] % 2 == 0) s1 += a[i]; else s2 += a[i]; } printf("%d,%d", s1, s2);
정답10,15
짝수(%2==0): 8+2=10, 홀수: 3+5+7=15. 출력 서식의 콤마까지 그대로 "10,15" — 공백을 넣으면 오답입니다. 변수 추적표(i, a[i], s1, s2)를 그리는 습관이 정확도를 만듭니다.

05틀리는 자리

왜 이걸 하나 — 코드 추적은 원리를 알아도 표기와 줄에서 깎인다.

자리새는 꼴적는 법기출
줄 수5줄 출력에서 한 줄을 빼먹는다printf 마다 \n 이 있는지 보고 줄 수를 먼저 센다2023-1회 2번 · 2023-3회 3번
괄호*p + 1 을 옆 칸으로 읽는다* 가 먼저. 괄호가 있어야 주소가 먼저 움직인다2023-3회 3번 · 2025-3회 2번
소수점 · 서식%.2f 를 정수로, %zu 앞의 「3.」을 소수점으로서식을 그대로 옮긴다. 2026-2회 12번의 3.2 는 「3.」 뒤에 22026-1회 2번 · 2026-2회 12번
공유 버퍼세 포인터에 세 값을 적는다같은 전역 배열이면 마지막 값이 전부2023-2회 12번
순환 첨자(i + 1) % 5i + 1마지막에 첫 칸으로 돌아가는 % 가 빈칸의 핵심2023-2회 9번
비교 방향오름차순에 < 를 적는다앞이 뒤보다 크면 바꾼다 → >2023-2회 14번
변수명빈칸에 비슷한 이름(idx1 · length)을 적는다코드에 선언된 이름 그대로. swap 의 마지막은 idx2, 호출은 size2023-1회 14번
공백 · 줄바꿈「4 BACDE」를 한 줄로「두 줄로 쓰시오」면 두 줄. printf 의 \n 을 따른다2025-1회 10번

C 출력 문제는 printf 의 서식 문자열이 곧 답의 꼴이다. 답을 적기 전에 서식(%d %c %s %.2f, 공백, \n)을 답 칸 옆에 베껴 두면 표기 감점이 사라진다.

06훈련

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

훈련 10문항은 포인터 문자열 3종, 포인터 누적, 2차원 이중 역참조, swap, 최솟값 · 최댓값, 버블 1회전, 선택 정렬, 포인터 순회, 값 · 주소 전달, 인덱스 체인이다. 정답은 전부 gcc 로 실행해 확인했다.

훈련 C · 1번C포인터 문자열 3종 세트
char *p = "COMPUTER"; printf("%s\n", p + 3); printf("%c\n", *(p + 1)); printf("%c\n", *p + 1);
정답PUTER / O / D (3줄)
p+3은 "3칸 옆부터 끝까지", *(p+1)은 "1칸 옆 글자", *p+1은 'C'+1='D' 코드 연산 — 괄호 위치가 전부입니다.
훈련 C · 2번C포인터 + 건너뛰기 누적
int a[5] = {2, 4, 6, 8, 10}; int *p = a, s = 0; for (int i = 0; i < 5; i += 2) s += *(p + i); printf("%d", s);
정답18
i=0,2,4만 방문: 2+6+10=18. *(p+i)는 a[i]와 완전히 동일합니다.
훈련 C · 3번C2차원 배열 이중 역참조
int a[2][3] = {{1,2,3},{4,5,6}}; printf("%d %d", *(*(a+1)+2), a[0][1] + *(a[1]));
정답6 6
*(*(a+1)+2)=a[1][2]=6. *(a[1])은 2행의 첫 원소 4 → 2+4=6. "a+i는 행 이동, +j는 열 이동".
훈련 C · 4번C포인터 swap
void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; } int main() { int a = 3, b = 7; swap(&a, &b); printf("%d %d", a, b); }
정답7 3
주소(&)를 넘겼으므로 함수가 원본 집을 직접 바꿉니다. 값만 넘겼다면(swap(a,b)) 3 7 그대로였을 것 — C-28과 비교.
훈련 C · 23번C최솟값·최댓값 동시 탐색
int a[] = {7, 3, 9, 1, 5}; int min = a[0], max = a[0]; for (int i = 1; i < 5; i++) { if (a[i] < min) min = a[i]; if (a[i] > max) max = a[i]; } printf("%d %d %d", min, max, max - min);
정답1 9 8
첫 원소를 기준으로 잡고 갱신. min/max 초기값을 0으로 잘못 두는 코드와 비교하는 변형도 출제됩니다.
훈련 C · 24번C버블 정렬 1회전 (중간 상태!)
int a[] = {5, 2, 4, 1, 3}; for (int j = 0; j < 4; j++) if (a[j] > a[j+1]) { int t = a[j]; a[j] = a[j+1]; a[j+1] = t; } for (int j = 0; j < 5; j++) printf("%d", a[j]);
정답24135
바깥 루프가 없는 1회전만: 가장 큰 5가 맨 뒤로 밀려남(2,4,1,3,5). 완전 정렬(12345)이라고 쓰면 오답 — 2025 1회에서 실제로 "중간 상태"를 물었습니다.
훈련 C · 25번C선택 정렬 완주
int a[] = {4, 1, 3, 2}; for (int i = 0; i < 3; i++) for (int j = i + 1; j < 4; j++) if (a[i] > a[j]) { int t = a[i]; a[i] = a[j]; a[j] = t; } printf("%d%d%d%d", a[0], a[1], a[2], a[3]);
정답1234
이중 루프가 끝까지 돌면 오름차순 완성. 부등호가 <였다면 내림차순(4321) — 2023 2회 기출 포인트.
훈련 C · 26번C포인터 순회 + 모음 세기
char *p = "informatics"; int c = 0; while (*p) { if (*p=='a' || *p=='e' || *p=='i' || *p=='o' || *p=='u') c++; p++; } printf("%d", c);
정답4
i, o, a, i → 4개. while(*p)는 "\0(값 0)을 만나면 거짓" — 문자열 끝 감지의 표준 관용구.
훈련 C · 28번C값 전달 vs 주소 전달
void f(int n) { n = n + 10; } void g(int *n) { *n = *n + 10; } int main() { int a = 5; f(a); printf("%d ", a); g(&a); printf("%d", a); }
정답5 15
f는 "복사본에 낙서"(원본 무변화), g는 "주소로 찾아가 원본 수정". C 함수 문제의 절반이 이 구분입니다.
훈련 C · 29번C인덱스 체인 (연결리스트의 원리)
int data[] = {10, 20, 30, 40}; int next[] = {2, -1, 3, 1}; int cur = 0; while (cur != -1) { printf("%d ", data[cur]); cur = next[cur]; }
정답10 30 40 20
0번(10)→next 2번(30)→3번(40)→1번(20)→-1 종료. "보물찾기 쪽지" — 저장 순서가 아니라 연결 순서대로 출력. 2025년 연결리스트 신유형의 핵심 원리.

직접 돌려 보는 법

이 컴퓨터에는 gcc 가 없어 도커로 돌렸다. 파워셸에서 코드를 main.c 로 저장한 뒤 아래를 치면 컴파일과 실행이 한 번에 된다(도커 데스크톱이 떠 있어야 한다).

docker run --rm -v "${PWD}:/w" -w /w alpine:3.20 sh -c "apk add --no-cache gcc musl-dev >/dev/null; gcc -w main.c -o a.out && ./a.out"

이 장의 기출 15개 · 훈련 10개는 위와 같은 방법(alpine 3.20 의 gcc)으로 실행해 답을 확인했다. sizeof(int)=4 · sizeof(int*)=8 인 64비트 환경이라 2026-2회 12번의 전제와 같다.

07백지 재현

왜 이걸 하나 — 아래를 안 보고 적을 수 있어야 포인터 문제가 끝난다.