본문 바로가기
c언어

c언어 포인터로 연산하는 방법 알아보기

by 개발자 L 2022. 12. 7.
반응형

c언어 포인터로 연산하는 방법 알아보기

네 안녕하세요, 이번 포스팅에서는 포인터로 연산을 하는 방법에 대하여 알아보도록 하겠습니다.

우리가 일반적인 변수들로 연산을 하듯이, 포인터로도 연산을 할 수 있어야겠죠?

그래야 실질적으로 사용이 가능해지니까요.

그러면 그러한 연산을 어떻게 사용을 하는지 한 번 알아보도록 하겠습니다.

 

1. 포인터로 덧셈과 뺄셈 연산 하기

포인터로도 덧셈과 뺄셈 연산을 할 수 있습니다.

그렇지만 연산이 되는 과정이 좀 다릅니다.

일반적인 변수를 연산한다면 말 그대로 변수에 저장이 된 값의 크기가 변화하지만,

포인터로 연산을 하게 되면 포인터가 가리키는 객체의 크기가 변화하게 됩니다.

예를 한 번 들어보도록 하겠습니다.

일반 변수 a = 100과, 포인터 변수 *p = 100이 있다고 가정을 해보도록 하겠습니다.

둘이 똑같이 '++' 연산자를 붙여서 각각 'a++', '*p++'를 만들었다고 합시다.

그러면 둘 다 101이 나올 것 같지만, 실제로는 일반 변수만 101이 나오고, 포인터 변수는 104가 나옵니다.

그래서 값이 변화하는 것이 아니라, 객체가 가리키는 자료형의 크기만큼 증가를 한 것입니다.

그걸 다시 풀어 말하면, 포인트는 변수의 값이 아닌 객체의 주소 값을 가리키고 있고, 그걸 참조하는 것이기 때문에 값이 직접적으로 증가하는 것이 아니라, 번지수가 증가한다고 볼 수 있습니다.

위와 같은 경우에는 변수가 int형, 다시 말해 정수형이라서 정수형 자료형의 크기인 4만큼 증가한 것이라고 보시면 됩니다.

그럼 제가 이러한 결과를 보여드리기 위해서 한 번 간단한 예제를 작성해보도록 하겠습니다.

#include <stdio.h>

int main()
{
    int *pi;
    double *pd;
    char *pc;

    pc = (char *) 100;
    pi = (int *) 1000;
    pd = (double *) 10000;

    printf("증가 전 : pc = %d, pi = %d, pd = %d\n", pc, pi, pd);

    pc++;
    pi++;
    pd++;

    printf("증가 후 : pc = %d, pi = %d, pd = %d\n", pc, pi, pd);

    printf("pc + 2 = %d, pi + 2 = %d, pd + 2 = %d\n", pc + 2, pi + 2, pd + 2);

    return 0;
}

이렇게 한 번 써봤는데, 제가 전에 절대 주소를 쓰지 말라고 이야기를 드렸을 겁니다.

그렇지만 여기에서 쓴 이유는, 이렇게 해야 결과치를 좀 더 명확히 보여드릴 수 있을 것 같아 어쩔 수 없이 사용을 했습니다.

그럼 결과를 보여드리도록 하겠습니다.

증가 전 : pc = 100, pi = 1000, pd = 10000
증가 후 : pc = 101, pi = 1004, pd = 10008
pc + 2 = 103, pi + 2 = 1012, pd + 2 = 10024

이렇게 나왔습니다.

번지수의 증가에 따라서 이렇게 값이 다르게 증가를 한 것을 볼 수가 있습니다.

원래 같으면 본연의 값이 1씩 증가하여야 하지만, 포인터가 가리키는 값은 다 주소 값이라 그러하다는 것을 다시 한번 기억해주셨으면 좋겠습니다.

 

1 - 1. 간접 참조 연산자와 증감 연산자의 사용

위의 예시에서 보셨듯이, 간접 참조 연산자와 증감 연산자는 같이 사용을 할 수 있습니다.

보통 사용되는 순서는 간접 참조 연산자가 붙은 변수인 *p를 먼저 수행을 한 후에 증감 연산자가 수행이 됩니다.

