상세 컨텐츠

본문 제목

포인터의 개념들

Maya API/C++

by hwano 2013. 12. 25. 18:04

본문

출처

http://blog.naver.com/netrance/110082192290

http://netrance.blog.me/110082920785

      

------------------------------------------------------------------------------------------

 

주소 ( address )란?

 

 

 

주소 ( address )란 무엇인가?

 

 

변수가 메모리의 어디에 있는지를 알기 위해 주소라는 개념이 도입되었다. 즉,

'주소는 메모리의 위치를 의미하며, 변수나 배열의 원소 등이 어디에 자리하는지' 를 주소를 통해 알 수 있다.

 

메모리의 주소는 정수값으로 표현된다.  일반적으로 메모리의 기본 단위는 바이트(byte)이며, n번째 바이트의 주소는 n이다.

32비트 컴퓨터의 경우 표현 가능한 주소의 범위는  0부터  232 - 1까지이다.

 

 

 

 

 

 

 

 

주소 연산자 ( address operator )의 사용법과 용도

 

 

 

주소 연산자란

 

주소 연산자는 변수의 주소 값을 구하기 위한 연산자. 변수가 메모리의 어느 곳에

위치하고 있는지 알 수 있다. 주소값은 포인터를 연산할 때 필요.

 

 

 

주소 연산자의 사용

 

주소 연산자의 기호는 '&'이다. 피연산자는 1개이며, 주로 변수 이름

num이라는 정수형 변수가 있다고 가정하고 그것의 주소를 알려면 &num이라고 작성

 

errorcode.c

#include <stdio.h>

 

void main()

{

    int num = 100;

    int address = &num;

 

    printf("변수 num의 값: %d\n", num);

    printf("변수 num의 주소값: %d\n", address);

}

 

위 코드를 컴파일하면, 다음과 같은 오류가 발생된다

 

errorcode.c(6): error C2440: 'initializing' : cannot convert from 'int *' to 'int'

 

주소값이 숫자인 것은 맞지만, 데이터형이 int가 아닌 int * 이다

 

 

 

주소 연산자로 연산한 결과값의 데이터 형은 무엇인가?

 

 

C++ 언어는 주소 연산의 결과를 다음과 같이 정의한다

 

' 피 연산자의 데이터형이 T이면, 주소 연산자로 계산된 주소값의 데이터형은 T* 이다. '

 

포인터는 주소값을 갖는 변수이다. 주소는 일반적인 산술 연산과는 다르게 취급되므로, char, short, int, long, float, double

등과 같은 기존 데이터형을 그대로 사용할 수 없다. 대신 T* 이 사용되며, T는 사용가능한 모든 데이터형을 의미한다.

 

 

T*는 포인터가 주소값을 통해 접근할 수 있는 메모리의 영역을 T형으로 취급함을 의미한다.

 

errorcode.c

#include <stdio.h>

 

void main()

{

    int num = 100;

    int * address = &num;

 

    printf("변수 num의 값: %d\n", num);

    printf("변수 num의 주소값: %d\n", address);

}

 

 

 

 

주소값은 무슨 용도로 사용되나

 

 

앞에서 주소값은 포인터가 갖는 값이라 말했다. 때문에 '주소값이 어떤 용도로 사용되는가'는

'포인터의 용도가 무엇인가?'와 같은 뜻으로 해석할 수 있다.

 

그것을 이용하여 컴퓨터의 메모리에 임의적으로 접근할 수 있다. 이는 저급 언어들이 갖는 특성이다.

 

 

 

 

 

 

 

 

포인터 ( pointer ) 란?

 

 

 

포인터 ( pointer ) 란?

 

포인터는 메모리의 주소값을 갖는 변수를 의미한다. 포인터를 이해하는 것은 주소값을 어떻게 활용하는지를

이해하는 것을 의미한다.

 

주소값을 이용하여 프로그램의 메모리에 임의적으로 접근할 수 있다. 프로그래머가 마음먹으면, 포인터를 통해

