일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- BFS
- pintos
- 핀토스
- C
- 이벤트 함수 실행 순서
- 4기
- User Stack
- 추상클래스와인터페이스
- 알고리즘수업-너비우선탐색2
- 백준
- anonymous page
- 다익스트라
- 알고리즘
- 네트워크
- 오블완
- KRAFTON JUNGLE
- Unity
- 파이썬
- 연결리스트
- c#
- TiL
- 유니티
- 크래프톤정글4기
- 전쟁-전투
- project3
- 크래프톤정글
- 크래프톤 정글 4기
- 크래프톤 정글
- 티스토리챌린지
- kraftonjungle
- Today
- Total
말감로그
[C언어] 포인터 본문
포인터(Pointer)란 데이터가 저장된 메모리의 주소값을 저장하는 변수이며, 포인터 변수라고도 한다.
-> 메모리의 주소, 즉 '어디'인지(=위치 정보)를 저장하는 전용 변수
int n = 100; // 변수의 선언
int *ptr = &n; // 포인터의 선언
-> int형 ptr 포인터는 n의 주소를 가리킨다.
주소 연산자(&) - 해당 변수의 주소값을 반환한다.
참조 연산자(*) - 포인터에 가리키는 주소에 저장된 값을 반환한다.
포인터의 연산
포인터는 값을 증가시키거나 감소시키는 등의 제한된 연산만을 할 수 있다.
포인터가 가리키고 있는 주소는 각각의 포인터 타입에 따라 달라진다.
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);
(%#x 는 0x를 붙이라는 뜻, %x, %p - 16진수로 표현)
실행 결과
포인터 ptr_char가 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_int가 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_double이 현재 가리키고 있는 주소값은 0입니다.
포인터 ptr_char가 1 증가 후에 가리키고 있는 주소값은 0x1입니다.
포인터 ptr_int가 1 증가 후에 가리키고 있는 주소값은 0x4입니다.
포인터 ptr_double이 1 증가 후에 가리키고 있는 주소값은 0x8입니다.
인수 전달 방법
1. 값에 의한 전달(call by value)
2. 참조에 의한 전달(call by reference)
값에 의한 전달(call by value)
- 인수로 전달되는 변수가 가지고 있는 값을 함수 내의 매개변수에 복사하는 방식이다.
- 이렇게 복사된 값으로 초기화된 매개변수는 인수로 전달된 변수와는 완전히 별개의 변수가 된다.
-> 함수 내에서의 매개변수 조작은 인수로 전달되는 변수에 아무런 영향을 미치지 않는다.
#include <stdio.h>
void local(int num)
{
num += 10;
}
int main(void)
{
int var = 10;
printf("변수 var의 초깃값은 %d입니다.\n", var);
local(var);
printf("local() 함수 호출 후 변수 var의 값은 %d입니다.\n", var);
return 0;
}
실행 결과
변수 var의 초기값은 10입니다.
local() 함수 호출 후 변수 var의 값은 10입니다.
-> 함수 내에서 매개변수 num의 값을 아무리 변경하더라도 원래 인수로 전달된 변수 var 값은 절대 변경되지 않는다.
참조에 의한 전달(call by reference)
- 해당 변수의 주소값을 전달한다.
- 함수의 매개변수에 인수로 전달된 변수의 원래 주소값을 저장하는 것이다.
-> 인수로 전달된 변수의 값을 함수 내에서 변경할 수 있게 된다.
#include <stdio.h>
void local(int *num)
{
*num += 10;
}
int main(void)
{
int var = 10;
printf("변수 var의 초깃값은 %d입니다.\n", var);
local(&var);
printf("local() 함수 호출 후 변수 var의 값은 %d입니다.\n", var);
return 0;
}
실행 결과
변수 var의 초기값은 10입니다.
local() 함수 호출 후 변수 var의 값은 20입니다.
num은 인수로 변수 var의 주소값을 전달 받는다. 따라서 함수 내에서 매개변수 num의 값을 변경하면, 원래 인수인 변수 var의 값도 같이 변경된다.
이중 포인터
- 포인터 변수를 가리키는 포인터
void 포인터
- 데이터의 타입을 명시하지 않은 포인터
- void 포인터는 주소값을 저장하는 것 이외에는 아무것도 할 수 없다.
- 반드시 먼저 사용하고자 하는 타입으로 명시적 타입 변환 작업을 거친 후에 사용해야 한다.
int num = 10; // 변수 선언
void* ptr_num = # // void 포인터 선언
printf("변수 num가 저장하고 있는 값은 %d입니다.\n", num);
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
*(int*)ptr_num = 20; // void 포인터를 통한 메모리 접근
printf("void 포인터 ptr_num가 가리키는 주소에 저장된 값은 %d입니다.\n", *(int*)ptr_num);
실행 결과
변수 num가 저장하고 있는 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 10입니다.
void 포인터 ptr_num가 가리키는 주소에 저장된 값은 20입니다.
-> 사용할 때마다 명시적 타입 변환을 하고 난 뒤에 사용해야 한다.
함수 포인터
- 프로그램에서 정의된 함수는 프로그램이 실행될 때 모두 메인 메모리에 올라가게 된다.
- 이때 함수의 이름은 메모리에 올라간 함수의 시작 주소를 가리키는 포인터 상수가 된다.
- 이렇게 함수의 시작 주소를 가리키는 포인터 상수를 함수 포인터라고 한다.
- 함수 포인터의 포인터 타입은 함수의 반환값과 매개변수에 의해 결정된다.
-> 함수의 원형을 알아야만 해당 함수에 맞는 함수 포인터를 만들 수 있다.
포인터를 사용하면 특정 데이터를 직접 조작하거나 다른 데이터로 전환할 수 있다.
게임 치트 엔진은 가동 중인 게임의 메모리에 직접 접근해서 16진수 형태로 이루어진 게임 내 변수값을 조작할 수 있는 도구이다.
꽤 많은 게임에서 프로그램 안에 있는 변수, 즉 메모리 주소는 프로그램을 껐다 켤 때마다 계속 바뀌게 되는데(동적 할당), 그런 게임을 재부팅할 때마다 어떤 값을 바꾸기 위한 메모리 주소를 매번 새로 찾아야 하니 번거롭다.
하지만 포인터를 찾는다면, 포인터가 그 메모리의 위치를 가리키기 때문에 매번 바뀌는 메모리 주소값을 다시 구하지 않아도 값을 수정할 수 있다.
매니지드 언어(Managed Language)
- 대표적인 언어로 C#, Java로 프로그래머가 별도로 조작하지 않아도 언어 자체적으로 메모리를 관리해주는 언어
언매니지드 언어(UnManaged Language)
- 대표적인 언어로 C,C++로 프로그래머가 주도적으로 메모리를 할당하고 해제하기 위해 malloc()이나 free()와 같은 메모리 제어 기능을 제공하는 언어
포인터가 어려운 이유?
1. 메모리에 직접 접근하는 위험성
- 포인터를 잘못 사용하면 시스템에 치명적인 버그를 일으킬 수 있다.
2. 까다로운 메모리 관리
- 메모리 누수 같은 복잡한 메모리 관리 문제 직접 관리해야 한다.
3. 복잡한 문법, 어려운 디버깅
포인터를 이해하면 뭐가 좋은가요?
1. 언어간 상호 운용성
- 다양한 언어 간의 전환이나 운용성을 이해하는 데 도움이 된다.
2. 알고리즘 및 자료구조 이해
- 일부 알고리즘 및 자료구조는 포인터를 사용, 포인터를 이해하면 더 효율적으로 프로그램 작성할 수 있다,
3. 보안 및 취약점 이해
- 소프트웨어 보완을 강화하는데 도움이 된다.
4. 메모리 관리에 대한 이해
- 컴퓨터의 동작 방식에 대해 더 깊은 이해를 얻을 수 있다.
5. 디버깅 능력 강화
- 메모리 관련 오류나 메모리 누수 등의 문제를 식별하는 데 도움이 된다.
6. 더 넓은 취업 기회
포인터 한 줄 정리 짤
참고
https://www.inflearn.com/pages/dc-pointer-202310
'언어 > C' 카테고리의 다른 글
[C언어] 연결리스트(Linked List) (0) | 2024.02.10 |
---|