happy coding

[c] 포인터 본문

self study/언어의 기본부터

[c] 포인터

yeoonii 2022. 11. 21. 16:36
TCP school을 보고 정리한 내용입니다.

포인터(pointer)

포인터란 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 부른다.

주소값이란 해당 데이터가 저장된 메모리의 시작 주소를 의미하며, C언어에서는 이러한 주소값을 1바이트 크기의 메모리 공간으로 나누어 표현한다.

포인터 연산자는 1. 주소 연산자(&) 2. 참조 연산자(*) 가 있다.

주소 연산자(&)

주소 연산자는 변수의 이름 앞에 사용하여, 해당 변수의 주소값을 반환한다. '&' 기호는 ampersand이며, 번지 연산자라고도 불린다.

참조 연산자(*)

참조 연산자는 포인터의 이름이나 주소 앞에 사용하여, 포인터에 가리키는 주소에 저장된 값을 반환한다. 

포인터의 선언

타입* 포인터이름;	//선언
타입* 포인터이름 = &변수이름;	//선언 동시에 초기화
타입* 포인터이름 = 주소값;	//선언 동시에 초기화

타입이란 포인터가 가리키고자 하는 변수의 타입을 명시한다. 그리고 포인터 이름은 포인터가 선언된 후에 포인터에 접근하기 위해 사용된다. 포인터를 선언한 후 참조 연산자를 사용하기 전에 포인터는 의도하지 않은 메모리의 값을 변경할 수 있기 때문에 반드시 먼저 초기화되어야 한다. 그렇기에 포인터의 선언과 동시에 초기화를 함께 하는 것이 좋다.

포인터의 참조

int x = 7;		//변수의 선언
int *ptr = &x;		//포인터의 선언
int *pptr = &ptr;	//포인터의 참조

포인터 변수는 메모리에서 변수의 위치를 나타내는 주소를 다루는 변수이므로, 그 크기는 일반적으로 CPU에 따라 결정된다. 따라서 32bit CPU에서는 1word의 크기가 4byte이므로, 포인터 변수의 크기 또한 4byte가 된다. 하지만 포인터 변수의 크기는 컴파일러로 컴파일할 때 그 크기까지 직접 명시할 수 있기 때문에 사용된 컴파일러의 정책에 따라 달라질 수 있다. 

포인터의 타입은 참조 연산자를 통해 값을 참조할 때, 참조할 메모리의 크기를 알려주는 역할을 하기 때문에, 포인터가 가리키는 변수의 타입에 따라 포인터의 타입 또한 바꿔준다.

포인터 연산

포인터는 값을 증가시키거나 감소시키는 등의 제한된 연산만 할 수 있는데, 여기에는 규칙이 있다. 1. 포인터끼리의 사칙연산은 아무런 의미가 없다. 2. 포인터끼리의 뺄셈은 두 포인터 사이의 상대적 거리를 나타낸다. 3. 포인터에 정수를 더하거나 뺄 수는 있으나, 실수와의 연산은 허용하지 않는다. 4. 포인터끼리 대입하거나 비교할 수 있다.

인수 전달 방법

함수를 호출할 때에는 함수에 필요한 데이터를 인수(argument)로 전달해 줄 수 있다. 이러한 방법에는 1. 값에 의한 전달(call by value) 2. 참조에 의한 전달(call by reference) 이 있다.

값에 의한 전달

값에 의한 전달 방법은 인수로 전달되는 변수가 가지고 있는 값을 함수 내의 매개변수에 복사하는 방식이다. 이렇게 복사된 값으로 초기화된 매개변수는 인수로 전달된 변수와는 완전히 별개의 변수가 된다. 따라서 함수 내에서의 매개변수 조작은 인수로 전달되는 변수에 아무런 영향을 미치지 않는다.

#include <stdio.h>
void local(int);

int main(void)
{
	int var = 10;
    printf("변수 var의 초깃값은 %d이다.\n",var);
    
    local(var);
    printf("local()함수 호출 후 변수 var의 값은 %d이다.\n",var);
    return 0;
}
void local(int num)
{
	num += 10;
}

위 코드에서 local() 함수의 매개 변수 num은 인수로 변수 var의 값을 전달받는다. 따라서 💚함수 내에서 매개변수 num의 값을 아무리 변경하더라도 원래 인수로 전달된 변수 var의 값은 절대 변경되지 않는다.

참조에 의한 전달(call by reference)

참조에 의한 전달 방법은 인수로 변수의 값을 전달하는 것이 아닌, 해당 변수의 주소값을 전달한다. 즉 함수의 매개변수에 인수로 전달된 변수의 원래 주소값을 저장하는 것이다. 이러한 방식을 통해 인수로 전달된 변수의 값을 함수 내에서 변경할 수 있게 된다.

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

int main(void)
{
	int var = 10;
    printf("변수 var의 초깃값은 %d이다.\n",var);
    
    local(&var);
    printf("local()함수 호출 후 변수 var의 값은 %d이다.\n",var);
    return 0;
}

void local(int* num)
{
	*num += 10;
}

위 코드에서 local() 함수의 매개변수 num은 인수로 변수 var의 주소값을 전달받기 때문에, 함수 내에서 매개변수 num의 값을 변경하면, 원래 인수인 변수 var의 값도 같이 변경된다.

다양한 포인터

포인터의 포인트란 포인터 변수를 가리키는 포인터를 의미하는데, 참조연산자(*)를 두 번 사용해 표현하며 이중 포인터라고도 부른다. 

int num = 10;		//변수 선언
int* ptr = &num;	//포인터 선언
int** pptr = &ptr;	//포인터의 포인터 선언

void 포인터

void 포인터는 일반적인 포인터 변수와는 달리 대상이 되는 데이터의 타입을 명시하지 않은 포인터이기에, 변수, 함수, 포인터 등 어떠한 값도 가리킬 수 있지만, 포인터 연산이나 메모리 참조와 같은 작업은 할 수 없다. 즉 void 포인터는 주소값을 저장하는 것 이외에는 아무것도 할 수 없는 포인터이며, 이 포인터를 사용할 때에는 반드시 먼저 사용하고자 하는 타입으로 명시적 타입 변환 작업을 거친 후 사용해야 한다.

int num = 10;		//변수 선언
void* ptr = &num;	//void 포인터 선언

*(int*)ptr = 20;	//void 포인터를 통한 메모리 접근

함수 포인터(function pointer)

함수의 이름은 메모리에 올라간 함수의 시작 주소를 가리키는 포인터 상수(constant pointer)가 되는데, 이러한 포인터를 함수 포인터라고 부른다. 함수 포인터의 포인터 타입은 함수의 반환값과 매개변수에 의해 결정되기 때문에 함수의 원형을 알아야만 해당 함수에 맞는 함수 포인터를 만들 수 있다.

void func(int,int);		//함수 원형
void (*ptr_func)(int,int);	//위의 함수 원형을 가지는 함수에 대한 함수 포인터

널 포인터(null pointer)

0이나 NULL을 대입하여 초기화한 포인터를 일컫는다. 널 포인터는 아무것도 가리키지 않는 포인터라는 의미이다.

 


값에 의한 전달과 참조에 의한 전달 이해안감

'self study > 언어의 기본부터' 카테고리의 다른 글

[c] 메모리 관리  (0) 2022.11.22
[c] 포인터와 배열  (0) 2022.11.22
[c] 배열  (0) 2022.11.19
[c] 재귀 호출  (0) 2022.11.19
[c] 변수의 유효 범위  (0) 2022.11.18
Comments