C언어, 포인터

Moon·2022년 10월 23일
0
post-custom-banner

해당 게시글은 동영상 강좌 - 'Do it! C언어 입문' - 13장 포인터 (1/3)
를 학습하며 정리한 글입니다.

게시글에 작성된 그림은 모두 업로드된 유튜브 영상에서 캡처해온 것입니다.


포인터 : 메모리를 효과적으로 사용하는 기술. 즉 운영체제가 메모리를 어떻게 관리하는 지 먼저 알아야한다. 운영체제가 메모리를 관리하는 방법에 따라(대표적으로 32비트, 64비트) 프로그래밍 방법이 달라진다.

32비트 운영체제 : 메모리를 관리할 때 주소로 표현하는데, 주소를 저장하는 크기가 32비트(4바이트)
64비트 운영체제 : 주소 저장하는 크기가 64비트(8바이트).
운영체제에서 실행되는 프로그램을 나누어 개발하게 됨. 32비트 프로그램은 64비트 운영체제에서도 동작함. 64비트는 32비트에서 동작하지 않음.

64비트 운영체제

  • 장점 : 동시 처리 능력이 좋음. 요즘 대부분 운영체제는 64비트를 지원함.
  • 단점 : 메모리 사용량 높음, 낮은 사양 컴퓨터에게는 손해
  • 현황 : 호환성이 떨어져 아직 많은 개발자들이 32비트 방식으로 프로그램을 개발.

메모리 주소 지정 방식

  • 운영체제는 메모리 주소를 1바이트 단위로 관리
  • 32비트 운영체제는 2^32번까지의 주소를 사용
  • 메모리를 사용하려면 반드시 사용할 메모리 주소를 지정해야 함


메모리 주소 지정 방식은 직접주소지정방식, 간접주소지정방식으로 나뉜다.
1. 직접 주소 지정 방식('102번지에 1042라는 값을 2바이트 크기로 저장하겠다')

  • 102 ~ 103번지에 2바이트 크기로 저장됨
  • 시작번지 102, 끝번지 103라고 말해야한다. 104번지는 다른 값이 할당되기 때문.
  • 1042가 4와 18로 나뉘는 이유는 8비트에 저장할 수 없기 때문에 두 개의 8비트에 나누어 저장.
  • 리틀 엔디언 : 작은 단위가 앞에 오는 방식

16진법으로 메모리 형태 표시하기

  • C언어에서는 2진수를 직접 사용하는 방법을 제공하지 않음
  • 2진수에 가장 가까운 표현법인 16진수 사용
  • 16진수 한 자릿수는 4비트로 표시 가능
  • 16비트 두 자리는 1바이트를 의미하기 때문에 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바이트 크기로 대입하라.

  • 102번지에 갔더니 108번지가 있음. 만약 주소가 바뀐다면 108번지가 아닌 다른 곳으로 갈 수 있다는 말임.

  • 포인터(Pointer)는 사물함 주소다!
    일반 변수도 주소를 저장할 수 있으나.. 굳이..? 아무 것도 못함!
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 키워드를 사용하는 여러가지 방법이 있다.

  • const 키워드는 왜 사용하는가? : 혹시나 *를 빠뜨리거나 바뀌면 안되는 값을 못 바꾸게 하도록!!
  1. int * const p; 의 의미는 ?
    p가 가지고 있는 주소를 변경하면 번역할 때 오류가 발생.
    초기 값(&data)을 주는 건 상관 없음업로드중..

  2. const int p; 의 의미는?
    p를 사용하여 대상의 값을 변경하여 번역할 때 오류 발생업로드중..


그렇다면, 포인터는 언제 사용할까?

Question. start 와 end 값 서로 바꾸기

만약, 포인터를 사용하지 않고 값을 바꾸려면 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);
}
profile
안녕하세요. Moon입니다!
post-custom-banner

0개의 댓글