언어/C

function - 포인터 인자, 배열 인자

세진개발 2022. 3. 20. 14:16

포인터를 배우면서 포인터가 어디에 쓰이는지에 대한 의문이 많이 들었다.

그래서 이번엔 함수에서 포인터를 어떻게 사용하는지에 대해서 공부할 것이다.

 

먼저, 단순한 형태의 함수의 예를 들어보자.

 

 

#include <stdio.h>

void val_test_f(int i) {
	i = 20;
}

int main() {
	int i = 10;
	val_test_f(i);
	printf("함수 호출 후 i값 : %d ", i);
	return 0;
}

 

 

위의 예와 같이 return을 사용하지 않고 main 함수에서 정의한 i의 값을 피호출 된 함수(val_test_f)에서 바꿀 수 없다. 

조금 더 구체적으로 C언어의 메모리 구조를 통해서 이해해보자.

 

 

메모리 구조

프로그램을 실행시키기 위해서 메모리 공간이 필요한데 스택(Stack), 힙(Heap), 데이터(Data) 영역으로 나뉘어 있다. 메모리 공간 메인 메모리(RAM)에 할당이 되고, 프로그램이 실행될 때 할당이 된다. 

  • 데이터 영역 (Data Area)
    • 전역 변수와 Static 변수가 할당되는 영역
    • 프로그램이 시작하면 동시에 할당
    • 프로그램이 종료되면 메모리에서 소멸
  • 스택 영역 (Stack Area)
    • 함수가 호출될 때 생성되는 지역 변수와 매개 변수가 저장되는 영역
    • 함수 호출이 완료되면 메모리에서 소멸
  • 힙 영역 (Heap Area)
    • 프로그래머가 관리하는 메모리 영역
    • 프로그래머의 필요에 의해서 메모리의 공간이 할당되고 소멸

 

예를 들어서 알아보자.

 

 

#include <stdio.h>
#include <stdlib.h> //malloc, free 함수가 선언된 헤더 파일

//함수 원형 선언
void func1(int);

//전역변수 선언
int a = 10;
int b = 20;

int main() {	
	int i = 100;
	int* numP;

	func1(i);

	numP = (int *)malloc(sizeof(int)); //int 타입 사이즈 만큼의 메모리를 할당

	printf("i의 값 : %d \n", i);
	printf("numP의 주소값 : %p", numP);

	free(numP); //동적으로 할당된 메모리 해제
	return 0;
}

void func1(int c) {
	c += 1;
}

 

 

먼저, 프로그램이 시작하면 전역 변수의 메모리가 할당 된다. 여기서 전역 변수는 main 함수의 호출보다 먼저 실행이 된다.

프로그램 실행 시

 

그리고 main 함수가 호출 된다. 프로그래머가 동적으로 할당한 메모리인 numP는 힙(Heap) 영역에 메모리가 할당되고, 지역 변수인 i = 100은 스택(Stack) 영역에 할당이 된다. 

main( ) 실행

 

다음으로 func1의 함수가 호출이 된다. 함수가 호출되면서 매개변수를 전달하고 이 과정에서 매개변수 초기화를 진행한다. 다음으로 함수 내에서 선언된 지역변수를 초기화한다. 매개변수, 지역변수는 Stack 영역에 할당한다.

 

func1( ) 실행

 

그리고  func1의 실행이 종료되면 func1의 Stack 영역은 사라진다. 

 

func1( ) 종료

 

printf 함수를 통하여 i의 값이 100이 나오는 것을 확인 할 수 있고, free(numP)를 사용하여 동적으로 할당된 메모리를 해제하면 Heap 영역에 할당된 메모리가 사라진다. 

 

free( ) 실행

 

마지막으로 main() 함수가 종료되고 프로그램이 종료되면 할당된 모든 메모리 공간은 지워진다.

 

프로그램 종료

 

 return 값을 사용하지 않고 main 함수의 값이 바뀌지 않은 예와 메모리가 할당되는 과정을 알아보았다. 다시 본론으로 들어가면 포인터를 활용하면 return값이 없는 피호출 된 함수로도 다른 함수에 정의된 변수의 값을 바꿀 수 있다. 예를 통하여 확인해보자.

 

 

포인터 매개변수

 

#include <stdio.h>

void change_val_f(int* p) {
	*p = 100;
}

int main() {	
	int i = 0;

	printf("i의 값 : %d \n", i);
	change_val_f(&i);
	printf("i의 값 : %d", i);
	return 0;
}

 

 

포인터를 매개변수로 사용하면 i의 값이 변하는 것을 확인할 수 있다.

void change_val_f(int *p) 의 함수를 보면 int 형 변수를 가리키는 포인터를 매개변수로 받고 있다.

그래서 main() 에서 다음의 함수를 호출할 때 주소 값을 인자로 전달하고 있다.

즉, i의 주소값을주소 값을 전달하고 change_val_f 함수에서 주소 값을 받아서 해당 주소 값에 들어있는 값을 100으로 바꿔주고 있다. 그래서 i를 출력하면 100이 나올 수 있는 것이다. 

 

 

배열의 주소값을 인자로 받기

 

이번에는 배열의 주소 값을 인자로 받아 배열 안에 값을 넣는 예를 들어보자.

 

#include <stdio.h>

void creat_arr(int * parr);

int main() {	
	int arr[5];

	creat_arr(arr);

	for (int i = 0; i < 5; ++i) {
		printf("%d \n",arr[i]);
	}
	

	return 0;
}

void creat_arr(int* parr) {
	for (int i = 0; i < 5; ++i) {
		parr[i] = i + 1;
	}
}

 

 

먼저, 함수 원형을 선언하고 main()에서 5개의 방을 가진 arr를 선언하자.

그리고 create_arr(int *parr)를 통하여 각 방에 값을 넣어준다. 

create_arr 함수는 매개변수로 arr의 주소 값을 사용했다. 그리고 for문을 통하여 값을 넣는데

[] 연산자는 자동으로 *(parr + i)로 바꿔주는 역할을 하므로 다음의 출력 값이 나올 수 있다는 것을 확인할 수 있다.