OS의 영역을 건드리는 것이 가능하다. ( 단, 어플리케이션들은 주어진 메모리 영역 내에서만 포인터로 접근이 가능하다 )

 

메모리의 어느 곳이든 갈 수 있으므로 포인터는 매우 강력한 수단이다. 이는 주로 저급언어들이 가지고 있는 특성이며

c++이 강력한 이유이기도 하다

 

 

 

포인터의 용도

 

포인터로 메모리에 접근하는 몇 가지의 패턴이 있다.

 

 

두 함수간의 데이터를 공유

 

본래 함수가 갖는 지역변수들은 다른 함수와 공유되지 않는다. 하지만 포인터를 이용하면 가능해진다.

함수 f가 다른 함수 g를 호출한다고 가정하면 g가 p라는 포인터 형의 파라메터를 가지고,

f는 자신의 변수 v의 주소값을 g의 파라메터에 대입한다.

 

그러면 g는 p를 통해 f의 변수 v에 접근할 수 있다. 이러한 방식으로 함수 간에 변수 공유가 가능해진다.

 

 

 

동적 메모리 할당받기

 

컴퓨터 시스템은 메모리 내에 힙 ( heap) 이라는 공간을 가지고 있으며, 프로그램은 이것의 일부를 할당받을 수 있다.

 

프로그래밍 언어에서는 '동적메모리 ( dynamic memory )를 할당받는다'라고 한다. 전역 또는 지역 변수와 달리

프로그램이 필요하면 언제나 메모리 공간을 받아서 쓴다는 이유로 동적이라는 용어가 붙는다.

 

할당받은 메모리 조각의 첫번째 주소는 포인터에 저장하고, 이를 통해 프로그램은 데이터를 기록하거나

읽는 등의 작업을 한다. 더 이상 사용하지 않으면, 프로그램은 이를 해제해야 한다. 해제 또한 포인터를 통해 처리된다.

 

힙은 여러 프로그램들이 사용한다. 그러므로 해제되지 않은 메모리 조각들이 많아지면, 프로그램이 힙을 이용하지

못하게 된다. 그로인해 시스템은 더 이상 동작하지 않을 수도 있다.

프로그래머는 동적 메모리를 사용할 때 이를 항상 주의해야 한다.

 

 

 

 

 

 

포인터를 정의하고 이를 이용하여 메모리에 접근하는 방법

 

 

 

포인터를 정의하는 문법

 

포인터는 다음의 형식으로 정의한다.

 

'데이터형 이름' * '포인터 이름';
'데이터형 이름' * '포인터 이름' = '주소값';

 

 

여기서 데이터형 이름에는 사용 가능한 모든 데이터형들이 위치 할 수 있다.

포인터를 통해 특정 주소에 데이터를 읽거나 기록하려면, 데이터의 크기와 유형을 알아야 한다. 가령, 포인터를

거쳐 읽거나 기록할 내용이 정수인지 또는 실수인지를 구별할 수 있어야 한다는 것

포인터를 정의할 때 데이터형 이름이 필요한 이유가 바로 여기에 있다.

 

포인터를 초기화하고 싶다면, 그것을 정의할 때 주소값을 대입해주면 된다. 변수의 주소값을 지정하려면,

주소연산자 &와 변수 이름을 차례로 '주소값' 위치에 놓는다.

 

 

 

int형 포인터를 정의하는 예

 

int * plnt;

 

이번에는 int형 변수의 주소값으로 포인터를 초기화한다.

 

int num = 123;

int * plnt = &num;

 

포인터를 통해 메모리의 특정 주소에 int형 데이터를 읽거나 기록하는 것이 가능해진다.

 

 

 

 

한 줄로 여러개의 포인터들을 정의하기

 

만일 여러 개의 포인터들을 한 줄로 정의하길 원한다면, 아래와 같은 방식으로 작성할 것

 

데이터형 이름 * 포인터 이름1, * 포인터 이름2, ......;

int *plnt1, *plnt2, *plnt3;

 

 

 

