뇌
포인터( pointer ) - 1 본문
포인터( pointer ) 란 ?
메모리의 주소값 혹은 변수의 주소값을 가르키는( 저장하는 ) 변수 이다.
32bit 환경에서의 주소값은 4byte 이고, 64bit 환경에서의 주소값은 8byte 이다.
선언 방식
자료형 *변수명; // ex) int *Ptr;
자료형 * 변수명; // ex) int * Ptr;
자료형* 변수명; // ex) int* Ptr;
* 은 어느 쪽에 써도 상관 없다.
포인터 변수 초기화
int a=1;
int *Ptr=&a;
int 형 변수 a 를 선언하고, int * 형 변수 Ptr 를 선언해 a 의 주소값을 넣을 수 있다.
&
는 주소값을 가져오는 '주소 연산자' 이다.
a 의 주소값이 0x12345678 이라면, &a 의 값은 0x12345678 이 된다.
// pointer.c
#include<stdio.h>
int main()
{
int a=1;
int *Ptr=&a;
printf("a 의 주소값 : %p, a 의 값 : %d\n", &a, a);
printf("Ptr 의 주소값 : %p, Ptr 의 값 : %d\n", Ptr, *Ptr);
return 0;
}
/*
[출력]
a 의 주소값 : 0x7ffeefbff4f8, a 의 값 : 1
Ptr 의 주소값 : 0x7ffeefbff4f8, Ptr 의 값 : 1
*/
int *형 Ptr 변수에 a의 주소값을 넣었으므로 Ptr의 값과 a의 주소값, a 값과 Ptr 값에 있는 주소의 값이 같다.
정리하자면,
Ptr == &a
*Ptr == a
이다.
여기서 *
은 '역참조 연산자' 로 말 그대로 역참조를 해서 값을 가져온다.
위 예제에서 a 의 주소값이 0x7ffeefbff4f8 이므로, Ptr 에도 0x7ffeefbff4f8 가 들어가고,
*Ptr 는 0x7ffeefbff4f8 주소에 있는 값, 즉 1 이 출력되는 것이다.
( 변수를 선언 할 때 있는 *은 포인터 형임을 알려주는 용도로 역참조 연산자와는 다르다. )
여기서 궁금한 게 생겼다.
포인터 변수는 주소를 저장하는데 모두 int *형으로만 하면 저장하면 되는 거 아닐까 ?
float 형 변수의 주소값을 int *형 변수에 넣어보고, 반대로도 해보고 등등 많이 해봤는데 제대로 된 값이 나오지 않는다.
찾아보니 엄청난 사실을 알았다.
자료형에 따라 메모리에 접근하는 방법이 달라지는 것 때문이다.
주소 값만을 저장하는 것은 상관없을지라도 *Ptr 이런 식으로 역참조를 할 때 문제가 생긴다.
예를 들어, long long 형 변수는 8 byte 이므로 역참조를 하면 8 byte 만큼 가져와야 하고,
char 형 변수도 마찬가지로 1 byte 이므로 역참조를 할 때 1 byte 만큼만 가져와야 한다.
여기서 int 형 변수를 사용하면 4 byte 만큼 가져오기 때문에 문제가 생기는 것이다.
포인터 연산
주소 값에 1 씩 증가하면 어떻게 될까?
나는 모든 자료형의 주소값이 1 byte 씩 증가할 것이라고 예상했다.
예제를 보자.
#include<stdio.h>
int main()
{
int a;
int *aa = &a;
char b;
char *bb = &b;
printf("a의 주소값 :\n");
printf("%p\n", aa);
printf("%p\n", aa+1);
printf("%p\n", aa+2);
printf("b의 주소값 :\n");
printf("%p\n", bb);
printf("%p\n", bb+1);
printf("%p\n", bb+2);
return 0;
}
/*
[출력]
a의 주소값 :
0x7ffeefbff4f8
0x7ffeefbff4fc
0x7ffeefbff500
b의 주소값 :
0x7ffeefbff4ef
0x7ffeefbff4f0
0x7ffeefbff4f1
*/
위 예제는 int 형 변수와 char 형 변수의 연산 값을 출력하는 코드이다.
출력 내용을 보면,
int 형인 a 의 주소값에 1씩 더할 때마다 주소 값은 4 byte 씩 증가하고,
char 형인 b 의 주소값에 1씩 더할 때마다 주소 값은 1 byte 씩 증가한다.
즉, 자료형의 byte 수 만큼 증가하는 것을 알 수 있다.
정리하면,
주소값의 연산은 (주소 값을 가진 변수 값) + n*(그 변수의 자료형 크기) 이 된다.
- 연산도 마찬가지이다.
#include<stdio.h>
int main()
{
int a;
int *aa = &a;
char b;
char *bb = &b;
printf("a의 주소값 :\n");
printf("%p\n", aa);
printf("%p\n", ++aa);
printf("%p\n", ++aa);
printf("b의 주소값 :\n");
printf("%p\n", bb);
printf("%p\n", ++bb);
printf("%p\n", ++bb);
return 0;
}
/*
[출력]
a의 주소값 :
0x7ffeefbff4f8
0x7ffeefbff4fc
0x7ffeefbff500
b의 주소값 :
0x7ffeefbff4ef
0x7ffeefbff4f0
0x7ffeefbff4f1
*/
증감연산자로도 할 수 있다.
call by value & call by reference
call by value 는 '값에 의한 호출', call by reference 는 '참조에 의한 호출'이다.
예제로 보자.
#include<stdio.h>
void func(int a, int b, int sum)
{
sum = a+b;
}
int main()
{
int a=1, b=2, sum=0;
func(a, b, sum);
printf("sum = %d", sum);
return 0;
}
/*
[출력]
sum = 0
*/
#include<stdio.h>
void func(int a, int b, int *sum)
{
*sum = a+b;
}
int main()
{
int a=1, b=2, sum=0;
func(a, b, &sum);
printf("sum = %d", sum);
return 0;
}
/*
[출력]
sum = 3
*/
코드의 차이는
첫번째 예제에서는 call by value 예제로, main 함수에서 func 함수에 변수 a, b, sum 의 값을 보낸다.
두번째 예제에서는 call by reference 예제로, main 함수에서 func 함수에 변수 a, b의 값과 sum 의 주소 값을 보낸다.
그런데 우리가 출력하고 싶은 값은 3 인데, 첫번째 예제의 출력 값은 0 이고, 두번째 예제의 출력 값은 3 이다.
왜 그런 것일까?
func 함수에 값을 보냈을 때 받는 변수는 main 함수와 다른 함수이고, 값만 복사해온다.
그렇기 때문에 main 함수의 변수 sum 과 func 함수의 변수 sum 은 다른 변수이므로,
첫번째 예제에서는 func 함수에서 연산한 sum 은 결과적으로는 무의미해지는 것이다.
하지만 두번째 예제는 주소값을 복사했기 때문에, 그 주소에 있는 값을 바꾸면 main 함수에 있는 변수 sum 의 값도 바꿀 수 있게된다.
char 형 변수 & char *형 변수
#include<stdio.h>
int main()
{
char arr1[]="hihi";
char *arr2="hihi";
printf("arr1[0], arr1 주소 값 : %p, %p\n", &arr1[0], arr1);
printf("arr2 주소 값 : %p", arr2);
return 0;
}
/*
[출력]
arr1[0], arr1 주소 값 : 0x7ffeefbff4f7, 0x7ffeefbff4f7
arr2 주소 값 : 0x100000f76
*/
char 형 변수 arr1 의 주소값은 arr[0] 의 주소값이고,
char *형 변수 arr2 는 메모리 공간에 'hihi' 라는 문자열을 저장하고, 첫번째 문자인 'h' 의 주소값을 반환해 arr2 에 저장된다.
주소만 봐도 두 변수가 다른 영역에 저장되는 것을 알 수 있다.
그럼 두 변수의 차이는 무엇일까?
#include<stdio.h>
int main()
{
char arr1[]="hihi";
char *arr2="hihi";
printf("arr1[0], arr1 주소 값 : %p, %p\n", &arr1[0], arr1);
printf("arr2 주소 값 : %p\n", arr2);
printf("%s\n", arr1);
printf("%s\n", arr2);
arr1[0]='H';
arr2[0]='H';
printf("%s\n", arr1);
printf("%s", arr2);
return 0;
}
/*
컴파일 오류
*/
각각 0번째 인덱스의 값을 넣었을 때, arr1 은 정상적으로 H 가 들어갔지만, arr2 는 들어가지 않았다.
즉, char *형 변수는 문자열을 바꿀 수 없다.
그래서 상수형태의 문자열이라고도 한다.
'C' 카테고리의 다른 글
포인터( pointer ) - 2 (0) | 2020.07.03 |
---|