전산 SMP 7 주차 2015. 11. 17 김범수 bskim45@gmail.com Special thanks to 박기석 (kisuk0521@gmail.com)
지난내용복습 Pointer and its applications
Pointer 란? 데이터접근에사용되는주소를저장하는타입 포인터도타입이다 int a = 4; char c = a ; int *p = &a; p : 0x10002200 0x10002204 0x10002205 4 a a c 메모리는긴일차원공간으로볼수있고, 각 byte 는자신만의주소를가지고있다.
Pointer 선언, 사용 Declaration int *ptr; 정수형데이터가저장된메모리의위치 ( 주소 ) 를저장할수있는포인터변수 포인터의타입크기 4 byte!(32 bit) ex) int : 4byte, char : 1 byte
Pointer 선언, 사용 int a = -123; int *p = &a; 0x10002200 변수 * & 주소 -123 0x10002200 a p p 가 a 를가리키고있다 포인터변수도결국 변수 이기때문에자신이가리키는주소를메모리어딘가에저장하고있다.
초기화되지않은변수를사용하면위험하다
함수 -Pointer 사용특징 포인터변수도일반변수처럼함수인자, 반환형으로사용가능! 함수가여러개의리턴값을가져야할때 여러인자들을포인터로받아서수정할수있다! 실제그메모리위치를찾아가값을바꿀수있다. 주의!! 함수안에서만들어진 local variable 을가리키는포인터는반환할수없다.
Local Variable 의주소를리턴하는경우 Local variable 는그함수 ( 블럭 ) 이끝나면사라진다. 알수없는곳으로의포인터를반환하는것과같다. int main (void) { int* p; p } p = fun(); printf( %d, *p); int* fun (void) {? } int a = 5; return &a; a 5
Pointer to Pointer ( 다중포인터 ) 포인터를가리키는포인터! 포인터도주소를저장하는하나의변수! 이기때문에메모리특정위치에저장된다. 0x10002000 0x10002100 0x10002200 0x10002100 0x10002200-123 q p a int a = -123; int *p = &a; int **q = &p;
Pointer 를선언할때타입이필요한이유 int *p; char *q; 어차피크기는 4byte(32 bit) 로다똑같은거아닌가? 컴퓨터가포인터로메모리에접근해데이터를읽어올때! 그포인터타입에따라가져오는 byte 수가달라진다. int * 포인터면여기서부터 4byte 까지읽어서정수로쳐 char * 포인터면여기서부터 1byte 만읽어서문자로쳐
void * 어떤데이터타입과도연관되지않는일반적형태의포인터 어떤포인터값도 void pointer 에넣어줄수있다. 어떤포인터값에도 void pointer 값을넣어줄수있다. 단, void pointer 는그상태그대로는 dereferencing 할수없다! Why? 타입이없음 내가보려는데이터의타입이뭐지? compile error casting 을통해서 dereferencing 할수있다. int a = -123; void *p; p = &a; printf( %d, *(int *)p);
배열포인터 (Pointer to Arrays) a &a[0]
포인터연산 포인터 p p ± n p + n * (sizeof (one element))
Arithmetic Operations on Pointers p + 5, 5 + p, p 5 p1 p2 p++, --p p1 >= p2 p1!= p2
Pointer Arithmetic 두포인터 p, q 에대하여가능한연산 p, q 모두같은데이터형을다루는포인터여야한다. p q, q p : p 의위치와 q 의위치의차이를단위수로계산 (int)p (int)q, (int)q (int)p : p 와 q 의주소값차이 * 주의 : p + q 는사용할수없다. ( 무엇보다의미가없다 ) 왜? (0x0000~0xffff)+(0x0000~0xffff) =?? 주소값 overflow 가일어날수있음 and 꼭 overflow 가아니더라도사용할수있는메모리주소는한정되어있기때문 15
Dereferencing *(a + i) a[i]
포인터와배열 int a[5]={10, 20, 30, 40, 50}; int *p=&a[2]; printf( %d %d %d %d\n,a[2], *(a+2), p[0], *(p+0)); // 30 출력 printf( %d %d %d %d\n,a[1], *(a+1), p[-1], *(p-1)); // 20 출력 a p 100 104 108 112 116 108 번지 10 20 30 40 50 a[0] a[1] a[2] a[3] a[4] p[-2] p[-1] p[0] p[1] p[2] *(a+0) *(a+1) *(a+2) *(a+3) *(a+4) *(p-2) *(p-1) *(p+0) *(p+1) *(p+2)
2 차원 Array table[2] == *(table+2) table[2][1] == (*(table+2))[1] ==*(table[2]+1) ==*(*(table+2)+1) 헷갈리니다차원배열에서는 Index 를사용하자
문자열과포인터 다시한번! 여러개의문자 + \0 이들어있는배열 배열의이름은첫번째 element 의주소와같다. str[0] H e l l o W o r l d! \0 str &str[0] 그래서 scanf 로 %s 받을때 & 안붙인다!
배열함수에넘겨주기 1 차원배열 Int ary1[10]; void function(int *ary); void function(int ary[]); 2 차원배열 int ary2[5][6]; void function(int (*ary)[6]); void function(int ary[][6]); 3 차원배열 (int ary[][3][4])
Scopes
Storage Class ( 변수의존재기간과접근범위 ) 지역변수정적변수전역변수 register 변수 지정자 auto static extern register 저장장소스택정적데이터영역정적데이터영역레지스터 선언위치함수내부함수내부 / 외부함수내부 / 외부함수내부 유효범위함수내부함수내부 / 외부프로그램전체함수내부 생존기간함수종료시프로그램종료시프로그램종료시함수종료시 초기값초기화안됨 0 으로초기화 0 으로초기화초기화안됨 22
언제어떤걸쓰나요 일반적으로전역변수의사용을자제하고, 지역변수인자동변수를이용 실행속도를빠르게하고싶을때 : 레지스터변수 함수나블록내부에서계속값을저장하고싶을때 : 정적지역변수 해당파일내부에서만변수를공유하고싶을때 : 정적전역변수 프로그램의모든영역에서값을공유하고싶을때 : 전역변수 전역변수는모든함수에서공유할수있는저장공간을이용할수있는장점이있지만어디선가잘못바꾸면프로그램전체에영향을미친다 위험해 함수의인자 (argument) 로선언된변수는지역변수와같이함수내부에서만유효
Dynamic Memory Allocation 동적할당
Two types of memory allocation
왜쓰나요? 배열의크기를입력받아그크기만큼의배열을만들고싶을때 int main(void) { int size; scanf( %d, &size); } int array[size]; Compile Error! 선언은항상맨위에와야한다. 선언후에배열이할당되어야하는데 이럴때는어떻게? 프로그램실행중에메모리를할당해데이터를저장할공간을생성하는방법
A Conceptual View of Memory We can refer to memory allocated in the heap only through a pointer.
Accessing Dynamic Memory
Dynamic Memory Allocation <stdlib.h> void *malloc (size_t size); void *calloc (size_t count, size_t size); void *realloc (void* ptr, size_t newsize); void free (void* ptr);
malloc 지정하는크기 (byte) 만큼메모리 ( 힙 ) 를할당 기본적으로 void * 로리턴 원하는타입으로 casting 해줘야한다 할당에실패한경우 NULL 리턴 void *malloc (size_t size); int* p = (int *)malloc(sizeof(int) * 4); int 형의크기가 4byte 이기때문에 16byte 의공간이동적으로할당 char *str = (char *)malloc(sizeof(char) * 10);
calloc void *calloc (size_t count, size_t size); count * size 만큼의메모리를할당하고해당힙영역을 0 으로초기화하여반환 int *p = (int *)calloc(4, sizeof(int)); char *str = (char *)calloc(10, sizeof(char)); malloc(sizeof(int)*4) calloc(4, sizeof(int))
realloc 할당받은메모리공간을새로운크기로재할당 void *realloc(void *ptr, size_t newsize); int *p = (int *)calloc(4, sizeof(int)); p = (int *)realloc(p, sizof(int) * 6); char *str = (char *)calloc(10, sizeof(char)); str = (char *)realloc(str, 20*sizeof(char));
free 동적할당한메모리반환 사용하지않을때 & 프로그램의마지막에반드시반환해야한다! void free (void * ptr); free(ptr); free(str);
동적할당으로 2 차원배열만들기 포인터배열 int **table; table = (int **)calloc (3, sizeof(int *)); table[0] = (int *)calloc(4, sizeof(int));
포인터의부적절한사용으로인한주요부작용들 Unreachable Memory ( 이공간을가리키는포인터가존재하지않는다 ) Dangling Pointer ( 어딘가가리키고있기는하지만, 그위치에뭐가있는지알수없다.) Buffer Overflow ( 할당되어있는양이상으로메모리를다루게되는경우 ) Segmentation Fault ( 잘못된주소접근 ) 35
허공에뜬메모리공간 (unreachable memory) { char *str; (1) str = (char *) malloc(200 * sizeof(char)); (2) (no free()!!) str = (char *) malloc(123 * sizeof(char)); (3) } (1) (2) (3) str str str??? 200 byte space 200 byte space Another 123 byte space
길잃은포인터 (dangling pointer) char *cp = NULL; (1) /*... */ { char c; cp = &c; (2) } /* c로할당되었던메모리공간이이시점에서할당해제된다. */ /* cp는이제길잃은포인터 (dangling pointer) 가되어버렸다. */ (3) (1) (2) (3) cp NULL cp cp??? c c c 변수가사라진상황. 이후에이공간이어떻게사용될지아무도모른다.
Buffer overflow 버퍼 (buffer) 컴퓨터내에서사용되는임시저장소 ( 메모리공간 ) 컴퓨터에서일어나는대부분의작업은효율을위해버퍼를많이사용한다. Buffer overflow Overflow = 넘친다 저장한내용이할당되어있는버퍼의영역바깥으로넘치는것 만일넘친영역이다른용도로사용되고있었다면, 그원래의값을파괴해버린다. 넘친영역의값이파괴됨을이용하여대상컴퓨터를혼란시켜침입하는기법이많이사용된다.
버퍼오버플로우의예 char cp[10]; (1) int a=0x12345678; strcpy(cp, "Hello, "); (2) printf("cp: %s a=%x\n", cp, a); strcat(cp, "World!"); (3) printf("cp: %s a=%x\n", cp, a); 의도한대로 cp: Hello, a=0x12345678 이출력됨??? 오류 or Buffer overflow - a 의값이바뀜 (1) (2) cp 10 byte space cp Hello,\0??? 10 byte space (3) cp Hello,Worl d!\0 10 byte space Buffer overflow!!
잘못된연산오류 (Segmentation fault) 지정한영역이외에엉뚱한위치 ( 주로 null 혹은쓰레기값 ) 를가리키는포인터가가리키는위치에다가어떤값을써넣으려고시도하는경우에발생한다. 현재각포인터가가리키는위치가유효한지꼼꼼히살펴봐야한다. e.g. int *p; // 초기화없이선언 *p = 123; // 초기화되지않은포인터 dereferencing 여긴어디나는누구? printf("%d\n", *p); //???
Problem 입력받은숫자크기의 integer array 를할당하고, array 에채울숫자를입력받은후출력하고, array 의 size 를다시입력받아서 array 를재할당한후숫자입력과출력을수행하는프로그램을만들자 malloc( 혹은 calloc) 과 realloc 함수를이용한다. find_min_max(int *arr, int size, int *max, int *min) 입력 : 최대 999999/ 최소 1 free 를잊지말자!
Tidy Up 동적으로할당된메모리는반드시시스템에반환해야함! 그래야다른프로그램이메모리공간을활용할수있다. ( 메모리 leak) 실행전에미리할당 vs 실행중에동적으로할당하고반환 공간이필요한만큼만쓰기때문에효율적! 메모리공간의크기는정해져있다. 얼마나큰공간이필요한지알수없을때 적당히크게? 공간이부족하거나, 공간이낭비되거나
다음시간 Enumeration, Structure, Union String (Advanced)