포인터

문자열 상수와 포인터

#include <stdio.h>

int main(void)
{
    char str[] = "love";
    const char *pStr = "you";

    printf("str출력 : %s\n", str);
    printf("pStr출력 : %s\n", pStr);
}
str출력 : love
pStr출력 : you

포인터 변수 pStr에는 실제 문자열 상수인 "you"가 저장되는 것이 아니다. 문자열 상수는 문자열을 메모리 공간에 저장하고, 메모리 주소값을 리턴한다. 변수 pStr은 실질적으로 문자열 상수의 주소값을 가지고 있는 것이다.

포인터 배열

포인터 배열은 포인터 변수를 여러개 사용하기 위해 사용한다.

#include <stdio.h>

int main(void)
{
    char *pArr[] = {"C언어", "자바", "베이직"};
    int i;

    for(i = 0; i < 3; i++)
    {
        printf("%s\n", pArr[i]);
    }
    return 0;
}
C언어
자바
베이직

2차원 배열

int arr[i][j];

i 행과 j 열로 이루어진 행렬로 메모리 구조를 생각하자.

#include <stdio.h>

int main(void)
{
    int i, j;
    int arr[2][3];
    arr[0][0] = 1;
    arr[0][1] = 2;
    arr[0][2] = 3;
    arr[1][0] = 4;
    arr[1][1] = 5;
    arr[1][2] = 6;

    printf("2차원 배열 값의 출력 결과 \n");
    printf("=======================\n");
    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 3; j++ )
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    printf("=======================");
    return 0;

}
2차원 배열 값의 출력 결과 
=======================
1 2 3 
4 5 6 
=======================       

함수와 포인터

기본적인 함수의 인자 전달 형태는 복사형태이다. A에서 B로 복사 시 A, B 모두 값이 존재한다.

int main(void)
{
	int a = 10;
    temp(a);
}

