Call-by-value 살펴보기
C++ 및 기타 많은 프로그래밍 언어에는 함수에 인수를 전달하는 두 가지 주요 방법인 값에 의한 호출과 참조에 의한 호출이 있습니다.
값별 호출은 함수에 인수를 전달하는 기본 방법입니다. call-by-value로 함수를 호출하면 인수의 값이 함수로 전달됩니다.
결론적으로, 함수는 인수의 복사본에서 작동하므로 함수 내부의 인수에 대한 변경 사항은 함수 외부의 원래 변수에 영향을 주지 않습니다.
예를 들어, 다음 코드에서:
void increment(int x) {
x++;
}
int main() {
int a = 5;
increment(a);
cout << a << endl; // Output: 5
}
increment(int x) 함수는 변수 a의 복사본을 받아 1씩 증가시키지만 원래 변수 a는 변경되지 않습니다.
포인터(pointer) 기본 개념
C++에서 포인터는 다른 변수의 메모리 주소를 저장하는 변수입니다. 포인터는 메모리 위치를 참조하고 프로그램이 해당 위치에 저장된 데이터를 조작할 수 있도록 하는 데 사용됩니다. 포인터 변수는 다른 변수와 마찬가지로 유형을 가지며 해당 유형은 가리키는 변수의 유형과 일치해야 합니다.
다음은 C++에서 포인터를 사용하는 방법의 예입니다.
int x = 5; // 값이 저장
int *p; //포인터 변수 p 선언
p = &x; //x의 주소를 px에 저장
y = *p; // 포인터 변수의 주소로 가서 값을 가져올 때 다시 * 를 붙인다.
이 예에서 x는 값이 5인 정수 변수이고, &x는 x의 메모리 주소이고, p는 x의 메모리 주소를 저장하는 정수에 대한 포인터입니다.
역참조 연산자라고도 하는 * 연산자를 사용하여 포인터가 가리키는 메모리 위치에 저장된 값에 액세스할 수 있습니다.
예를 들어
cout << "x = " << x << endl;
cout << "*p = " << *p << endl;
cout << "y = " << y << endl;
그러면 다음이 출력됩니다.
x = 5
*p = 5
y = 5
포인터가 가리키는 메모리 위치에 저장된 값을 변경할 수도 있습니다.
*p = 10;
cout << "x = " << x << endl;
그러면 다음이 출력됩니다.
x = 10
포인터를 사용하여 배열의 요소를 가리킬 수 있습니다. 예를 들면 다음과 같습니다.
int arr[5] = {1,2,3,4,5};
int *p = arr;
여기서 p는 배열 arry의 첫 번째 요소를 가리키며 포인터 산술을 사용하여 배열의 요소를 탐색할 수도 있습니다.
포인터는 동적 데이터 구조를 생성 및 조작하거나 보다 효율적으로 함수에 많은 양의 데이터를 전달하거나 보다 효율적인 알고리즘을 생성하는 등 여러 가지 방법으로 유용할 수 있습니다. 그러나 주의해서 사용하지 않으면 메모리 누수나 버퍼 오버플로와 같은 오류가 발생할 수 있으므로 위험할 수도 있습니다.
배열을 적용한 포인터(pointer) 살펴보기
변수를 선언하고 메모리에 저장(할당)할때 메모리의 구조에 맞게 순차적으로 저장되지 않습니다. 메모리에 저장(할당) 될때는 마음대로 할당되는데, 이를 순차적으로 할당하고 싶을때는 배열(array)를 적용하면 순차적으로 값을 저장(할당)할 수 있습니다.
int numbers[] = {1,4,3,7,5,9,10,12};
int *p2 = &numbers[0];
printf("p2 points %d now. \n", *p2);
// p2 points 1 now.
p2++;
printf("p2 points %d now. \n", *p2);
// p2 points 4 now.
p2++;
printf("p2 points %d now. \n", *p2);
// p2 points 3 now.
위의 코드를 보면, number[]를 배열(array)로 선언했고, 해당 영역에 {1,4,3,7,5,9,10,12} 값들을 저장(할당) 했습니다. 그리고, *p2를 포인터 변수로 선언했으며, 해당 포인터 변수 *p2가 &number의 첫번째 값 '1'을(&number[0]) 가리키게 했습니다. (→ 정확한 표현으로는 '1'이 저장된 메모리 주소를 가리키는 것입니다.) 그리고 나서 p2++를 하나씩 증가시켜 아래 메모리 그림을 보면 옆으로 한칸씩 해당 주소로 옮겨지도록 되어 있는 코딩입니다.
그 결과, 순차적으로 p2 주소값이 증가하고, 증가한 주소에 저장된 p2 값인 1, 4, 3 값이 출력되는 것을 알 수 있습니다.
레퍼런스(reference) 기본 개념
C++에서 참조는 동일한 메모리 위치를 다른 변수로 참조하는 방법입니다. 포인터와 비슷하지만 몇 가지 중요한 차이점이 있습니다.
비유를 들자면, 논문을 작성하다보면 타 논문을 참조(인용)하는 경우가 많은데 이러한 원리와 비슷하다. 나의 논문을 완성하기 위해 인터넷(→인터넷을 메모리 주소라고 생각하자)에 등록된 논문(→데이터라고 생각하자)을 나의 논문(→현재 작성하고 있는 논문 Filed)으로 가져오는 것이다. 즉, 내가 논문을 인용하기 위해 타 논문의 본체를 가져오는 것이 아니라 인터넷에 등록된 본체의 주소만을 가져와서 사용하는 것과 유사하다.
다음과 같이 변수 이름 앞에 "&" 기호를 사용하여 참조를 선언합니다.
int x = 5;
int& y = x;
이제 변수 y는 변수 x와 동일한 메모리 위치를 "참조"하므로 y에 대한 모든 변경 사항은 x에도 적용됩니다.
예를 들어, y = 10을 하면; x도 10이 됩니다.
참조는 종종 함수가 전달된 변수를 수정하고 함수에서 여러 값을 반환하는 데 사용할 수 있음을 명확히 하기 위해 함수 매개 변수로 사용됩니다.
참조는 개체 복사 오버헤드를 피하거나 여러 변수가 항상 동일한 개체를 참조하도록 하려는 특정 상황에서도 유용합니다.
Call-by-reference 살펴보기
참조는 변수에 별명(별칭)을 하나 붙여주는 것이다. 참조에 의한 호출은 함수가 복사본이 아닌 원래 변수에서 작동하는 함수에 인수를 전달하는 방법입니다. call-by-reference로 함수를 호출하면 인수의 메모리 주소가 함수로 전달되므로 함수 내부의 인수에 대한 모든 변경 사항이 함수 외부의 원래 변수에 반영됩니다. 또한, 참조자의 수에는 제한이 없으며, 참조자를 대상으로도 참조자를 선언할 수 있다.
예를 들어, 다음 코드에서:
void increment(int &x) {
x++;
}
int main() {
int a = 5;
increment(a);
cout << a << endl; // Output: 6
}
increment(int &x) 함수는 변수 a의 메모리 주소를 받아 1씩 증가시키고 원래 변수 a도 증가시킵니다.
참조별 호출이 값별 호출보다 더 효율적일 수 있다는 점에 유의해야 합니다. 변수의 복사본을 생성할 필요가 없기 때문입니다. 그러나 함수가 원래 변수를 의도하지 않은 방식으로 수정하면 예기치 않은 동작이 발생할 수 있으므로 주의해서 사용하지 않으면 더 위험할 수도 있습니다.
참조자의 선언 가능 범위는 다음과 같다.
1. 참조자는 변수에 대해서만 선언이 가능하다. 선언됨과 동시에 누군가를 참조해야만 된다. 참조자는 변수에 또 다른 이름을 붙이는 것이기 때문에 상수를 대상으로 참조자를 선언할 수는 없다.
int &ref = 20;
2. 미리 참조자를 선언했다가, 후에 누군가를 참조하는 것은 불가능하다.
int &ref;
3. 참조자를 선언하면서 NULL 값으로 초기화하는 것도 불가능하다.
int &ref = NULL;
포인터(*) 와 레퍼런스(&)를 정리하면
'*' 연산자는 포인터를 역참조하는 데 사용됩니다. 즉, 포인터가 가리키는 메모리 주소에 저장된 값을 검색합니다.
'&' 연산자는 변수의 메모리 주소를 얻는 데 사용됩니다. C++에서 참조는 변수의 별칭입니다.
즉, 이미 존재하는 변수의 다른 이름입니다. '&' 연산자는 변수에 대한 참조를 만드는 데 사용됩니다.
다음은 C++에서 포인터(pointer)와 레퍼런스(reference)를 통해 주소값을 가질 수 있는 포인터 변수 선언 예입니다.
int x = 5;
int *ptr = &x; // 'ptr' is a pointer to the memory address of 'x'
std::cout << "x = " << x << std::endl; // prints "x = 5"
std::cout << "ptr = " << ptr << std::endl; // prints the memory address of 'x'
std::cout << "*ptr = " << *ptr << std::endl; // prints the value stored at the memory address 'ptr' is pointing to, which is "5"
다음은 C++에서 참조(reference)를 사용하여 x 변수의 또다른 별칭 변수 &ref를 선언한 예입니다.
int x = 5;
int &ref = x; // 'ref' is an alias for the variable 'x'
std::cout << "x = " << x << std::endl; // prints "x = 5"
std::cout << "ref = " << ref << std::endl; // prints "5"
ref = 10;
std::cout << "x = " << x << std::endl; // prints "x = 10"
포인터와 달리 참조는 정의될 때 초기화되어야 하며 나중에 다른 개체를 참조하도록 변경할 수 없다는 점은 주목할 필요가 있습니다.
'C++ > 입문편' 카테고리의 다른 글
[보충 설명] 8.1.강 malloc & free와 new&delete에 대해 알아보자 (0) | 2023.01.22 |
---|---|
8강. 실행중인 프로그램의 메모리 공간을 살펴보자 (데이터, 스택, 힙, malloc & free) (0) | 2023.01.22 |
6강. 단일 및 이중 배열 기본 구조 및 예시 살펴보기 (0) | 2023.01.22 |
5강. Switch/Case문 기본 개념 설명 및 코드 예시를 살펴보자 (0) | 2023.01.22 |
4강. IF-ELSE 조건문 기초 설명 및 사용 예시를 살펴보자 (0) | 2023.01.22 |
댓글