함수에 인자를 전달하는 방식에는 크게 두가지가 존재한다.
-> 기본적으로 C언어에서 지원하는 방식으로, 함수에서 값을 복사해서 전달하는 방식이다.
-> 인자로 전달되는 변수를 함수의 매개변수로 복사하여 사용하는데, 이렇게 되면 인자로 전달한 변수와는 별개의 변수가 되며, 매개변수를 변경해도 원래 변수에는 영향을 미치지 않는다.
다음은 Call by value를 활용한 예시이다.
#include <stdio.h>
void swap(int a, int b){
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main()
{
int a,b;
a = 10;
b = 20;
printf("swap 전 : %d %d\n", a,b);
swap(a,b);
printf("swap 후 : %d %d\n", a,b);
return 0;
}
// swap 전 : 10 20
// swap 후 : 10 20
실행결과를 살펴보면 swap을 진행했음에도 불구하고 값이 swap되지 않은 것을 볼 수 있다.
-> Call by Reference 방식은 함수에서 값을 전달하는 대신 주소값을 전달하는 방식이고,
주소값 자체를 복사해서 넘겨주는 방식을 Call by address 방식이라고 한다.
#include <stdio.h>
void swap(int *a, int *b){
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main(){
int a,b;
a = 10;
b = 20;
printf("swap 전 : %d %d\n", a,b);
swap(&a,&b);
printf("swap 후 : %d %d\n", a,b);
return 0;
}
// swap 전 : 10 20
// swap 후 : 20 10
실행결과를 살펴보면 그냥 변수를 넘겨줄 때와는 다르게 원래 변수의 값도 바뀐다.
1. 배열(Array)은 포인터 변수와 같은 기능을 하며, 첫번째 요소의 주소값을 나타낸다.
#include <stdio.h>
int main(void){
int arr[5] = {10,20,30,40,50};
int *arrPtr = arr; // 배열의 첫번째 원소의 위치가 주소가 된다.
printf("%d\n", *arrPtr); // 10
printf("%d\n", arr[0]); // 10
return 0;
}
위와 같이 & 연산자를 쓰지 않아도 arr 자체가 주소값이기 때문에, 바로 포인터에 대입이 가능하다.
scanf로 문자열을 입력 받을 때, & 연산자를 쓰지 않아도 되는 것도 이와 같은 이유에서이다.
2. 포인터 변수도 일반 변수처럼 증감 연산이 가능하다.
#include <stdio.h>
int main()
{
int arr[5] = {10, 20, 30, 40, 50};
double arr2[5] = {10.1, 20.2, 30.3, 40.4, 50.5};
int *arrPtr = arr;
double *arrPtr2 = arr2;
printf("포인터 주소 : %d %d\n", arrPtr++, arrPtr2++);
printf("증가 연산 후 : %d %d\n", arrPtr, arrPtr2);
printf("변수 값 : %d %.2f\n", *arrPtr, *arrPtr2);
arrPtr += 2;
arrPtr2 += 2;
printf("증가 연산 후 : %d %d\n", arrPtr, arrPtr2);
printf("변수 값 : %d %.2f\n", *arrPtr, *arrPtr2);
return 0;
}
// 포인터 주소 : -8144 -8112
// 증가 연산 후 : -8140 -8104
// 변수 값 : 20 20.20
// 증가 연산 후 : -8132 -8088
// 변수 값 : 40 40.40
실행결과에서 주목해야 할 점은
int형 포인터는 +1 할 때마다, 4씩 증가한다. 그래서 다음 배열 원소의 위치를 가리킬 수 있다.
그래서 arrPtr += 2에서 index 1번을 가리키고 있던 것을 index 3번을 pointing 하게 되고,
더 나아가 double형 포인터는 +1 할 때마다 8씩 증가한다.
즉, 포인터변수가 n만큼 더하거나 뺄 때 자료형의 크기 * n 만큼 증가하거나 감소한다는 것을 알 수 있다.
예제) 포인터를 이용하여 버블 정렬 함수 만들기
#include <stdio.h>
void bubbleSort(int arr[]) # 배열을 파라미터로 넘겨줌, 이때 배열은 주소 그 자체이다.
{
int temp;
// bubble sort
for(int i=0; i < 9; i++)
{
for(int j=0; j < 9; j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main()
{
int arr[10];
for(int i=0; i<10; i++)
{
scanf("%d", &arr[i]);
}
bubbleSort(arr); # 주소를 가리키는 &를 적어주지 않아도 된다.
for(int i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
이중포인터는 포인터 변수의 주소값을 저장할 수 있다.
포인터 변수도 내부적으로 메모리 공간을 가지고 있고, 주소를 가지고 있다.
#include <stdio.h>
int main(void){
int a = 7;
int* ptr = &a;
*ptr = 8 ;// 간접접근(역참조) indirection
int **pptr = &ptr;
**pptr = 9; // double indirection
return 0;
}
차례대로 천천히 살펴보자.
1) int형 변수 a에 7을 대입한다.
2) ptr 포인터 변수에 변수 a의 주소값을 저장한다.
3) 를 사용하여 ptr에 들어있는 주소로 가서 8을 저장한다.
4) ptr변수의 주소를 ** pptr에 저장한다.
5) pptr에 저장된 주소를 역참조하여 9를 저장한다.
즉, pptr에 저장된 ptr의 주소 -> ptr에 저장된 a의 주소 -> a의 주소 도착(이곳에 값 대입)
따라서, 최종적으로 변수 a에 저장되는 값은 9가 저장된다.
🤍