[C언어] 포인터 알아보기

ET의 공부/C언어|2020. 12. 22. 21:32

호출 함수에 있는 변수 바꾸기

a와 b의 값을 바꾸기 위해서는 임시로 저장할 변수 temp를 만들어 값을 변경할 수 있습니다.

temp = x; //temp는 x의 값을 갖는다.
x = y; //x는 y의 값을 갖는다.
y = temp; // y는 temp의 값(x의 값)을 갖는다.
//두 변수 변경 완료

 

그러면 이 변경 방법을 함수로 만들어서 구현해보겠습니다.

#include <stdio.h>

void changeValue(int x, int y); //함수 선언

int main () {
    int x = 9;
    int y = 1;
    printf("변경 전 x: %d, y: %d\n",x,y);
    changeValue(x,y);
    printf("변경 후 x: %d, y: %d\n",x,y);
    printf("\n");
    return 0;
}

void changeValue(int x, int y){
    int temp;
    temp = x;
    x = y;
    y = temp;
    printf("함수 안에서의 값 x: %d , y: %d\n",x,y);
    return;
}
//실행 결과
//변경 전 x: 9, y: 1
//함수 안에서의 값 x: 1 , y: 9
//변경 후 x: 9, y: 1

 

함수 안에서 분명 값이 변경된 것을 확인할수 있는데 main문에서는 값이 변경되지 않았습니다. 간단하게 하자면 메인 함수의 변수의 값을 함수 내부에서 변경하는 것이 아닌 다른 변수(전달된) 값을 사용하기 때문에 호출 함수에서 값을 변경하여도 메인 변수에는 영향을 끼치지 않습니다. 이를 해결하기 위해 포인터에 대해 알아보도록 하겠습니다. 우선은 주소(address)를 알아내는 &연산자부터 가보겠습니다.

 

변수의 주소를 확인 하는법: & 연산자

C언어를 시작하면서(저도 그렇듯) 처음 마주치는 난간은 뭐니 뭐니 해도 포인터(pointer)일 것입니다. 포인터는 주소(address)를 저장하는 변수입니다. 단항 연산자 &는 변수가 저장되어 있는 주소를 알아낼 수 있습니다.(주소는 메모리의 위치라고 생각하세요.)

그런데 자의든 타의든 우리는 주소값을 알아내는 &연산자를 이미 사용한 적이 있습니다.

scanf("%d",&a); //전달인자를 위해 a의 주소값을 사용한다.(&a)

이미 입력을 받는 함수인 scanf("%d",&a);은 입력을 받아 a의 값을 변경합니다. 일반적으로 ,return 값을 사용하지 않고 호출 함수를 사용하여 값을 변경하는 경우에는 주소 값을 사용합니다. 다음은 주소에 대한 간단한 예제입니다.

 

#include <stdio.h>

int main () {
    int x = 9;
    printf("값: %d , 주소 값: %p\n",x,&x);
    return 0;
}
//실행 결과
//값: 9 , 주소 값: 0x7ffeefbff5c8

 

printf("값: %d , 주소 값: %p\n",x,&x);에서 &x를 통해 주소 값을 확인할 수 있습니다. 그럼 함수에서 전달 인자로 받는 값과 메인에 있는 변수가 같은 변수인지 확인해 보겠습니다.

#include <stdio.h>

void addressView(int x); //함수 선언

int main () {
    int x = 9;
    printf("값: %d , 주소 값: %p\n",x,&x);
    addressView(x);
    return 0;
}

void addressView(int x){

    printf("함수 안에서의 값: %d , 주소 값: %p\n",x,&x);
    return;
}
//실행 결과
//값: 9 , 주소 값: 0x7ffeefbff5c8
//함수 안에서의 값: 9 , 주소 값: 0x7ffeefbff5ac

 같은 9의 값을 갖는 x인데 0x7ffeefbff5c8 와 0x7ffeefbff5ac로 서로 다른 주소 값을 갖습니다. 이는 서로 다른 변수라는 뜻이죠. 그럼 이제 포인터에 대해 알아보겠습니다.

 

포인터 기초

포인터는 주소를 값으로 갖는 변수입니다. ptr이라는 변수가 있다면 아래와 같이 사용할수 있습니다.

ptr = &x; //ptr은 x의 주소를 갖는 변수입니다.

이를 해석하자면 "ptr은 x를 가리킨다." 가 되겠습니다.  단순하게 이제 y를 가리키고 싶으면 ptr = &y; 로 y의 주소 값을 대입해주면 됩니다.

 

간접 연산자: *

이제 ptr은 x의 주소(&x)를 갖는 변수입니다. 주소 값을 알고 있으니 가리키고 있는 x의 값 또한 알수 있습니다. 간접 연산자인 *을 사용하여 x의 값을 알아내 보겠습니다.

ptr = &x; //x의 주소를 가리킨다.
value = *ptr; //x의 값

요약하자면

- 주소 연산자: &  // 변수 앞에 사용, 해당 변수의 주소를 얻는다.

- 간접 연산자 : * // 포인터의 이름이나 주소 앞에 사용, 가리키는 주소에 저장되어 있는 값을 얻는다.

 

포인터 선언

우리는 정수형의 경우 int, 실수형은 float 등 변수 앞에 자료형을 명시해줍니다. 그럼 포인터의 자료형은 어떻게 정의될까요? pointer라는 자료형이 있는 것이 아니라 포인터가 가리키는 변수의 데이터형(자료형) 또한 함께 선언해주어야 합니다.( 자료형 마다 메모리를 차지하는 크기가 다르기 때문)

int *ptr;
char *cptr;
float *fptr;

그럼 앞서 호출 함수를 이용하여 두 변수의 값을 바꾸는(실패했었던) 프로그램을 포인터를 이용해 다시 작성해보겠습니다.

#include <stdio.h>

void changeValue(int *x, int *y); //함수 선언

int main () {
    int x = 9;
    int y = 1;
    printf("변경 전 x: %d, y: %d\n",x,y);
    changeValue(&x,&y);
    printf("변경 후 x: %d, y: %d\n",x,y);
    printf("\n");
    return 0;
}

void changeValue(int *x, int *y){
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
    printf("함수 안에서의 값 x: %d , y: %d\n",*x,*y);
    return;
}
//실행 결과
//변경 전 x: 9, y: 1
//함수 안에서의 값 x: 1 , y: 9
//변경 후 x: 1, y: 9

 

이전과 다르게 값이 변경된것을 보실 수 있습니다. 이전과 달라진 부분을 말하자면

- void changeValue(int *x, int *y); 전달인자의 자료형은 정수형 포인터인 int *(변수명) 이 되었습니다. 함수의 전달 값으로 주소를 받는다는 것을 의미합니다. 

- changeValue(&x,&y); 주소 값(포인터)을 전달받으므로 주소 연산자 &을 사용하여 변수의 주소 값을 전달합니다.

 -   temp = *x;

     *= *y;

     *= temp;  우리가 바꿔야 할것은 변수의 값이므로 간접 연산자 *로 값을 대입, 교환해줍니다.

 

==> 풀이하자면 x와 y를 교환하는 프로그램입니다. 이는 함수에 x와 y의 주소 값을 전달합니다.(함수가 변수에 접근할 수 있게), 함수 내부에서는 간접 연산자 *를 이용하여 값을 알아내고 변경합니다.

댓글()