세리프 따라잡기

WEEK05 - TIL C언어 포인터에 대해 본문

SW사관학교 정글

WEEK05 - TIL C언어 포인터에 대해

맑은 고딕 2022. 4. 29. 20:21

- 변수 / 주소 연산자 / 역참조 연산자 / 포인터의 차이

 

 

1. 포인터 사용하기

변수는 어디에 생기는 걸까?

변수는 컴퓨터의 메모리에 생성된다. 메모리에 일정한 공간을 확보해놓고, 원하는 값을 저장하거나 가져오는 방식이다.

포인터가 어렵게 느껴진다면? 메모리를 이해하지 못한 것! > 메모리를 이해하면 어렵지 않다.

 

보통 변수는 이름으로 사용하는데, 변수는 메모리의 특정 장소에 위치하고 있으므로 메모리 주소로도 표현할 수 있음

== 일상생활에서 집을 구분할 때 주소를 사용하는 것과 같은 원리.

 

변수의 메모리 주소를 구해보자.

&변수 ← 사용법

#include <stdio.h>

int main(){
    int num1 = 10;
    printf("%p\n", &num1); //0x7fff61f81fe4 → 메모리 주소 출력. 컴퓨터마다 실행할 때마다 달라짐
    return 0;
}

변수의 메모리 주소를 구할 때는 변수 앞에 & (주소 연산자)를 붙이면 된다. 여기서는 &num1과 같이 num1 앞에 &를 붙여서 변수 num1의 메모리 주소를 구했다.

 

메모리 주소는 0x7fff61f81fe4과 같이 16진수 형태며 printf에서 서식 지정자 %p를 사용하여 출력한다. (pointer의 약어로 p를 사용한다) + 16진수로 출력하는 %x를 사용해도 된다!

 

 

 

+ 주소의 길이도 다른 이유!

다음과 같이 시스템이 32비트인지 64비트인지에 따라 메모리 주소의 범위도 달라진다.

  • 32비트: 16진수 8자리
    • 0x00000000 ~ 0xFFFFFFFF
    • 예) 0x008AF7FC
  • 64비트: 16진수 16자리
    • 0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF
    • 예) 0x00000000008AF7FC
    • 64비트 메모리 주소는 0x00000000`00000000처럼 8자리씩 끊어서 `를 붙이기도 합니다.

16진수 8자리 또는 16자리로 된 숫자가 나오면 일단 메모리 주소라 생각하면 된다!

 

2. 포인터 변수 선언하기

c언어에서 메모리 주소는 포인터 변수에 저장한다

 

- 사용법

자료형 *포인터이름; // 선언 (이때 *을 붙이는 위치에 따른 차이는 없다)

포인터 = &변수;

#include <stdio.h>

int main(){
    int *numPtr; //포인터 변수 선언
    int num1 = 10; //int형 변수를 선언하고 10저장

    numPtr = &num1; //num1의 메모리 주소를 포인터 변수에 저장

    printf("%p\n", numPtr); //0x7fff4a3eb98c, 포인터 변수 numPtr의 값 출력
    printf("%p\n", &num1); //0x7fff4a3eb98c, 변수 num1의 메모리 주소 출력
    // 컴퓨터마다, 실행할 때마다 달라짐

    return 0;
}

포인터 == 메모리 주소 : 같은 의미이다~

 

포인터 변수를 선언할 때는 자료형을 알려주고 *를 붙이는 방식을 사용. 만약 변수가 int형이면 이 변수의 메모리 주소를 저장하는 포인터는 int *이라 한다. 여기서 int *는 영어로 'pointer to int'라고 읽는데 int형 공간을 가리키는 포인터라는 뜻이다. (간단하게 int 포인터라고도 함)

포인터는 메모리의 특정 위치를 가리킬 때 사용한다. 포인터는 프레젠테이션에서 사용하는 레이저 포인터의 그 포인터를 말한다. (어떤 위치를 콕 찝어서 가리키는 역할)

int *numPtr;     // 포인터 변수 선언
int num1 = 10;

numPtr = &num1;   // num1의 메모리 주소를 포인터 변수에 저장

다음 코드를 그림으로 표현한다면 다음과 같다. numPtr은 10이 저장된 메모리 공간을 가리킨다.

즉, 변수 num1이 있는 공간을 가리키는 것.

 

3. 역참조 연산자 사용하기

메모리 주소가 있는 곳으로 이동해서 값을 가져오고 싶다면 역참조(dereference) 연산자 *를 사용

#include <stdio.h>

int main(){
    int *numPtr; //포인터 변수 선언
    int num1 = 10; //int형 변수를 선언하고 10저장

    numPtr = &num1; //num1의 메모리 주소를 포인터 변수에 저장

    printf("%d\n", *numPtr); //10, 역참조 연산자로 num1의 메모리 주소에 접근하여 값을 가져옴

    return 0;
}

역참조 연산자 *는 포인터 앞에 붙인다. numPtr 앞에 *를 붙이면 numPtr에 저장된 메모리 주소로 가서 값을 가져옴.

이때 numPtr이 num1의 메모리 주소를 저장하고 있으므로 num1의 값인 10이 출력

즉, 포인터는 변수의 주소만 가리키며 역참조는 주소에 접근하여 값을 가져옴

+ 포인터를 선언할 때도, 역참조를 할 때도 *를 쓰는데, 구분점은 다음과 같다.

포인터를 선언할 때 = '이 변수가 포인터다'라고 알려주는 역할

포인터에서 사용할 때 = '포인터의 메모리 주소를 역참조하겠다'라는 뜻

int *numPtr;                // 포인터. 포인터를 선언할 때 *
printf("%d\n", *numPtr);    // 역참조. 포인터에 사용할 때 *

포인터 변수에 역참조 연산자를 사용한 뒤 값을 저장(할당)해보자! [*포인터 = 값;]

#include <stdio.h>

int main(){
    int *numPtr; //포인터 변수 선언
    int num1 = 10; //int형 변수를 선언하고 10저장

    numPtr = &num1; //num1의 메모리 주소를 포인터 변수에 저장
    *numPtr = 20;

    printf("%d\n", *numPtr); //20
    printf("%d\n", num1); //20

    return 0;
}

역참조 연산자는 값을 가져올 수도 있고 값을 저장할 수도 있다.

 

값이 같은 이유 >> numPtr에는 num1의 메모리 주소가 저장되어 있으므로 역참조 연산자로 값을 저장하면 결국 num1에 저장하게 된다.

역참조 연산자는 자료형을 바꾸는 효과를 낸다. 즉, int *numPtr; 에서 *numPtr처럼 역참조하면 pointer to int에서 pointer to를 제거해 그냥 int로 만든다. (int 포인터 → int)

 

만약 포인터 numPtr에 변수 num1을 할당한다면 역참조 연산자로 자료형을 맞춰주면 된다.

 

#도움이 된 사이트

Comments