본문 바로가기

etc

[C언어] 포인터에 대해 알아보자

반응형

1. 포인터의 기본

포인터란

포인터

  • 일반 변수의 메모리 내 주소번지를 갖는 자료형
  • 포인터를 사용하면 변수명을 통하지 않고 사용하고자 하는 대상에 직접 접근을 할 수 있어 프로그램을 간결하고 효율적으로 제어할 있음

포인터의 자료형

      • 주소 연산자 (&) - 일반 변수명에 사용. 해당 변수의 메모리 주소를 가리킴
      • 간접 연산자 (*) - 포인터 변수명에 사용. 주소가 저장되어 있는 메모리 위치를 가리킴

 

1. 포인터 선언

1) 선언하는 방법

자료형 * 변수명;

int * ptr;
      • *연산자가 1개면 1차원 포인터
      • 1차원 포인터는 일반변수의 주소를 값으로 가짐
int i = 3;
int * ptr = &i;

여기서 변수 ptr 1차원 포인터로, 일반 변수인 i 주소값을 가리킨다. 그리고 int * 변수 ptr 정수형으로 선언된 변수의 주소가 들어올 있음을 의미한다.

2) 포인터 변수의 크기 = 4바이트

포인터 변수 ptr 자체의 크기는 4바이트로 char* 선언되든 double* 선언되든 상관없다. 다음 포인터 변수 3개는 크기가 모두 4바이트이다.

char* ptr1;
int* ptr2;
double* ptr3;

3) * 위치: Anywhere

* 선언하려는 자료형과 포인터 변수명 사이에만 위치하면 된다.

int* ptr1;
int * ptr2;
int *ptr3;

 

2. 포인터를 이용한 일반 변수로의 접근

int a = 10; 처럼 변수 a 변수명으로 직접 접근할 수도 있지만, 포인터를 이용할 수도 있다.

int a = 10;
int *ptr = &i;


printf("ptr의 주소값: %p\n", &ptr);
printf("i의 주소값: %p\n", &i);
printf("i의 주소값: %p\n\n", ptr);


printf("i의 값: %d\n", i);
printf("i의 값: %d\n, *ptr);
      • ptr은 변수 i의 주소값이다 (= &i)
      • *ptr은 포인터가 가리키는 주소에 저장된 값이다 (= i)
      • 포인터로 선언한 변수(=ptr)에 * 연산자를 붙이면, 포인터가 가리키는 주소에 저장된 값 의미한다. 따라서 위에서 *ptr 값은 10(= i)이다.

포인터를 이용하여 변수값 변경하기

int i = 3;
int *ptr = &i;
*ptr = i+2;   //포인터를 통해 i값을 바꿈

위와 같이 포인터를 이용하여 포인터가 가리키는 곳의 값을 바꾸고 연산도 있다.

ex)

int i = 10;
int *ptr = &i;

*ptr = i+20;
printf("i값 : %d\n", i);     // ➡️ i값: 30

i = i+20;
printf("i값 : %d\n", *ptr);   // ➡️ i값: 50

따라서, int b = a; 달리, 포인터를 이용하면 i와 ptr값이 서로 연결 된다.

포인터의 유용성

위처럼 포인터를 이용하여 변수값 변경하기를 함수에 이용하자.

원래 함수를 사용하면 함수 속에서 사용한 지역변수는 함수 밖에서 결과값이 반영되지 않는다. 왜냐하면 말그대로 지역변수이기 때문에 함수 안에서만 사용하는 값이기 때문이다. 물론 return 값을 사용하여 밖으로 내보낼 수는 있지만, return 사용하지 않고도 포인터를 이용하면 함수속에서도 변수의 값을 바로 변경할 있게 된다.

포인터를 함수의 인자로 사용하면 주소값을 전달할 있으므로 변수 자체가 함수에 전달된다.

(ex)

Swap(&x, &y);
…
void Swap(int* a, int* b) {
   int temp;
   
   temp = *a;
   *a = *b;
   *b = temp;
}

위와 같은 함수를 작성하게 되면, 수를 바꾸기 위해 x와 y의 주소를 Swap()함수에 넘겨주면 Swap()함수에서 처리된 결과가 x y 값에 반영된다.

 

 

2. 인자 전달 방법

인자(매개변수) 전달 방법?

: 함수에 사용될 데이터를 보내는 방법

 

1. 인자를 값으로 전달(=Call by value)

   C언어의 가장 대표적인 인자 전달 방식.

   함수가 호출되면 인자값을 스택(Stack)이라는 기억장소에 복사한다. 값을 복사하기 때문에 호출된 함수에서 인자값을 바꾸더라도 main()함수는 전혀 영향 받지 않으므로 두 함수는 독립적이며 안전하다.

 

