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
제가 두 자리만 출력하게 만들었더니 이렇게 나왔습니다.
여기까지 포인터로 연산을 하는 방법에 대하여 알아보았는데요,
다음 포스팅에서는 포인터와 함수에 대하여 알아보도록 하겠습니다.
긴 글 읽어주신 독자분들께 진심으로 감사드립니다~
'c언어' 카테고리의 다른 글
c언어 포인터와 배열과의 관계 알아보기(배열의 이름과 포인터의 관계, 포인터를 배열처럼 사용하기, 배열 매개 변수) (0) | 2022.12.07 |
---|---|
c언어 포인터와 함수와의 관계 알아보기(값에 의한 호출(call - by- value, 참조에 의한 호출(call - by - reference), swap(), scanf(), 포인터를 사용하는 반환 값) (0) | 2022.12.07 |
c언어 포인터 사용 시 주의할 점에 대하여 알아보기 (0) | 2022.12.07 |
c언어 간접 참조 연산자 알아보기(*, &) (0) | 2022.12.07 |
c언어 포인터 맛보기 (0) | 2022.12.07 |
댓글