C언어 - 포인터

Jocy·2022년 5월 2일
0
post-thumbnail

포인터란?

변수의 주소값을 저장하는 것을 말하고 포인터 변수라고 부르기도 합니다.
포인터 변수를 선언할 때는 담고자 하는 자료형에 *(참조연산자)를 붙여서 선언 합니다.

포인터 변수의 크기는 모두 동일합니다. 동일한 운영체제에서는 동일한 크기를 갖습니다.
32비트 시스템이면 4바이트, 64비트 시스템이면 8바이트 크기로 갖습니다.
자료형에 따라 선언하는 자료형이 달라지는 이유는 가리킬 주소가 어떤 자료형을 갖는지 알려주기 위해서 입니다.

포인터 연산을 할 때에 그 주소로 찾아가 int형이면 4바이트만큼, double형이면 8바이트씩 읽어들여야 합니다. 가리키는 변수에 맞춰 포인터 변수도 자료형을 맞춰준다고 생각하면 됩니다.

포인터 사용시 유의사항

int, double 같은 경우는 정수형이기 때문에 원하는 숫자를 초기화 시켜줄 수 있었습니다.
하지만 포인터는 주소값을 담는 변수이기 때문에 숫자가 아닌 NULL(0)로만 가능합니다.
NULL로 초기화 하지 않고 그냥 선언한 후에 주소값을 넣어도 괜찮습니다.
포인터를 초기화할 때 쓰는 0은 0번지를 가리키는 것이 아니라 아무것도 없다는 뜻 NULL(0) 입니다.

참조 연산자 *

*두개의 피연산자가 있을 때 곱셈연산자 이고 하나의 피연산자가 있을 때는 참조 연산자 입니다.
참조 연산자는 포인터의 이름이나 주소 앞에 사용하며, 포인터가 가리키는 주소에 저장된 값을 반환합니다.

#include <stdio.h>

int main()
{
	int *p = NULL; 
	int num = 15;

	p = &num;
	printf("포인터 p가 가리키는 값 : %d\n", *p); // 15
	printf("num의 값 : %d\n\n", num); // 15

	*p += 5;
	printf("포인터 p가 가리키는 값 : %d\n", *p); // 20
	printf("num 값 : %d\n\n", num); // 20

	(*p)++;
	printf("포인터 p가 가리키는 값 : %d\n", *p); // 21
	printf("num 값 : %d\n\n", num); // 21

	*p++;
	printf("포인터 p가 가리키는 값 : %d\n", *p); // 231351231 (쓰레기값)
	printf("num 값 : %d\n", num); // 21

	return 0;
}

(*p)++*p++는 같은 증감 연산자이지만 값이 다르게 나옵니다.
증감연산자(++)가 참조연산자(*) 보다 우선순위가 높습니다.
(*p)++는 일반 수학과 같이 소괄호의 내용(주소 참조)을 먼저 실행하고 증감연산자를 처리해서 num이 증가했다고 볼 수 있습니다.
*p++ 는 변수 p의 주소값을 증가시키고 포인터 변수에 들어있는 주소값이 증가합니다.
증가한 주소에는 아무것도 선언되어 있지 않기 때문에 쓰레기값이 나오게 됩니다.

포인터는 함수를 사용할 때 진가를 발휘합니다.
원래 변수는 함수에서 수정할 수 없다라고 했는데 포인터를 사용하면 메모리의 주소를 넘겨주면서 함수에서도 메모리에 직접적으로 참조할 수 있기 때문에 변수의 값을 바로 수정하는 것이 가능합니다.

포인터를 함수에 사용하는 방법

#include <stdio.h>

void numPlus(int num)
{
	num += 5;	
}

void pointerPlus(int *num)
{
	*num += 5;
}

int main()
{
	int num = 15;
	printf("num 값 : %d\n", num); // 15

	numPlus(num);
	printf("numPlus 사용 후 : %d\n", num); // 15

	pointerPlus(&num);
	printf("pointerPlus 사용 후 : %d\n", num); // 20

	return 0;
}

Call by value & Call by reference

인자를 전달하는 방식에는 크게 2가지 Call by value & Call by reference 가 있습니다.

Call by value

함수에서 값을 복사해서 전달하는 방식으로, 인자로 전달되는 변수를 함수의 매개변수에 복사 합니다.
복사되면 인자로 전달한 변수와는 별개의 변수가 되며, 매개변수를 변경해도 원래의 변수에는 영향을 미치지 않습니다. 원본 값을 바꿀 필요가 없는 경우에는 Call by value를 사용합니다.

#include <stdio.h>

void swap(int a, int b)
{
	int temp;
	
	temp = a;
	a = b;
	b = temp;
}

int main()
{
	int a, b;
	
	a = 10;
	b = 20;
	
	printf("swap 전 : %d %d\n", a, b); // swap 전 : 10 20
	
	swap(a, b);
	
	printf("swap 후 : %d %d\n", a, b); // swap 후 : 10 20
	
	return 0;
}

Call by reference

함수에 값 대신 주소를 전달하는 방식 Call by reference 라고 합니다.
C언어는 엄밀히 말하면 주소값을 복사해서 넘겨주는 것이라
Call by value, Call by address 방식이라고 합니다.
C언어는 공식적으로는 Call by reference를 공식적으로 지원하지 않습니다.
의미적, 결과적으로 Call by address를 이용해서 Call by reference와 같이 사용할 수 있습니다.

#include <stdio.h>

void swap(int *a, int *b)
{
	int temp;

	temp = *a;
	*a = *b;
	*b = temp;
}

int main()
{
	int a, b;

	a = 10;
	b = 20;

	printf("swap 전 : %d %d\n", a, b);  // swap 전 : 10 20

	swap(&a, &b);

	printf("swap 후 : %d %d\n", a, b); // swap 후 : 20 10

	return 0;
}

포인터 연산과 배열

배열의 이름은 포인터 변수와 같은 기능을 하며, 첫번째 요소의 주소값을 나타냅니다.

포인터 연산

#include <stdio.h>

int main()
{
	int arr[5] = {10, 20, 30, 40, 50};
	double arr2[5] = {10.1, 20.2, 30.3, 40.4, 50.5};
	int *arrPtr = arr;
	double *arrPtr2 = arr2;

	printf("포인터 주소 : %d %d\n", arrPtr++, arrPtr2++); // 포인터 주소 : 207556368 207556400
	printf("증가 연산 후 : %d %d\n", arrPtr, arrPtr2); // 증가 연산 후 : 207556372(+4) 207556408(+8)
	printf("변수 값 : %d %.2f\n", *arrPtr, *arrPtr2); // 변수 값 : 20 20.20

	arrPtr += 2;
	arrPtr2 += 2;

	printf("증가 연산 후 : %d %d\n", arrPtr, arrPtr2); // 증가 연산 후 : 207556380(+8) 207556424(+16)
	printf("변수 값 : %d %.2f\n", *arrPtr, *arrPtr2); 변수 값 : 40 40.40

	return 0;
}

포인터 변수가 n만큼 더하거나 뺄 떄 자료형의 크기 x n 만큼 증가, 감소한다는 것입니다.
arr(배열의 첫번째 요소의 주소)+(i x 자료형의 크기)만큼 더해지면 배열 i와 같아집니다.
*(arr+i) == arr[i] 입니다.

profile
Software Engineer

0개의 댓글