많은 입문자를 좌절시킨 그 이름 포인터.
아직도 아리쏭 하지만 C의 최고의 무기는 포인터라고 했다.
우리 함께 라피신 통과를 위해 공부해보자.
포인터는 아주 쉽게 생각해 주소를 저장하는 변수라고 생각하자!
- 포인터 선언 방법
포인터는 이렇게 자료형 뒤에 *를 붙이는 것으로 사용할 수 있다!
int* p;
*의 용도는 크게 두 개로 나뉜다.
1.포인터를 위와 같이 선언할 때.
2.해당 주소의 값에 접근할 때 사용.
&의 용도는 한 가지이다.
1. 원하는 변수의 주소를 나타내고 싶을 떄.
int main(){
int a = 5;
int* pa;
pa = &a;
printf("주소값을 확인하려면 그냥 변수값을 쓰자. %x \n", pa);
printf("주소의 값을 접근하려면 이렇게 사용하자. %d \n", *pa);
return 0;
};
//출력
주소값을 확인하려면 그냥 변수값을 쓰자. x234e0
주소의 값을 접근하려면 이렇게 사용하자. 5
위와 같이 pa는 a의 주소값을 할당받고,
*pa는 a의 값이 출력되게 된다.
int minus(int num1, int num2) {
return num2-num1;
};
여기서 포인터를 활용하는 이유는 그냥 값을 전달해준다면 컴퓨터는 인식하지 못한다.
따라서 a와 b의 주소를 주고 a와 b의 값을 서로 바꿔주면 된다.
+ swap함수 단계에선 값이 바뀌지만, main함수로 돌아가면 다시 똑같은 값이다.
//2.넘어온 a와 b의 주소값의 값을 받아주고 리턴한다.
int swap(int* num1, int* num2){
int temp;
temp = *num1;
*num1 = *num2;
*num2 = temp;
return 0;
};
int main() {
int a = 5;
int b = 10;
int* pa;
int* pa2;
//a의 주소값을 pa에 할당
pa = &a;
//b의 주소값을 pa2에 할당
pa2 = &b;
printf("포인터끼리의 연산 : b - a : %d \n ", *pa - *pa);
printf(" add함수 사용 : %d \n , add(*pa, *pa2));
//swap함수 사용
//1.a와 b의 값을 넘겨주는 것이 아닌 주소를 넘겨준다.
swap(&a, &b);
printf("swap함수 사용 : a = %d , b = %d \n", a , b);
return 0;
};
#include <stdio.h>
int main(){
int a;
int* pa;
pa = &a;
char b;
double c;
char* pc = &b;
double* pb = &c;
printf("pa의 값 : %p \n", pa);
printf(" (pa + 1)의 값 : %p \n", pa + 1);
printf("pc의 값 : %p \n", pc);
printf(" (pc + 1)의 값 : %p \n", pc + 1);
printf("pb의 값 : %p \n", pb);
printf("(pc + 1)의 값 : %p \n", pb + 1);
printf("pa의 값 : %p \n", pa);
printf(" (pa - 1)의 값 : %p \n", pa - 1);
printf("pc의 값 : %p \n", pc);
printf(" (pc - 1)의 값 : %p \n", pc - 1);
printf("pb의 값 : %p \n", pb);
printf("(pc - 1)의 값 : %p \n", pb - 1);
return 0;
}
// 출력
// pa의 값 : 000000000061FE04
// (pa + 1)의 값 : 000000000061FE08
// pc의 값 : 000000000061FE03
// (pc + 1)의 값 : 000000000061FE04
// pb의 값 : 000000000061FDF8
// (pc + 1)의 값 : 000000000061FE00
// pa의 값 : 000000000061FE04
// (pa - 1)의 값 : 000000000061FE00
// pc의 값 : 000000000061FE03
// (pc - 1)의 값 : 000000000061FE02
// pb의 값 : 000000000061FDF8
// (pc - 1)의 값 : 000000000061FDF0
...
이렇게 결과를 보면 자료형 크기만큼 더해진다.
pa의 경우는 16진수를 기준으로 4씩 커지고, pb는 char이 1byye니 1씩, double은 8씩
커짐을 알 수 있다.
반대로 빼는 연산을 해도 동일하다.
// #include <stdio.h>
// int main(){
// int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
// for(int i = 0; i < 10; i++){
// printf("%d번째의 주소값은 : %p입니다. \n", i, &arr[i]);
// }
// return 0;
// }
//출력
// 0번째의 주소값은 : 000000000061FDF0입니다.
// 1번째의 주소값은 : 000000000061FDF4입니다.
// 2번째의 주소값은 : 000000000061FDF8입니다.
// 3번째의 주소값은 : 000000000061FDFC입니다.
// 4번째의 주소값은 : 000000000061FE00입니다.
// 5번째의 주소값은 : 000000000061FE04입니다.
// 6번째의 주소값은 : 000000000061FE08입니다.
// 7번째의 주소값은 : 000000000061FE0C입니다.
// 8번째의 주소값은 : 000000000061FE10입니다.
// 9번째의 주소값은 : 000000000061FE14입니다.
위 3번에서 공부한 대로 우리는 해당 반복문의 결과를 어느 정도 유추할 수 있을 것이다.
p+1는 p의 주소값에 p + 4를 의미하고 , p+3은 p + (3x4)를 의미한다는 것을 알 수 있다.
#include <stdio.h>
int main(){
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
int* parr;
int i;
parr = &arr[0];
for (int i = 0; i < 10; i++)
{
printf("arr[%d]의 주소 : %p \n", i, &arr[i]);
printf("(parr + %d)의 값 : %p \n", i, (parr + i));
if(&arr[i] == (parr +i)){
printf(" --> 일치 \n");
}else{
printf(" --> 불일치 \n");
}
}
return 0;
}
//출력
// arr[0]의 주소 : 000000000061FDE0
// (parr + 0)의 값 : 000000000061FDE0
// --> 일치
// arr[1]의 주소 : 000000000061FDE4
// (parr + 1)의 값 : 000000000061FDE4
// --> 일치
// arr[2]의 주소 : 000000000061FDE8
// (parr + 2)의 값 : 000000000061FDE8
// --> 일치
// ...
parr+1 parr+2 ...가 될 때마다 다음 인덱스의 값을 의미하기 때문에,
&arr[1]이나 parr+1은 동일하다.
예를 들어
arr[1]랑 *(parr+1)를 하게 된다면 요소의 값은 의미하기 때문에
아래와 같은 출력이 나온다.
+ 추가 - 배열에서 배열의 이름은 배열의 첫 번째 인덱스를 나타낸다.
// arr[0]의 주소 : 1
// (parr + 0)의 값 : 1
// --> 일치
// arr[1]의 주소 : 2
// (parr + 1)의 값 : 2
// --> 일치
// arr[2]의 주소 : 3
// (parr + 2)의 값 : 3
// --> 일치
// arr[3]의 주소 : 4
// #include <stdio.h>
#include <stdio.h>
int main(){
int a;
int *pa;
int **ppa;
pa = &a;
ppa = &pa;
a = 3;
printf(" a : %d // *pa : %d // **ppa : %d \n", a , *pa, **ppa);
printf(" a : %p // pa : %p // *ppa : %p \n", &a, pa, *ppa);
printf(" a : %p // ppa : %p \n", &pa, ppa);
return 0;
}
// 출력
// a : 3 // *pa : 3 // **ppa : 3
// a : 000000000061FE14 // pa : 000000000061FE14 // *ppa : 000000000061FE14
// a : 000000000061FE08 // ppa : 000000000061FE08
위 코드에서
pa는 a를 가리키고, ppa는 pa를 가리킨다.
ppa -> pa -> pa라고 생각하면 된다.
그러니 즉 동일한 값이 출력된다.
int main(){
int arr[2][3] = { {1, 1, 1}, {2, 2, 2}};
printf("전체 크기 : %d \n", sizeof(arr));
printf("총 열의 갯수 : %d \n", sizeof( arr[0]) / sizeof(arr[0][0]));
printf("총 행의 개수 : %d \n" , sizeof(arr) / sizeof(arr[0]));
return 0;
}
//출력
//전체 크기 : 24
//총 열의 개수 : 3
//총 행의 개수 : 2
위에서 arr[0]이라 선언하면 자동적으로 컴파일러가 arr[0][3]이라고 생각한다.
첫 번째 결과의 이유는 int형 요소가 3개인 배열 2개가 존재하니 4 * 3 * 2라서 결과가 이렇게 나온 것이고,
두 번째는 arr[0]은 arr[0][3]이니 즉 12이다. (4*3) 거기다 나누기 arr[0][0]이니깐 (4)
12 /4 3이다.
세 번째는 arr = 24m arr[0]은 12이니깐 24 / 12 즉 2.