함수와 const의 연관성과 간단하게 함수에서의 배열에 대한 수업을 진행하였다.
Const
#include <iostream>
int main()
{
const int* p{};
*p = 0;
p = 0;
}
1. *p = 0;
- p가 가리키는 값을 변경하려고 시도하는 것.
- const int *p의 경우 상수를 가리키는 포인터를 의미하는 것이므로, p(포인터)가 가리키는 메모리 위치에 저장된 값을 변경하는 것은 금지되기에 컴파일 오류가 일어나게 된다.
2. p = 0;
- 이 줄은 포인터 p 자체의 값을 변경하는 것이므로 즉, 포인터 p가 다른 주소를 가리키도록 설정
- const int *p에서 p는 값이 상수인 것이지 포인터 자체는 상수가 아니다. 그렇기에 포인터 p가 가리키는 위치를 변경하는 것은 허용되기에 문법적으로 올바르다.
요약 :
- *p = 0;는 값을 변경하려고 해서 오류가 발생합니다. ( p가 가리키는 값이 읽기 전용이기 때문 )
- p = 0;는 포인터 자체를 변경하는 것이므로 허용됩니다. ( 포인터는 쓰기 가능 )
즉, 변수를 기준으로 수식어가 나왔을 때 const가 존재할 경우 읽기 전용 const가 존재하지 않다면 쓰기 전용이라는 개념이다.
2. const int* p와 int const* p의 차이점
const int *p{};
int const *p{};
두 구문의 차이점은?
- 두 구문은 동일한 의미를 갖는다.
- const는 변수의 읽기 전용 성격을 지정하는데, const가 앞에 오거나 뒤에 오더라도 p가 가리키는 값은 변경되지 않는다는 의미를 가지게 된다.
- 원래 const는 자료형 뒤에 위치해야 하지만, const의 경우 최종 자료형의 경우 예외적으로 앞에 적을 수 있다.
Cosnt는 왜 사용을 하는가?
- 포인터를 사용할 때 값(내용물)을 변경하고 싶지 않은 경우
( 예시 : const int* p => 읽기 전용 (전달하기 위한 값) ) - 내가 이 함수 인수에 자료형을 사용했을 때 서로 간에 어떤 식으로 주고받는 지를 결정하는데 가장 중요한 것
- 또한 포인터가 하는 일이 많기 때문에 포인터가 가리키는 값이 읽기 전용인지 쓰기 가능한지를 명확히 표현 을 해야 하기 때문에 const의 사용이 중요하다.
예시)
#include <iostream>
void rectangleArea(int nWight, int nHeight, int* pArea);
int main()
{
int nArea{};
rectangleArea(5, 4, &nArea);
printf("%d\n", nArea);
}
void rectangleArea(int nWight, int nHeight, int* pArea)
{
*pArea = nWight * nHeight;
}
프로토 타입 선언: void rectangleArea(int nWight, int nHeight, int* pArea);
- 함수가 어떤 매개 변수를 받을지, 반환 타입은 무엇인지 설명
함수 정의 : void rectangleArea(int nWight, int nHeight, int* pArea) { *pArea = nWight * nHeight; } :
- 매개변수로 전달받은 nWidth, nHeight를 사용해 사각형의 면적을 계산하고, 그 결과를 포인터 pArea가 가리키는 주소에 저장한다.
함수 호출: rectangleArea(5, 4, &nArea) :
- nWight = 5, nHeight = 4 값을 전달하고, pArea가 nArea의 주소를 가리키게 됨으로 함수 내부에서 *pArea = 5 * 4가 수행되고, nArea의 값이 20으로 변경된다.
포인터와 함수 호출 관련 개념 정리
- 포인터를 전달하면 무조건 내용물을 바꾼다
- 함수의 매개변수에 포인터가 사용되면, 해당 포인터가 가리키는 메모리의 값을 변경할 수 있다.
- 변수명 앞에 수식어가 하나 붙어 있으면, 그 변수의 내용을 수정할 수 있음을 의미한다.
- 예시: void func(int* p);
- Call by Value vs. Call by Reference
- call by value는 기본적으로 선언되면서 복사가 된다는 개념을 지니고 있다. 기본 자료형(int, float)인 경우 값만 복사되어도 무관하지만, 구조체나 클래스를 다룰 때는 포인터나 참조자를 사용하여 복사 비용을 줄일 수 있다.
- C언어에서는 call by Value만 존재하며, 포인터를 전달하더라도 이는 포인터 자체의 값(주소)을 복사하는 것이다.
그렇기에 주소값을 전달하느 방식은 "참조에 의한 호출(call by reference)"라 오해할 수 있지만 실제로는 call by value이기에 혼동하지 않아야 한다
- Call by Value (값에 의한 호출)
- 일반적으로 const를 굳이 붙이지 않는다.
- 이유:
void func(int); -> 4바이트
void func(const int); -> 8바이트
const를 사용하면 불필요하게 메모리 사용량이 증가할 수 있다. - 하지만 const를 사용하는 경우도 존재하며, 사용 빈도가 낮을 뿐이다.
간단한 예제를 풀고 마무리해보겠다.
#include <iostream>
void add1(int n1, int n2, int* pResult);
void add2(const int * p1, const int* p2, int* pResult);
int main()
{
int nData1{};
int nData2{};
int nResult{};
scanf_s("%d", &nData1);
scanf_s("%d", &nData2);
add1(nData1, nData2, &nResult);
printf("%d\n", nResult);
add2(&nData1, &nData2, &nResult);
printf("%d\n", nResult);
}
void add1(int n1, int n2, int* pResult)
{
*pResult = n1 + n2;
}
void add2(const int* p1, const int* p2, int* pResult)
{
*pResult = *p1 + *p2;
}
- add1 함수:
- 인자로 두 정수(n1, n2)와 결과를 저장할 포인터(pResult)를 받는다.
- 두 정수(n1, n2)의 값을 더한 결과를 포인터 pResult가 가리키는 메모리 위치에 저장.
- 즉, 값 자체를 받아와서 더한 후 결과를 포인터로 전달한다.
- add2 함수:
- 두 정수의 주소(p1, p2)와 결과를 저장할 포인터(pResult)를 받는다.
- p1과 p2가 가리키는 값을 더한 결과를 pResult가 가리키는 메모리 위치에 저장.
- 즉, 주소값을 받아와 해당 주소에 있는 값을 참조하여 더한 후, 결과를 포인터로 전달한다.
핵심 정리- add1: 두 정수의 값을 받아서 그 값을 더한 후 포인터로 결과를 전달
- add2: 두 정수의 주소를 받아 해당 주소의 값을 더한 후 포인터로 결과를 전달
'C++' 카테고리의 다른 글
[강의] 10월 4일 수업정리 (1) | 2024.10.05 |
---|---|
[강의] 10월 2일 수업정리 (1) | 2024.10.03 |
[강의] 9월 27일 수업정리 (0) | 2024.09.27 |
[강의] 9월 26일 수업정리 (1) | 2024.09.27 |
[강의] 9월 25일 수업정리 (1) | 2024.09.26 |