임베디드 개발(before)/IoT 임베디드 SW 개발자 양성과정

[8일차] C언어 교육8-(포인터 배열, 배열 포인터, 함수 포인터)

주운녕 2020. 7. 22. 17:39

1. 포인터 변수로 배열의 주소를 받는 경우.

int arr[4] = {2, 4, 7, 9};
int * p;
p=arr;

arr[2] = 100; //이렇게 대입할 경우.
//다음도 가능하다.
p[2] = 100;
*(p+2) = 100;

2. 이중 포인터 : 포인터 변수의 주소를 저장하는 포인터 변수.

1) 포인터 변수를 교환하는 함수의 매개변수

#include <stdio.h>

void swap_ptr(char** ppa, char** ppb);

int main(void)
{
	char* pa = "success";
	char* pb = "failure";

	printf("pa -> %s, pb -> %s\n", pa, pb);   // 바꾸기 전에 문자열 출력
	swap_ptr(&pa, &pb);                       // 함수 호출
	printf("pa -> %s, pb -> %s\n", pa, pb);   // 바꾼 후에 문자열 출력

	return 0;
}

void swap_ptr(char** ppa, char** ppb) //바로 이곳!!! 함수의 매개변수!!!
{
	char* pt;

	pt = *ppa;		// "success" 라는 문자열 주소 이동
	*ppa = *ppb;
	*ppb = pt;
}

2) 포인터 배열을 처리하는 함수의 매개변수

#include <stdio.h>

void print_str(char** pps, int cnt);

int main(void)
{
	char* ptr_ary[] = {"eagle", "tiger", "lion", "squirrel"};   // 초기화
	int count;                        // 배열 요소 수를 저장할 변수

	count = sizeof(ptr_ary) / sizeof(ptr_ary[0]);  // 배열 요소의 수 계산
	print_str(ptr_ary, count);        // 배열명과 배열 요소 수를 주고 호출

	return 0;
}

void print_str(char** pps, int cnt)   // 매개변수로 이중 포인터 사용
{
	int i;

	for (i = 0; i < cnt; i++)         // 배열 요소 수만큼 반복
	{
		printf("%s\n", pps[i]);       // *(pps+i)로도 쓸수 있다.
	}
}

 

3. 포인터 배열 : 어렵게 생각하지 말고, 주소(=배열명)를 저장하는 배열이라 생각하면 된다.   

#include <stdio.h>

int main(void)
{
	int ary1[4] = { 1, 2, 3, 4 };         // 1차원 배열의 선언과 초기화
	int ary2[4] = { 11, 12, 13, 14 };
	int ary3[4] = { 21, 22, 23, 24 };
	int* pary[3] = { ary1, ary2, ary3 };  // 포인터 배열에 각 배열명(=배열의 주소) 초기화
	int i, j;                             // 반복 제어 변수

	for (i = 0; i < 3; i++)               // 3행 반복
	{
		for (j = 0; j < 4; j++)           // 4열 반복
		{
			printf("%5d", pary[i][j]);    //<- 배열표현 // 포인터 표현 : *(pary[i]+j)
		}
		printf("\n");                     // 한 행을 출력한 후에 줄 바꿈
	}

	return 0;
}

 

4. 배열 포인터 -> 보통 2차원 배열 값을 받을 때 많이 씀. (1차원도 가능)

int (*p)[4] => int형 요소 4개를 저장하는 배열(ex: arr[4])의 주소(arr)를 저장하는 "포인터

  • 이차원 배열배열명을 받는 함수의 매개변수로 사용
#include <stdio.h>
void func(int(*)[4]);
int main(void)
{
	int ary[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };
	func(ary);				// 배열명을 인수로 주고 함수 호출
    
	return 0;
}

void func(int(*pa)[4])		// 1행당 4개의 요소를 가지는 배열의 주소를 저장한다.
{
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%5d", pa[i][j]);  // pa를 2차원 배열처럼 사용 //포인터식 *(pa[i]+j)로도 가능
		}
		printf("\n");
	}
}

 

5. 고정되어 있는 주소 값을 포인터 상수(int * const p 처럼 주소 값이 고정되어 있는 거)라고 표현하기도 한다.

 

6. 함수 포인터 : 함수포인터도 결국 포인터이다. 어렵게 생각할 것 없이, 함수들의 주소(=함수명)를 저장하는 변수로 볼 수 있다.

           1) 함수 자체를 매개변수로 받고 싶을 때. - 코드를 더 간결화 할 수 있다.

           2) 다른 파일에 있는 정적함수 (static 함수)를 호출할 때 함수의 주소로 접근하여 호출 할 때.

           3) call back 기능 구현 시 사용된다.  함수 포인터는 대상이 실재하지 않더라도 비워두고 프로그래밍을 진행할 수 있다.

              => 프로그램을 제작하는 그 당시에는 존재하지 않는 함수"를 호출할때 이용. ( 예를 들면 프로그램에 플로그인 기능이 필요한 경우, 플러그인은 아직 존재하지 않고 나중에 추가 될 수 있기 때문에 프로그램에서는 함수 포인터로 플러그인 함수에 접근한다.)

 

7. 함수 포인터 배열 예시. -> 함수 포인터 배열의 각  인덱스에 각 함수의 메모리 주소를 저장한다.

#include<stdio.h>

int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);

int main(void){
	int (*fp[3])(int, int);	

	int sel, num1, num2;
	printf("메뉴 입력 : 더하기 0번, 빼기 1번, 곱하기 2번\n");
	scanf("%d", &sel);
	printf("계산할 식 2개 입력\n");
	scanf("%d %d", &num1, &num2);

	fp[0]=add;
	fp[1]=sub;
	fp[2]=mul;
	printf("이것이 결과 : %d\n", fp[sel](num1, num2));

	return 0;
}
int add(int a, int b){
	return a + b;
}
int sub(int a, int b){
	return a - b;
}
int mul(int a, int b){
	return a * b;
}

*) 함수 포인터에 배열을 사용하여 배열의 각각의 요소에 함수가 들어가 있는 것을 볼 수 있다.

*) 성격이 비슷한 함수를 묶어 사용할 때 유용할 것 같다. => 코드 간결화 가능

 

8. void 포인터 : 자료형이 정해지지 않은 포인터. 어떤 자료형이든 주소 저장이 가능하지만 사용(출력)시엔 형 변환 연산(캐스팅)을 통해 자료형을 맞춰주어야 한다. (ex : void *vp)

 


* 개인적인 학습 목적으로 작성한 글이기에 내용에 잘못된 정보가 있을 수 있습니다.