포인터의기초 (2) - 포인터 사용하기1

43
C 언언언 언언언 (1) 언언언언 언언언 "언언언언언 언언언언" NHN NEXT 언언언 언언언언 언 언언언언언언 언언언 언언언 언언언 .

Upload: hoyoung-jung

Post on 17-Jan-2015

447 views

Category:

Documents


1 download

DESCRIPTION

포인터의기초 (2) - 포인터 사용하기1

TRANSCRIPT

Page 1: 포인터의기초 (2) - 포인터 사용하기1

C 언어와 포인터 (1)

포인터의 사용법

"완전학습을 지향하는" NHN NEXT 정호영

나눔고딕 및 나눔고딕코딩 글꼴을 설치해 주세요 .

Page 2: 포인터의기초 (2) - 포인터 사용하기1

포인터의 용도

1. 원하는 메모리 번지에 직접 값을 쓰기2. 변수의 동적 할당3. Call by Reference4. 배열과 문자열의 조작5. 구조체의 효율적 사용

등등…

Page 3: 포인터의기초 (2) - 포인터 사용하기1

0. void 포인터 이야기

Page 4: 포인터의기초 (2) - 포인터 사용하기1

void * 는 무엇이든 담을 수 있는 그릇입니다 .

void *ptr;int a = 10;double d = 3.14;ptr = &a; //okptr = &d; //ok

Page 5: 포인터의기초 (2) - 포인터 사용하기1

void 포인터에 * 연산자를 사용하면 어떻게 될까요 ?

void *ptr;double d = 3.14;ptr = &d; //okprintf("%f\n", *ptr);

Page 6: 포인터의기초 (2) - 포인터 사용하기1

void 포인터는 포인티의 타입이 정해지지 않았기 때문에 읽을 수 없습니다 .

그럼 어떻게void 포인터의 포인티 값을 읽을까요 ?

Page 7: 포인터의기초 (2) - 포인터 사용하기1

정답은 캐스팅 ( 형변환 ) 입니다 .

1. void 포인터를 포인티의 포인터 타입으로 형변환

2. * 연산자를 붙여서 값을 읽는다 .

예제

(double *) ptr

void *ptr;double d = 3.14;ptr = &d; //okprintf("%f\n", *((double *)ptr)); //ok

*((double *) ptr)

Page 8: 포인터의기초 (2) - 포인터 사용하기1

불편한 void* 를 왜 사용하는지는 조금 뒤에…

Page 9: 포인터의기초 (2) - 포인터 사용하기1

1. 메모리에 직접 값을 쓰기

Page 10: 포인터의기초 (2) - 포인터 사용하기1

메모리의 원하는 번지에 직접 값을 쓰는 경우가 있습니다 . 하드웨어를 직접 제어하는 경우 , 많이 사용합니다 .

예 ) 0x378 번지의 값이 1 이면 USB 가 살아있다 .

char *alive = (char *) 0x378;if (*alive == 1) { //USB 가 살아 있구나 . 뭘 하지 ?

} else { //USB 가 안 살아있음 , 다른 일을 하자 .}

Page 11: 포인터의기초 (2) - 포인터 사용하기1

2. 동적할당

malloccallocfree

Page 12: 포인터의기초 (2) - 포인터 사용하기1

지난 시간에 우리 마음대로 사용할 수 있는 heap 이라고 하는 공간이 있다고 했습니다 .

이 공간을 배정받는 함수

malloc()calloc()

반납하는 함수 free()

Page 13: 포인터의기초 (2) - 포인터 사용하기1

void *malloc(size_t size);

void free(void *ptr);

void *calloc(size_t n, size_t size);

우리가 요청한 크기만큼 힙에 공간을 할당해 줍니다 .리턴값은 요청한 공간의 시작주소입니다 .

free 함수는 할당한 메모리를 해제할 때 사용합니다 .할당하고 해제하지 않으면 leak 이 발생할 수 있습니다 .

Page 14: 포인터의기초 (2) - 포인터 사용하기1

예제 ) int 크기만큼 공간을 할당받고 싶어요 .

//NAVER cast void * to int * in this case!int *ptr = malloc(sizeof(int));*ptr = 50;printf("%d\n", *ptr);free(ptr);

Page 15: 포인터의기초 (2) - 포인터 사용하기1

예제 ) int 3 개만큼 동적 할당을 하고 싶어요 .

* 포인터의 덧셈이 잠깐 나왔습니다 .

int *ptr = malloc(sizeof(int)*3);int *temp;*ptr = 10;temp = ptr + 1;*temp = 20;printf("%d\n", *ptr); //10printf("%d\n", *(ptr + 1)); //?

