본문 바로가기

CS/C

What is pointer?

포인터란??

포인터는 가리키는 변수의 시작 주소(메모리의 위치)를 기호화(&변수)한 것으로 "무엇을 가리키는 것"을 의미.

즉, 6칸인 장롱에 각 서랍의 고유이름(1번 칸, 2번 칸, 3번 칸, 4번 칸, 5번 칸, 6번 칸)에 여름 옷, 겨울 옷 등의 라벨을 붙이고 서랍 안에 옷들을 넣는다.

그럼 여기서 서랍의 고유이름들은 변수의 주소가 되겠고, 따로 붙인 라벨은 변수가 되겠으며, 서랍 안에 옷들은 data들이 되겠다.


 

※ 메모리 주소.

메모리는 위치를 구분하기 위해 바이트 단위로 표시하는 물리적인 주소값을 갖는다.

하나의 변수를 선언하면 컴퓨터는 그 데이터 타입의 크기만큼 메모리에 연속된 바이트의 메모리 영역을 할당한다.


1byte의 메모리 주소는 4bit씩 묶어서 16진수로 표현하면 2개의 숫자 00~FF까지 표현할 수 있다.

따라서 32bit는 32/4=8이므로 8개의 숫자로 주소를 표현.

32bit 컴퓨터에서 가상 주소는 4GB(4GB = 0~16진수로 8개의 F:FFFFFFFF = 0xFFffFFff)까지 할당하여 사용.

데이터를 저장할 때 워드(word) 단위로 정수를 저장.

32bit 컴퓨터라면 1word는 4byte가 된다. 이것이 하나의 주소. 그리고 주소는 대개 2 또는 4의 배수로 시작한다.


 


포인터를 사용 경우.

1) 하드웨어를 프로그램으로 제어하기 위하여 메모리를 지정하고 직접 접근하고 참조할 수 있는 수단.

2) 문자열을 저장하거나 자료구조의 예제들을 표현하는 경우.

3) 함수들 사이에 커뮤니케이션을 하기 위한 것으로 호출 함수의 매개변수에 영향을 주거나 배열을 조작하는 함수인 경우.

4) 매개변수를 포인터 변수로 선언함으로서 피호출 함수(정의 함수)로부터 하나 이상(여러 개)의 값을 호출한 함수로 전달할 수 있다.


포인터 사용 방법.

1) &변수 : 변수의 포인터(주소)를 구하기 위해 '&변수'와 같이 사용. 즉, 변수로 할당된 메모리의 시작 주소를 포인터라 부른다.

2) *변수 : 포인터로 표시한(&변수) 메모리에 접근하기 위해 '*변수'와 같이 사용. 즉, *포인터 변수를 사용하여 데이터를 읽거나 저장.



ex) 일반 변수, 포인터, 상수와 주소 사용 방법에 대해 전반적인 개념을 소개한다.






※ 특정 메모리 주소를 직접 지정하는 방법

이처럼 직접 주소를 사용하려면 반드시 캐스트 연산자를 붙여야 한다. 즉, (int *)loc처럼 캐스트 연산자를 사용하여 정수형 포인터로 만들어주면 특정한 메모리 주소값을 인식할 수 있다.

따라서 *(int *)loc는 loc가 가리키는 곳의 내용을 의미하므로, 주소가 y로 할당한 메모리 주소와 같으므로 변수 y와 같다.



포인터 변수

포인터 변수는 특정한 데이터를 저장하는 것이 아니라 오직 포인터를 저장할 수 있는 변수다.

포인터 변수가 포인터를 저장하면 그 포인터가 가리키는 메모리 영역을 똑같이 가리키게 된다.

이후 *포인터 변수를 사용하여 그 메모리 영역을 참조하면 대상을 읽어오거나 저장할 수 있다.


※ 알아둘 것

1) 포인터는 메모리 주소(번지, 위치)이다. 즉, 상수이다. 

    메모리 주소는 기호 &로 표기.

    주소값 상수는 피연산자로 사용할 수 없다.

ex)    &x는 사용가능하지만, &100은 불가능하다.


2) 포인터 변수는 다른 주소값을 저장하여 가리키는 대상을 바꿀 수 있다.

    그러나 메모리 주소 이외에는 어떤 것도 저장할 수 없다.


3) 포인터, 포인터 변수, 포인터명은 같은 용어.


사용

데이터 타입 *포인터 변수;

 포인터 선언과 사용

 예문

#include <stdio.h>

#으로 시작하는 전처리기


int main()

{

포인터 선언문;

포인터 변수 = 가리킬 변수의 주소;

포인터 사용;

return 0;

}

 #include<stdio.h>


int main()

{

int y = 33;

int *pt;  //포인터 선언

pt = &y;  //포인터 변수에 y주소 저장

printf("%d\n", y);  //y의 데이터 출력

printf("%d\n", *pt);  //포인터 데이터 출력

return 0;

}



tip)

int *pt;

int* pt;

둘 다 같다.



※ 아스테릭(asterisk : *)

포인터 연산자.

변수는 데이터를 직접 참조하여 처리할 수 있으므로 직접 참조라 한다.

*포인터변수명은 포인터변수가 가지고 있는 (지정된 메모리 번지에 저장되어 있는) 데이터 내용을 간접으로 가져오는 것으로 이를 역참조(dereferencing)또는 간접참조(indirection)이라 한다.



※ 알아둘 것