포인터에 주소값을 대입하기

 

초기화 할 때와 마찬가지로 대입 연산자와 주소값을 이용하여 포인터에 값을 대입한다

 

'포인터 이름' = '주소값';

 

int num = 123;
int * pInt;

 

/* ... */

 

pInt = &num;

 

 


포인터에 주소값을 대입할 때 주의할 점

 


당연한 이야기지만, 포인터에 값을 대입할 때 포인터의 데이터형과 주소값의 데이터형이 일치해야 합니다.

그렇지 않으면 컴파일할 때 오류 또는 경고 메시지가 발생한다. 

 

#include <stdio.h>

 

void main()
{
    float * pFloat;
    int num;

    pFloat = &num;    /* 오류. 데이터형이 일치하지 않음. */


    printf("포인터 pFloat가 가리키는 주소: %d\n", pFloat);
}

 

 

 


메모리에 접근하는데 필요한 간접 연산자 *

 

 


포인터를 이용하여 특정 주소로부터 데이터를 읽거나 기록할수 있다. 이는 간접 연산자(indirect operator)를 통해서 가능하다.

이 연산자는 하나의 포인터를 피연산자로 갖는 단항 연산자이다

 

*'변수 이름'
또는
*'배열 이름'[인덱스 값]

 

 

 

위와 같은 형식으로 간접 연산자를 이용. 이것이 또 다른 피연산자 역할을 한다.

 

대입 연산자의

왼쪽에 놓이면, 포인터가 가리키는 주소로 값을 입력할 수 있습니다.

오른쪽에 놓이면, 그 주소로부터 값을 가져옵니다.

 

 

int num1 = 123, num2;
int * pNum = &num;

num2 = *pNum;    /* 포인터 pNum을 통해 num1의 값 123이 대입됨 */
*pNum = 456;    /* 456이 포인터 pNum을 통해 num1에 대입됨 */

 

 

 

 

 


통합 예제

 

 


지금까지 설명드린 내용들을 하나의 예제 프로그램으로 모아볼까요? 이번 예제는 한 함수의 포인터를 이용해 값을 1 증가시키는 프로그램입니다. (첨부 파일을 올렸으니 참고 바랍니다.)

 

 

#include <stdio.h>

 

void add_1_to_integer(int * pInteger)
{
    *pInteger = *pInteger + 1;
}

 

void main()
{
    int num = 123;

 

    printf("add_1_to_integer 함수 호출 전 num 값: %d\n", num);
    add_1_to_integer(&num);
    printf("add_1_to_integer 함수 호출 후 num 값: %d\n", num);
}

 

 

add_1_to_integer 함수는 포인터 변수를 파라메터로 갖는다.

이 함수는 이 포인터를 통해 함수 내부 또는 외부의 메모리 공간에 접근을 할 수 있다.

 

이 예제에서는 main 함수가 add_1_to_integer 함수를 호출할 때 지역 변수 num의 주소값을 파라메터 pInteger로 대입합니다.

 

그러면 add_1_to_integer 함수는 pInteger를 이용하여 main 함수의 num 변수에 접근이 가능해 집니다.

이는 하나의 변수를 두 개의 함수가 공유함을 의미합니다.

 

 

add_1_to_integer 함수의 내용을 자세히 설명하면

 

*pInterger + 1

pInteger가 변수 num을 가리키므로 간접 연산자 *를 이용하면, 그 지점의 값을 가져올 수 있습니다. 이 식은 그것에 1을 더합니다. num의 값이 123이므로 123 + 1은 124입니다.

 

*pInteger

이제 124를 =의 왼쪽 피연산자인 *pInteger에 대입하려 합니다. pInteger는 변수 num을 가리킨다고 말씀드렸죠? pInteger를 통해 num의 값은 124가 대입됩니다.

 

 

 

실행 결과

프로그램을 실행한 결과는 다음과 같습니다.

 


 

 

 

 

 

 

 

 

 

 

 

 

관련글 더보기