03-1 포인터와 메모리
- C++에서 가장 강력한 도구로 대표적으로 포인터가 뽑힌다.
- 포인터를 이해하기 위해서는 데이터가 메모리에 저장되는 구조를 알아야한다.
- 포인터는 데이터가 저장된 메모리 주소를 저장하는 변수이다.
- 포인터 변수의 크기는 데이터 형식과는 관련이 없음. 모든 포인터 변수의 크기는 동일
- 포인터 변수를 선언할 때 데이터 형식을 지정하는 이유는 해당 포인터가 가리키는 데이터의 형식을 명시하기 위함.
- 다중 포인터도 가능!
포인터와 연산자
char char_value = 'A';
char *char_pointer = &char_value; # &(주소 연산자: 피연산자의 주소를 불러옴)`
cout << "char\_pointer:" << \*char\_pointer << endl; # \*(역참조 연산자)
배열과 포인터
- 배열 선언 --> 자료형 배열_이름[크기] = {값1, 값2, 값3, ...., 값n}
- 원소 접근 --> 배열_이름[인덱스] # 항상 0부터 시작함. 가장 마지막 원소는 "배열_이름[n-1]
포인터 연산으로 배열의 원소에 접근하기
- 배열의 인덱스로 접근 and 포인터 연산으로 각 원소에 접근
- -> &lotto[0] == lotto + 0과 동일함 --> 동일한 주소 반환
- -> &배열_변수[인덱스] == 배열_변수 + 인덱스
- -> ** 값을 불러오려면 *(역참조 연산자) 사용해야함!
- -> ex) *(lotto + 7)
동적 메모리 할당
- 동적 메모리를 할당하기 위해서는 new 키워드를 사용
- 자료형 *변수_이름 = new 자료형;
- 필요없는 시점에 delete 키워드로 반드시 해제해야함(직접).
- delete 변수_이름;
- 동적 할당 메모리 해제 이유: 함수의 매개변수나 지역변수처럼 대부분의 일반 변수는 Stack에 메모리가 할당되고 함수의 호출과 함께 할당되며 반환되면 자동으로 소멸함. 하지만 동적 할당 변수는 Heap이라는 메모리 영역에 존재하며 계속 유지됨. 메모리 영역도 훨씬 큼
- segmentation fault
- 프로그램이 허용되지 않은 메모리 영역에 접근을 시도하거나, 허용되지 않은 방법으로 메모리 영역제 접근을 시도할 때 발생
- 포인터를 역참조하기 전에 포인터가 유효한 메모리를 가리키는지 확인이 필수
- 유효하지 않은 메모리를 가리키는 포인터를 역참조하려고 하면 위 오류나 런타임 오류가 발생할 수 있음
03-2 함수와 구조체
- function은 특정 작업을 수행하는 코드 집합
- 기본적인 구성
int(반환 형식) func(함수이름) (int _arg1, int _arg2) (매개변수) { 함수 몸체 }
함수 선언의 4가지 필수 요소
- 반환 형식: 함수가 반환할 값의 자료형. 반환할 값이 없을 때는 void로 표기
- 함수 이름: 함수를 호출할 때 사용할 이름. 함수의 이름은 문자나 _(밑줄)로 시작하며 숫자X, 공백 X
- 매개 변수: 함수가 호출될 때 전달받은 값을 저장하는 변수로, 함수 내부에서만 사용할 수 있는 지역 변수. 전달 받을 값이 없을 때는 비워두거나, void로 매개변수가 없음을 표기할 수 있음.
- 함수 몸체: 함수의 기능을 정의하는 부분
function.cpp 작성 중 size 관련 궁금증 답변
🔍 핵심 차이점
- main 함수에서의
sizeof(score) int score[5] = {90, 75, 80, 100, 65}; score_size = sizeof(score) / sizeof(int);
- score는 이 시점에서는 정확히 크기가 정해진 배열이야. 즉, int[5]형이야.
- sizeof(score)는 배열 전체의 바이트 크기를 줘. int가 4바이트라면, 5 * 4 = 20바이트.
- sizeof(int)는 당연히 4바이트.
- 따라서 score_size = 20 / 4 = 5.
- average 함수에서의
sizeof(_array) int average(int _array[], int _count) { score_size = sizeof(_array) / sizeof(int); // ... }
- 여기서 _array는 배열처럼 보이지만 실제로는 포인터야!
- C++에서는 배열을 함수의 인자로 전달할 때 배열이 포인터로 decay(변형) 돼서 전달돼. 즉, _array는 사실상 int* _array로 해석돼.
- sizeof(_array)는 포인터의 크기를 반환해. 일반적으로 8바이트 (64비트 시스템 기준).
- sizeof(int)는 여전히 4바이트.
- 결과적으로 score_size = 8 / 4 = 2가 되는 것처럼 보일 수도 있어.
- 하지만 정확한 값은 시스템 아키텍처에 따라 다르고, 어쨌든 중요한 건 sizeof(_array)는 배열 전체 크기가 아니라 포인터 크기라는 점이야.
✅ 요약
- main() 함수 내 --> 배열 전체 크기 --> score는 int[5] 타입
- average() 함수 내 --> 포인터 크기 --> _array는 int*로 해석됨 (배열 → 포인터 decay)
## 동일 표현
int average(int _array[], int _count)
int average(int* _array, int _count)
즉, _array는 score 배열의 첫 번째 요소의 주소를 가리키는 포인터.
함수 안에서 _array[i]라고 쓰는 건, 포인터 연산을 이용한 배열 접근.
동일 표현
sum += _array[i];
sum += *(_array + i);
구조체
- 단일 변수만 취급하고 return 하니까 답답하죠?
- 그래서 구조체가 있다.
- 구조체는 구조체 변수 선언을 해야지 사용할 수 있음
- Ex) 사람의 정보 구조체
struct Person
{
std::string name;
int age;
float height;
float weight;
};
Person adult;
adult.name = "Siwon"
adult.age = 26;
adult.height = 173;
adult.weight = 73;
<Person adult;>
03-3 정적 변수와 상수 변수
static과 const는 c++ 언어에서 자주 혼동하는 키워드이다.
정적 변수 선언하기 - static
지역 변수와 전역 변수의 차이를 간단하게 복습
- 지역 변수는 선언된 지점에서 생성되고 해당 블록이 끝나면 소멸(auto duration)
- --> 지역 변수(local variable): 함수 내부에 선언된 변수로, 해당 블록 내에서만 효력이 있음
- --> 전역 변수(global variable): 전역 범위에 선언된 변수로, 해당 파일 전체에서 효력이 있다.
static의 기능
- 지역 변수에 static 키워드를 사용하면 auto duration에서 static duration으로 변수의 유효 범위가 바뀜
- 즉 static 키워드는 지역 변수를 정적 변수로 바꾼다.
- 이렇게 선언된 정적 변수는 선언된 블록이 끝나더라도 값을 유지함.
- static_variable_1.cpp 참고
static으로 정적 변수를 선언할 때는 반드시 초기화 해줘야한다. --> static int ID = 0; 이런식으로
안하면 자동으로 0으로 초기화함.
- ID 같은 걸 차례대로 생성할 때도, 유용하게 사용됨
상수 변수 선언하기 - const
constant(상수)란 변하지 않는 값임.
- 변수에 const 키워드를 사용하면, 값을 변경할 수 없게 됨.
void topic_callback(const std_msgs::msg::String & msg) const;
- callback 함수가 읽기 전용이라는 것을 의미, 내부에 멤버 변수들을 절대 바꿀 수 없음.
int getValue() const {
return this->value; // O 가능
this->value = 10; // X 오류! const 함수에서는 값 못 바꿈
}
- const 변수를 사용할 때는 반드시 초기화를 해야하며, 초기화를 하지 않으면 컴파일 오류가 발생한다.
- 그리고 초기화 이후에 새로운 값을 넣으려고 해도 컴파일 오류가 발생한다. ROS 코드에서 저렇게 하는 이유도 읽기 전용 코드이며 안에서 멤버 변수들을 절대 바꾸지 않도록 방지하는 역할임.
포인터 변수의 상수화
- 일반 변수를 상수화하는 건 컴파일 오류만 주의하면 되지만, 포인터 변수를 상수화할 때는 const 키워드의 위치에 따라 상수화할 대상이 달라지므로 구분해서 사용해야함.
const int *ptr = &a --> *ptr 상수화 = 포인터 변수가 가리키는 값을 상수화
- ptr은 일반 포인터
- 가리키는 값(*ptr)이 상수
- 따라서 *ptr = 2;는 금지지만(a를 바꾸는 것), ptr = &b;처럼 가리키는 대상은 변경 가능
int *const ptr = &a --> ptr 상수화 = 포인터 변수 자체를 상수화
- 가리키는 값은 변경됨
- ptr은 포인터인데, 포인터 자체가 const
- 즉, ptr이 다른 주소를 가리키는 건 금지
const int *ptr = &a;는 “내가 (ptr로서) a를 볼 수는 있는데, 손대면 안 돼!” 이런 느낌
int a = 0;
int b = 1;
const int *ptr = &a; // *ptr를 상수화 = 포인터 변수 자체를 상수화
cout << "before ptr: " << *ptr << endl;
// *ptr = 2; // 컴파일 에러
ptr = &b; // ptr이 다른 걸 가리키는 건 됨
a = 3; // 이건 또 됨
// const가 제한하는 대상이 "포인터를 통해 값을 바꾸는 것"만 금지라는 사실
cout << "after ptr: " << *ptr << endl;
cout << "after a: " << a << endl; // a가 변경됨
------------------------------------
int a = 0;
int b = 1;
int *const ptr = &a; // ptr 상수화 = 포인터 변수 자체를 상수화 --> 가리키는 값 a 는 변경 가능함
cout << "before ptr: " << *ptr << endl;
*ptr = 2; // 가리키는 값 a를 변경
// ptr = &b; // 컴파일 에러
cout << "after ptr: " << *ptr << endl;
cout << "after a: " << a << endl; // a가 변경됨
return 0;
03-4 레퍼런스 변수
C++ 언어에서는 포인터 대신 사용할 수 있는 레퍼런스라는 변수 형식을 제공한다.
- 포인터를 사용하면 최적화와 성능을 향상시킬 수 있지만 어려워서 잘못 사용할 수도 있음.
- 레퍼런스를 통해서 포인터의 이점을 누릴 수도 있음.
레퍼런스 사용하기
C++에서 제공하는 3가지 변수 형식
- 일반 변수: 값을 저장하는 변수
- 포인터 변수: 메모리 주소를 저장하는 변수
- 레퍼런스: 변수에 또 다른 이름, 별칭을 부여
레퍼런스 변수 선언
- 자료형 &레퍼런스_변수_이름 = 대상_변수_이름;
- 레퍼런스로 사용할 때는 메모리 주소가 아닌 원본 변수를 참조하겠다는 의미
- 함수의 매개변수를 레퍼런스로 선언하고 호출하면 호출할 때 넘긴 변수에 각각 별칭(또 다른 이름)이 부여된다고 이해하면 됨. 즉, 실제 변수는 하나지만 이름이 2개가 되는 것.
int &ref_a = a; int &ref_b = b;- 레퍼런스는 포인터를 비교적 안전하게 사용할 수 있도록 만든 도구
- 포인터처럼 원본 값에 접근할 수는 있지만, 원본 자체나 공간의 크기, 메모리 주소등은 변경하지 못하게 막은것
※ 이 글은 직접 구매한『Do it! C++ 완전 정복』(문종채, 조규남 저, 성안당)을 참고하여 개인적으로 학습한 내용을 정리한 것입니다. 본문에 사용된 내용 및 예제 코드는 책의 내용을 기반으로 하되, 이해를 돕기 위한 개인적인 해석과 실습 결과를 포함하고 있습니다.
참고한 예제 코드: [GitHub - mystous/DoItCPP](https://github.com/mystous/DoItCPP)
예제 코드의 일부는 위 오픈소스 저장소를 참고하거나 수정하여 활용했으며, 해당 저장소는 학습용으로 공개되어 있습니다.
'개발 언어 > C++' 카테고리의 다른 글
| [C++ 기초부터 심화까지 Chapter 06. 객체지향과 클래스] (0) | 2025.05.08 |
|---|---|
| [C++ 기초부터 심화까지 Chapter 05. 예외 처리 구문] (0) | 2025.05.07 |
| [C++ 기초부터 심화까지 Chapter 04. 실행 흐름 제어] (0) | 2025.05.07 |
| C++ 공부에 관하여 (0) | 2025.05.07 |