포인터
포인터란 메모리 상에 위치한 특정한 데이터의 (시작)주소값을 보관하는 변수을 말한다
1
포인터에 주소값이 저장되는 데이터의 형) *(포인터의 이름);
포인터는 위 코드와 같이 써서 사용할 수 있다
단항 & 연산자
단항 & 연산자는 피연산자의 주소값을 불러오는 연산자로
1
&(주소값을 계산할 데이터)
이렇게 사용할 수 있다
코드
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main() {
int *p;
int a;
p = &a;
printf("포인터 p 에 들어 있는 값 : %p \n", p);
printf("int 변수 a 가 저장된 주소 : %p \n", &a);
}
출력
1
포인터 p 에 들어 있는 값 : 0x7fff894c8b3c int 변수 a 가 저장된 주소 : 0x7fff894c8b3c
위 코드를 뜯어보면 포인터 p에
1
p=&a;
로 a가 저장된 주소를 넣었으니 같은 주소가 출력된다
물론 포인터 또한 변수 이기 때문에 특정한 메모리 공간을 차지하여 자신의 주소를 가진다는 것을 명심해야 한다
단항 * 연산자
단항 * 연산자는 주소값에서 해당 주소값에 대 응되는 데이터를 가져오는 연산자로
1
*(소 값을 보관하는 데이터의 형)
이렇게 사용할 수 있으며
단항연산자 * 의 의미는 나에 저장된 주소값에 해당하는 데이터로 생각하라는 뜻이다
코드
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main() {
int *p;
int a;
p = &a;
a = 2;
printf("a 의 값 : %d \n", a);
printf("*p 의 값 : %d \n", *p);
}
출력
1
2
a 의 값 : 2
*p 의 값 : 2
위 코드를 뜯어보면 a에 주소를 포인터 p에 넣고
1
a = 2;
라고 a에 값을 2로 만들었으니 포인터 p에 출력 또한 2로 출력된다
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main() {
int *p;
int a;
p = &a;
p = 3;
printf("a 의 값 : %d \n", a);
printf("*p 의 값 : %d \n", *p);
}
출력
1
2
a 의 값 : 3
*p 의 값 : 3
위 코드 또한 뜯어보면 앞에서 말한 것처럼 단항 연산자 *는 포인터에 저장된 주소에 변수와 동일한 데이터로 인식하게 하니
1
p = &a;
로 변수 a에 주소가 담긴 포인터 p에
1
p = 3;
을 한 것은 a = 3과 같은 의미를 지녀 a에 값이 3으로 출력된 것이다
정리
코드
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main(){
int a = 10;
int *P;
P = #
}
위와 같은 코드가 있을 때
1
2
3
4
5
6
* a = 10
* &a = 100
* p = 100
* *p = 10
* &p = 200
a에 주소와 p에 주소를 보기 쉽게 100과 200으로 가정하면 이렇게 요약할 수 있다
즉 앞에 기호가 없으면 자신의 값이 나오고 *가 붙으면 주소에 담긴 값을 &가 붙으면 자신의 주소 값이 나온다
라고 정리할 수 있다
포인터의 연산
포인터는 값을 증가시키거나 감소시키는 등의 제한된 연산만을 할 수 있다
그 규칙으로는
포인터끼리의 덧셈, 곱셈, 나눗셈은 아무런 의미가 없다
포인터끼리의 뺄셈은 두 포인터 사이의 상대적 거리를 나타낸다
포인터에 정수를 더하거나 뺄 수는 있지만 실수와의 연산은 허용하지 않는다
포인터끼리 대입하거나 비교할 수 있다
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
int main() {
int a;
char b;
double c;
int* pa = &a;
char* pb = &b;
double* pc = &c;
printf("pa : %p \n",pa);
printf("pa+1 : %p \n",pa+1);
printf("pb : %p \n",pb);
printf("pb+1 : %p \n",pb+1);
printf("pc : %p \n",pc);
printf("pc+1 : %p \n",pc+1);
출력
1
2
3
4
5
6
pa : 0x7ffcf64a2e04
pa+1 : 0x7ffcf64a2e08
pb : 0x7ffcf64a2e03
pb+1 : 0x7ffcf64a2e04
pc : 0x7ffcf64a2e08
pc+1 : 0x7ffcf64a2e10
위 코드를 보면 int 형인 포인터 pa는 주소 값이 int 값인 4만큼만 증가하고 char 형인 pb는 주소 값이 char값인 1만큼 증가,double형인 pc는 8만큼 증가한 걸 볼 수 있다
코드
1
2
3
4
5
6
7
8
#include <stdio.h>
int main() {
int a;
int* pa = &a;
printf("pa 의 값 : %p \n", pa);
printf("(pa - 1) 의값: %p \n", pa - 1);
}
출력
1
2
pa 의 값 : 0x7ffe4f4fa47c
(pa - 1) 의 값 : 0x7ffe4f4fa478
마찬가지로 4만큼 빠진 걸 볼 수 있다
이중 포인터
이중 포인터란 포인터의 (시작)주소값을 보관하는 변수을 말한다
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
int main()
{
int *P1;
int **P2;
int num = 10;
P1 = #
P2 = &P1;
printf("%d\n", **p2);
return 0;
}
출력
1
10
코드를 뜯어보면
1
2
3
P1 = #
P2 = &P1;
P1 포인터에 num에 주소 값을 담고 P2라는 이중 포인터에 P1 포인터에 주소 값을 담는다 그 후 포인터를 한 번 역참조하면
P1에 담겨진 num에 주소 값이 나왔겠지만 **로 포인터를 두 번 역참조하여 num에 값을 출력한다
포인터 배열
포인터 배열이란 말그대로 포인터들의 배열을 말한다
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main() {
int *arr[3];
int a = 1, b = 2, c = 3;
arr[0]= &a;
arr[1] = &b;
arr[2] = &c;
printf("a : %d, *arr[0] : %d \n", a, *arr[0]);
printf("b : %d, *arr[1] : %d \n", b, *arr[1]);
printf("b : %d, *arr[2] : %d \n", c, *arr[2]);
printf("&a : %p, arr[0] : %p \n", &a, arr[0]);
}
출력
1
2
3
4
a : 1, *arr[0] : 1
b : 2, *arr[1] : 2
b : 3, *arr[2] : 3
&a : 0x7ffe8a2fa4e4, arr[0] : 0x7ffe8a2fa4e4
위 코드를 뜯어보자
1
int *arr[3];
이 부분은 말 그대로 int,char형 포인터와 같이 배열의 형을 int*로 선언한 것이다
여기서 배열의 각각의 원소는 int 를 가리키는 포인터 형으로 선언된다
1
2
3
arr[0]= &a;
arr[1] = &b;
arr[2] = &c;
이 부분은 배열에 각 인덱스에 변수에 주소를 집어넣은 것이다
그러니
1
2
3
a : 1, *arr[0] : 1
b : 2, *arr[1] : 2
b : 3, *arr[2] : 3
위와 같은 출력이 나온 것이다