Notice
Recent Posts
Recent Comments
Link
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Tags
more
Archives
Today
Total
관리 메뉴

문타쿠, 공부하다.

[C언어 코딩 도장] Unit 34. 포인터 사용하기 본문

C언어/C언어 코딩 도장

[C언어 코딩 도장] Unit 34. 포인터 사용하기

개발새발 문타쿠 2023. 9. 9. 22:54

INTRO

"우리가 변수를 사용할 때, 변수는 어디에 생기는 것일까?"

  • 변수는 컴퓨터의 메모리에 생성된다.
  • 즉, 메모리에 일정한 공간을 확보해두고 원하는 값을 저장하거나 가져오는 방식
#include <stdio.h>

int main(void)
{
    int num1 = 10;

    printf("%p\n", &num1);

    return 0;
}

  • 변수는 num1과 같이 이름으로 사용하지만, 메모리의 특정 장소에 있으므로 메모리 주소로도 표현할 수 있다.
  • 메모리 주소는 고정된 것이 아니라 컴퓨터마다, 실행할 때마다 달라짐
  • 변수의 메모리 주소를 구할 때는 변수 앞에 &(주소 연산자)를 붙이면 된다.
  • 서식 지정자로 %p(포인터) 사용
  • 메모리 주소는 16진수 형태이므로 %x, %X를 사용해도 된다.
  • 시스템이 32비트인지 64비트인지에 따라 메모리 주소의 범위도 달라진다.

34.1 포인터 변수 선언하기

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

#include <stdio.h>

int main(void)
{
    int *numPtr;
    int num1 = 10;

    numPtr = &num1;

    printf("%p\n", numPtr);

    printf("%p\n", &num1);

    return 0;
}

  • 포인터와 메모리 주소는 같은 의미
  • 포인터는 메모리의 특정 위치를 가리킬 때 사용

34.2 역참조 연산자 사용하기

포인터 변수에는 메모리 주소가 저장되어 있다.

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

#include <stdio.h>

int main(void)
{
    int *numPtr;
    int num1 = 10;

    numPtr = &num1;

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

    return 0;
}

  • 포인터는 변수의 주소만 가리키며, 역참조 연산자는 주소에 있는 값에 접근한다.

포인터 선언과 역참조?

  • 포인터를 선언할 때 *는 "이 변수가 포인터다 = 메모리 주소를 저장하는 변수다"라고 알려주는 역할
  • 포인터에 사용할 때 *는 "포인터의 메모리 주소를 역참조하겠다 = 포인터가 가지고 있는 메모리 주소의 값을 가져오겠다"라는 뜻

"포인터 변수에 역참조 연산자를 사용한 뒤 값을 저장(할당)"

#include <stdio.h>

int main(void)
{
    int *numPtr;
    int num1 = 10;

    numPtr = &num1;

    *numPtr = 20;

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

    return 0;
}

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


변수는 메모리 주소를 몰라도 값을 가져오거나 저장할 수 있다.

 

주소 연산자 &는 변수의 메모리 주소를 구한다.

 

포인터는 변수의 메모리 주소만 가리킨다.

 

역참조 연산자 *는 메모리 주소를 거쳐서 그 안에 있는 값을 가져오거나 저장한다.


34.3 디버거에서 포인터 확인하기

디버거를 사용하면 변수의 메모리 주소, 포인터, 역참조를 손쉽게 확인할 수 있다.


34.4 다양한 자료형의 포인터 선언하기

C언어에서 사용할 수 있는 모든 자료형은 포인터로 만들 수 있다.

#include <stdio.h>

int main(void)
{
    long long* numPtr1;
    float* numPtr2;
    char* cPtr1;

    long long num1 = 10;
    float num2 = 3.5f;
    char c1 = 'a';

    numPtr1 = &num1;
    numPtr2 = &num2;
    cPtr1 = &c1;

    printf("%lld\n", *numPtr1);
    printf("%f\n", *numPtr2);
    printf("%c\n", *cPtr1);

    return 0;
}


34.5 void 포인터 선언하기

#include <stdio.h>

int main(void)
{
    int num1 = 10;
    char c1 = 'a';
    int* numPtr1 = &num1;
    char* cPtr1 = &c1;

    void* ptr;

    ptr = numPtr1;
    ptr = cPtr1;

    numPtr1 = ptr;
    cPtr1 = ptr;

    return 0;
}
  • void 포인터는 자료형이 정해지지 않은 특성 때문에 어떤 자료형으로 된 포인터든 모두 저장할 수 있다.
  • 반대로 다양한 자료형으로 된 포인터에도 void 포인터를 저장할 수 있다.
  • 이러한 특성때문에 void 포인터는 범용 포인터라고 한다. -> 직접 자료형을 변환하지 않아도 암시적으로 자료형이 변환되는 방식
  • 단, void 포인터는 자료형이 정해지지 않았으므로 값을 가져오거나 저장할 크기도 정해지지 않았기에, 역참조할 수 없다.
  • 마찬가지로 void 키워드로는 변수를 선언할 수 없다.

34.6 이중 포인터 사용하기

포인터를 선언할 때 *를 두 번 사용하면 포인터의 포인터(이중 포인터)를 선언한다.

#include <stdio.h>

int main(void)
{
    int* numPtr1;
    int** numPtr2;
    int num1 = 10;

    numPtr1 = &num1;

    numPtr2 = &numPtr1;

    printf("num1의 주소\n");
    printf("&num1: %p\n\n", &num1);

    printf("num1의 값\n");
    printf("num1: %d\n\n\n", num1);

    printf("numPtr1의 주소\n");
    printf("&numPtr1: %p\n\n", &numPtr1);

    printf("numPtr1이 가지고 있는 값 = num1의 주소\n");
    printf("numPtr1: %p\n\n", numPtr1);

    printf("numPtr1이 가지고 있는 값(num1의 주소)의 값 = num1의 값\n");
    printf("*numPtr1: %d\n\n\n", *numPtr1);

    printf("numPtr2의 주소\n");
    printf("&numPtr2: %p\n\n", &numPtr2);

    printf("numPtr2가 가지고 있는 값 = numPtr1의 주소\n");
    printf("numPtr2: %p\n\n", numPtr2);

    printf("numPtr2가 가지고 있는 값(numPtr1의 주소)의 값 = num1의 주소\n");
    printf("*numPtr2: %p\n\n", *numPtr2);

    printf("numPtr2가 가지고 있는 값(numPtr1의 주소)의 값(num1의 주소)의 값(num1의 값)\n");
    printf("**numPtr2: %d\n\n", **numPtr2);

    return 0;
}

  • 포인터도 실제로는 변수이기 때문에 메모리 주소를 구할 수 있다.
  • 포인터의 메모리 주소는 일반 포인터에 저장할 수 없고 이중 포인터에 저장해야 한다. -> 포인터 변수끼리 서로 주소를 저장하는 것은 허용되지 않음

34.7 잘못된 포인터 사용

  • 포인터는 메모리 주소를 저장하는 용도이므로 값을 직접 저장하면 안된다.
  • 하지만 만약 실제로 존재하는 메모리 주소라면 포인터에 직접 저장할 수 있다.