해당 게시글은 동영상 강좌 - 'Do it! C언어 입문' - 13장 포인터 (1/3)
를 학습하며 정리한 글입니다.
게시글에 작성된 그림은 모두 업로드된 유튜브 영상에서 캡처해온 것입니다.
포인터 : 메모리를 효과적으로 사용하는 기술. 즉 운영체제가 메모리를 어떻게 관리하는 지 먼저 알아야한다. 운영체제가 메모리를 관리하는 방법에 따라(대표적으로 32비트, 64비트) 프로그래밍 방법이 달라진다.
32비트 운영체제 : 메모리를 관리할 때 주소로 표현하는데, 주소를 저장하는 크기가 32비트(4바이트)
64비트 운영체제 : 주소 저장하는 크기가 64비트(8바이트).
운영체제에서 실행되는 프로그램을 나누어 개발하게 됨. 32비트 프로그램은 64비트 운영체제에서도 동작함. 64비트는 32비트에서 동작하지 않음.
64비트 운영체제
메모리 주소 지정 방식
메모리 주소 지정 방식은 직접주소지정방식, 간접주소지정방식으로 나뉜다.
1. 직접 주소 지정 방식('102번지에 1042라는 값을 2바이트 크기로 저장하겠다')
16진법으로 메모리 형태 표시하기
직접 주소 지정 방식은 C언어의 변수 문법과 같다.
0x0412값을 0x00000066번지에 2바이트 크기로 대입하라는 말은
어셈블리어로 살펴보면 'mov word ptr[0x00000066h], 0412h'라고 표현할 수 있다.
이는 C언어의 변수 설정과 유사한데, C언어도 결국 직접 주소 지정 방식을 사용하는 것이다.
short birthday; // birthday가 메모리 0x00000066에 위치한다고 가정
birthday = 0x0412; // mov word ptr[0x00000066h], 0412h로 번역
C언어가 주소를 사용하지 않고 변수라는 개념을 사용하는 이유는 인간에게 메모리 주소 대신 변수 이름을 사용하는 게 코드를 더 쉽게 이해 가능하기 때문이다! (기계는 주소가 편해~)
C언어는 직접 주소 지정 방식의 한계를 갖는다. 변수 개념을 사용하기 때문에 문법 구조상 서로 다른 함수에 존재하는 변수를 참조하는게 불가능하다.
보통은 이런 한계를 극복하기 위해, 함수의 매개변수와 변환값을 이용하여 서로 다른 함수에 존재하는 지역 변수들 간에 값을 전달한다.
간접 주소 지정 방식 : 102번지에 4바이트 크기의 '주소'가 저장되어 있는데, 이 주소에 가서 '값' 1042를 2바이트 크기로 대입하라.
int addr = 0x0000006C //저장된 주소의 메모리에 가서 읽거나 저장하는 기능은 없음
C언어는 간접 주소 지정 방식으로 동작하는 포인터 문법을 제공(위에서의 사물함 주소가 포인터)
(32비트 운영체제에서) 포인터 변수는 자료형을 선언하지 않아도 무조건 크기가 4바이트. 앞에 붙은 자료형은 보통 변수의 크기를 선언하는데, 이미 포인터는 4바이트라고 알려줬음. 그렇다면 이 자료형은 무엇이냐. 포인터가 지정된 주소로 가서 행위를 할 때 사용할 크기를 말함.
과정을 다시 정리해보면, 0x00000066으로 찾아갔더니 포인터 사물함 주소(0x0000006C)가 있고, 그 주소에 가서 자료형 사용범위(short)에 따라 값(0x0412)을 저장함
변수의 주소는 프로그램이 실행될 때마다 달라지기 때문에 포인터 변수에 주소를 직접 입력하는 것보다 프로그램 안에 선언한 다른 변수의 주소를 받아 사용하는 것이 안전.
#include <stdio.h>
void main(){
short birthday;
short *ptr; // 자신의 크기는 4바이트, 가리키는 대상의 크기는 2바이트
ptr = &birthday;
// birthday = 0x0412; 이 방식은 위험함
printf("birthday 변수의 주소는 %p입니다. \n.", ptr);
}
*초보가 하는 흔한 실수
3번 ptr = &birthday; 는 birthday의 변수 주소를 ptr 변수에 대입
4번 *ptr = 1042; 는 ptr이 가리키는 주소로 가서 1042라는 값을 넣으라는 이야기임. 즉 birthday = 1042를 의미함
먼저, short ptr은 포인터 선언을 위해서 진행한 것이고, 그 이후에 ptr이라는 값에 &birthday 주소를 넣어준 것임. ptr에 &birthday를 넣어준 게 아님에 유의!! 헷갈린다면 위 그림처럼 직접 적어가는 게 처음 공부할 때 더 좋다.
포인터 변수에서 const 키워드를 사용하는 여러가지 방법이 있다.
int * const p; 의 의미는 ?
p가 가지고 있는 주소를 변경하면 번역할 때 오류가 발생.
초기 값(&data)을 주는 건 상관 없음
const int p; 의 의미는?
p를 사용하여 대상의 값을 변경하여 번역할 때 오류 발생
그렇다면, 포인터는 언제 사용할까?
만약, 포인터를 사용하지 않고 값을 바꾸려면 2개의 메모리만 있을 때는 한 값이 삭제됨. 직접 주소 지정 방식을 사용하면 함수 복잡도가 높아지지만 간접주소지정방식(포인터)을 사용하면 손쉽게 바꿀 수 있음
#include <stdio.h>
void Swap(int *pa, int *pb){
int temp = *pa; // *pa = 96, *pb = 5
*pa = *pb; // *pa = 5, *pb = 5
*pb = temp; // *pa = 5, *pb = 96
}
void main(){
int start = 96, end = 5;
printf("before: start = %d, end = %d\n", start, end);
if(start > end){
Swap(&start, &end);
}
printf("after: start = %d, end = %d \n", start, end);
}