본문 바로가기

문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - 함수에서 포인터 활용하기(함수끼리의 변수의 값 변경, 포인터를 활용한 함수 문제 해결, SWAP함수, 배열을 인자로 받아들이는 함수, 상수를 인자로 받아들이..

반응형

 


- 이제 포인터를 실제로 써먹을 수 있는 방법을 배울 수 있다.
* 혹시 아직 포인터에 대한 내용을 잘 모른다면, 아래 자료를 참고해 주세요 :)

https://vansoft1215.tistory.com/6
https://vansoft1215.tistory.com/9
https://vansoft1215.tistory.com/10
https://vansoft1215.tistory.com/11




 1. 함수끼리의 변수의 값 변경
- 저번 내용에서, main함수에서 다른 함수에서 정의된 변수의 값을 바꿀 수 없다고 이야기 했다.
ex )

i의 값이 전혀 바뀌지 않았다는 것을 알 수 있다.


그 이유는 함수 change_val을 호출할 때, change_val함수 안에서 정의된 변수인 i는, main함수의 i의 값을 전달 받은 후에, change_val 함수 안에서 정의된 변수 i의 값을 3으로 변경하게 된다.
그런데, main함수의 i가 아닌, chage_val함수 내에서 정의된 변수 i의 값이 3으로 변경되는 이기 때문에, 결국 main함수의 i값에는 아무런 영향도 미치지 않는다.

 




 2. 포인터를 활용한 함수 문제 해결
이 부분에 대한 문제를 해결하기 위해, 포인터의 내용을 활용해보기로 한다.

위의(1.함수끼리의 변수의 값 변경) 내용에서 문제가 발생했던 것은, 각 함수는 다른 변수들에 대한 것들을 아무것도 알지 못한다는 점이었다. 즉, change_val이라는 함수에서 i라는 변수를 이용한다면, 컴파일러는 변수 i가 오직 chage_val함수에서만 정의되었다고 생각하지, 다른 함수에서 정의되었는지는 상관하지 않는다는 것이다.

위의 결과를 통해, 호출 이후의 i의 값이 0에서 3으로 바뀌었다는 것을 알 수 있다.

a. 함수의 정의부분
int change_val(int *pi){ // int형의 변수를 가리키는 pi라는 이름의 포인터로 인자를 받고 있음.
... (중략) ...
*pi = 3; // pi는 i의 주소값을 저장하고 있으므로, *pi를 통해서 main함수의 변수 i에 간접적으로 접근할 수 있다.
... (중략) ...
}

b. 함수의 호출 부분
change_val(&i);

// i라는 변수의 주소값을 인자로 전달하고 있다. 따라서 함수를 호출하게 될 경우 포인터변수 pi는, main함수의 int형 i변수의 주소값을 저장하게 된다. 따라서 pi는 i를 가리키게 된다.




 3. SWAP함수 (두 변수의 값을 교환하는 함수)
ex 1 ) 잘못된 swap함수

swap함수는 두 변수의 값을 교환해주는 함수인데, 이 코드에서 원하는 결과가 나오지 않는 것을 볼 수 있다.

a. swap함수 부분
int swap(int a, int b){ // 2개의 인자가 전달되어야 한다.
int temp = a;

a = b;
b = temp;

return 0;
}

b. swap함수 호출 부분
swap(i , j);
// swap함수의 변수 a,b는 모두 swap함수 내부에서 선언된 변수다.
// 다시 말하자면 변수 a,b와 i,j는 초기값이 동일하다는 것을 제외하면, 어떠한 연관도 없다는 것이다. 마치 아래와 같은 작업을 한 것과 같다.

Int i , j;
Int temp, a, b;
// 함수를 호출하여 인자를 전달하는 부분
a = i;
b = j;
// main함수의 내용을 swap 함수에서 실행
temp = a;
a = b;
b = temp


- 결과적으로는 i,j 두 값에 영향을 주지 않았다는 것이다.(변경되지 않음)


ex 2 ) 올바른 swap 함수 작성

a. swap함수 부분
int swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
return 0;
}
- 모든 과정이 동일하지만, 주소값을 받아와서 i와 j의 실제 값을 변경할 수 있도록, 간접주소접근방식을 사용했다는 것에서 차이가 존재한다.

 


