본문으로 바로가기

   

 

인자 전달의 기본 방식은 값의 복사이다.
→ 함수 호출시 전달되는 인자의 값은 매개변수에 복사가 된다.

배열을 통째로 넘겨받으려면 매개변수로 배열을 선언할 수 있어야 한다.
하지만 이것이 허용되지 않으니 배열을 통째로 넘기는 것은 불가능한 일이다.
대신에 함수 내에서 배열에 접근할 수 있도록 배열의 주소 값을 전달하는 것은 가능하다.

#include <stdio.h>

void ShowArayElem(int * param, int len)
{
	int i;
	for (i = 0; i < len; i++) {
		printf("%d", param[i]); //20행에 선언된 배열에 저장된 값을 참조만 한다.
		printf("\n");
	}
	
}

void AddArayElem(int * param, int len, int add)
{
	int i;
	for (i = 0; i < len; i++) {
		param[i] += add; //배열에 저장된 값을 변경
	}
}

void main()
{
	int arr[3] = { 1,2,3 };
	AddArayElem(arr, sizeof(arr) / sizeof(int), 1);
	ShowArayElem(arr, sizeof(arr) / sizeof(int));

	AddArayElem(arr, sizeof(arr) / sizeof(int), 2);
	ShowArayElem(arr, sizeof(arr) / sizeof(int));

	AddArayElem(arr, sizeof(arr) / sizeof(int), 3);
	ShowArayElem(arr, sizeof(arr) / sizeof(int));

}

 

 

이렇듯 배열의 주소 값만 안다면 어디서든 배열에 접근하여 저장된 값을 참조하고 변경할 수 있다.

다음 두 함수에는 int형 배열의 주소 값을 인자로 전달 받을 수 있도록 int형 포인터 변수가 선언되었다.
void ShowArayElem (int * param, int len) { ... }
void AddArayElem (int * param, int len, int add) { ... }

그런데 이를 대신하여 다음과 같이 선언이 가능하다.
void ShowArayElem (int param[], int len) { ... }
void AddArayElem (int param[], int len, int add) { ... }

즉, int param[]과 int * param은 완전히 동일한 선언이다. 하지만, 다음과 같은 선언은 불가능하다.
int main(void)
{
        int arr[3] = {1,2,3};
        int * ptr = arr;      // int ptr[] = arr; 로 대체 불가능.
}

함수 내에서는 인자로 전달된 배열의 길이를 계산할 수 없다. 따라서, 배열의 크기나 길이 정보도 함께 인자로 전달해야 한다.

call-by-value
값을 전달하는 형태의 함수 호출.

call-by-reference
주소 값을 전달하는 형태의 함수 호출.

const
변수를 상수화하는 목적. 이러한 const 선언은 포인터 변수를 대상으로도 선언이 가능하다.
void main()
{
        int num = 20;
        const int * ptr = &num;
        *ptr = 30;  //컴파일 에러
        num = 40; //컴파일 성공
}
포인터 변수 ptr을 이용해서 ptr이 가리키는 변수에 저장된 값을 변경하는 것을 허용하지 않는다.
 const선언을 하는 이유는  코드의 안전성을 높여서이다.


문제1)
변수 num에 저장된 값의 제곱을 계산하는 함수를 정의하고, 이를 호출하는 main 함수를 작성해보자. 단, 여기서는 다음 두 가지 형태로 함수를 정의해야 한다.
- Call-by-value 기반의 SquareByValue 함수
- Call-by-reference 기반의 SquareByReference 함수
SquareByValue 함수는 인자로 전달된 값의 제곱을 반환해야 하며, SquareByReference함수는 정수가 저장되어 있는 변수의 주소 값을 인자로 받아서 해당 변수에 저장된 값의 제곱을 그 변수에 다시 저장해야 한다.

#include <stdio.h>

int SquareByValue(int num) {
	return num * num;
}

int SquareByReference(int* ptr) {
	return (*ptr) * (*ptr);
}
void main()
{
	int num = 4;
	printf("%d\n", SquareByValue(5));
	SquareByReference(&num);
	printf("%d\n", SquareByReference(&num));
}

 

 

문제2)
세 변수에 저장된 값을 서로 뒤바꾸는 함수를 정의해보자. 예를 들어서 함수의 이름이 Swap3라하면, 다음의 형태로 함수가 호출되어야 한다.     Swap(&num1, &num2, &num3);
그리고 함수 호출의 결과로 num1에 저장된 값은 num2에, num2에 저장된 값은 num3에, 그리고 num3에 저장된 값은 num1에 저장되어야 한다.

#include <stdio.h>

void Swap(int * ptr1, int * ptr2, int * ptr3)
{
	int temp;
	temp = *ptr3;
	*ptr3 = *ptr2;
	*ptr2 = *ptr1;
	*ptr1 = temp;
}

void main()
{
	int num1 = 10, num2 = 20, num3 = 30;

	Swap(&num1, &num2, &num3);
	printf("num1 : %d\nnum2 : %d\nnum3 : %d\n", num1, num2, num3);
}

 

 

문제3)
아래에 정의된 함수를 보자. 인자로 전달되는 정보를 참조하여 int형 배열요소 전체를 출력하는 함수이다. 아래의 함수의 매개변수 선언에서 매개변수 arr을 대상으로 const 선언을 한 이유가 무엇이겠는가?
이 함수를 정의한 사람의 의도를 이야기해보자.

void ShowAllData(const int * arr, int len)
{
	int i;
	for (i = 0; i < len; i++)
		printf("%d", arr[i]);
}

 

문제에서 정의한 함수의 기능은 인자로 전달된 배열의 전체요소를 출력하는 것이다. 

따라서 프로그래머가 실수로라도 배열요소의 값을 바꾸는 일은 없어야 한다. 그래서 매개변수 arr에 const 선언을 추가한 것이다. 

 


문제4)
아래의 예제는 한 가지 지적할만한 사항을 지니고 있다. 그것이 무엇인지 이야기해 보자. 특히 이와 관련해서 ShowData 함수를 유심히 관찰하기 바란다.

void ShowData(const int * ptr)
{
	int * rptr = ptr;
	printf("%d \n", *rptr);
	*rptr = 20;
}

void main()
{
	int num = 10;
	int * ptr = &num;
	ShowData(ptr);
}

 

3행에 int * rptr = ptr; <<< 이미 1행에 매개변수에 const선언을 했는데 ptr을 대입하고있다.
결과적으로 rptr을 이용해서 ptr이 가리키는 변수에 저장된 값을 변경할 수 있는 상황이 되어버렸다.
const선언을 한 의미가 없어진 것이다.