|
| 1 | +--- |
| 2 | +sidebar_position: 3 |
| 3 | +title: 프로그래밍 기법 |
| 4 | +description: 배열, 포인터, 구조체, 재귀 호출의 네 가지 주요 주제에 초점을 맞추어 학습 |
| 5 | +--- |
| 6 | + |
| 7 | +## 핵심 요약 |
| 8 | + |
| 9 | +- 배열 (Arrays): 동일한 자료형의 데이터 집합을 메모리의 연속된 공간에 저장하는 자료구조이다. 인덱스를 통해 각 요소에 빠르게 접근할 수 있으며, 1차원뿐만 아니라 다차원 형태로도 구성이 가능하다. |
| 10 | +- 포인터 (Pointers): 변수의 메모리 주소값을 직접 저장하고 다루는 변수이다. 메모리에 대한 직접적인 접근을 가능하게 하여 동적 메모리 할당, 배열 및 구조체의 효율적 관리 등 고급 프로그래밍 기법의 근간을 이룬다. |
| 11 | +- 구조체 (Structs): 서로 다른 자료형의 변수들을 하나의 논리적 단위로 묶어 새로운 자료형을 정의하는 기능이다. 배열과 달리 이질적인 데이터 그룹을 만들 수 있어, '레코드'와 같은 복잡한 데이터 형태를 표현하는 데 매우 유용하다. |
| 12 | +- 재귀호출 (Recursive Calls): 함수가 자기 자신을 다시 호출하는 프로그래밍 기법이다. 복잡한 문제를 동일한 구조를 가진 더 작은 문제들로 분할하여 해결하는 데 효과적이며, 특정 알고리즘을 간결하게 표현할 수 있게 한다. |
| 13 | + |
| 14 | +--- |
| 15 | + |
| 16 | +## 배열 (Arrays): 심층 분석 |
| 17 | + |
| 18 | +배열은 동일한 자료형의 원소(element)들을 모아 메모리에 연속적으로 할당한 자료 그룹이다.<br/ > |
| 19 | +각 원소는 고유한 번호인 '인덱스(index)'를 통해 구별되며, C 언어에서 인덱스는 항상 0부터 시작한다. |
| 20 | + |
| 21 | +### 1차원 배열 |
| 22 | + |
| 23 | + 가장 기본적인 형태의 배열로, 데이터를 한 줄로 나열한 구조를 가진다. |
| 24 | + |
| 25 | +- 선언 형식: 자료형 배열이름 [배열 요소의 개수]; |
| 26 | + - 자료형: 배열에 저장될 모든 원소의 데이터 타입을 지정한다. |
| 27 | + - 배열이름: 변수 이름 규칙에 따라 정의한다. |
| 28 | + - 배열 요소의 개수: 대괄호([]) 안에 배열의 크기를 명시한다. |
| 29 | +- 메모리 할당: 배열 선언 시 할당되는 총 메모리 크기는 (자료형의 메모리 크기) × (배열 요소의 개수)로 결정된다. |
| 30 | + |
| 31 | +| 배열 선언 예 | 의미 | 메모리 할당 크기 | |
| 32 | +| ------------- | ----------------------------------------------- | ---------------- | |
| 33 | +| char c[100]; | char형 원소 100개로 구성된 배열 c (c[0]~c[99]) | 1바이트 × 100 | |
| 34 | +| int i[100]; | int형 원소 100개로 구성된 배열 i (i[0]~i[99]) | 4바이트 × 100 | |
| 35 | +| short s[100]; | short형 원소 100개로 구성된 배열 s (s[0]~s[99]) | 2바이트 × 100 | |
| 36 | +| long l[100]; | long형 원소 100개로 구성된 배열 l (l[0]~l[99]) | 4바이트 × 100 | |
| 37 | + |
| 38 | +- 초기화: 선언과 동시에 중괄호`{ }` 안에 초깃값 리스트를 지정하여 초기화할 수 있다. |
| 39 | + - 자료형 `배열이름[배열크기] = { 초깃값 리스트 };` |
| 40 | + - 초깃값의 개수가 배열 크기보다 작으면 나머지 원소들은 0으로 자동 초기화된다. |
| 41 | + - 초깃값의 개수가 배열 크기보다 많으면 컴파일 오류가 발생한다. |
| 42 | +- 문자 배열: 문자열(String)을 저장하기 위해 char형 배열을 사용한다. 문자열은 문자들의 연속적인 나열이며, 큰따옴표(" ")로 표현된다. |
| 43 | + - 초기화 방법: 문자열 리터럴`"String"`을 사용하거나, 개별 문자를 리스트`{'S', 't', 'r', 'i', 'n', 'g', '\0'}`로 지정할 수 있다. |
| 44 | + |
| 45 | +### 다차원 배열 |
| 46 | + |
| 47 | + 2차원 이상의 배열로, 행과 열 또는 면, 행, 열의 구조를 논리적으로 표현한다. |
| 48 | + |
| 49 | +- 선언 형식: |
| 50 | + - 2차원: 자료형 `배열이름 [행 개수][열 개수];` |
| 51 | + - 3차원: 자료형 `배열이름 [면 개수][행 개수][열 개수];` |
| 52 | +- 구조: |
| 53 | + - 논리적 구조: 2차원은 표(table), 3차원은 입방체(cube) 형태로 인식된다. |
| 54 | + - 물리적 구조: 메모리에는 차원과 관계없이 모든 원소가 1차원적으로 연속되게 저장된다. |
| 55 | +- 초기화: 1차원 배열과 유사하게 초깃값 리스트를 사용한다. 차원별로 중첩된 중괄호를 사용하여 구조를 명확히 하거나, 모든 값을 나열하여 순서대로 초기화할 수 있다. |
| 56 | + |
| 57 | +<br/> |
| 58 | + |
| 59 | +--- |
| 60 | + |
| 61 | +<br/> |
| 62 | + |
| 63 | +## 포인터 (Pointers): 심층 분석 |
| 64 | + |
| 65 | +포인터는 변수의 메모리 주소값을 값으로 가지는 특별한 변수이다.<br/ > |
| 66 | +포인터 변수가 특정 변수의 주소를 저장하는 것을 '해당 변수를 가리킨다(pointing)'고 표현하며,<br/ > |
| 67 | +이를 통해 메모리 영역에 직접 접근하고 제어할 수 있다. |
| 68 | + |
| 69 | +### 선언 및 개념 |
| 70 | + |
| 71 | +- 정의: "변수의 메모리 주소값" 그 자체 또는 주소값을 저장하는 "포인터 변수"를 의미한다. |
| 72 | +- 선언 형식: 자료형 `*포인터이름;` |
| 73 | + - `*` 기호는 해당 변수가 포인터임을 나타낸다. |
| 74 | + - 자료형은 포인터 자체가 아닌, 포인터가 가리킬 |
| 75 | + |
| 76 | +### 주요 연산자 |
| 77 | + |
| 78 | +포인터는 두 가지 핵심 연산자를 통해 동작한다. |
| 79 | + |
| 80 | +| 연산자 | 이름 | 설명 | 사용 예시 | |
| 81 | +| ------ | ----------- | ---------------------------------------------------------------------------------------- | ----------------------------------- | |
| 82 | +| `&` | 주소 연산자 | 변수 앞에 사용하여 해당 변수의 시작 메모리 주소를 반환한다. | `ptr = &i;` | |
| 83 | +| `*` | 참조 연산자 | 포인터 변수 앞에 사용하여, 해당 포인터가 가리키는 메모리 주소에 저장된 값을 읽거나 쓴다. | `*ptr = 20;` 또는<br/ > `j = *ptr;` | |
| 84 | + |
| 85 | +### 포인터 초기화 방법 |
| 86 | + |
| 87 | +포인터 변수에 유효한 주소값을 할당하는 주요 방법은 다음과 같다. |
| 88 | + |
| 89 | +1. 주소 연산자 `&` 사용: 기존 변수의 주소를 할당한다. |
| 90 | +2. 동적 메모리 할당: `malloc` 등의 함수를 통해 할당된 메모리의 시작 주소를 지정한다. |
| 91 | +3. 문자열 리터럴 할당: `char*` 포인터에 문자열의 시작 주소를 직접 지정한다. |
| 92 | +4. 배열 이름 사용: 배열의 이름은 그 자체로 배열의 시작 주소를 의미하므로 포인터에 할당할 수 있다. |
| 93 | +5. 배열 첫 번째 요소의 주소 사용: `&array[0]` 와 같이 배열의 첫 번째 요소의 주소를 명시적으로 지정한다. |
| 94 | + |
| 95 | +### 고급 활용 |
| 96 | + |
| 97 | +- 포인터 배열 (Pointer Array): 포인터 변수들로 구성된 배열. `자료형 *포인터배열이름[배열크기];` 형태로 선언한다. 각 배열의 원소가 포인터이므로, 여러 개의 문자열이나 동적으로 할당된 메모리 블록들을 효율적으로 관리할 수 있다. |
| 98 | +- 포인터의 포인터 (Pointer to Pointer): 다른 포인터 변수의 주소를 저장하는 포인터로, 이중 포인터라고도 한다. `자료형 **포인터이름;` 형태로 선언한다. 포인터 배열을 함수 인자로 전달하거나, 함수 내에서 포인터 변수 자체의 값을 변경할 때 사용된다. |
| 99 | + |
| 100 | +<br/> |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +<br/> |
| 105 | + |
| 106 | +## 구조체 (Structs): 심층 분석 |
| 107 | + |
| 108 | +구조체는 하나 이상의 변수를 묶어 새로운 자료형으로 정의하는 기능이다. 배열과 달리, 서로 다른 자료형의 변수들을 멤버(항목)로 포함할 수 있다. |
| 109 | + |
| 110 | +### 핵심 개념 |
| 111 | + |
| 112 | +- 정의: "서로 다른 자료형을 그룹으로 묶을 수 있"는 사용자 정의 자료형. |
| 113 | +- 배열과의 차이점: 배열은 동일 자료형(homogeneous)의 집합인 반면, 구조체는 다른 자료형(heterogeneous)의 집합을 만들 수 있다. |
| 114 | +- 활용: 이름, 입사 연도, 연봉 등 여러 필드(field)로 구성된 레코드(record)와 같은 복잡한 데이터를 표현하는 데 매우 유용하다. |
| 115 | + |
| 116 | +### 선언 및 사용 |
| 117 | + |
| 118 | +구조체는 3단계의 과정을 거쳐 사용된다. |
| 119 | + |
| 120 | +1. 구조체형 선언: struct 키워드를 사용하여 구조체의 이름과 내부 멤버 변수들을 정의한다. |
| 121 | +2. 구조체 변수 선언: 정의된 구조체형을 사용하여 변수를 선언한다. |
| 122 | +3. 구조체 변수 사용: 선언된 변수의 내부 멤버에 접근하여 데이터를 저장하거나 사용한다. |
| 123 | + |
| 124 | +- 선언 형식: |
| 125 | +- 사용 형식: struct 구조체형이름 구조체변수이름; |
| 126 | + |
| 127 | +### 데이터 항목 참조 |
| 128 | + |
| 129 | +구조체 변수의 멤버에 접근하기 위해 두 가지 연산자가 사용된다. |
| 130 | + |
| 131 | +| 연산자 | 이름 | 설명 | 사용 예시 | |
| 132 | +| ------ | ------------- | ----------------------------------------------------------------------------------- | --------- | |
| 133 | +| . | 점 연산자 | 구조체 변수의 데이터 항목을 지정할 때 사용한다. (예: `Lee[i].name`) | |
| 134 | +| -> | 화살표 연산자 | 구조체를 가리키는 포인터를 통해 데이터 항목을 지정할 때 사용한다. (예: `ptr->name`) | |
| 135 | + |
| 136 | +> 참고: 화살표 연산자 `p->member`는 `(*p).member`와 완전히 동일한 표현이다. |
| 137 | +
|
| 138 | +### 구조체 연산 |
| 139 | + |
| 140 | +- 복사 연산: 같은 구조체형을 가진 변수들끼리는 대입 연산자(=)를 통해 모든 멤버의 내용을 한 번에 복사할 수 있다. |
| 141 | +- 주소 구하기 연산: 주소 연산자(&)를 사용하여 구조체 변수의 시작 주소를 얻을 수 있다. |
| 142 | + |
| 143 | +<br/> |
| 144 | + |
| 145 | +--- |
| 146 | + |
| 147 | +<br/> |
| 148 | + |
| 149 | +## 재귀호출 (Recursive Calls): 심층 분석 |
| 150 | + |
| 151 | +재귀호출(순환호출)은 함수가 실행 도중에 자기 자신을 다시 호출하는 프로그래밍 기법이다. |
| 152 | + |
| 153 | +### 핵심 개념 |
| 154 | + |
| 155 | +- 정의: "자기 자신을 호출하여 순환 수행되는 것". |
| 156 | +- 원리: 해결하려는 문제를 동일한 구조를 가진 더 작은 하위 문제로 분할하고, 가장 작은 단위의 문제(베이스 케이스)부터 해결해 나가는 방식이다. |
| 157 | +- 베이스 케이스 (Base Case): 재귀의 연쇄적인 호출을 멈추게 하는 조건이다. 이 조건이 없으면 함수는 무한히 자신을 호출하여 스택 오버플로 오류를 발생시킨다. |
| 158 | + |
| 159 | +### 작동 원리: 팩토리얼 예제 |
| 160 | + |
| 161 | +팩토리얼 함수 `factorial(n)`은 재귀의 대표적인 예이다. |
| 162 | + |
| 163 | +- 재귀적 정의: n > 0일 때, `factorial(n) = n \_ factorial(n - 1)` |
| 164 | +- 베이스 케이스: n = 0일 때, `factorial(0) = 1` |
| 165 | + |
| 166 | +`factorial(3)`을 호출하면, 내부적으로 `factorial(2)`, `factorial(1)`, `factorial(0)`이 순차적으로 호출된다. `factorial(0)`이 베이스 케이스에 도달하여 1을 반환하면, 호출의 역순으로 결과값이 계산되어 최종적으로 `3 * 2 * 1 * 1 = 6`이 반환된다. |
| 167 | + |
| 168 | +### 재귀의 종류 |
| 169 | + |
| 170 | +- 직접 재귀 (Direct Recursion): 함수가 자기 자신을 직접 호출하는 경우 (예: factorial 함수). |
| 171 | +- 간접 재귀 (Indirect Recursion): 함수 X가 함수 Y를 호출하고, 다시 함수 Y가 함수 X를 호출하는 것처럼 다른 함수를 통해 간접적으로 자기 자신이 호출되는 구조. |
| 172 | + |
| 173 | +### 구현 |
| 174 | + |
| 175 | +재귀 알고리즘이 적합한 경우는 문제, 함수, 또는 데이터 구조 자체가 재귀적으로 정의될 때이다.<br/> |
| 176 | +재귀로 표현 가능한 대부분의 문제는 반복문(loop)을 이용해서도 구현할 수 있다.<br/> |
| 177 | +재귀는 코드를 간결하게 만들 수 있는 장점이 있지만, 함수 호출에 따른 오버헤드가 발생할 수 있다. |
0 commit comments