학습 기록/C

(7) 포인터

romi__ 2024. 11. 11. 19:54

/C

 

📌 포인터의 개념

- 주소값의 이해: 주소값 = 데이터가 저장된 메모리의 시작 주소

 

- 포인터란 메모리의 주소값을 저장하는 변수이다.

int n = 100;
int *ptr = &n;

 

위의 예시에서 n이라는 변수를 선언하고, *ptr을 통해 포인터를 선언하였다.

 

- 포인터 연산자에는 주소연산자와 참조연산자가 있다.

 

- 주소연산자(&)는 변수 이름 앞에 기호를 붙여서 사용한다. 해당 변수의 주소값을 반환하여 번지연산자라고도 불리며, 기호 &은 앰퍼샌드라고 읽는다.

 

- 참조연산자(*)는 포인터 이름이나 주소 앞에 붙여서 사용한다. 포인터가 가리키는 주소에 저장된 값을 반환한다.

 

- 포인터를 선언하는 방식은 아래와 같다.

타입 * 포인터이름 = &변수이름;
타입 * 포인터이름 = 주소값;

 

- 포인터를 참조할 때는 참조연산자를 활용한다.

int x = 7;
int * ptr = &x;
int * pptr = &ptr;

 

#include <stdio.h>

int main(void)
{
	int num01 = 1234;
	double num02 = 3.14;
	
	int* ptr_num01 = &num01;
	double* ptr_num02 = &num02;
	
	printf("포인터의 크기는 %d입니다.\n", sizeof(ptr_num01));
	printf("포인터 ptr_num01이 가리키고 있는 주소값은 %#x입니다.\n", ptr_num01);
	printf("포인터 ptr_num02가 가리키고 있는 주소값은 %#x입니다.\n", ptr_num02);
	printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
	printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %f입니다.\n", *ptr_num02);
	return 0;
}

//포인터의 크기는 8입니다.
//포인터 ptr_num01이 가리키고 있는 주소값은 0xb06343dc입니다.
//포인터 ptr_num02가 가리키고 있는 주소값은 0xb06343e0입니다.
//포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 1234입니다.
//포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 3.140000입니다.

 

 

 

📌 포인터 연산

- 포인터 연산의 규칙 네 가지

  • 포인터끼리의 덧셈, 곱셈, 나눗셈은 의미가 없다.
  • 포인터끼리의 뺄셈은 두 포인터의 상대적 거리를 의미한다.
  • 포인터에 정수를 더하거나 빼는 것이 가능하다. 다만 실수와의 연산은 허용되지 않는다.
  • 포인터끼리의 대입/비교가 가능하다.

 

- 예시 코드를 살펴보자.

#include <stdio.h>

int main(void)
{
	char* ptr_char = 0;
	int* ptr_int = NULL;
	double* ptr_double = 0x00;
	
	printf("포인터   ptr_char가 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_char);
	printf("포인터    ptr_int가 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_int);
	printf("포인터 ptr_double이 현재 가리키고 있는 주소값은 %#x입니다.\n", ptr_double);
	
	printf("포인터   ptr_char가 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_char);
	printf("포인터    ptr_int가 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_int);
	printf("포인터 ptr_double이 1 증가 후에 가리키고 있는 주소값은 %#x입니다.\n", ++ptr_double);
	return 0;
}

 

main 함수 내에 있는 포인터는 전부 0을 가리키는 포인터이다. 1 증가 연산 이후 포인터가 가리키는 주소는 각각의 타입에 따라 달라진다. 증가 폭이 포인터가 가리키는 변수의 타입 크기와 동일하다는 것을 알 수 있다.

예를 들어, int형 포인터라면 증가폭은 int형 타입의 크기인 4바이트만큼 증가하게 된다. (뺄셈에도 동일하게 적용된다)

 

- 포인터의 비교/대입 연산의 예시 코드는 아래와 같다.

#include <stdio.h>

int main(void)
{
	int num01 = 10;
	int num02 = 20;
	int *ptr_num01 = &num01;
	int *ptr_num02 = &num02;
		
	if (ptr_num01 != ptr_num02)		// 포인터끼리의 비교 연산 
	{
		printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
		printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num02);
		printf("포인터 ptr_num01과 ptr_num02는 현재 다른 주소를 가리키고 있습니다.\n\n");
		ptr_num02 = ptr_num01;		// 포인터끼리의 대입 연산 
	}
	
	printf("포인터 ptr_num01이 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num01);
	printf("포인터 ptr_num02가 가리키고 있는 주소에 저장된 값은 %d입니다.\n", *ptr_num02);
	
	if (ptr_num01 == ptr_num02)		// 포인터끼리의 비교 연산 
	{
		printf("포인터 ptr_num01과 ptr_num02는 현재 같은 주소를 가리키고 있습니다.\n");
	}

	return 0;
}

 

 

 

 

📌 인수전달방법

- 함수 호출 시에 필요한 데이터를 인수(argument)로 전달 가능하다. 전달 방법에는 값에 의한 전달과 참조에 의한 전달이 있다.

 

- 값에 의한 전달: 인수로 전달되는 변수의 값을 함수 내의 매개변수로 복사하는 방법이다. 여기서 변수와 매개변수는 완전히 다른 별개의 값이 되므로 함수 내에서 매개변수를 조작해도 변수에 영향이 가지 않는다.

#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;
}

//변수 var의 초기값은 10입니다.
//local() 함수 호출 후 변수 var의 값은 10입니다.

 

 

- 참조에 의한 전달: 변수의 주소값을 인수로 전달한다. 따라서 함수 내에서 변수의 값을 변경할 수 있다.

#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;
}

//변수 var의 초기값은 10입니다.
//local() 함수 호출 후 변수 var의 값은 20입니다.

 

 

 


📌 다양한 포인터
- 포인터의 포인터: 포인터 변수를 가리키는 포인터를 의미한다. 참조연산자(*)를 두 번 사용하여 표현하여 이중포인터라고도 부른다.

- void포인터: 대상이 되는 데이터의 타입을 명시하지 않은 포인터를 의미한다. 따라서 어떤 값도 가리킬 수 있지만, 포인터 연산이나 메모리 참조는 불가능하다. 사용할 때마다 사용하고자 하는 타입으로 명시적 타입 변환을 하는 과정 또한 필요하다.

- 함수 포인터: 함수는 프로그램 실행 시에 모두 메인 메모리에 올라가게 된다. 함수의 이름은 메모리에 올라간 함수의 시작 주소를 가리키는 포인터 상수가 되는데, 이 포인터 상수가 함수 포인터이다. 함수 포인터 타입은 반환값과 매개변수로 정해진다.

- 널 포인터: 0, NULL타입을 대입하여 초기화한 포인터를 의미한다. 즉, 아무것도 가리키지 않는 포인터이다.

 

void func(int, int); //이런 함수 원형이 있다면
void (*ptr_func) (int, int); //함수 포인터는 이렇게 선언

'학습 기록 > C' 카테고리의 다른 글

(9) 메모리의 관리  (0) 2024.11.14
(8) 포인터와 배열  (1) 2024.11.12
(6) 배열  (0) 2024.11.10
(5) 함수  (0) 2024.11.09
(4) 제어문  (0) 2024.11.08