1. 포인터

💡 자료형 *변수명

리마인드

  • * : 주소로 가서 값을 가져와라
  • & : 주소가 뭔지 불러와라
  • *& : 서로 상쇄 (아무것도 없는 거라고 생각)

 

포인터의 특징

  • 기본 설명
    •  
    • void main() { int* p = NULL; int num = 15; p = # printf("포인터 p의 값 : %d \\n", p); // 93929524 printf("int 변수 num의 주소 : %d \\n", &num); // 93929524 printf("%d\\n", num); // 15 printf("%d\\n", *p); // 15 : *p = num의 주소 }
  • 변수/클래스/스트럭쳐의 주소값을 저장하는 변수
  • 그대로 불러오면 용량이 크므로 링크 따는 것, 포인터로 원본을 변경할 수 있음 (연산 가능)
  • 포인터 변수의 크기는 모두 동일하지만, 자료형을 선언하는 이유는 가리킬 주소가 어떤 자료형을 갖는지 알려주기 위함 (가리키는 변수에 맞춰 포인터 변수도 자료형을 맞추면 됨)
  • 포인터는 무조건 NULL(0)으로 초기화, 아니면 바로 주소값 넣기
  • 연산자의 우선순위 : 참조 연산자(*)가 증감연산자(++, --)보다 순위가 높음
    • void main()
      {
      	int *p = NULL; 
      	int num = 15;
      	p = #
      
      	printf("포인터 p가 가리키는 값 : %d\\n", *p);      // 15
      	printf("num의 값 : %d\\n\\n", num);               // 15
      
      	*p += 5;
      	printf("포인터 p가 가리키는 값 : %d\\n", *p);      // 20 : *p(num) + 5
      	printf("num 값 : %d\\n\\n", num);                 // 20 : 위 값이 num에 들어감
      
      	(*p)++;
      	printf("포인터 p가 가리키는 값 : %d\\n", *p);      // 21 : *p(num) + 1
      	printf("num 값 : %d\\n\\n", num);                 // 21 : 위 값이 num에 들어감
      
      	*p++;
      	printf("포인터 p가 가리키는 값 : %d\\n", *p);      // -13545343 : 주소를 찾아가지 않고 주소값이 들어있는 변수 p를 증가, 쓰레기값 출력
      	printf("num 값 : %d\\n", num);                   // 21
      }
      
  • 함수에서는 인자를 전달할 때 복사해서 사용하므로 전달해주는 원래 변수는 함수에서 수정 불가(call by value 어쩌구 개념 등장), 이때 포인터로 메모리의 주소를 넘겨주면 변수 값을 바로 수정 가능
    • void pointerPlus(int *num)
      {	*num += 5; }
      
      void numPlus(int num)
      {	num += 5;	}
      
      void main()
      {
      	int num = 15;
      	printf("num 값 : %d\\n", num);
      
      	numPlus(num);
      	printf("numPlus 사용 후 : %d\\n", num);      // 15
      
      	pointerPlus(&num);
      	printf("pointerPlus 사용 후 : %d\\n", num);  // 20
      }
      

 

상수 포인터

  • 포인터가 가리키는 변수를 상수화 (포인터를 통해 값 변경 불가)
    • void main()
      {
      	int num = 10;
      	const int* ptr = #
      }
      
  • 포인터를 상수화 (주소값 변경 불가)
    • void main()
      {
      	int num = 10;
      	int *const ptr = #
      }
      
  • 둘 다 상수화
    • void main()
      {
      	int num = 10;
      	const int *const ptr = #
      }
      

 

 

 

2. 포인터와 함수


Call by Value

  • C언어에서 지원하는 방식
  • 함수에서 값을 복사해서 전달하는 방식, 인자로 전달되는 변수를 함수의 매개변수에 복사 (인자로 전달한 변수와는 별개의 변수가 되기 때문에 원본 값이 바뀌지 않음, Swap 코드 참고)
  • Swap 코드 예시
    • void swap(int a, int b)
      {
      	int temp;
      	
      	temp = a;
      	a = b;
      	b = temp;
      }
      
      void main()
      {
      	int a, b;
      	
      	a = 10;
      	b = 20;
      	
      	printf("swap 전 : %d %d\\n", a, b);  // 10 20
      	swap(a, b);
      	printf("swap 후 : %d %d\\n", a, b);  // 10 20
      	}
      

Call by Reference

  • C언어에서는 공식적으로 지원하지 않으므로, Call by Address(주소값을 복사해서 넘겨주기)를 이용해야 함
  • 함수에서 값을 전달하는 대신 주소값을 전달하는 방식
  • Swap 코드 예시
    • void swap(**int *a, int *b**)
      {
      	int temp;
      
      	**temp = *a;
      	*a = *b;
      	*b = temp;**
      }
      
      void main()
      {
      	int a, b;
      
      	a = 10;
      	b = 20;
      
      	printf("swap 전 : %d %d\\n", a, b);    // 10 20
      	**swap(&a, &b)**;
      	printf("swap 후 : %d %d\\n", a, b);    // 20 10
      }
      

 

 

3. 포인터와 배열


  • 다시 한번 리마인드
    • * : 주소로 가서 값을 가져와라
    • & : 주소가 뭔지 불러와라
    • *& : 서로 상쇄 (아무것도 없는 거라고 생각)

 

특징

  • 배열의 이름은 주소값 (포인터 변수의 값)
  • 포인터 주소에 증가 연산을 하는 경우, 자료형의 크기 x N만큼 증가함
  • *(arr+i) == arr[i]
    • type형 포인터를 대상으로 n 크기만큼 값을 변경시키면 n x sizeof(type) 크기만큼 값이 증가/감소한다. (int 형일 경우 4씩, double 형일 경우 8...) 그래서 위와 같이 배열일 경우 *(arr+i) == arr[i] 가 성립
  • 특징 총정리
void main()
{
	int arr[5] = { 10, 20, 30, 40, 50 };   // 배열 (이름, 주소) : arr (10, 00000024)
	int* arrPt = arr;			                 // 포인터            : 00000024

	// 배열의 [이름]은 [배열의 첫번째 값]과 동일
	printf("%d\\n", *arrPt);           // 10
	printf("%d\\n", arr[0]);           // 10

	// 배열[i] = 포인터[i]
	printf("%d\\n", arrPt[0]);        // 10
	printf("%d\\n", arrPt[1]);        // 20

	// 포인터(배열주소)에 숫자를 더하면 : (자료형의 크기 x n)
	printf("%d\\n", arrPt);          // 00000024
	printf("%d\\n", arrPt + 1);      // 00000028

	// 배열에 숫자를 더하면 : 배열 주소에 숫자를 더한 것과 동일
	printf("%d\\n", arr);             // 00000024
	printf("%d\\n", arr + 1);         // 00000028

	// 포인터(배열주소)에 숫자 더한 후 값을 가져오기
	printf("%d\\n", *(arr + 1));      // 20 = arr[1]
	printf("%d\\n", *(arr + 2));      // 30 = arr[2]

	// 배열 각 인덱스의 주소값
	printf("%d\\n", &(arr[0]));       // 00000024
	printf("%d\\n", &(arr[1]));       // 00000028

	// 따라서 : ***(arr+i) == arr[i]**
}
  • 함수 인자로 배열 전달할 때 배열의 크기를 sizeof로 전달
    • **void 함수(int *arr, int len)**
      {
      		for (int i=0; i < len; i++)
      			printf("%d\\n", arr[i]);
      }
      
      void main()
      {
      	int arr[10] = {1, 2, 3, 4, 5};
      	**함수(arr, sizeof(arr) / sizeof(int));**
      }
      
  • 연습문제 : 포인터로 버블 정렬 함수 만들기
    • #include <stdio.h>
      
      void bubbleSort(int* arr, int len)
      {
      	int temp;
      	for (int i = 0; i < len-1; i++)
      	{
      		for (int j = 0; j < len-1; j++)
      		{
      			if (*(arr + j) > *(arr + j + 1))
      			{
      				temp = *(arr + j);
      				*(arr + j) = *(arr + j + 1);
      				*(arr + j + 1) = temp;
      			}
      		}
      	}
      }
      
      int main()
      {
      	int arr[10];
      	for (int i = 0; i < 10; i++)
      	{
      		scanf("%d", &arr[i]);
      	}
      
      	bubbleSort(arr, sizeof(arr) / sizeof(int));
      
      	for (int i = 0; i < 10; i++)
      	{
      		printf("%d ", arr[i]);
      	}
      
      	return 0;
      }
      
      // 10 5 8 2 9 1 4 6 11 15
      

 

 

4. 이중 포인터(더블 포인터)

💡 포인터 변수를 가리키는 또다른 포인터 변수

  • 또 다시 한번 리마인드
    • * : 주소로 가서 값을 가져와라
    • & : 주소가 뭔지 불러와라
    • *& : 서로 상쇄 (아무것도 없는 거라고 생각)

 

이중 포인터 선언과 사용

#include <stdio.h>

void main()
{
	int num = 10;
	int* ptr = &num;
	int** dptr = &ptr;

	printf("%d\\n", *dptr);      // ptr의 주소
	printf("%d\\n", *(*dptr));   // ptr이 가리키는 값 (num), = **dptr
}

#include <stdio.h>

int main()
{
	int num = 10;
	int* ptr = &num;
	int** dptr = &ptr;
	int* ptr2;

	printf("%d %d\\n", num, &num);                  // 10, 10000024
	printf("%d %d\\n", ptr, *ptr);                  // 10000024, 10 : num의 주소값, ptr(num)의 값
	printf("%d %d %d\\n", dptr, *dptr, **dptr);     // 10000012 10000024 10 : dptr의 주소값, prt의 주소값, ptr(num)의 값
	
	ptr2 = *dptr;   // ptr2에 ptr에 저장된 값을 넣음 (num의 주소)
	*ptr = 20;      // ptr(num)에 20을 넣음

	printf("%d %d\\n", ptr2, *ptr2);                // 10000024 20 : ptr의 주소값, ptr2(num)의 값
	printf("%d %d\\n", num, **dptr);                // 20 20 : num의 값, ptr(num)의 값

	return 0;
}

 

 

포인터 변수 대상의 Call by Reference

  • 함수를 이용해서 싱글 포인터 변수 값을 변경하는 경우, 같은 싱글 포인터 변수를 사용하면 값 변경하지 못함
    • (ptr1, ptr2 자체의 주소가 넘어가는 게 아니라, 저장된 값(num의 주소)과 같은 주소값을 가진 변수 p1, p2가 새로 생겨나기 때문)
    #include <stdio.h>
    
    void swapPtr(int* p1, int* p2)
    {
    	int* tmp = p1;
    	p1 = p2;
    	p2 = tmp;
    }
    
    void main()
    {
    	int num1 = 10, num2 = 20;
    	int* ptr1, * ptr2;
    	ptr1 = &num1, ptr2 = &num2;
    
    	printf("*ptr1, *ptr2 : %d %d\\n", *ptr1, *ptr2);    // 10 20
    
    	swapPtr(ptr1, ptr2);
    	printf("*ptr1, *ptr2 : %d %d\\n", *ptr1, *ptr2);    // 10 20
    }
    
  • 호출되는 함수의 매개변수를 더블 포인터 변수로 바꾸고, 인자로 싱글 포인터 자체의 주소값을 넘겨야 값이 제대로 변환됨
    • #include <stdio.h>
      
      void swapPtr(int**p1, int**p2)      // 더블 포인터 변수 선언 
      {
      	int* tmp = *p1;
      	*p1 = *p2;
      	*p2 = tmp;
      }
      
      void main()
      {
      	int num1 = 10, num2 = 20;
      	int* ptr1, * ptr2;
      	ptr1 = &num1, ptr2 = &num2;
      
      	printf("*ptr1, *ptr2 : %d %d\\n", *ptr1, *ptr2);    // 10 20
      
      	swapPtr(&ptr1, &ptr2);      // 싱글 포인트 변수의 주소값을 함수의 인자로 사용
      	printf("*ptr1, *ptr2 : %d %d\\n", *ptr1, *ptr2);    // 20 10
      }
      

 

+ Recent posts