차곡차곡

[C/L] 포인터 본문

Language/C

[C/L] 포인터

sohy 2022. 11. 5. 10:50

포인터란?

메모리의 주소를 가지고 있는 변수. 즉, 포인터가 저장하고 있는 것은 값이 아닌 변수의 주소이다. 포인터도 변수이기 때문에 사용하기 위해서는 먼저 선언을 해야 하는데, 포인터를 선언할 때 포인터가 가리키는 자료형을 함께 작성해야 한다. int형 포인터는 int형 변수만, char형 포인터는 char형 변수만 가리킬 수 있는 것이다. 

int number = 10;   // 정수형 변수 number 선언
int *p;   // 정수를 가리키는 포인터 p
p = &number;   // 변수 number의 주소가 포인터 p로 대입

 

간접 참조 연산자 *

포인터를 통하여 포인터가 가리키는 위치의 값을 읽어오거나 변경할 수 있는데, 이때 필요한 것이 * 기호이다. 포인터가 가리키는 위치에 저장된 값을 가져오려면 변수 앞에 * 기호를 붙여야 한다. 

printf("%d\n", *p)   // 10이 출력된다.

 

* 의 용도

1. 포인터 선언할 때

2. 해당 주소의 값에 접근할 때

 

포인터 연산

포인터에서 대해서는 덧셈과 뺄셈 연산만 가능하다. 만약 증감 연산자를 사용하여 연산을 하면 항상 1만큼 연산되는 것이 아니라, 가리키는 객체의 크기만큼 증감된다. 즉 char형 포인터를 증가시키면 char형의 크기인 1바이트만큼 증가하고, int형 포인터를 증가시키면 int형의 크기인 4바이트만큼 증가한다.

포인터 타입 증감되는 값
char 1
short 2
int 4
float 4
double 8

 

함수와 포인터

C언어는 기본적으로 '값에 의한 호출'만 가능하다. 즉 함수 안에서 매개 변수를 변경하더라도 원본 변수는 변경되지 않는다. 하지만 포인터를 함수에 전달하면 '참조에 의한 호출'을 흉내낼 수 있다.

  • 값에 의한 호출(call-by-value) : 함수가 호출될 때 복사본이 함수로 전달되면 값에 의한 호출이다. 함수 안에서 매개 변수를 변경하여도 원본에는 영향을 주지 않는다.
  • 참조에 의한 호출(call-by-reference) : 만약 함수가 호출될 때 원본을 함수로 전달하는 방법이다. 함수 안에서 매개변수를 변경하면 원본 변수가 변경된다.
#include <stdio.h>

void modify(int *ptr)
{
  *ptr = 99;   // 매개 변수를 통하여 원본을 변경한다.
}

int main(void)
{
  int number = 1;
  modify(&number);   // 주소 전달
  printf("number = %d\n", number);   // number = 99 이 출력된다.
  
  return 0;
}

 

포인터 사용시 주의할 점

  • 초기화하지 않고 사용하기 : 포인터가 선언만 되고 초기화가 되지 않았다면 포인터는 임의의 주소를 가리키게 된다. 따라서 이런 상태에서 포인터를 이용하여 메모리의 내용을 변경한다면 문제가 발생할 수 있다. 포인터가 아무것도 가리키고 있지 않을 때는 NULL(0)로 설정하는 것이 바람직하다. NULL은 stdio.h에 정수 0으로 정의되어 있다. 
int *p = NULL;
  • 포인터 자료형과 변수의 자료형은 일치하여야 한다 : 포인터는 메모리의 어디든지 가리킬 수 있지만 포인터에 의하여 참조되는 객체가 얼마만큼의 크기이고 무엇이 어떤 형식으로 저장되어 있는지를 결정하는 것은 포인터의 자료형이다. 

 

배열과 포인터

배열 이름이 포인터라면 포인터도 배열 이름처럼 간주되고 배열과 똑같이 사용할 수 있다. 

#include <stdio.h>

int main(void)
{
  int a[] = { 10, 20, 30, 40, 50 };
  int *p;
  
  p = a;
  printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);   // a[0]=10 a[1]=20 a[2]=30
  printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0], p[1], p[2]);   // p[0]=10 p[1]=20 p[2]=30
  
  return 0;
 }

어떤 경우에 포인터를 배열처럼 사용할까? 만약 대용량의 데이터가 필요한 함수가 있다고 했을 때 모든 데이터를 복사해서 전달하면 시간이 너무 많이 걸릴 것이다. 이런 경우에 데이터가 있는 위치만 전달하여 필요한 부분만 사용할 수 있도록 한다. 배열을 전달받을 때 매개변수를 포인터 변수로 해주면 배열이 복사되지 않고 배열의 주소가 전달된다.

#include <stdio.h>

void sub(int *ptr)
{
  printf("%d \n", ptr[10]);   // 11이 출력된다.
}

int main(void)
{
  int large_data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
  sub(large_data);
  
  return 0;
}

 

 

** 포인터를 사용하는 가장 큰 이유는 넘겨야 할 값이 방대할 경우 주소 값만 알려주는 것이 더 간결하기 때문이다. 데이터를 전부 복사해서 함수로 넘기는 것보다 데이터가 있는 위치를 포인터로 알려주는 것이 더 효율적이다. 

 

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

[C/L] 문자열  (0) 2022.12.11
[C/L] 수식과 연산자  (0) 2022.07.20
[C/L] 변수와 자료형  (0) 2022.07.09
[C/L] C언어 기초  (4) 2022.07.03
Comments