2024년도 1회, 2회, 3회의 정보처리기사 실기 기출문제 속 C 언어 문제를 정리하였다.
2024년 1회 정보처리기사 실기
문제 1. 시프트 연산자 (», «)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #include <stdio.h>
#include <stdlib.h>
main(int argc, char *argv[]) {
int v1 = 0;
int v2 = 35;
int v3 = 29;
if (v1 > v2 ? v2 : v1) {
v2 = v2 << 2;
} else {
v3 = v3 << 2;
}
printf("%d", v2 + v3);
return 0;
}
|
더보기
[정답]
151
[풀이]
1
2
3
4
5
| if (v1 > v2 ? v2 : v1) {
v2 = v2 << 2;
} else {
v3 = v3 << 2;
}
|
v1 > v2 ➔ 0 > 35 ➔ false (0)
C언어에서 if(값)은 값이 0이면 false고, 0이 아니면 true이다.
여기서는 결과가 0이므로 false다. ➔ else문이 실행된다.
1
2
| v2 = 35 (2진수) ⟹ 100011
v3 = 29 (2진수) ⟹ 11101
|
v3 « 2 (왼쪽 시프트 연산자)
왼쪽으로 2칸 이동
1
2
3
4
| 왼쪽으로 2칸 이동한 값
(2진수) ⟹ 1110100
(10진수로 변환) ⟹ 116
|
v3 = 116이 된다.
v2 + v3
= 35 + 116
= 151
시프트 연산자 : 비트를 이동시키는 연산자
| 종류 | 의미 | 형식 | 설명 |
|---|
| » | 오른쪽으로 이동 | 5 » 2 | 5의 이진표현을 오른쪽으로 2칸 이동 |
| « | 왼쪽으로 이동 | 5 « 2 | 5의 이진표현을 왼쪽으로 2칸 이동 |
c언어에서 return 0;은 프로그램 종료
1
2
3
4
| main(int argc, char *argv[]) {
return 0; // 프로그램을 정상적으로 종료했음을 의미
}
|
문제 2. 포인터 기반 문자열 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include <stdio.h>
#include <string.h>
void reverse(char* str, int len) {
char temp;
char *p1 = str;
char *p2 = str + len - 1;
while(p1 < p2) {
temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2--;
}
}
int main() {
char str[100] = "ABCDEFGH";
int len = strlen(str);
reverse(str, len);
for(int i = 1; i < len; i += 2) {
printf("%c", str[i]);
}
return 0;
}
|
더보기
[정답]
GECA
[풀이]
strlen() 함수
C 표준 라이브러리 함수이다. 이 함수는 문자열의 길이(문자 개수)를 반환한다.
문자열 str에서 널 문자(\0)가 나올 때까지 문자를 하나씩 세어 개수를 반환한다.
1
2
| char str[100] = "ABCDEFGH";
int len = strlen(str); // 8
|
reverse 함수
- 문자열의 앞과 뒤를 바꿔서 뒤집는 함수이다.
p1은 문자열의 시작, p2는 문자열의 끝을 가리킨다.
1
2
3
| reverse(str, len);
// str = "ABCDEFGH"
// len = 8
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| void reverse(char* str, int len) {
char temp;
char *p1 = str;
char *p2 = str + len - 1;
while(p1 < p2) {
temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2--;
}
}
|
초기 상태 :
1
2
| p1 -> A (index 0)
p2 -> H (index 7)
|
reverse() 실행 과정
1
2
3
4
5
6
7
8
| temp -> A
p1 -> H
p2 -> A
서로 교환이 됨 : H B C D E F G A
포인터 이동
p1 -> B (index 1)
p2 -> G (index 6)
|
| 단계 | p1 위치 (문자) | p2 위치 (문자) | 교환 후 문자열 상태 |
|---|
| 초기 | A (index 0) | H (index 7) | A B C D E F G H |
| 1 | A ↔︎ H | | H B C D E F G A |
| 2 | B ↔︎ G | | H G C D E F B A |
| 3 | C ↔︎ F | | H G F D E C B A |
| 4 | D ↔︎ E | | H G F E D C B A |
| 종료 | p1 ≥ p2 | | 반복 종료 |
결과 문자열 : HGFEDCBA
1
2
3
| for(int i = 1; i < len; i += 2) {
printf("%c", str[i]);
}
|
인덱스 1부터 시작하여 2씩 증가하며 출력한다.
(홀수 인덱스의 문자만 출력한다.)
1
2
3
4
| i = 1, str[1] = G
i = 3, str[3] = E
i = 5, str[5] = C
i = 7, str[7] = A
|
따라서 최종 출력 결과는 GECA이다.
문제 3. 문자 판별 함수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| #include <stdio.h>
#include <ctype.h>
int main() {
char *p = "It is 8";
char result[100];
int i;
for (i = 0; p[i] != '\0'; i++) {
if (isupper(p[i]))
result[i] = (p[i] - 'A' + 5) % 25 + 'A';
else if (islower(p[i]))
result[i] = (p[i] - 'a' + 10) % 26 + 'a';
else if (isdigit(p[i]))
result[i] = (p[i] - '0' + 3) % 10 + '0';
else if (!(isupper(p[i]) || islower(p[i]) || isdigit(p[i])))
result[i] = p[i];
}
result[i] = '\0';
printf("%s\n", result);
return 0;
}
|
더보기
[정답]
Nd sc 1
[풀이]
isupper, islower, isdigit 함수
<ctype.h> 헤더 파일에 포함된 문자 판별 함수이다.
이 함수들은 각각 문자가 대문자, 소문자, 숫자인지를 판별하며, 결과로 참이면 0이 아닌 정수, 거짓이면 0을 반환한다.
주로 조건문에서 if (isupper(ch))처럼 사용된다.
- isupper(c) : c가 대문자 알파벳(A~Z)인지 확인
- islower(c) : c가 소문자 알파벳(a~z)인지 확인
- isdigit(c) : c가 숫자 문자(0~9)인지 확인
- 숫자 자체가 아니라, 문자 형태의 숫자여야 하며, 작은 따옴표로 감싸야 한다.
1
2
3
4
5
6
7
8
9
10
| for (i = 0; p[i] != '\0'; i++) {
if (isupper(p[i]))
result[i] = (p[i] - 'A' + 5) % 25 + 'A';
else if (islower(p[i]))
result[i] = (p[i] - 'a' + 10) % 26 + 'a';
else if (isdigit(p[i]))
result[i] = (p[i] - '0' + 3) % 10 + '0';
else if (!(isupper(p[i]) || islower(p[i]) || isdigit(p[i])))
result[i] = p[i];
}
|
반복문에서는 문자열 "It is 8"의 각 문자를 하나씩 검사하면서 변환한다.
1. 대문자 처리 (isupper)
1
| result[i] = (p[i] - 'A' + 5) % 25 + 'A';
|
'A'를 기준으로 0 ~ 25 범위의 숫자로 변환- 여기에 5를 더해서 오른쪽으로 5칸 이동
%25로 범위를 제한- 다시
'A'를 더해서 문자로 복원
2. 소문자 처리 (islower)
1
| result[i] = (p[i] - 'a' + 10) % 26 + 'a';
|
'a' 기준으로 변환 후- 10칸 이동
%26으로 알파벳 범위 유지
3. 숫자 처리 (isdigit)
1
| result[i] = (p[i] - '0' + 3) % 10 + '0';
|
- 숫자를 정수로 변환 후
- 3 더하기
%10으로 한 자리 유지
4. 기타 문자 처리
변환 과정
| 문자 | 종류 | 변환 결과 |
|---|
| I | 대문자 | N |
| t | 소문자 | d |
| (공백) | 기타 | (공백) |
| i | 소문자 | s |
| s | 소문자 | c |
| (공백) | 기타 | (공백) |
| 8 | 숫자 | 1 |
1
| printf("%s\n", result);
|
최종적으로 Nd sc 1이 출력된다.
문제 4. 구조체와 포인터를 이용한 함수 처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| #include <stdio.h>
struct BankAcc {
int accNum;
double bal;
};
double sim_pow(double base, int year) {
double r = 1.0;
for (int i = 0; i < year; i++) {
r = r * (1.0 + base);
}
return r;
}
void initAcc(struct BankAcc *acc, int x, double y) {
acc->accNum = x;
acc->bal = y;
}
void test01(struct BankAcc *acc, double en) {
if (en > 0 && en < acc->bal) {
acc->bal -= en;
} else {
acc->bal += en;
}
}
void test02(struct BankAcc *acc) {
acc->bal = acc->bal * sim_pow(0.1, 3);
}
int main() {
struct BankAcc myAcc;
initAcc(&myAcc, 9981, 2200.0);
test01(&myAcc, 100.0);
test02(&myAcc);
printf("%d and %0.2f", myAcc.accNum, myAcc.bal);
return 0;
}
|
더보기
[정답]
9981 and 2795.10
[풀이]
struct BankAcc myAcc;
1
2
3
4
| struct BankAcc {
int accNum;
double bal;
};
|
myAcc라는 구조체 변수를 선언한다.
은행 계좌 정보를 저장하는 구조체 정의(BankAcc)후, 두 개의 멤버 변수를 포함한다.
1
| initAcc(&myAcc, 9981, 2200.0); // myAcc의 주소를 넘겨서 초기화 실행
|
1
2
3
4
| void initAcc(struct BankAcc *acc, int x, double y) {
acc->accNum = x;
acc->bal = y;
}
|
acc는 struct BankAcc 타입의 구조체 변수를 가리키는 포인터이다.
myAcc.accNum = 9981myAcc.bal = 2200.0
1
2
3
4
5
6
7
8
9
10
11
12
| // 입출금 처리 함수
/*
en이 양수이고 현재 잔액보다 작으면 -> 출금
현재 잔액보다 많거나, 음수이거나, 0 포함 등 그 외의 경우 -> 입금
*/
void test01(struct BankAcc *acc, double en) {
if (en > 0 && en < acc->bal) {
acc->bal -= en; // 출금을 의미
} else {
acc->bal += en; // 입금을 의미
}
}
|
en > 0 && en < acc->bal ➔ 참acc->bal -= en; 실행myAcc.bal -= 100.0- 2200.0 - 100.0 = 2100.0
1
2
3
4
5
6
7
8
9
10
11
| void test02(struct BankAcc *acc) {
acc->bal = acc->bal * sim_pow(0.1, 3);
}
double sim_pow(double base, int year) {
double r = 1.0;
for (int i = 0; i < year; i++) {
r = r * (1.0 + base);
}
return r;
}
|
- 현재
acc->bal 값은 2100.0 sim_pow(0.1, 3) ⟹ 1.331
| i | r 초기값 | 연산 (r = r * (1.0 + base)) | 연산 후 r 값 |
|---|
| 0 | 1.0 | 1.0 * (1.0 + 0.1) = 1.1 | 1.1 |
| 1 | 1.1 | 1.1 * (1.0 + 0.1) = 1.21 | 1.21 |
| 2 | 1.21 | 1.21 * (1.0 + 0.1) = 1.331 | 1.331 |
acc->bal = acc->bal * sim_pow(0.1, 3)
= 2100.0 * 1.331
= 2795.1
1
| printf("%d and %0.2f", myAcc.accNum, myAcc.bal);
|
- 형식 지정
%d : 정수형 (int) 출력%.2f : 소수점 둘째 자리까지 실수형(double) 출력 ➔ 반올림
myAcc.accNum ⟹ 9981myAcc.bal ⟹ 2795.1- 출력값 :
9981 and 2795.10
따라서 최종 출력은 9981 and 2795.10이 된다.
포인터와 구조체
- 구조체를 함수의 매개변수로 전달할 경우
- 값이 아닌 주소(포인터)를 전달하면 함수 내부에서 구조체 값을 직접 수정할 수 있다.
- 구조체를 값으로 전달하면 원본은 변경되지 않지만, 포인터로 전달하면 원본 데이터가 변경된다.
struct BankAcc *acc는 구조체를 가리키는 포인터이며, acc->bal과 같은 방식으로 멤버에 접근한다.- 구조체 변수는
. 연산자를, 구조체 포인터는 -> 연산자를 사용한다.
2024년 2회 정보처리기사 실기
문제 5. 값에 의한 전달(Call by Value)과 switch문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include <stdio.h>
void swap(int a, int b) {
int t = a;
a = b;
b = t;
}
int main() {
int a = 11;
int b = 19;
swap(a, b);
switch(a) {
case 1:
b += 1;
case 11:
b += 2;
default:
b += 3;
break;
}
printf("%d", a-b);
}
|
더보기
[정답]
-13
[풀이]
1
2
3
| int a = 11;
int b = 19;
swap(a, b);
|
1
2
3
4
5
| void swap(int a, int b) {
int t = a;
a = b;
b = t;
}
|
swap() 함수 안에서만 a와 b값이 바뀌고, main()함수의 a와 b에는 아무 영향이 없다.
이는 C언어가 값에 의한 전달(Call by Value) 방식을 사용하기 때문이다.
즉, swap(a, b)를 호출할 때, main의 a, b 값이 복사되어 함수의 매개변수 a, b에 전달된다.
swap() 함수에서 사용되는 변수 a, b, t는 모두 해당 함수 내부에서 선언된 지역 변수이다.
1
2
3
4
| // swap() 함수 내에서의 변수 변화
t = 11
a = 19
b = 11
|
하지만 이 변화는 복사된 값에만 적용되므로
main 함수의 a = 11, b = 19는 그대로 유지된다.
[지역 변수]
- 함수가 실행될 때 생성되고, 함수가 종료되면 메모리에서 사라진다.
- 다른 함수의 변수와는 완전히 독립적인 메모리 공간을 사용한다.
- 따라서
swap()에서의 변경은 main에 영향을 주지 않는다.
1
2
3
4
5
6
7
8
9
| switch(a) {
case 1:
b += 1;
case 11:
b += 2;
default:
b += 3;
break;
}
|
현재 a 값은 11이다.
따라서 case 11: 부터 실행된다.
case 11: 뒤에 break가 없기 때문에
아래의 default까지 계속 실행되는 fall-through가 발생한다.
default 부분이 실행된다.
default에는 break문이 있어서 switch문이 종료된다.
최종적으로 b값은 24이다.
현재 a의 값은 11, b의 값은 24
11 - 24 = -13
최종 출력값은 -13이다.
🤔 만약 swap이 실제로 적용되려면?
주소를 전달하는 방식(포인터 사용)으로 바꾸기!
1
2
3
4
5
| void swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
|
함수 호출 방식 변경
&a, &b : 변수의 주소 전달*a, *b : 해당 주소에 있는 실제 값 접근- 함수 내부에서 주소를 통해 main 함수의 변수 값을 직접 변경하게 된다.
switch문 실행
1
2
3
4
5
6
7
8
9
10
11
12
| switch(조건값) {
case 값1:
실행문1;
break;
case 값2:
실행문2;
break;
...
default:
기본 실행문;
break;
}
|
- 조건값에 따라 어떤
case가 실행될지 결정된다. break : 현재 case 블록을 빠져나간다.break가 없으면, 일치한 case부터 아래 코드들이 계속 실행되는 fall-through가 발생한다.default : 위의 case들 중 어떤 것도 일치하지 않을 경우 실행된다. (선택사항)
문제 6. 2차원 배열과 포인터 연산
1
2
3
4
5
6
7
8
9
| #include <stdio.h>
int main() {
int arr[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int* parr[2] = {arr[1], arr[2]};
printf("%d", parr[1][1] + *(parr[1]+2) + **parr);
return 0;
}
|
더보기
[정답]
21
[풀이]
1
| int arr[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
2차원 배열 arr는 행(row) 기준으로 순서대로 값이 채워진다.
1
2
3
4
5
6
7
| arr[0] = {1, 2, 3}
arr[1] = {4, 5, 6}
arr[2] = {7, 8, 9}
arr[0][0] = 1, arr[0][1] = 2, arr[0][2] = 3
arr[1][0] = 4, arr[1][1] = 5, arr[1][2] = 6
arr[2][0] = 7, arr[2][1] = 8, arr[2][2] = 9
|
1
| int* parr[2] = {arr[1], arr[2]};
|
parr은 int형 포인터 배열이다.
arr[1], arr[2]는 각각 배열이지만, 사용될 때는 첫 번째 요소의 주소로 변환된다.
| 인덱스 | 의미 | 가리키는 값 |
|---|
| parr[0] | arr[1] ➔ &arr[1][0] | 4부터 시작 |
| parr[1] | arr[2] ➔ &arr[2][0] | 7부터 시작 |
parr[0]은 {4, 5, 6}의 시작 주소를,
parr[1]은 {7, 8, 9}의 시작 주소를 가리킨다.
1
| printf("%d", parr[1][1] + *(parr[1]+2) + **parr);
|
parr[1][1]parr[1]은 arr[2]의 시작 주소(&arr[2][0])를 가리킨다.parr[1][1] ➔ arr[2][1]- 값 : 8
*(parr[1]+2)parr[1]은 arr[2][0]의 주소(parr[1]+2) ➔ 2칸 이동 ➔ arr[2][2]*(parr[1]+2) ➔ 해당 위치의 값- 값 : 9
**parrparr은 포인터 배열이므로, 첫 번째 요소(parr[0])의 주소를 의미한다.*parr ➔ parr[0] ➔ arr[1] ➔ &arr[1][0]**parr ➔ arr[1][0]- 값 : 4
[최종 계산]
8 + 9 + 4 = 21
따라서 출력값은 21이다.
문제 7. 포인터를 이용한 문자열 복사와 널 문자 반복 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include <stdio.h>
#include <string.h>
void sumFn(char* d, const char* s) {
while (*s) {
*d = *s;
d++;
s++;
}
*d = '\0';
}
int main() {
const char* str1 = "first";
char str2[50] = "teststring";
int result = 0;
sumFn(str2, str1);
for (int i = 0; str2[i] != '\0'; i++) {
result += i;
}
printf("%d", result);
return 0;
}
|
더보기
[정답]
10
[풀이]
문자열 포인터와 문자 배열의 차이
1
| const char* str1 = "first";
|
str1은 문자열 상수 “first”의 시작 주소를 가리키는 포인터이다.- “first”는 읽기 전용 메모리 영역(문자열 리터럴 영역)에 저장된다.
*str1 = 'A'와 같이 값을 변경하려고 하면 런타임 오류가 발생한다.
1
| char str2[50] = "teststring";
|
str2는 문자 배열이다.- 배열 내부는 수정 가능한 메모리 공간이다.
- 다른 문자열을 복사해 넣을 수 있다.
1
2
3
4
5
6
7
8
| void sumFn(char* d, const char* s) {
while (*s) {
*d = *s;
d++;
s++;
}
*d = '\0';
}
|
| s가 가리키는 값 | d에 복사 | d의 상태 |
|---|
| ‘f’ | ‘f’ | “f” |
| ‘i’ | ‘i’ | “fi” |
| ‘r’ | ‘r’ | “fir” |
| ’s’ | ’s’ | “firs” |
| ‘t’ | ‘t’ | “first” |
| ‘\0’ | 종료 | “first\0” |
1
2
3
4
| // 널 문자를 만날 때까지 반복한다는 의미의 코드이다.
while (*s) {}
while (*s != '\0') {}
|
반복 조건이 while (*s)이기 때문에 널 문자(‘\0’)를 만나기 전까지만 복사한다.
sumFn() 함수 실행 후, str2 == "first"가 된다. (기존 "teststring"은 덮어씌워진다)
1
2
3
4
5
6
| // 문자열 길이만큼 인덱스를 누적함
for (int i = 0; str2[i] != '\0'; i++) {
result += i;
}
printf("%d", result);
|
이제 str2는 "first\0"이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| str2[0] = 'f'
result = 0 + 0 = 0
str2[1] = 'i'
result = 0 + 1 = 1
str2[2] = 'r'
result = 1 + 2 = 3
str2[3] = 's'
result = 3 + 3 = 6
str2[4] = 't'
result = 6 + 4 = 10
str2[5] = '\0'
반복문 종료
|
따라서 최종 result값은 10이다.
문제 8. -> 연산자를 이용한 구조체 포인터 멤버 접근
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #include <stdio.h>
struct node {
int data;
struct node *Next;
};
int main() {
struct node *head = NULL;
struct node a = {10, 0};
struct node b = {20, 0};
struct node c = {30, 0};
head = &a;
a.Next = &b;
b.Next = &c;
printf("%d", head->Next->data);
return 0;
}
|
더보기
[정답]
20
[풀이]
연결 리스트 (Linked List)
각 노드가 데이터와 다음 노드를 가리키는 포인터로 구성된 자료 구조이다.
1
2
3
4
| struct Node {
int data; // 데이터를 저장하는 변수
struct node *Next; // 다음 노드의 주소를 저장하는 포인터
};
|
1
2
3
4
| struct node *head = NULL;
struct node a = {10, 0};
struct node b = {20, 0};
struct node c = {30, 0};
|
각 노드는 다음과 같이 초기화된다.
1
2
3
| a: data=10, Next=NULL
b: data=20, Next=NULL
c: data=30, Next=NULL
|
연결 관계 설정
1
2
3
| head = &a; // head가 a의 주소를 가리킴
a.Next = &b; // a가 가리키는 주소는 b의 주소를 가리킴
b.Next = &c; // b가 가리키는 주소는 c의 주소를 가리킴
|
1
| head -> a -> b -> c -> NULL
|
head는 a의 주소를 저장한다.a의 Next 포인터는 b의 주소를 가리킨다.b의 Next 포인터는 c의 주소를 가리킨다.
1
| printf("%d", head->Next->data);
|
head는 a를 가리킴head->Nexthead->Next->data
따라서 20이 출력된다.
2024년 3회 정보처리기사 실기
문제 9. 일반 지역변수와 static 변수의 차이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include <stdio.h>
int func() {
static int x = 0;
x += 2;
return x;
}
int main() {
int x = 0;
int sum = 0;
for(int i = 0; i < 4; i++) {
x++;
sum += func();
}
printf("%d", sum);
return 0;
}
|
더보기
[정답]
20
[풀이]
1
2
3
4
5
| int func() {
static int x = 0;
x += 2;
return x;
}
|
x는 static 지역변수이다.- 함수가 호출될 때마다 새로 생성되는 것이 아닌, 프로그램 시작 시 메모리가 할당되고, 해당 함수가 처음 실행될 때 초기화된다.
- 이후 함수가 호출될 때마다 이전 값이 그대로 유지된다.
- 즉,
func() 함수가 호출될 때마다 x는 누적 증가한다.
1
2
3
4
| int main() {
int x = 0; // 지역변수 x를 0으로 초기화
int sum = 0; // 합계를 저장할 변수 sum을 0으로 초기화
}
|
main의 x는 일반 지역변수이다.- 반복문에서
x++가 되지만, 이 값은 func()의 x와 전혀 다른 변수이다.
1
2
3
4
| for (int i = 0; i < 4; i++) {
x++; // main의 x
sum += func(); // static x 사용
}
|
| i | main의 x | func()의 static x | 반환값 | sum |
|---|
| 0 | x = 1 | x = 2 | 2 | sum = 2 |
| 1 | x = 2 | x = 4 | 4 | sum = 6 |
| 2 | x = 3 | x = 6 | 6 | sum = 12 |
| 3 | x = 4 | x = 8 | 8 | sum = 20 |
따라서 sum은 20이 된다.
static 지역변수
- 프로그램 시작 시 메모리가 할당되고, 프로그램 시작 전에 0으로 초기화되며, 명시한 초기값으로는 한 번만 초기화된다.
- 함수가 종료되어도 소멸되지 않는다.
- 이전 호출의 값을 그대로 유지한다.
- 유효 범위(
scope)는 함수 내부이지만, 수명(lifetime)은 프로그램 종료 시까지이다. - 메모리 영역 : 데이터 영역
일반 지역변수
- 함수(또는 블록) 호출 시 생성된다.
- 블록이 끝나면 소멸된다.
- 유효 범위와 수명이 모두 블록 내부로 한정된다.
- 메모리 영역 : 스택 영역
문제 10. 포인터 이동과 값 교환
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| #include <stdio.h>
struct Node {
int value;
struct Node* next;
};
void func(struct Node* node) {
while (node != NULL && node->next != NULL) {
int t = node->value;
node->value = node->next->value;
node->next->value = t;
node = node->next->next;
}
}
int main() {
struct Node n1 = {1, NULL};
struct Node n2 = {2, NULL};
struct Node n3 = {3, NULL};
n1.next = &n3;
n3.next = &n2;
func(&n1);
struct Node* current = &n1;
while (current != NULL) {
printf("%d", current->value);
current = current->next;
}
return 0;
}
|
더보기
[정답]
312
[풀이]
1
2
| n1.next = &n3;
n3.next = &n2;
|
n1의 next 포인터에 n3의 주소를 저장한다.n3의 next 포인터에 n2의 주소를 저장한다.
1
2
3
4
5
| (주소 흐름)
n1 → n3 → n2 → NULL
(value)
[1] [3] [2]
|
1
2
3
4
5
6
7
8
| void func(struct Node* node) {
while (node != NULL && node->next != NULL) {
int t = node->value;
node->value = node->next->value;
node->next->value = t;
node = node->next->next;
}
}
|
1
2
3
| int t = node->value; // t = 1
node->value = node->next->value; // n1.value = 3
node->next->value = t; // n3.value = 1
|
1
2
3
4
| n1 → n3 → n2
(value 변화)
[3] [1] [2]
|
1
| node = node->next->next; // node가 n2를 가리키게 된다
|
1
2
3
4
| n1 → n3 → n2
[3] [1] [2]
↑
node
|
1
| while (node != NULL && node->next != NULL)
|
node != NULL : OKnode->next != NULL : NO (n2.next = NULL)
반복문이 종료된다.
1
2
3
4
5
6
| struct Node* current = &n1;
while (current != NULL) {
printf("%d", current->value);
current = current->next;
}
|
1
2
3
4
| n1 → n3 → n2
[3] [1] [2]
↑
current
|
포인터를 다음 노드로 이동시키며, current->value값을 하나씩 출력한다.
따라서 312가 출력된다.
문제 11. 이중 포인터의 역참조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| #include <stdio.h>
void func(int** arr, int size) {
for (int i = 0; i < size; i++) {
*(*arr + i) = (*(*arr + i) + i) % size;
}
}
int main() {
int arr[] = {3, 1, 4, 1, 5};
int* p = arr;
int** pp = &p;
int num = 6;
func(pp, 5);
num = arr[2];
printf("%d", num);
return 0;
}
|
더보기
[정답]
1
[풀이]
1
2
| int* p = arr; // p → arr[0]
int** pp = &p; // pp → p
|
p는 배열 arr의 시작 주소를 저장하는 포인터pp는 포인터 p의 주소를 저장하는 이중 포인터
1
2
3
4
5
| void func(int** arr, int size) {
for (int i = 0; i < size; i++) {
*(*arr + i) = (*(*arr + i) + i) % size;
}
}
|
*(*arr + i) 뜯어보기
arr == pp*arr == p(*arr) + i = p + i*(p + i) == arr[i]
*(*arr + i)는 arr[i]와 같다.
arr[i] = (arr[i] + i) % size와 동일하다.
| i | 결과값 | 변경 후 arr 배열 |
|---|
| 0 | (arr[0] + 0) % 5 = 3 % 5 = 3 | {3, 1, 4, 1, 5} |
| 1 | (arr[1] + 1) % 5 = 2 % 5 = 2 | {3, 2, 4, 1, 5} |
| 2 | (arr[2] + 2) % 5 = 6 % 5 = 1 | {3, 2, 1, 1, 5} |
| 3 | (arr[3] + 3) % 5 = 4 % 5 = 4 | {3, 2, 1, 4, 5} |
| 4 | (arr[4] + 4) % 5 = 9 % 5 = 4 | {3, 2, 1, 4, 4} |
최종 배열 상태 : {3, 2, 1, 4, 4}
1
2
| num = arr[2]; // num = 1
printf("%d", num);
|
따라서 1이 출력된다.
이중 포인터의 역참조
이중 포인터를 두 번 역참조하여 실제 배열 원소에 접근하는 코드이다.
arr → pp (포인터의 주소)*arr → p (배열의 시작 주소)*(*arr + i) → arr[i] (실제 배열 원소)
* 연산자를 사용할 때마다 포인터를 한 단계씩 따라 들어가 실제 값에 도달하게 된다.
정리
시프트 연산자
비트를 이동시키는 연산자이다.
| 종류 | 의미 | 형식 | 설명 |
|---|
| » | 오른쪽으로 이동 | 5 » 2 | 5의 이진표현을 오른쪽으로 2칸 이동 |
| « | 왼쪽으로 이동 | 5 « 2 | 5의 이진표현을 왼쪽으로 2칸 이동 |
문자 판별 함수 (isupper, islower, isdigit)
각각 문자가 대문자, 소문자, 숫자인지 판별하며, 결과로 참이면 0이 아닌 정수, 거짓이면 0을 반환한다.
- isupper(c) :
c가 대문자 알파벳(A~Z)인지 확인 - islower(c) :
c가 소문자 알파벳(a~z)인지 확인 - isdigit(c) :
c가 숫자 문자(0~9)인지 확인- 숫자 자체가 아니라, 문자 형태의 숫자여야 하며, 작은 따옴표로 감싸야 한다.
static 변수와 지역변수 차이
static 지역변수
- 프로그램 시작 시 메모리가 할당되고, 프로그램 시작 전에 0으로 초기화되며, 명시한 초기값으로는 한 번만 초기화된다.
- 함수가 종료되어도 소멸되지 않는다.
- 이전 호출의 값을 그대로 유지한다.
- 유효 범위(
scope)는 함수 내부이지만, 수명(lifetime)은 프로그램 종료 시까지이다. - 메모리 영역 : 데이터 영역
일반 지역변수
- 함수(또는 블록) 호출 시 생성된다.
- 블록이 끝나면 소멸된다.
- 유효 범위와 수명이 모두 블록 내부로 한정된다.
- 메모리 영역 : 스택 영역