b. swap함수 호출 부분
swap(&i, &j); // i와 j의 주소값을 전달하는 모습을 확인할 수 있다.
- 즉, swap함수 내부에서 a와 b의 값을 교환하는 것이 아닌, a와 b가 가리키는 두 변수의 값을 교환한 것이므로 결과적으로 i와 j의 값을 바뀌게 할 수 있었던 것이다.


- 즉, 어떠한 함수가, 특정한 타입의 변수/배열의 값을 바꾸려면, 함수의 인자는 반드시 그 타입을 가리키는 포인터를 이용해야 한다는 것이다.

 




 4. 배열을 인자로 받아들이는 함수
ex ) 배열을 인자로 받아서, 그 배열의 원소 값들을 1씩 증가시키는 함수

a. add_number 함수
add_number(int *parr){// 특정한 타입의 '값'을 변경하는 함수를 제작하려면, 반드시 그 타입을 가리키는 포인터를 인자로 가져야 함. 따라서 int*형이 나옴.

int i;
for(i=0 ; i<3 ; i++) {
parr[i]++; // parr이 가리키는 배열의 i+1번째 원소에 접근할 수 있다.
}
return 0;
}

b. add_number 호출 함수
add_number(arr);
// arr은 배열의 시작 주소 값을 가지고 있다. 즉, arr = &arr[0];
// 따라서 포인터 변수 parr은 배열 arr의 시작 주소, 즉 배열 arr을 가리키게 된다.



 

 

 5. 상수를 인자로 받아들이는 함수

read_val함수에서 val을 const int형으로 선언하였기 때문에 함수를 호출할 때, val의 값은 인자로 전달된 값으로 초기화되고, 결코 바뀔 수 없다.

따라서 함수 내부에서 val = 5; 와 같이 값을 변경하려 하면 오류가 발생할 수 밖에 없다.

 




 6. 함수 포인터
- 함수는 메모리 상에 존재하게 된다.
- 즉, 함수는 그 자체로 주소값을 가진다는 것이며, 이 시작 주소를 가리키는 함수포인터가 존재한다. 따라서, 함수 포인터가 함수를 가리키기 위해서는 그 함수의 시작주소를 알아야 한다.


ex 1 ) 함수 포인터 예시 1

a. 함수 포인터 pmax 정의
int (*pmax)(int, int);

[ 이 정의를 보고 알 수 있는 상황 ]
- 함수의 리턴값(반환형)은 int형이다.
- 인자 두 개가, 각각 int인 함수를 가리킨다.

[ 즉, 함수 포인터의 일반적인 정의는 이렇게 된다. ]
(함수의 리턴형) (*포인터 이름) (첫번째 인자 타입, 두번째 인자 타입, ...)
// 만일 인자가 없다면 그냥 괄호 안을 비워둔다. ex. Int(*a)();


b. 함수 포인터 pmax가 max를 가리키는 부분
pmax = max; // max는 함수 이름 : max()
- 배열과 마찬가지로, 함수의 이름은 '함수의 시작 주소값'을 나타낸다.pmax = &max와 같은 형태는 옳지 않다.

 


ex 2 ) 함수 포인터 예시 2

a. 함수 포인터 pfunc
int (*pfunc)(int, int);
// 반환형이 int이고, 두 개의 인자 각각의 포인터 형이 int인 함수를 가리키는, 함수포인터 pfunc


b. pfunc = max, pfunc = donothing;
// donothing함수와, max함수 모두 위의 함수 포인터 정의 조건을 만족하고 있다. 따라서 pfunc는 두 개의 함수를 모두 가리킬 수 있는 조건을 갖춘다.

cf )
int increase(int (*arr)[3], int row){ } 함수의 같은 경우, 두 번째 인자의 형은 int형이지만, 첫번째 인자의 형은 무엇인가? 

: 이는, 인자에서 단순히 변수의 이름만 빼면 파악할 수 있다.
: 따라서, int (*pfunc)(int (*)[3], int); 이런 형태로 적용시키면 된다.

 


[ 그렇다면 함수포인터는 왜 사용하냐고? ]
비슷한 형태의 코드의 경우, 계속 반복해서 코드를 작성하다보면 코드의 유연성(효율성)이 떨어지고 길어지게 된다.
이를 방지하기 위해, 함수 포인터를 활용해서 인자를 받는 방식을 사용하면 좋다.


* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형