2. 인자를 주소로 전달(=Call by reference)

  • 전달하려는 변수의 주소를 함수에 전달하는 방식.
  • 포인터를 이용한 일반 변수로의 접근을 함수에 응용한 방법

함수에 변수 자체를 전달해야 할 때 사용하며, C언어에서는 포인터를 사용한다. 변수의 주소값을 주소 연산자(&)를 이용해 함수에 넘겨준다. 그 주소값을 받는 함수에서는 간접 연산자(*)를 이용해 이 주소값을 가리키는 값을 읽고 저장한다. 앞에서 다루었던 Swap()함수 예제가 여기에 해당한다.ㅜ주소를 이용해서 인자를 전달하면 여러 가지로 유용하다.

(ex)

int main(void) {
   …
   CountIncrement(&a);
}

void CountIncrement(int* n) {
   (*n)++;
}

여기서, *n++; 입력하게 되면 증가연산자가  우선순위가 높으므로 *(n+1)값을 출력하게 된다(n 다음 주소값이 가지고 있는 변수값을 출력하게 ). (*n)++; 입력하게 되면, 우선순위가 높은 ()괄호 속을 먼저 진행하게 되므로, n 주소값에 들어있는 변수값을 +1 값을 출력하게 된다.

 

 

3. 포인터와 배열

1. 포인터와 배열의 관계

배열명 : 해당 데이터가 있는 시작 주소를 의미하는 포인터 상수.

  • 상수이므로, 배열명의 값을 바꾸면 안된다. ,  배열명에 증감연산자(++,--) 등을 이용해 배열명의 값을 바꿀  없다.
  • ex) 배열명이 a라면, a = a+1처럼 배열명으로 연산할  없다.
  • 하지만 포인터는 변수이므로 포인터에 증감연산자 등을 이용해 값을 바꿔도 된다.

,  배열의 첫 번째 원소가 저장된 주소를 의미한다. 따라서

int a[4] = [10, 20, 30, 40];
int *p = &a[0]

 ,  p = a와 p = &a[0]은 동일한 번지 가리킨다.

배열 a 주소를 표현하는 방법 3가지

구분 1 2 3
방법1(배열첨자) &a[0] &a[1] &a[2]
방법2(배열명) a a+1 a+2
방법3(포인터) p p+1 p+2
  • 배열명: a = 배열의 시작 주소, a+1 = 배열 a  번쨰 주소
  • 따라서, +1 산술적인 의미의 숫자가 아닌 그 다음 주소를 의미한다.
  • 포인터 변수 p 같은 방법으로 배열의 주소를 표시할  있다.

배열 a 값을 포현하는 방법 4가지

구분 1 2 3 4
방법1 a[0] a[1] a[2] a[3]
방법2 *a *(a+1) *(a+2) *(a+3)
방법3 *p *(p+1) *(p+2) *(p+3)
방법4 p[0] p[1] p[2] p[3]
int *p = a;
*a = *p = a[0] = p[0]

a = 배열의 시작 주소, *a = 배열 a 시작 주소의 .   번째 

 

4. 포인터와 문자열

포인터를 이용한 문자열 표현

선언 방법

char *포인터명 = "문자열";

(ex)

char *p = "MOON";
p = "M"문자의 주소값(배열의 첫 번째 주소값)
p+1 = "O"문자의 주소값
p+2 = "O"문자의 주소값
p+3 = "N"문자의 주소값
p+4 = \0 널문자 : 문자의 마지막을 나타냄

 

출력방법 2가지

예제1

char *pC = "C programming";

printf("%s\n", pC); => 문자열을 변환기호 이용해서 출력

while(*pC)

printf("%c", *pC++);  => 반복문을 이용해  글자씩 출력

*pC = pC의 첫 번째 주소값에 들어있는 값
*(pC+1) = pC  번째 주소값의 다음 주소값에 들어있는 
*pC++는 문자 1개를 출력한 후 다음 위치로 이동하기 때문에 다음 문자를 출력하게 되는 것임

 

예제2

char *pStr[3] = {"english", "math", "korean"}

여기서 pStr[0] = "e" 주소값, pStr[1] = "m" 주소값, pStr[2] = "k" 주소값

따라서 이와 같이 [0], [1], [2], 각 문자열의 시작 주소를 가리킨다.

 

char *pStr[] = {"english", "math", "korean"}

위와 같이 배열의 크기를 초기에 지정하지 않아도 된다. 미리 지정하게 되면 불필요한 메모리의 낭비를 막을 수 있다.

반응형