13장. 포인터
13-1 운영체제의 메모리 관리 방식
메모리 주소 지정 방식
운영체제는 메모리 주소를 1바이트 단위로 관리한다. 즉, 1바이트 단위로 주소가 매겨져 있다.
그러므로 프로그래머가 메모리를 사용할 때 한 번에 읽거나 저장할 크기를 명시해야 한다.
직접 주소 지정 방식
메모리 그림이 나올 때마다 주소를 적어 직접 그려 보자.
그리고, 메모리에 저장될 값과 메모리 주소를 모두 16진수에 익숙해지고 계산에 익숙해 지도록 16진수로 표기해 보자.
직접 주소 지정 방식은 C 언어의 '변수' 문법과 같다
왜 C언어는 어셈블리어나 기계어처럼 주소를 직접 사용하지 않고 변수라는 개념을 사용할까?
변수를 사용하면 변수 이름만으로 해당 변수 (메모리)의 용도와 대입되는 값이 어떤 의미와 관련된 숫자라는 것을 예상할 수 있어 전체 코드를 더 쉽게 이해할 수 있게 된다.
C 언어에서 직접 주소 지정 방식의 한계
C 언어의 직접 주소 지정 방식은 변수 개념을 사용하기 때문에 문법 구조상 서로 다른 함수에 존재하는 변수를 참조할 수 없다.
간접 주소 지정 방식
명령을 바꾼다 = 기계어를 바꿔야 한다 = 코드를 다시 번역해서 실행 파일을 만들어야 한다.
하지만, 메모리에 있는 주소를 변경 = 단순한 데이터 조작 = 코드를 다시 번역할 필요 없이 프로그램 실행 중에도 변경할 수 있다.
그러므로, 프로그래머 입장에서 좀 더 복잡해도 간접 주소 지정 방식을 활용하는 것이 더 낫다.
13-2 포인터
포인터란?
C 언어의 일반 변수가 자신이 위치한 메모리에서만 값을 읽거나 쓸 수 있는 직접 주소 지정 방식으로 동작하기 때문에, 메모리 주소를 나타내기 위한 특별한 변수를 선언하기 위해 포인터 문법을 추가로 제공
short *ptr; // 자료형, 포인터 변수임을 나타낼 수 있는 기호, 포인터 이름
포인터는 주소를 나타내므로 32비트 기준으로 4바이트로 크기가 고정되어 있다.
포인터 앞의 자료형은 저장되어 있는 자료의 크기이다.
변수가 저장된 메모리 공간의 주소 얻기
포인터가 가리키는 대상의 주소를 직접 입력하여 항상 같은 주소를 사용하게 하려는 시도는 대부분 오류가 날 것이다.
다른 프로그램의 메모리 영역에 잘못 접근하는 일이 없도록 프로그램 안에 선언한 다른 변수의 주소를 받아와서 사용
short birthday; // short형 birthday 변수를 선언
short *ptr; // 포인터형 변수를 선언
ptr = &birthday // birthday 변수의 주소를 ptr 변수에 대입
*
는 여러 의미로 쓰이는데, 앞의 코드에서 *
는 포인터 변수 선언을 위한 키워드이고,*ptr = 1042;
와 같은 *
연산자는 ptr 포인터가 가리키는 주소의 변수에 가서 값 (0x0412)을 대입하겠다는 의미이다.
'ptr=' 형태는 포인터 변수에 주소를 저장한다
포인터 변수는 주소를 저장하기 때문에 메모리 크기가 4바이트로 고정
포인터 변수에 저장된 주소는 포인터가 가리키는 대상 메모리의 시작 주소를 의미
'*ptr=' 형태는 포인터가 가리키는 대상에 값을 저장한다
번지 지정 연산자 *
를 이용하여 포인터가 가리키는 대상의 값을 변경할 수 있다.
하지만, 실제 코드에서 프로그램이 어떤 주소 영역에 할당될 지 예측할 수 없기 때문에 직접 주소를 넣어서 사용하기 어렵다.
다른 함수에 선언된 지역 변수 사용하기
포인터는 변수 이름이 아니라 주소로 대상을 찾아가기 때문에 함수가 아니더라도 대상 메모리의 값을 읽거나 쓸 수 있다.
직접 주소 지정 방식으로 다른 함수에 선언한 변수 사용하기
앞에서 보았듯이, 매개변수로 다른 함수의 변수 값을 받으면 가능하지만, 수정은 할 수 없음
간접 주소 지정 방식 (포인터)으로 다른 함수에 선언한 변수 사용하기
선언된 변수의 값만 받아 오는 것이 아닌 해당 변수의 값을 변경할 수도 있다.
두 변수의 값 서로 바꾸기
함수를 작성해서 문제를 해결하려 하다 보면,
직접 주소 지정 방식으로 매개변수로 값을 받아와서 변수 값을 교환하려 하면 생각대로 움직이지 않는다.
포인터를 이용해 두 변수 값 바꾸기
호출하는 함수에 포인트를 이용해 값을 전달하면 호출되는 함수가 종료된 후에도 값이 변경된 채로 남아 있어
원하는대로 변수의 값이 바뀐 것을 알 수 있다.
즉, 다른 함수에 선언한 지역 변수 값도 수정할 수 있다.
13-3 포인터와 const 키워드
포인터를 사용할 때 자주 발생하는 실수들
포인터 변수의 주소 값 (주소 값이 가리키는 값이 아닌)이 포인터 변수에 저장되어 있는 주소 값으로 변경되는 경우
실행은 되지만 원치 않는 결과가 나올 수 있어서 디버깅하기 힘들다.
const 키워드로 주소 변경 실수 막기
피호출자에서 호출자로부터 전달받은 주소를 변경하는 경우는 거의 없으므로,
피호출자의 매개변수에 const
키워드를 선언하면 위와 같은 실수를 막을 수 있다.
포인터 변수에서 const 키워드를 사용하는 여러 가지 방법
1) int *const p
: 변수 p에 const 속성이 적용 - p에 저장된 주소는 변경할 수 없다.
2) const int *p
: 변수 p가 가리키는 대상에 const 속성이 적용 - 대상의 값을 변경할 수 없다.
3) const int *const p
: 변수 p와 p가 가리키는 대상 모두에 const 속성이 적용
13-4 포인터 변수의 주소 연산
사용할 메모리의 범위를 기억하는 방법
포인터 변수가 가리킬 대상의 크기를 변수 선언 시에 하므로 사용할 메모리의 시작 주소만 기억
포인터 변수의 주소 연산
포인터 변수에 저장된 주소도 정수 값이므로 연산할 수 있는데,
대상의 자료형에 따라 연산 값이 다르다.
13-5 포인터와 대상의 크기
포인터 변수의 가리키는 대상의 자료형과 변수의 자료형은 같게 선언하는 것이 일반적이지만,
변수의 자료형을 형변환하고, 포인터 변수의 주소 연산을 이용해 원하는 바이트만큼 잘라서 출력할 수 있다.
13-6 void *형 포인터
대상의 크기가 정해져 있지 않은 void *형 포인터
사용할 메모리의 시작 주소만 알고 끝 주소를 모를 때 사용하는 포인터 형식
대입하고 싶은 값이 있을 때 '사용할 크기'를 표기해 주면 일반 포인터처럼 사용할 수 있다.
void *형 포인터 활용하기
전달되는 주소의 형식이 여러 가지라면 그리고 이 형식은 사용할 때마다 달라질 수도 있다면,
각 형식에 맞춰 조건문을 작성할 수도 있고, void *형
과 조건문에 같이 들어갈 매개변수를 조건문으로 만들어 사용할 수 있다.
꼭 어느 것이 좋은 것인지 판단할 수는 없지만 자료형이 여러가지라면 void *형
을 고려해 보자.
'Programming Language > Do it! C언어' 카테고리의 다른 글
[Do it! C언어 입문] 15장. 배열과 포인터 (0) | 2022.11.11 |
---|---|
[Do it! C언어 입문] 14장. 표준 입력 함수 (0) | 2022.11.11 |
[Do it! C언어 입문] 12장. 배열과 문자열 (0) | 2022.11.11 |
[Do it! C언어 입문] 11장. 지역 변수와 전역 변수 (0) | 2022.11.11 |
[Do it! C언어 입문] 10장. 시프트 연산자와 비트 연산자 (0) | 2022.11.11 |