포인터 선언 시 포인터는 데이터가 저장된 메모리 주소를 지정하지 않고 이름만 갖고 있으므로 실체를 알 수 없게    

되어 의미없는 쓰레기값을 갖는다.

따라서 포인터 변수에 주소값을 대입하여 할당해주어야 한다.

그렇지 않으면 엉뚱한 결과나 시스템 다운이 일어날 수 있다.



※ 알아둘 것

*1000 = 32;

이와 같이 정수에 값을 저장할 수 없다.

하지만 cast 연산자를 이용하여 할 수 있다.

정확히 말하자면 1000이란 정수를 주소화한 뒤 그 주소에 값을 대입하는 것이다.

*(int *)1000 = 32;

위와같이 사용하면 된다.




ex) 변수, 포인터를 선언하는 방법과 변수와 포인터의 내용과 시스템에서 할당받은 주소값을 출력하는 여러 형태를 보여준다.

사용하는 컴퓨터 시스템마다 변수에 배당하는 메모리 주소는 다를 수 있다.









널 포인터(NULL)

NULL pointer는 '어떤 것도 가리키지 않는 주소값'을 의미하는 할당되지 않은 포인터이다.


형식 : #define NULL (char *)0

->     (char *)0은 정수 0이지만 함수의 매개변수가 일정하지 않은 가변 인수(void * 포인터로 처리)로 사용되는 경우

0으로 하면 널 포인터인지 정수 0인지 알 수 없으므로 (char *)0을 사용하는 것이 안전함.


사용 시기

1) 에러 발생 시 검사할 필요가 있는 경우.

2) 함수를 사용할 때 매개변수의 끝을 알리는 경우.


※ 알아둘 것

1) 널 문자는 문자열 마지막에 붙어서 문자열을 구분한다. (NULL, '\0')

char ch1 = '\0';

char ch2 = 0;

위 두 문장은 같다.

2) char *pt = " ";

널 문자로 인식하게 되어 특정한 메모리 영역을 할당하게 된다.

3) 포인터 변수는 항상 0으로 초기화 할 것.






주소 연산자(&)

포인터 변수는 데이터가 아니라 메모리 영역의 주소이다.


형식 : 포인터 변수 = &변수;


변수명 앞에 주소 연산자(&)를 붙이면 그 변수가 할당된 메모리의 시작 주소를 구한다.

엠퍼샌드(ampersand : &)로 표시.

직접 주소값을 대입하려면 캐스트 연산자 (int *) 주소 를 사용한다.

메모리 번지는 4byte 형식으로 16진수로 표현.



※ 일반 변수와 포인터의 차이점.

 종류

내용

예문 

일반 변수 

 데이터 

int x = 100;

변수 x에 100을 대입.

포인터

 메모리 주소를 갖는 상수

포인터는 &변수를 의미. 

*포인터 변수 (label)

포인터 변수가 가리키는 메모리 주소에 저장된 데이터 

int *pt;

포인터 변수 pt의 내용은 int 타입. 

&변수 (address)

변수에 할당된 메모리의 시작 주소

(&는 주소 연산자) 

pt = &x;

포인터 pt에 변수 x의 번지 대입. 



※ 알아둘 것.

1) 포인터 선언(실제 사용할 데이터) : 타입 *포인터 변수 => int *pt;

2) 포인터가 가리키는 객체(*포인터 변수) 주소 할당 : 포인터 변수 = &변수;  => pt = &y;



ex) 포인터를 사용하는 방법.






※ 포인터를 사용하기 위한 단계.

1) 포인터 변수 선언 : 타입 *포인터 변수

2) 데이터 가리키기 : 포인터 변수 = &변수

3) 데이터의 값을 읽기 : 변수 = *포인터 변수

4) 데이터에 값을 저장 : *포인터 변수 = 값


※ 알아둘 것.

1) 엔디언(endian) : 여러 바이트로 구성된 하나의 데이터를 메모리에 어떠한 바이트 순서로 저장하는 방식.

2) 리틀 엔디언(little endian) : 주소의 가장 낮은 주소에 최하위 바이트 자릿수를 기록하고, 점차 높은 주소에 높은 자릿수

    를 저장하는 방식. (intel 계열에서 사용)

3) 빅 엔디언(big endian) : 리틀 엔디언의 반대. (Motorola, Sun ,Sparc 같은 RISC에서는 빅 엔디언 사용.)


ex) 0x23456789의 엔디언.

(23은 최상위 바이트(MSB : Most Significant Byte), 89는 최하위 바이트(LSB: Least Significant Byte))

 0

2

메모리 주소

 89

67 

45 

23 

리틀 엔디언

 23

45 

67 

89 

빅 엔디언



void 포인터 변수

void *pt;

void 포인터 변수(void * 타입)는 사용자가 원하는 모든 데이터 타입의 포인터로 형 변환하여 저장가능.

즉, void로 선언하고 포인터변수를 직접 사용하면 에러가 난다!!

왜!? void는 가리키는 데이터 타입이 정해지지 않았기 때문!!

따라서 형 변환 후 사용해야 한다. 

ex)

void *pty;

printf("%d\n", *pty)    //에러!!

printf("%d\n", *(int *)pty);   // 형 변환 하여 출력 가능!! 일시적으로 int타입의 포인터가 생성!




ex) 포인터를 사용하여 정수, 단일 문자, 문자열을 저장하고 출력하는 방법.




'CS > C' 카테고리의 다른 글

Variadic is  (0) 2012.10.27