[TIL/크래프톤 정글9기] 34일차(C언어 응용 포인터)

blueprint·2025년 6월 13일

크래프톤정글9기

목록 보기
29/55

다중 포인터

  • 포인터 변수도 하나의 기억공간이므로 포인터(물리적 주소)를 구할 수 있다.
    포인터변수포인터 -> 이중포인터
  • 이중포인터로 변수를 참조할 떄는 참조연산자(*)두 번 사용해야 한다.

이중포인터변수

  • 이중포인터 또한 하나의 포인터이므로 포인터변수에 저장하여 사용
  • 이중포인터변수도 참조연산자 두 번 사용하여 참조
int val = 10;
int *ip;
int **ipp;
ip = &val;
ipp = &ip;
printf("%d\n", **ipp);

이중포인터변수의 사용 예

  • 두 포인터변수의 값을 바꾸는 함수를 만들기
char *ap = "success";
char *bp = "failure";

exchange_ptr(&ap, &bp); // 함수의 호출
void exchange_ptr(char **app, char **bpp);
// 이중포인터가 전달인자로 넘어오므로 매개변수로 이중포인터변수를 선언

  • 함수안에서는 임시 포인터변수를 선언하여 ap. bp의 값을 바꿈
#include <stdio.h>

void exchange(char **, char **);
void line_up(double *, double *, double *); 

int main(){
	char *ap = "success";
    char *bp = "failure";
    print("ap -> %s, bp -> %s\n",ap, bp);
    exchange(&ap,&bp);
    printf("ap -> %s, bp -> %s\n",ap, bp);
    return 0;
}

void exchange(char **app, char **bpp){
    char *tp;
    tp = *app;
    *app = *bpp;
    *bpp = tp;
};

연습

  • 포인터 배열의 배열명을 전달인자로 받는 함수
  • 포인터 배열에 저장된 문자열을 출력하는 프로그램
  • 반복문을 사용하여 문자열을 출력하는 부분을 함수로 작성
  • 단, 함수를 호출하는 문장은 다음과 같이 작성
int main(){
	char *ptr_ary[] = {"abc", "defg", "hijk", "mnopqr"};
    int count, i;

    count = sizeof(ptr_ary)/sizeof(ptr_ary[0]); // 배열 요소 계산
    for(i = 0; i<count;i++) printf("%s\n", ptr_ary[i]);  // 여기 변경
    return 0;
}
#include <stdio.h>

void str_prn(char **, int);

int main(){
	char *ptr_ary[] = {"abc", "defg", "hijk", "mnopqr"};
    int count, i;

    count = sizeof(ptr_ary)/sizeof(ptr_ary[0]); // 배열 요소 계산
    // for(i = 0; i<count;i++) printf("%s\n", ptr_ary[i]);  // 여기 void str_prn(ptr_ary, count); 변경
    str_prn(ptr_ary, count);
    return 0;
}

void str_prn(char **spp, int count){
    int i;
    for(i=0;i<count;i++) printf("%s\n", spp[i]);
}

배열 포인터

  • 배열명은 첫 번쨰 배열요소를 가리키는 포인터의 기능을 함
int nums[5] = {10, 20, 30, 40, 50};
int *ip = nums; // 배열명은 포인터이므로 포인터변수에 저장
printf("%d\n", *nums); // 첫번째 기억공간을 참조하여 10 출력
printf("%d\n", *(nums+4)); // nums의 값이 100이면 116번지 참조

  • 배열의 기억공간 전체를 나탄는 논리적 변수의 기능을 함
    • 논리적 변수(상수) 이므로 -> 직접 데이터를 저장할 수는 없으나
    • 응용 자료형으로서 -> 변수가 가지는 형태와 크기에 대한 정보를 가짐

배열 포인터가 가리키는 것은?

  • 배열포인터는 배열명에 주소연산을 수행하면 구해짐
    • 이 때 배열명은 논리적 변수의 기능이며 그 배열 전체를 가리키는 포인터가 구해짐
    • 배열 포인터에 정수값을 거하면 배열 전체의 크기를 곱해서 더해줌

2차원 배열의 배열명은 배열포인터

  • 2차원 배열의 배열명은 첫 번쨰 부분배열을 가리키는 배열포인터

2차원 배열에서 배열명으로 기억공간을 참조하는 과정

  • 1차원의 물리적 기억공간을 행렬의 2차원 구조로 참조할 수 있는 것은 2차원 배열의 배열명부분배열명이 가각 배열포인터포인터로서 적절한 기능을 수행하기 때문

2차원 배열 포인터 변수

  • 2차원 배열포인터를 저장 -> 배열포인터변수
int (*ap)[4];
  • 배열포인토변수가 2차우ㅠㅓㄴ 배열의 배열명을 저장하면 2차원 배열처럼 사용
