목차
1. 포인터란?
2. 왜 포인터를 사용하나?
1. 다른 지역(함수) 지역 변수를 제어할 때
2. 문자열 저장(참조용)
3. 지역 변수로 만든 배열을 다른 함수에 전달할 때
4. 동적 메모리 영역(Heap) 사용
포인터는 다른 언어엔 없는 문법이다.
그럼 왜 배울까? 다른 언어엔 다른 이름으로 있기도 하고
향후 java를 배울 때 중요하기 때문이다.(레퍼런스라고 불리며 문법적으로 같음)
여담으로
c언어를 공부하는 사람들 중 많은 사람들이 이 포인터 때문에 포기를 한다고 한다.....
근데 사실 어려운 게 아니라 다른 느낌의 문법이기 때문에 헤매는 것이라 한다.
anyway... 오늘은 이 포인터를 다 마스터할 생각으로 공부할 필요는 없다.
느낌을 알고 그 느낌을 나중에 java 참조 변수 때 이어갈 수 있게 하자.
1. 포인터란?
포인터(Pointer) - Data가 있는 곳의 위치(주소)를 통해 제어하는 문법
포인터 변수 : 주소값을 가진 변수 - 8byte
포인터 상수 : 주소값을 가진 상수 - 배열이름(배열의 시작 위치)
- 일반 변수
int a = 10; 를 선언하면
메모리에 Data를 저장한다.
- 포인터 변수
반면, 포인터 변수를 선언하면
( int* p; )
메모리에 p라는 위치에 주소값을 저장한다.
포인터 변수는 8byte를 차지한다.
p는 a의 주소값을 가지고 있는데
역참조서식인 *을 이용하면
a의 값을 불러올 수 있다.
(*p)
모든 자료형에서 포인터 변수는
사용가능하다.
scanf를 사용해서 값을 넣을 수도 있다.
다만 대입연산자를 사용하거나, scanf를 사용할 때
역참조한 것을 넣어야하는지, 포인터 변수를 넣어야 하는지
잘 생각해야 한다.!!!
다시 이런 코딩을 했다고 가정해 보자.
이를 그림으로 표현하면
이렇게 되는 것이다.
int형 num라는 변수에 10의 값을 넣었고
pointer 변수 p1에 num의 주소를 넣었다.
그리고 포인터 변수 p2에 p1에 들어있는 주소값을 넣었다.
즉, num의 주소를 넣은 상황이다.
이런 상황에선
num 값을 바꾸면
포인터 변수로 출력하는 값 역시 변하게 된다.
그림으로 설명하자면
이렇게 되는 것이다.
n1의 주소 값을 참조하지 않고
n2의 주소 값을 참조하게 바꾼 것이다.
2. 왜 포인터를 쓸까?
크게 4가지 이유가 있다.
1. 다른 지역(함수) 지역 변수를 제어할 때
2. 문자열 저장(참조용)
3. 지역 변수로 만든 배열을 다른 함수에 전달할 때
4. 동적 메모리 영역(Heap) 사용
1. 다른 지역(함수) 지역 변수를 제어할 때 - call by address, Reference
우선 함수 기능을 이용할 때 만약 포인터를 쓰지 않으면
return을 사용하고,
그 값을 받을 변수에 대입을 해주는 등의 작업을 해야 한다.
하지만 포인터변수를 사용하면
이렇게 사용할 수 있다.
*p++ 을 하게 되며 우선순위 때문에
*이 먼저 인식되고 p++을 하게 된다.
그럼 p가 가진 주소가 아닌
p +1이 가진 주소로 찾아가게 하게 돼버린 것이다.
이런 경우 ()를 사용하면 해결이 된다.
(*p)++;
2. 문자열 저장(참조용)
문자열을 저장하는 방법은
우리가 어제 한 배열을 통한 방법이 있다.
그러나 포인터 역시
문자열을 저장할 수 있다.
char형 포인터 변수를 만들고
그 안에 문자열을 넣으면
그 문자열 상수의 시작 주소 값을
포인터 변수에 넣어서
저장하는 방식이다.
그럼 이렇게 사용하는데 장점이 있을까?
1. 문자열의 제한이 없다.(100글자든 1000글자든 초기에 크기를 설정할 필요 없이 사용 가능)
2. 배열로 만든 문자열은 바꾸려면 [0] 번부터 바꿔줘야 해서 귀찮은데
포인터 변수를 사용하면 int형 변수처럼 쉽게 변경이 가능하다.
그러나 단점도 있다.
배열로 하였을 땐 scanf가 잘 작동한다.
하지만 포인터로 쓸 땐 작동이 되지 않는다.
(p는 주소값이기 때문이고, 현재는 쓰레기값이 들어가 있다.)
3. 지역 변수로 만든 배열을 다른 함수에 전달할 때
주소의 더하기는 숫자의 더하기와 다르다.
주소의 더하기
즉, +1 은 그 옆집으로 이동하는 것이다.
만약 int a = 10;이라서 a가 #100에 있다고 가정하면
a의 옆집은 #104이다.(int형이 4바이트이므로)
그래서 int* p = &a; (p가 가진 주소 값은 #100)라고 할 때
p+1 은 #104가 되는 것이다.
보는 것과 같이 output 함수에서
포인터 변수를 배열처럼 사용해서 출력할 수 있다.
(int p[ ])도 사실 주소를 받은 포인트 변수지만
표기법만 허용한 것이다.
4. 동적 메모리 영역(Heap) 사용
들어가기 앞서서 RAM 구조를 다시 알아보자.
RAM의 구조
Code - 상수, 코드, 함수 |
Data - 전역, 정적(static) |
Stack - 지역, 매개변수 |
Heap - 동적 메모리 영역(사용자 정의 메모리) |
Heap (동적 메모리 영역)에서 메모리는 할당(생성) 받거나 해제(소멸)할 수 있다.
할당(생성) : malloc();
해제(소멸) : free();
Heap 메모리를 쓰는 이유는
그럼 한번 코드를 작성해 보자.
우선 지금까지 우리는
#include <stdio.h>만 써왔다.
하지만 이제부턴 malloc()이 포함된
<malloc.h>와 <stdlib.h>를 사용해야 한다.
(사실, 둘 중 하나만 써도 상관없음)
<malloc.h> memory allocate를 말하는 것이고, malloc과 free만 사용이 가능한 것이며
<stdlib.h>는 standard library를 뜻하는 것으로 malloc, free 이외에도 나머지 기능을 포함하고 있다.
다시 돌아와서
main 함수 안에
malloc();을 쓸 때
() 안에는 내가 원하는 byte 수를 써야 한다.
그러나!! malloc(4);를 해서 사용하려 하니
사용이 불가하다... 부를 수 있는 명칭이 없기 때문이다..
heap 메모리는 이 공간에 이름을 붙일 수 없다. (int a;처럼 a를 줄 수 없음.)
대신
malloc(4)으로 할당한 메모리의 주소를
가져와서 거기에 넣어야 한다.
그리고 heap 메모리 사용 후에는
위의 사진처럼 free(4);를 하여 동적 메모리 해제를 해줘야 한다.
왜 필요한지? 이런 부분은
우리가 c언어 개발자가 아니기도 하고
너무 길어지니 생략하자...
넘어가서
포인터를 배열로 사용할 수 있다.
즉 ps라는 포인트 배열에
각각 aaa, bbb, ccc라는 배열의 시작 주소가 저장이 되어 있는 것이다.
이 사진과 위의 설명은 같은 말이다.
그리고 포인터 변수를 선언하고
그냥 내버려두지 않는다.
그냥 내버려두면 쓰레기 값이 들어갈 테고,
운이 나쁘면 이 쓰레기 값이 어떤 변수의 주소일 수도 있다.
그래서 NULL;을 넣어준다.
아무것도 가리키지 않는다.라는 뜻이기도 하고
초기 값으로 넣어두는 값이기도 하다.
'# 개발 > C언어' 카테고리의 다른 글
c언어 수행 능력 확인 문제 (1) | 2023.01.13 |
---|---|
c언어 #9 - 구조체 (국비11일차) (0) | 2023.01.13 |
c언어 연습문제 #8 (국비9일차) (0) | 2023.01.11 |
c언어 #7 - 배열, 문자열 (국비9일차) (0) | 2023.01.11 |
c언어 연습문제 #7 (국비8일차) (0) | 2023.01.10 |