포인터 완전정복
- 프로그래밍/C/C++
- 2018. 6. 28.
포인터 완전정복
이글은 이해하기 전에 코드 분석을 할줄 알아야 이해가 된다.
코드 분석도 실력이니 모두 코드 분석을 해야된다.
일단 배우기 전에 알아야 할 것이 있다.
배열 이름은 배열의 시작 주소 라는 것이다..
그게 무슨 말이야?? 한번 코드를 보고 분석을 해보면 된다.
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { int array[3] = { 10, 20, 30 }; printf("%x %x %x\n", array, array + 0, &array[0]); printf("%x %x\n", array + 1, &array[1]); printf("%x %x\n", array + 2, &array[2]); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <stdio.h> int main() { int array[3] = { 10, 20, 30 }; int *p = array; printf("%x %x %x\n", p, p + 0, &p[0]); printf("%x %x\n", p + 1, &p[1]); printf("%x %x\n", p + 2, &p[2]); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> int main() { int array[3] = { 10, 20, 30 }; int *p = array; printf("%d %d %d\n", *p, *(p + 0), p[0]); printf("%d %d\n", *(p + 1), p[1]); printf("%d %d\n", *(p + 2), p[2]); return 0; } | cs |
주소의 가감산을 이용해서 배열에 접근을 해보겠다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main() { int array[3] = { 10, 20, 30 }; int *p = array; p = array + 1; printf("%d %d %d\n", p[-1], p[0], p[1]); printf("%d %d %d\n", *p, *(p + 1), *(p + 2)); p = array + 2; printf("%d %d %d\n", p[-2], p[-1], p[0]); printf("%d %d %d\n", *(p - 2), *(p - 1), *p); return 0; } | cs |
포인터를 이용해서 배열의 값을 변경할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> int main() { int array[3] = { 10, 20, 30 }; int *p = array; *p = 40; printf("%d %d %d\n", p[0], p[1], p[2]); *(p + 1) = 50; printf("%d %d %d\n", p[0], p[1], p[2]); *(p + 2) = 60; printf("%d %d %d\n", p[0], p[1], p[2]); return 0; } | cs |
위의 코드를 보고 배열을 저렇게 사용 할 수 있으면 문자 배열도 사용할 수 있을거 같지 않나?? 라는 생각이 든다.
한번 해보자.
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { char array[] = { 'A', 'B', 'C' }; char *p = array; printf("%c %c %c\n", p[0], p[1], [2]); printf("%c %c %c\n", *(p + 0), *(p + 1), *(p + 2)); return 0; } | cs |
이번에는 배열 포인터를 알아보겠다.
배열 포인터는 2차원 배열을 포인터로 간접접근을 하기위해 쓰이는 방법이라고 보면 된다.
배열 포인터는 int (*p)[3]; 이렇게 선언을 해주면 된다.
자료형 (*이름)[열 길이] 라고 보면 된다.
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { int array[2][3] = { 10,20,30,40,50,60 }; int(*p)[3] = array; printf("%d %d %d\n", p[0][0], p[0][1], p[0][2]); printf("%d %d %d\n", p[1][0], p[1][1], p[1][2]); return 0; } | cs |
1차원적으로도 접근이 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> int main() { int array[2][3] = { 10,20,30,40,50,60 }; int *p = array; printf("%d %d %d\n", p[0], p[1], p[2]); printf("%d %d %d\n", p[3], p[4], p[5]); return 0; } | cs |
포인터 배열에 대해서 알아보자. 말 그대로 우리가 배열을 선언 하듯이 포인터도 배열이 있다.
자료형 *이름[몇줄?]; 이렇게 변수를 만들어 주면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main() { int a = 10, b = 20, c = 30; int *pointer[3] = { &a, &b, &c }; printf("%x %x %x\n", &a, &b, &c); printf("%x %x %x\n", pointer[0], pointer[1], pointer[2]); printf("%x %x %x\n", *(pointer + 0), *(pointer + 1), *(pointer + 2)); printf("\n"); printf("%d %d %d\n", a, b, c); printf("%d %d %d\n", *pointer[0], *pointer[1], *pointer[2]); printf("%d %d %d\n", **(pointer + 0), **(pointer + 1), **(pointer + 2)); return 0; } | cs |
< 출력 >
문자열 배열에 대해서 알아보자.
1 2 3 4 5 6 7 8 9 10 | #include <stdio.h> int main() { char arr[3] = "ABC"; printf("%c %c %c\n", arr[0], arr[1], arr[2]); printf("문자열 크기 출력 : %d\n", sizeof(arr)); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { char arr[] = "ABC"; printf("%s\n", arr); printf("%s\n", arr + 1); printf("%s\n", arr + 2); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { char arr1[] = { 'A', 'B', 'C', 'D', '\0' }; char arr2[] = { 'A', 'B', 'C', 'D' }; printf("%s\n", arr1); printf("%s\n", arr2); return 0; } | cs |
포인터와 문자열에 대해서 알아보자.
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h> int main() { char *p = "ABCD"; printf("%s\n", p); printf("%s\n", p + 1); printf("%s\n", p + 2); printf("%s\n", p + 3); return 0; } | cs |
포인터는 항상 주소만 보관하는 변수이다. 그리고 포인터를 통해 문자열을 변경하면 에러가 난다.
왜?? 문자는 상수이기 때문이다. 만약 문자를 변경하고 싶으면 문자열을 배열에 저장해서 변경하면 된다.
포인터 정말 쉽죠잉??
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main() { char a = 'A'; char b = 'B'; char *const p = &a; *p = 'C'; printf("%c\n", *p); printf("%c\n", a); p = &b; return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int main() { char a = 'A'; char b = 'B'; const char *const p = &a; *p = 'C'; return 0; } | cs |
void형 포인터에 대해서 알아보겠다. void는 ~이 하나도 없다 라는 뜻이다.
void형 포인터는 자료형이 없는 포인터 변수라고 보면 된다. int형이든 char형이든 아무거나 다 들어간다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main() { char c = 10; double d = 3.1; void *e = &c; printf("e의 주소값 : %x\n", e); printf("c의 주소값 : %x\n", &c); e = &d; printf("e의 주소값 : %x\n", e); printf("d의 주소값 : %x\n", &d); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> int main() { char c = 10; double d = 3.1; void *e = &c; printf("e의 주소값 : %x\n", e); printf("c의 값 : %d\n", *(char*)e); e = &d; printf("e의 주소값 : %x\n", e); printf("d의 값 : %lf\n", *(double*)e); return 0; } | cs |
이번에는 함수 포인터에 대해서 알아보자.
함수 포인터 선언은 자료형(*이름)(매개변수) 이렇게 선언을 해주면 된다.
그냥 코드로 보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> void hello() { printf("코드를 분석해라의 블로그\n"); } int add(int i) { i = i + 1; return i; } int main() { int a = 10; int(*fp)() = hello; int(*fp1)(int) = add; fp(); fp = add(a); a = fp1(a); printf("%d\n", a); return 0; } | cs |
이제 구조체 포인터에 대해서 알아보자.
① 구조체 멤버 변수 포인터
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <stdio.h> struct point { int *a; int *b; }; int main() { int num1 = 4; int num2 = 5; struct point p1; p1.a = &num1; p1.b = &num2; printf("%x %x\n", p1.a, p1.b); printf("%x %x\n", &num1, &num2); p1.a = NULL; //구조체 이름과 구조체 첫번째 멤버 주소값 같음. printf("%x %x\n", &p1, &p1.a); return 0; } | cs |
뭐.. 나머지는 설명은 안해도 알거라고 밑는다 모르겠다면 위에부터 다시 읽는것을 추천한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> struct point { int *a; int **b; }; int main() { int num1 = 10; struct point p1; p1.a = &num1; p1.b = &p1.a; printf("%d %d %d\n", num1, *p1.a, **p1.b); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> struct point { char name[20]; char url[40]; }; int main() { struct point po = { "aossuper7", "aossuper8.tistory.com" }; struct point *p = &po; printf("%s %s\n", po.name, po.url); printf("%s %s\n", (*p).name, (*p).url); printf("%s %s\n", p->name, p->url); return 0; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <stdio.h> struct point { char name[20]; char url[40]; }; int main() { struct point po = { "aossuper7", "aossuper8.tistory.com" }; struct point *p = &po; struct point **pp = &p; printf("%s %s\n", po.name, po.url); printf("%s %s\n", (*p).name, (*p).url); printf("%s %s\n", p->name, p->url); printf("%s %s\n", (**pp).name, (**pp).url); printf("%s %s\n", (*pp)->name, (*pp)->url); return 0; } | cs |
⑤ 자기 참조 구조체와 외부 참조 구조체
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h> struct point { char name[20]; int money; struct point *link; }; int main() { struct point p1 = { "aossuper7", 90, NULL }; struct point p2 = { "aossuper8", 80, NULL }; struct point p3 = { "aossuper9", 70, NULL }; p1.link = &p2; p2.link = &p3; printf("%s %d\n", p1.name, p1.money); printf("%s %d\n", p1.link->name, p1.link->money); //p1.link 안에 p2의 name, money 출력 printf("%s %d\n", p1.link->link->name, p1.link->link->money); //p1.link 안에 p2.link 안에 p3의 name, money 출력 return 0; } | cs |
이런식으로 되어있다. 포인터를 배울때는 항상 그림으로 생각을 하면 편하다.
⑤ - 1 외부 참조 구조체
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <stdio.h> struct point { int a; int b; }; struct blog { char name[20]; struct point *link; }; int main() { struct blog b1 = { "aossuper7", NULL }; struct blog b2 = { "aossuper8", NULL }; struct point p1 = { 30, 40 }; struct point p2 = { 10, 20 }; b1.link = &p1; b2.link = &p2; printf("%s %d %d\n", b1.name, b1.link->a, b1.link->b); printf("%s %d %d\n", b2.name, b2.link->a, b2.link->b); return 0; } | cs |