void temp(b)
{
{

a : 실인수
b : 형식인수, 매개변수, 인자, 인수

배열형

배열형의 인자는 포인터형으로 받는다.

#include <stdio.h>
void func(int *pArr);

int main(void)
{
    int arr[] = {1, 2, 3, 4, 5};
    int i;

    func(arr);
    for(i = 0; i < 5; i++)
    {
        printf("배열의 요소 출력 : %d\n",arr[i]);
    }
    return 0;
}

void func(int *pArr)
{
    int i;
    for(i = 0; i < 5; i++)
    {
        printf("배열의 요소 출력 : %d\n",*(pArr + i));
    }
    printf("\n");
}
배열의 요소 출력 : 1
배열의 요소 출력 : 2
배열의 요소 출력 : 3
배열의 요소 출력 : 4
배열의 요소 출력 : 5

배열의 요소 출력 : 1
배열의 요소 출력 : 2
배열의 요소 출력 : 3
배열의 요소 출력 : 4
배열의 요소 출력 : 5

배열을 포인터로 받아와 사용할 경우, 배열의 크기를 알 수 없기 때문에 사이즈를 인자로 전달해주어야 한다.

#include <stdio.h>
int func(int *pArr, int size);

int main(void)
{
    int arr[] = {1, 2, 3, 4, 5};
    int sumArr, sizeArr;

    sizeArr = sizeof(arr) / sizeof(int);
    sumArr = func(arr, sizeArr);
    printf("배열의 총 합 : %d\n", sumArr);
    
    return 0;
}

int func(int *pArr, int size)
{
    int i, sum = 0;
    for(i = 0; i < size; i++)
    {
        sum += *(pArr + i);
    }
    return sum;
}
배열의 총 합은 : 15

값 호출 방식과 참조호출 방식

값 호출 방식(call by value)

실인수의 값이 형식 인수로 전달되는 방식으로, 실인수의 메모리가 형식 인수로 복사되어 메모리가 별도로 관리된다.

#include <stdio.h>

void callValue(int b);

int main(void)
{
    int a = 1;
    int *pa = &a;
    callValue(a);
    printf("실인수 a의 출력 : %d\n", a);
    return 0;
}

void callValue(int b)
{
    int *pb = &b;
    b = b + 3;
    printf("형식인수 b의 출력 : %d\n", b);
}
형식인수 b의 출력 : 4
실인수 a의 출력 : 1
#include <stdio.h>

void Swap(int a, int b);

int main(void)
{
    int x = 10, y = 20;
    printf("초기값 x = %d, y = %d\n", x, y);
    Swap(x, y);
    printf("함수 밖에서 변경 후 x = %d, y = %d\n", x, y);

    return 0;
}

void Swap(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
    printf("함수 안에서 변경 후 a = %d, b = %d\n", a, b);
}
초기값 x = 10, y = 20
함수 안에서 변경 후 a = 20, b = 10
함수 밖에서 변경 후 x = 10, y = 20

참조 호출 방식(call by reference)

함수 호출 시 전달인자로 메모리 접근에 사용되는 주소값을 전달한다. 원본 메모리의 주소값을 통해 사용하므로, 다른 함수 안에서 값을 변경 했을 때 실인수의 값도 변경된다.

#include <stdio.h>
void callReference(int *b);

int main(void)
{
    int a = 1;
    printf("a 초기값 : %d\n", a);
    callReference(&a);
    printf("실인수 a 출력 : %d\n", a);

    return 0;
}

void callReference(int *b)
{
    *b = *b + 3;
    printf("실인수 b 출력 : %d\n", *b);
}
a 초기값 : 1
실인수 b 출력 : 4
실인수 a 출력 : 4
#include <stdio.h>

void Swap(int *a, int *b);

int main(void)
{
    int x = 10, y = 20;
    printf("초기값 x = %d, y = %d\n", x, y);
    Swap(&x, &y);
    printf("함수 밖에서 변경 후 x = %d, y = %d\n", x, y);

    return 0;
}

void Swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
    printf("함수 안에서 변경 후 a = %d, b = %d\n", *a, *b);
}
기값 x = 10, y = 20
함수 안에서 변경 후 a = 20, b = 10
함수 밖에서 변경 후 x = 20, y = 10

사용자로부터 두 수를 입력받아 합과 곱을 출력하는 예제(참조호출 활용)

#include <stdio.h>
void sum_mul(int i, int j, int *hap, int *mul);

int main(void)
{
    int a,b;
    int x, y;

    printf("두개의 정수 입력 : ");
    scanf("%d%d", &a, &b);

    sum_mul(a, b, &x, &y);
    printf("두 수의 합 : %d\n", x);
    printf("두 수의 곱 : %d\n", y);
}

void sum_mul(int i, int j, int *hap, int *mul)
{
    *hap = i + j;
    *mul = i * j;    
}
개의 정수 입력 : 10 20
두 수의 합 : 30
두 수의 곱 : 200

함수 포인터

함수도 메모리 어딘가에 정의되어 있고, 필요할 때 그 위치를 참조(호출)하여 사용한다. 함수도 엄밀히 포인터임을 알 수 있다.

선언 방법

int (*func)(int a);
자료형(*함수 포인터 이름)(인자 목록);
#include <stdio.h>
int Add(int a, int b);
int Min(int a, int b);

int main(void)
{
    int a, b, sel, result;
    int (*fPtr)(int a, int b);

    while(1)
    {
        printf("다음 중 선택하시오 (1. 덧셈 2. 뺄셈. 3. 종료) : ");
        scanf("%d", &sel);

        switch (sel)
        {
        case 1:
            fPtr = Add;
            break;
        case 2:
            fPtr = Min;
            break;
        case 3:
            return 0;
        default:
            break;
        }

        printf("두 정수를 입력하시오 : ");
        scanf("%d %d", &a, &b);
        result = fPtr(a, b);
        printf("결과 : %d\n", result);
    }
    return 0;
}

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

int Min(int a, int b)
{
    return a - b;
}
다음 중 선택하시오 (1. 덧셈 2. 뺄셈. 3. 종료) : 1
두 정수를 입력하시오 : 3 5
결과 : 8
다음 중 선택하시오 (1. 덧셈 2. 뺄셈. 3. 종료) : 2   
두 정수를 입력하시오 : 1 5
결과 : -4
다음 중 선택하시오 (1. 덧셈 2. 뺄셈. 3. 종료) : 3

정적 바인딩

변수와 함수들이 컴파일 타임에 메모리의 크기와 위치가 결정되는 경우

동적 바인딩

변수와 함수들이 런타임(프로그램이 실행되는 시간) 에 메모리의 크기와 위치가 결정되는 경우

함수 포인터를 사용하는 이유

예를 들어 vscode이클립스에서 플러그인에 새로운 함수가 추가될 때마다 매번 다시 컴파일해야하는 비효율적인 상황이 생기는데, 호출하는 함수를 함수 포인터를 이용하여 동적 바인딩을 하면 매번 컴파일 할 필요가 없게 된다.

이런 예시처럼 함수포인터는 프로그램의 확장성과 유용함을 위해 사용한다.

NULL 포인터

null포인터란 아무것도 가르키지 않는 포인터이다. 국룰 주로 함수의 동작 에러 체크 용도로 자주 사용된다.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[] = "Love";
    char *p;
    int a;
    p = strchr(str, 'v');
    
    if (p != NULL)
    {
        *p = 'b';
        printf("변경 결과 : %s\n", str);
    }
    else
    {
        printf("NULL을 리턴하였습니다.\n");
    }
    return 0;
}
변경 결과 : Lobe
profile
Done is better than perfect

0개의 댓글