이런 식으로 씁니다.

*p++;

하지만 p 자체가 아니라 p가 가리키는 대상을 증가시키고 싶다면 다른 방법을 택해야 합니다.

이런 식으로 쓰셔야 적용이 됩니다.

(*p)++;

이렇게 괄호로 묶어줘야 p가 가리키는 대상을 지목을 하게 됩니다.

그 외에 포인터를 쓰는 방법들은 이러합니다.

a = *p++; // p가 가리키는 값을 a에 대입을 한 후에 p를 증가시킴
a = (*p)++; // p가 가리키는 값을 a에 대입을 한 후에 p가 가리키는 값을 증가
a = *++p; // p를 증가시킨 후에 p가 가리키는 값을 a에 대입
a = ++*p; // p가 가리키는 값을 가져온 후에 그 값을 증가시켜 a에 대입

 

보통은 이러한 경우들을 씁니다.

그럼 한 번 간단하게 포인터로 증감 연산을 해보도록 하겠습니다.

#include <stdio.h>

int main()
{
    int a = 10;
    int *p = &a;

    printf("a = %d, p = %p\n", a, p);
    (*p)++;
    printf("a = %d, p = %p\n", a, p);

    printf("a = %d, p = %p\n", a, p);
    *p++;
    printf("a = %d, p = %p\n", a, p);

    return 0;
}

이렇게 간단하게 한 번 작성을 해봤는데, 이제 결과가 어떻게 나오려나 궁금하시죠?

지금 바로 보여드리겠습니다.

a = 10, p = 0061FF18
a = 11, p = 0061FF18
a = 11, p = 0061FF18
a = 11, p = 0061FF1C

이렇게 변했습니다.

위의 경우는 p가 가리키는 대상인 a를 증가시킨 것이고,

아래의 경우는 p값 자체를 증가를 시킨 것입니다.

이렇게 연산 결과에서 차이가 나기 때문에 본인이 증감시키고자 하는 객체가 무엇인지 잘 인지하셔야 실수를 하지 않습니다.

반응형

 

2. 포인터의 형 변환(캐스팅)

포인터 역시 형 변환(캐스팅)을 할 수 있습니다.

하지만 포인터의 캐스팅은 되도록이면 하지 않는 것이 좋고,

꼭 필요한 경우에만 하는 것이 좋습니다.

캐스트 연산자를 써주지 않는다면 바로 경고가 발생해버리기 때문입니다.

그리고 자료형이 서로 다를 경우에 언더플로우가 발생을 할 수 있기 때문입니다.

예를 들어서 변수의 자료형은 int형인데, 포인터의 자료형이 double형일 경우에 double형이 상위 자료형이라서 원래대로라면 컴파일러가 자동적으로 double형으로 상위 변환을 시켜주지만,

그 경우는 어디까지나 일반 변수간 형 변환에 해당이 되는 것이고,

포인터와 변수 간 형 변환에서는 잉여 바이트가 남게 되므로 그 잉여 바이트가 덮어씌워지는거라서 언더 플로우가 납니다.

제가 간단한 예제를 작성을 해봤는데요,

이런 식으로 작성을 하셔야 오류가 나지 않습니다.

#include <stdio.h>

int main()
{
    int data = 0x0a0b0c0d;
    char *p;

    p = (char *)&data;

    for(int i = 0; i < 4; i++)
    {
        printf("*(p + %d) = %02x \n", i, (p + i));
    }

    return 0;
}

여기서 포인트는 상위 자료형으로 형 변환을 시키지 않았다는 것입니다.

앞서 말씀드렸듯이, 상위 형 변환을 하게 되면 언더 플로우가 나니까 절대 하지 마세요.

그럼 결과를 보도록 하겠습니다.

*(p + 0) = 0d
*(p + 1) = 0c 
*(p + 2) = 0b
*(p + 3) = 0a

제가 두 자리만 출력하게 만들었더니 이렇게 나왔습니다.

 

여기까지 포인터로 연산을 하는 방법에 대하여 알아보았는데요,

다음 포스팅에서는 포인터와 함수에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글