int ary[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int (*ap)[4];
int i,j;
ap=ary;
for(i=0;i<3;i++){
	for(j=0;j<4;j++){
    	prinf("%5d", ap[i][j]);
    }
    printf("\n");
}

배열 포인터

  • 해석 방법: 단항 -> 후치 -> 근치
void ary_prn(int(*ap)[4]);

연습

  • 2차원 문자배열에 입출력하는 함수 만들기
  • 5개의 문자열을 2차원 배열에 입력 받아 출력
      1. 2차원 문자배열은 메인함수에 선언
      1. 입출력 작업은 각각 함수를 호출해서 수행
      1. 문자열의 길이는 최대 80자 제한
#include <stdio.h>

void input_string(char (*)[80]);
void print_string(char (*)[80]);


int main(){
	char str[5][8];

    input_string(str);
    print_string(str);

    return 0;
}

void input_string(char (*sp)[80]){
    int i;

    for(i=0;i<5;i++) gets(sp[i]);
}

void print_string(char (*sp)[80]){
    int i;

    for(i=0;i<5;i++) puts(sp[i]);
}

함수 포인터

  • 함수 포인터는 함수의 이름
    • 함수명은 함수의 정의가 있는 메모리의 위치값이며 함수를 가리킴
  • 함수명이 포인터라는 증거는 참조연산자를 사용하면 알 수 있음
    아래는 sum(10, 20) 과 같음(실제로는 이렇게 쓰지 않음, 예시)

함수 포인터 변수

  • 함수 포인터가 가리키는 자료형은 함수의 형태

    • 함수의 형태를 매개변수의 개수와 형태, 리턴값의 형태
  • 함수포인터를 함수포인터변수에 저장하여 호출 가능

int (*fp)(int, int); // 함수포인터변수 선언
fp = sum; // 함수명을 함수포인터변수에 저장
fp(10,20); // 함수포인터변수로 함수 호출, (*fp)(10,20)도 사용 가능 

함수포인터는 어디에 사용하는가?

  • 함수포인터변수는 함수의 형태만 같으면 기능과 상관없이 모든 함수 포인터를 저장할 수 있다.
// 형태가 같다
int sum(int, int);
int mul(int, int);
int max(int, int);

int (*fp)(int, int);
// 아래와 같이 모두 사용 가능
fp = sum;
fp = mul;
fp = max;
  • 형태가 같은 다양한 기능의 함수를 선택적으로 호출하는데 사용할 수 있다.
#include <stdio.h>

void func(int (*fp)(int, int));
int sum(int, int);
int mul(int, int);
int max(int, int);

int main(){
    int sel;
    printf("1. 합, 2. 곱, 3. 큰 값\n");
    scanf("%d", &sel);
    
    switch(sel){
        case 1: func(sum); break;
        case 2: func(mul); break;
        case 3: func(max); break;
    }

	return 0;
}

void func(int (*fp)(int, int)){
	int a, b;
	int res;
	scanf("%d%d", &a, &b);
	res = fp(a, b);
	printf("%d\n", res);
}

int sum(int a, int b){
	return a+b;
}

int mul(int a, int b){
	return a*b;
}

int max(int a, int b){
	if(a<b){
		return b;
	}
	else{
		return a;
	}
}

연습

  • exchange 하수와 fun 함수 메인함수가 다음과 같이 작성되었을 때, exchange 함수와 func 함수를 만들기
  • func 함수는
    • 전달 인자로 받은 두 값을 출력한 후
    • exchange 함수를 사용하여 두 값을 바꾸고 다시 출력
  • exchagne함수는 포인터를 전달 인자로 받아서 그들이 가리키는 두 값을 바꿈
void exchange(double*, double*);
void func(void(*)(double*,double*), double *,double*);
int main(){
	double a=10, b=20;
    printf("a is %lf and b is %lf\n", a,b);
    func(exchange, &a, &b);
    printf("a is %lf and b is %lf\n", a,b);
    return 0;
}

정답 코드

#include <stdio.h>

void exchange(double*, double*);
void func(void(*)(double*,double*), double, double);

int main(){
	double a=10, b=20;

    func(exchange, a, b);
    return 0;
}

void exchange(double *ap, double *bp){
    double temp;

    temp = *ap;
    *ap = *bp;
    *bp = temp;
}

void func(void(*fp)(double*,double*), double a,double b){
    printf("a is %lf and b is %lf\n", a,b);
    fp(&a, &b);
    printf("a is %lf and b is %lf\n", a,b);
}

void 포인터

  • void 포인터는 가리키는 자료형에 대한 정보가 없는 포인터
int a;   // int형 변수 선언
(void *) &a; // int형 변수의 포인터를 void포인터로 강제 형 변환
  • void포인터 변수는 가리키는 자료형이 정해져 있지 않으므로 모든 포인터를 저장 가능

void 포인터변수의 사용

  • 가르키는 자료형에 대한 정보가 없으므로 사용할 때는 형변환하여 사용한다
 printf("%d\n", *(int *)vp);
 vp=(int *)vp +1;
 int *ip=(int *)vp;
  • void포인터변수를 사용하면 다양한 형태의 포인터를 전달인자로 받을 수 있는 함수를 만들수 있음
int a=10, b=20;
double x=0.15, y=0.5;
excahnge(&a, &b); // int형 포인터를 전달인자로 주고 호출한다
exchange(&x, &y); // double형 포인터를 전달인자로 준다.
  • 정수와 실수를 모드 교환할 수 있는 함수 프로그램
#include <stdio.h>
#include <string.h>

void exchange(char *, void *, void *);


int main(){
    int a=10, b=20;
	double da=1.5, db=2.5;

    exchange("int", &a, &b);
    printf("%d, %d\n", a,b);
    exchange("double", &da, &db);
    printf("%lf, %lf\n", da, db);
    return 0;
}

void exchange(char *type, void *vp1, void *vp2){
    int itp;
    double dtp;

    if(strcmp(type, "int")==0){
        itp=*(int *)vp1;
        *(int *)vp1 = *(int *)vp2;
        *(int *)vp2 = itp;
    }

    if(strcmp(type, "double")==0){
        dtp=*(double *)vp1;
        *(double *)vp1 = *(double *)vp2;
        *(double *)vp2 = dtp;
    }
}

0개의 댓글