Page 16: 포인터의기초 (2) - 포인터 사용하기1

예제 ) calloc 을 사용해 봅시다 .

* 포인터의 덧셈이 잠깐 나왔습니다 .

int *ptr = calloc(3, sizeof(int));int *temp;*ptr = 10;temp = ptr + 1;*temp = 20;printf("%d\n", *ptr); printf("%d\n", *(ptr + 1));

Page 17: 포인터의기초 (2) - 포인터 사용하기1

3. Call by Reference

Page 18: 포인터의기초 (2) - 포인터 사용하기1

C 언어의 먼 선조인 포트란은함수에 인자를 전달할 때 원본을 그대로 전달했습니다 .

아래 언어가 포트란이라면…void foo(int i) { i = i + 10; }

main() { a = 100; foo(a); printf("%d\n", a);}

// 포트란이라면 ( 오해하지 마세요 )// 200// C 니까100

Page 19: 포인터의기초 (2) - 포인터 사용하기1

조금 더 자세한 설명을 위해 용어를 정의합니다 .

argument: 실제 호출하는 쪽에서는 넘겨주는 변수parameter: 함수의 정의에 있는 변수

앞쪽에서 parameter: iargument: a

모르는 분이 엄청 많습니다 .

Page 20: 포인터의기초 (2) - 포인터 사용하기1

중요한 건 C 언어는

argument 에서 parameter 로 갈 때 복사본을 전달해 준다는 사실입니다 .

이것을 call by value 라고 합니다 .

-- return 값도 마찬가지로 복사가 일어납니다 .

Page 21: 포인터의기초 (2) - 포인터 사용하기1

call by value - argument 의 값이 바뀌지 않는다 . - 복사본을 전달한다 .

call by reference - argument 의 값이 바뀐다 . - 원본을 전달한다 .

call by value 는 성능상 문제가 되기도 합니다 .

Page 22: 포인터의기초 (2) - 포인터 사용하기1

그런데 C 언어에는 call by reference 가 없습니다 !!뭐라고 ??

Page 23: 포인터의기초 (2) - 포인터 사용하기1

Java

- 일반 변수는 call by value- 배열 , 클래스 등 복합 타입 변수는 call by reference- 포인터는 완전히 사라졌다 !

Page 24: 포인터의기초 (2) - 포인터 사용하기1

c++

- 선언에 & 를 사용하면 참조 연산자 !- c++ 최대의 실수- 포인터도 여전히 사용할 수 있다 .

프로그래머의 욕심은 끝이 없고 , 같은 실수를 반복한다 .

Page 25: 포인터의기초 (2) - 포인터 사용하기1

c# = C++ + C++

기본 골격은 java 와 같지만 내 마음대로 선언도 가능

parameter 에 ref 키워드 사용하면 call by referenceargument 에도 반드시 ref 키워드를 사용해야 함

Page 26: 포인터의기초 (2) - 포인터 사용하기1

C 언어에는

call by reference 흉내가 있습니다 .call by address 라고 하는 게 더 맞는 표현입니다 .이 때 포인터를 사용합니다 !

Page 27: 포인터의기초 (2) - 포인터 사용하기1

void foo(int *i) { *i = *i + 10;}

int main(void) { int a = 100; foo(&a); printf("%d\n", a); return 0;}

// 실행 결과200

Page 28: 포인터의기초 (2) - 포인터 사용하기1

int 변수에 대해 call by reference 를 하기 위해서int * 를 사용했습니다 .

char 변수의 call by reference 를 하려면 ?double 변수의 call by reference 를 하려면 ?

int* 변수의 call by reference 를 하려면 ?

Page 29: 포인터의기초 (2) - 포인터 사용하기1

int 변수에 대해 call by reference 를 하기 위해서int * 를 사용했습니다 .

char 변수의 call by reference 를 하려면 ? char *double 변수의 call by referenc 를 하려면 ? double *

int* 변수의 call by reference 를 하려면 ? int **

Page 30: 포인터의기초 (2) - 포인터 사용하기1

int myalloc(int *ptr, unsigned int size) 를 구현합니다 .- int 전용 동적할당 함수- 성공하면 size, 실패하면 -1 을 리턴

