Topic 3: 포인터와 배열 🎯
🎯 학습 목표
- 포인터와 배열의 관계를 깊이 이해할 수 있다
- 포인터 산술 연산을 활용할 수 있다
- 2차원 배열과 포인터를 다룰 수 있다
- C-스타일 문자열을 포인터로 처리할 수 있다
📖 포인터와 배열의 관계
배열 이름은 첫 번째 요소를 가리키는 포인터와 같은 역할을 합니다.
아날로지: 아파트 주소와 같습니다.
- 배열 이름: 아파트 전체 주소 (101동)
- 포인터: 특정 호수 주소 (101동 501호)
- 인덱스: 층과 호수 번호
🔍 기본 관계 이해
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // arr는 &arr[0]과 동일
cout << "=== 배열과 포인터의 관계 ===" << endl;
cout << "arr 주소: " << arr << endl;
cout << "&arr[0] 주소: " << &arr[0] << endl;
cout << "ptr 주소: " << ptr << endl;
// 모두 같은 값
cout << "\n=== 첫 번째 요소 접근 ===" << endl;
cout << "arr[0]: " << arr[0] << endl;
cout << "*arr: " << *arr << endl;
cout << "*ptr: " << *ptr << endl;
cout << "*(arr + 0): " << *(arr + 0) << endl;
return 0;
}
배열 요소 접근 방법들
#include <iostream>
using namespace std;
int main() {
int numbers[4] = {100, 200, 300, 400};
int* p = numbers;
cout << "=== 다양한 접근 방법 ===" << endl;
for(int i = 0; i < 4; i++) {
cout << "인덱스 " << i << ":" << endl;
cout << " 배열 표기법: numbers[" << i << "] = " << numbers[i] << endl;
cout << " 포인터 표기법: *(numbers + " << i << ") = " << *(numbers + i) << endl;
cout << " 포인터 변수: *(p + " << i << ") = " << *(p + i) << endl;
cout << " 포인터 인덱스: p[" << i << "] = " << p[i] << endl;
cout << endl;
}
return 0;
}
🔢 포인터 산술 연산
포인터 산술 연산은 배열 순회와 메모리 탐색의 핵심입니다.
기본 산술 연산
#include <iostream>
using namespace std;
int main() {
double data[6] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
double* ptr = data;
cout << "=== 포인터 산술 연산 ===" << endl;
cout << "double 크기: " << sizeof(double) << " 바이트" << endl;
cout << "배열 시작 주소: " << ptr << endl;
// 포인터 증가
for(int i = 0; i < 6; i++) {
cout << "ptr + " << i << ": 주소=" << (ptr + i)
<< ", 값=" << *(ptr + i) << endl;
}
// 포인터 간 차이
double* end = data + 5;
cout << "\n포인터 간 차이: " << (end - ptr) << " 요소" << endl;
return 0;
}
포인터로 배열 순회
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int scores[8] = {85, 92, 78, 96, 88, 75, 91, 83};
int* current = scores;
int* end = scores + 8;
cout << "=== 포인터로 배열 순회 ===" << endl;
// 방법 1: 인덱스 사용
cout << "방법 1 (인덱스):" << endl;
for(int i = 0; i < 8; i++) {
cout << setw(3) << current[i] << " ";
}
cout << endl;
// 방법 2: 포인터 증가
cout << "방법 2 (포인터 증가):" << endl;
current = scores; // 포인터 재설정
for(int i = 0; i < 8; i++, current++) {
cout << setw(3) << *current << " ";
}
cout << endl;
// 방법 3: 포인터 비교
cout << "방법 3 (포인터 비교):" << endl;
current = scores; // 포인터 재설정
while(current < end) {
cout << setw(3) << *current << " ";
current++;
}
cout << endl;
return 0;
}
📊 2차원 배열과 포인터
2차원 배열은 포인터의 포인터로 이해할 수 있습니다.
2차원 배열 기초
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
cout << "=== 2차원 배열과 포인터 ===" << endl;
// 2차원 배열 출력
cout << "배열 인덱스 방법:" << endl;
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 4; j++) {
cout << setw(3) << matrix[i][j] << " ";
}
cout << endl;
}
// 포인터 방법
cout << "\n포인터 방법:" << endl;
int* ptr = matrix[0]; // 첫 번째 행의 첫 번째 요소
for(int i = 0; i < 12; i++) {
cout << setw(3) << *(ptr + i) << " ";
if((i + 1) % 4 == 0) cout << endl;
}
return 0;
}
행 포인터 사용
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int table[4][3] = {
{10, 20, 30},
{40, 50, 60},
{70, 80, 90},
{100, 110, 120}
};
cout << "=== 행 포인터 사용 ===" << endl;
// 각 행을 포인터로 처리
for(int row = 0; row < 4; row++) {
int* rowPtr = table[row]; // 행의 첫 번째 요소 포인터
cout << "행 " << row << ": ";
for(int col = 0; col < 3; col++) {
cout << setw(4) << *(rowPtr + col) << " ";
}
// 행의 합계 계산
int sum = 0;
for(int col = 0; col < 3; col++) {
sum += *(rowPtr + col);
}
cout << "| 합계: " << sum << endl;
}
return 0;
}
📝 C-스타일 문자열과 포인터
C-스타일 문자열은 char 배열이며, 포인터로 효율적으로 처리할 수 있습니다.
문자열 기본 조작
#include <iostream>
#include <cstring>
using namespace std;
int main() {
// 다양한 문자열 선언 방법
char str1[] = "Hello"; // 배열
char str2[20] = "World"; // 고정 크기 배열
const char* str3 = "C++"; // 포인터 (문자열 리터럴)
cout << "=== 문자열과 포인터 ===" << endl;
cout << "str1: " << str1 << " (크기: " << sizeof(str1) << ")" << endl;
cout << "str2: " << str2 << " (크기: " << sizeof(str2) << ")" << endl;
cout << "str3: " << str3 << " (크기: " << sizeof(str3) << ")" << endl;
// 문자열 길이 계산 (포인터 사용)
const char* text = "Programming";
int length = 0;
const char* ptr = text;
while(*ptr != '\0') {
length++;
ptr++;
}
cout << "\n문자열 \"" << text << "\"의 길이: " << length << endl;
cout << "strlen() 결과: " << strlen(text) << endl;
return 0;
}
문자열 처리 함수 구현
#include <iostream>
using namespace std;
// 문자열 길이 계산
int myStrlen(const char* str) {
int length = 0;
while(*str++) {
length++;
}
return length;
}
// 문자열 복사
void myStrcpy(char* dest, const char* src) {
while(*src) {
*dest++ = *src++;
}
*dest = '\0'; // 널 문자 추가
}
// 문자열 비교
int myStrcmp(const char* str1, const char* str2) {
while(*str1 && (*str1 == *str2)) {
str1++;
str2++;
}
return *str1 - *str2;
}
// 문자열 연결
void myStrcat(char* dest, const char* src) {
// dest의 끝으로 이동
while(*dest) {
dest++;
}
// src를 dest 끝에 복사
while(*src) {
*dest++ = *src++;
}
*dest = '\0';
}
int main() {
char buffer[100];
const char* source = "Hello, World!";
cout << "=== 사용자 정의 문자열 함수 ===" << endl;
// 문자열 복사
myStrcpy(buffer, source);
cout << "복사된 문자열: " << buffer << endl;
cout << "길이: " << myStrlen(buffer) << endl;
// 문자열 연결
myStrcat(buffer, " Welcome to C++!");
cout << "연결된 문자열: " << buffer << endl;
// 문자열 비교
const char* str1 = "apple";
const char* str2 = "banana";
int result = myStrcmp(str1, str2);
cout << "\n\"" << str1 << "\"와 \"" << str2 << "\" 비교: ";
if(result < 0) {
cout << "첫 번째가 사전상 앞섬" << endl;
} else if(result > 0) {
cout << "두 번째가 사전상 앞섬" << endl;
} else {
cout << "두 문자열이 같음" << endl;
}
return 0;
}
🎯 실습: 간단한 동적 문자열
#include <iostream>
#include <cstring>
using namespace std;
// 문자열을 동적으로 복사하는 함수
char* copyString(const char* source) {
// 문자열 길이 계산
int len = strlen(source);
// 동적 메모리 할당 (+1은 null 문자를 위함)
char* copy = new char[len + 1];
// 문자열 복사
strcpy(copy, source);
cout << "\"" << source << "\"를 복사했습니다. (길이: " << len << ")" << endl;
return copy;
}
// 문자열 연결 함수
char* concatenateStrings(const char* str1, const char* str2) {
int len1 = strlen(str1);
int len2 = strlen(str2);
// 두 문자열을 합칠 공간 할당
char* result = new char[len1 + len2 + 1];
// 첫 번째 문자열 복사
strcpy(result, str1);
// 두 번째 문자열 연결
strcat(result, str2);
return result;
}
int main() {
cout << "=== 동적 문자열 처리 ===" << endl;
const char* original = "Hello";
const char* world = " World!";
// 문자열 복사
char* copy = copyString(original);
cout << "복사된 문자열: " << copy << endl;
// 문자엱 연결
char* combined = concatenateStrings(original, world);
cout << "연결된 문자열: " << combined << endl;
// 메모리 해제
delete[] copy;
delete[] combined;
cout << "\n모든 메모리가 해제되었습니다." << endl;
return 0;
}
🎯 실습: 문자열 기본 처리
#include <iostream>
#include <cstring>
using namespace std;
// 문자열 길이 계산 (포인터 사용)
int stringLength(const char* str) {
int length = 0;
while(*str != '\0') { // null 문자까지
length++;
str++; // 포인터 증가
}
return length;
}
// 문자열 복사 함수
void copyString(char* dest, const char* src) {
while(*src != '\0') {
*dest = *src; // 문자 복사
dest++;
src++;
}
*dest = '\0'; // null 문자 추가
}
// 문자열 대문자로 변환
void toUpperCase(char* str) {
while(*str != '\0') {
if(*str >= 'a' && *str <= 'z') {
*str = *str - 32; // 대문자로 변환
}
str++;
}
}
int main() {
cout << "=== 포인터로 문자열 처리 ===" << endl;
const char* original = "hello world";
char buffer[50];
// 문자열 길이 계산
int len = stringLength(original);
cout << "\"" << original << "\"의 길이: " << len << endl;
// 문자열 복사
copyString(buffer, original);
cout << "복사된 문자열: " << buffer << endl;
// 대문자로 변환
toUpperCase(buffer);
cout << "대문자 변환: " << buffer << endl;
return 0;
}
💡 모범 사례와 주의사항
✅ 좋은 습관
-
배열 경계 확인
int arr[5]; int* ptr = arr; for(int i = 0; i < 5; i++) { // 경계 체크 cout << *(ptr + i) << endl; }
-
const 포인터 활용
void printArray(const int* arr, int size) { for(int i = 0; i < size; i++) { cout << arr[i] << " "; // 수정 불가 } }
-
널 포인터 체크
void safeStringLength(const char* str) { if(str != nullptr) { cout << "길이: " << strlen(str) << endl; } }
❌ 피해야 할 실수
-
배열 경계 초과
int arr[3] = {1, 2, 3}; int* ptr = arr; cout << *(ptr + 5); // 위험한 접근!
-
문자열 리터럴 수정
const char* str = "Hello"; // str[0] = 'h'; // 오류! 리터럴 수정 불가
-
포인터 산술 오류
int* ptr1 = new int(10); int* ptr2 = new int(20); // int diff = ptr2 - ptr1; // 의미없는 연산
🔍 성능 최적화 팁
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
void arrayIndexAccess(int* arr, int size) {
int sum = 0;
for(int i = 0; i < size; i++) {
sum += arr[i]; // 인덱스 접근
}
}
void pointerArithmetic(int* arr, int size) {
int sum = 0;
int* end = arr + size;
while(arr < end) {
sum += *arr++; // 포인터 증가
}
}
int main() {
const int SIZE = 1000000;
int* bigArray = new int[SIZE];
// 배열 초기화
for(int i = 0; i < SIZE; i++) {
bigArray[i] = i;
}
// 인덱스 접근 시간 측정
auto start = high_resolution_clock::now();
arrayIndexAccess(bigArray, SIZE);
auto end = high_resolution_clock::now();
auto indexTime = duration_cast<microseconds>(end - start);
// 포인터 산술 시간 측정
start = high_resolution_clock::now();
pointerArithmetic(bigArray, SIZE);
end = high_resolution_clock::now();
auto pointerTime = duration_cast<microseconds>(end - start);
cout << "=== 성능 비교 (배열 크기: " << SIZE << ") ===" << endl;
cout << "인덱스 접근: " << indexTime.count() << " microseconds" << endl;
cout << "포인터 산술: " << pointerTime.count() << " microseconds" << endl;
delete[] bigArray;
return 0;
}
Last updated on