int myalloc (int *ptr, unsigned int numb) { //implement}

int main(void) { int *ptr = NULL; int ret = myalloc(ptr, 1); if (ret == -1) return 1; *ptr = 5054; printf("%d\n", *ptr); return 0; }

Page 31: 포인터의기초 (2) - 포인터 사용하기1

어떻게 구현하든지 앞의 함수는 틀렸습니다 .argument 로 전달한 ptr 은 값이 바뀌지 않기 때문입니다 .int * 를 call by reference 로 호출하려면int ** 를 사용합니다 .

int myalloc (int **ptr, unsigned int numb) { *ptr = malloc(sizeof(int) * numb); if (ptr) return numb; else return -1;}

int main(void) { int *ptr = NULL; int ret = myalloc(&ptr, 1); if (ret == -1) return 1; *ptr = 5054; printf("%d\n", *ptr); return 0; }

Page 32: 포인터의기초 (2) - 포인터 사용하기1

이중 포인터의 용법 1

단일 포인터의 call by reference 를 위해

Page 33: 포인터의기초 (2) - 포인터 사용하기1

4. 포인터와 배열

Page 34: 포인터의기초 (2) - 포인터 사용하기1

C 언어의 배열은 90% 정도 포인터랑 닮았습니다 .포인터에도 배열 연산을 그대로 쓸 수 있습니다 !

int arr[5]; double b[]= {1, 2, 3, 4, 5}; // 자동으로 5 개가 됨

Page 35: 포인터의기초 (2) - 포인터 사용하기1

이 코드를 실행해 보고 알아낸 사실을 토의해 봅시다 .

int main(void){ int a[5] = {1,2,3,4,5}; printf("%p\n", a); printf("%p %d\n", &a[0], a[0]); printf("%p %d\n", &a[1], a[1]); return 0;}

Page 36: 포인터의기초 (2) - 포인터 사용하기1

놀랍게도 배열이름 변수에는 배열의 첫번째 원소의 주소가 들어갑니다 !

arr = &arr[0]

int 의 주소를 저장한 변수 = int *

포인터를 배열에 대입해서 쓸 수 있을까요 ?

Page 37: 포인터의기초 (2) - 포인터 사용하기1

HULL?

int main(void){ int a[5] = {1,2,3,4,5}; int *ptr = a; //not &a, &a[0] 도 OK printf("%p\n", ptr); printf("%p %d\n", ptr, *ptr); printf("%p %d\n", ptr + 1, *(ptr +1)); return 0;}

Page 38: 포인터의기초 (2) - 포인터 사용하기1

HULL?

무언가 수상하지요 ?

int main(void){ int a[5] = {1,2,3,4,5}; int *ptr = a; //not &a, &a[0] 도 OK printf("%p\n", ptr); printf("%p %d\n", ptr + 0, *(ptr + 0)); printf("%p %d\n", ptr + 1, *(ptr + 1)); return 0;}

int main(void){ int a[5] = {1,2,3,4,5}; printf("%p\n", a); printf("%p %d\n", &a[0], a[0]); printf("%p %d\n", &a[1], a[1]); return 0;}

Page 39: 포인터의기초 (2) - 포인터 사용하기1

HULL?

포인터가 미쳐 날뛰고 있습니다 !!

int main(void) { int a[5] = {1,2,3,4,5}; int *ptr = a; printf("%p\n", ptr); printf("%p %d\n", &ptr[0], ptr[0]); printf("%p %d\n", &ptr[1], ptr[1]); return 0;}

int main(void) { int a[5] = {1,2,3,4,5}; printf("%p\n", a); printf("%p %d\n", &a[0], a[0]); printf("%p %d\n", &a[1], a[1]); return 0;}

Page 40: 포인터의기초 (2) - 포인터 사용하기1

사실 a[i] 는 *(a + i) 랑 완전히 똑같습니다 .사람의 편의를 위해 읽기 쉽게 만들어 준 거랍니다 .이런 걸 syntax sugar 라고 합니다 .

a[i] = *(a + i)

* 나중에 다시 나오지만 a->b 도 syntax sugar 입니다 .

Page 41: 포인터의기초 (2) - 포인터 사용하기1

다 아는 산수 한 번 해볼까요 ?a[3] = *(a + 3) = *(3 + a) = 3[a]

Page 42: 포인터의기초 (2) - 포인터 사용하기1

int main() { int a[5] = {1,2,3,4,5}; for (int i = 0; i < 5; i++) printf("%d\n", i[a]); return 0;}

( 형 ~ 오빠 ~, 이거 어디가 잘못된 건지 좀 가르쳐 주세요 .)

잘못된 건 비뚤어진 너의 마음이다 ㅋㅋ .죄송합니다 .

된다고 절대 사용하면 안 됩니다 .

Page 43: 포인터의기초 (2) - 포인터 사용하기1

To be continued…