윤 홍 란 hryun@sookmyung.ac.kr 포인터 윤 홍 란 hryun@sookmyung.ac.kr
내용 포인터의 개념 포인터 연산 문자열과 문자 배열 그리고 문자열 포인터(배열 관점) 1차원 배열과 포인터(포인터 관점) 다차원 배열과 포인터 배열 포인터 배열을 함수의 인자로 전달하는 방법 포인터 배열 명령 라인 인수 프로그램 예제
포인터의 개념 개념 메모리의 특정 위치를 접근(access)하기 위한 mechanism. 포인터란 메모리의 임의의 주소를 지정하는 것. 포인터 변수란 임의의 주소를 저장하는 변수. 포인터 변수는 *기호로 선언. 예 int v; /* 변수 v */ &v /* v의 address */ int *vp; /* pointer 변수 */ vp = &v; 메모리 구조 주소 1 2 3 4 1000 메모리 끝 바이트 바이트 바이트 바이트
일반적인 변수의 경우 변수 선언의 의미 변수의 메모리 할당 구조 변수를 선언하면 컴파일러는 주소를 찾아 변수의 데어터형 크기만큼 메모리를 확보한다. 변수의 메모리 할당 구조 변수 TABLE 구조 변수 이름 시작 주소 데이터형 a 1000 int b 1002 float c 1006 int a = 10; float b = 10.11; float c; c = a + b a b c 각 변수가 독점적사용 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 10 10.11 20.11 2바이트 4바이트 결과값을 여기서부터 4 바이트 로 저장 c= a + b;
포인터사용 포인터의 의미 포인터는 자신이 가리키는 데이터가 있는 주기억장치의 주소를 갖고 있다. 포인터 이용 장점 데이터 접근 속도가 빠르다. 어떤 연산은 단지 포인터만을 사용해야 가능하다. 포인터가 할 수 있는 일 배열의 요소를 다룰 수 있다. 함수의 인수로 사용되어 데이터를 변경할 수 있다. 메모리를 동적으로 할당할 수 있다. 함수 호출을 동적으로 할 수 있다.
포인터사용 포인터의 선언 포인터 선언 형식 : 데이터형 *포인터 변수명, *포인터 변수명 ; 포인터의 선언 예 문자열 포인터 선언 : char *ps; 정수형 포인터 선언 : int *pi; 실수형 포인터 선언 : float *pf; Long형 포인터 선언 : long *pl; 주소 연산자(&): 단항 연산자로 대상체의 주소를 나타냄. 포인터 선언 형식 : 데이터형 *포인터 변수명, *포인터 변수명 ; 데이터형:포인터가 가리키는 대상체(object)의 데이터형 *:변수를 포인터로 정의하는 간접 연산자
포인터사용 포인터 변수의 초기화 포인터 선언과 동시에 초기화 : 데이터형 *포인터 변수명 = &변수명; int a=100; int *ip = &a; 포인터 선언 후 대입문을 이용한 초기화 : 포인터 변수명 = &변수명; int x = 100, *ip; ip = &x; int j = 100; char c = ‘A’; float f = 100.21; int *pj = &j; char *pc = &c; float *pf = &f; 100 102 104 1000 1001 1002 1003 1004 1005 1006 1000 1002 1003 100 ‘A’ 100.21 pj pc pf j c f pj pc pf
포인터사용 포인터 사용시 주의할 점 int형 변수의 주소 값은 int형 포인터 변수에, double형 변수의 주소 값은 반드시 double형 포인터 변수에 넣어야 한다. 포인터는 항상 기억장소를 가리키고 있어야 한다. 포인터는 사용하기 전에 어떤 변수를 가리키도록 초기화 되어야 한다.
포인터 연산 포인터 가능 연산 1) p = &x 포인터에 변수의 주소를 할당(p는 포인터 변수) 3) p++,p-- 증감 연산자를 이용한 포인터 증가, 감소 4) p1 – p2 두 개의 포인터의 뺄셈 5) p+4,p-2 포인터에 정수를 더하기, 빼기 6) if(pa<pb) 포인터 비교 포인터 불가능 연산 1) 포인터의 곱셈 p * 2 2) 포인터의 나눗셈 p/2 3) 모든 실수형 포인터 연산 p + 4.5
* 와 & i j p 1 2 ? i j p 1 2 i j p 1 2 i j p 2 2 * 는 '&'와 상반되는 연산자 임. (예) int i = 1, j = 2, *p; p = &i; /* i의 address를 p에 배정한다. */ p = &j; /* j의 address를 p에 배정한다. */ i = *p; /* i에 p가 point하는 값을 배정한다. */ /* == (i = j); */ i j p 1 2 ? i j p 1 2 i j p 1 2 i j p 2 2
포인터 연산 포인터에 정수를 더하거나 빼기의 의미 int * ip; /* 포인터 ip가 현재 주소 1000을 가리킨다 */ 포인터 변수에 정수를 더하는 것은 주소+정수가 아니다. 실제 주소 = 포인터가 가리키는 주소 + 정수 × 포인터 변수 데이터형 크기 (char: 1바이트, float: 4바이트, int: 2바이트, long: 4바이트, double: 8바이트) int * ip; /* 포인터 ip가 현재 주소 1000을 가리킨다 */ ip + 1 → 1002 (1000 + 1 × 2바이트) ip + 3 → 1006 (1000 + 3 × 2바이트) ip - 2 → 996 (1000 - 2 × 2바이트) float *fp; /* 포인터 fp가 현재 주소 2000을 가리킨다 */ fp + 1 → 2004 (2000 + 1 × 4바이트) fp + 3 → 2012 (2000 + 3 × 4바이트)
Call by Value와 Call by reference 값에 의하여 호출하고 인수의 값을 건네주는 방식 #include <stdio.h> void change(int x, int y); void main(void) { int a=3, b=5; printf("호출 전 a=%d, b=%d\n", a, b); change(a, b); printf("호출 후 a=%d, b=%d\n", a, b); } void change(int x, int y) { int temp; temp = x; x = y; y = temp; printf("change 함수 내 a=%d, b=%d\n", x, y); } 호출 전 a = 3, b = 5 Change 함수 내 a = 5, b = 3 호출 후 a = 3, b = 5
Call by Value와 Call by reference 참조에 의해 호출하고 인수의 address를 건네 주는 방식 void change(int *x, int *y) /* 두 개의 매개변수는 포인터 변수이다 */ void main( ) { …… change(&a, &b); /* 주소 연산자를 이용해서 변수의 주소를 넘긴다 */ … } void change(int *x, int *y) { int temp; temp = *x; *x = *y; *y = temp; }
(예). void swap(int. p, int. q). {. int temp;. temp =. p;. p =. q; (예) void swap(int *p, int *q) { int temp; temp = *p; *p = *q; *q = temp; } void main() { int i = 3, j = 5; swap(&i, &j); printf(" %d %d", i, j); } main swap p q 실행 결과 : 5 3 ↑ ↑ i j swap(&i, &j); temp 3 5
문자열과 문자 배열 그리고 문자열 포인터 문자열과 문자 배열의 차이점 문자 배열은 배열 원소인 문자가 연속적으로 메모리에 저장된 형태 문자열은 문자열의 각 원소가 연속적으로 메모리에 저장된 형태 char ch[5] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’}; 문자 배열의 메모리 구조 1000 1001 1002 1003 1004 a b c d e Ch[0] Ch[1] Ch[2] Ch[3] Ch[4] 문자열(“abcde”)의 메모리 구조 2000 2001 2003 2004 2005 2006 a b c d e \0
문자열과 문자 배열 그리고 문자열 포인터 문자열을 다루는 방법 문자열의 초기화 문자열을 문자 배열로 다루기 위해서는 마지막에 널(‘\0’)을 포함 시킨다. 문자열의 초기화 문자 배열 선언과 동시에 문자열 초기화 char ch[5]={‘a’, ‘b’, ‘c’, ‘d’, ‘e’}; char ch[5]={‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘\0’}; ① char ch[6] = “abcde”; →널(‘\0’) 문자를 포함한 크기만큼 개수를 지정 ② char ch[6] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘\0’}; →문자열을 개별 요소처럼 저장 ③ char ch[ ] = “abcde”; ③과 같은 경우는 선언 후에는 대입 연산자를 이용해서 문자 배열에 대입할 수 없다.
문자열과 문자 배열 그리고 문자열 포인터 포인터를 이용한 문자열의 초기화 선언 후 문자열을 대입하기 위해서는 아래 방법을 사용해야 한다. 1) 문자열의 각 문자를 문자 배열 원소에 대입한다. ch[0] = ‘a’;ch[1] = ‘b’;ch[2] = ‘c’;ch[3] = ‘d’;ch[4] = ‘e’;ch[5] = ‘\0’; 2) 문자열을 복사하는 라이브러리 함수 strcpy( )를 이용한다. strcpy(ch, “abcde”); 포인터를 이용한 문자열의 초기화 문자열의 시작 주소를 포인터가 가리키게 하여 포인터를 마치 배열명 대신 사용하여 문자열을 다룰 수 있다. char *ps = “abcde” 또는 char *ps;ps = “abcde”;
표준 라이브러리에 있는 문자열 조작 함수(string.h) char *strcat(char *s1, char *s2); 두개의 문자열 s1과 s2를 결합하여 s1에 결과 저장. s1은 결과를 저장할 수 있을 만큼의 충분한 공간을 point할 수 있도록 해야 함. 문자열 s1이 return 됨. int strcmp(char *s1, char *s2); 두 문자열이 인자로 전달됨. s1과 s2를 사전적 순서로 비교하여 s1이 작으면 음수, 크면 양수, 같으면 0을 return. int strlen(char *); '\0'을 제외한 문자의 개수를 returen. char *strcpy(char *s1, char *s2); 문자열 s2를 '\0'이 나올때까지 s1에 복사. s1의 내용은 s2의 내용으로 overwrite 됨 s1에 충분한 공간이 마련되어야 함. pointer s1이 return됨.
선언 및 초기화 수식 문장 char s1[] = "beautiful big sky country", s2[] = "how now brown cow"; 수식 값 strlen(s1) 25 strlen(s2 + 8) 9 strcmp(s1, s2) 음의 정수 문장 출력되는 값 printf("%s", s1+10); big sky country strcpy(s1 + 10, s2 + 8); strcat(s1, "s!"); printf("%s", s1); beautiful brown cows!
1차원 배열과 포인터 배열 연산과 포인터 연산 px + 1의 의미 배열 연산 : 선언문에서 배열을 정의하거나 또는 수식 중 첨자를 사용해 배열 원소를 참조하는 것. 포인터 연산 : 포인터가 배열을 가리키고 포인터에 관계된 연산을 하는 것. px + 1의 의미 int x[5] = { 1, 2, 3, 4, 5 }; int *px; px = x; /* px = &x[0]와 같다. */ 포인터 px는 현재 배열의 시작 주소
1차원 배열과 포인터 *(px + i)의 의미 배열 x의 i번째 값을 나타낸다. #include <stdio.h> void main( ) { int a[4] = {1, 2, 3, 4}; int *pi, index; pi = a; /* pi = &a[0]와 동일, pi는 배열 a의 시작 주소를 갖는다. */ for(index = 0; index < 4; ++index) printf(“a[%d]의 주소:%u, pi + %d의 주소: %u, *(pi+%d)의 값: %d \n”, index, &a[index], index, pi + index, index,*(pi+index)); } a[0]의 주소: 1000, pi + 0의 주소: 1000 *(pi + 0)의 값: 1 a[1]의 주소: 1002, pi + 1의 주소: 1002 *(pi + 1)의 값: 2 a[2]의 주소: 1004, pi + 2의 주소: 1004 *(pi + 2)의 값: 3 a[3]의 주소: 1006, pi + 3의 주소: 1006 *(pi + 3)의 값: 4
배열과 포인터 배열과 포인터의 공통점 배열과 포인터의 차이점 array와 ptr은 주소를 나타낸다. array[index]와 *(ptr + index)는 같다. array[index]와 ptr[index]는 같다. *array 혹은 *ptr이 가능하다. *(array + index) 혹은 *(ptr + index)가 가능하다. 배열과 포인터의 차이점 배열명은 상수이고 포인터는 변수이다. 배열명에 주소를 대입할 수 없다. 배열명에는 증감 연산자(array++, array--)를 쓸 수 없다.
배열과 포인터 따라서, 아래의 표현은 모두 같은 연산을 수행한다. /* int a[N], i, *p; p = a; */ for (p = a; p < &a[N]; ++p) sum = sum + *p; ≡ for (i = 0; i < N; ++i) sum = sum + *(a + i); sum = sum + a[i]; sum = sum + p[i]; 초과된 문자가 다른 변수가 사용하고 있는 메모리에 덮어 쓸 수 있다.
다차원 배열과 포인터 배열의 정의 2차원 배열 다루기 int x[3]; /* 1차원 배열의 정의 */ int y[3][2]; /* 2차원 배열의 정의 */ int z[3][2][2]; /* 3차원 배열의 정의 */ 2차원 배열 구조
배열을 함수의 인자로 전달하는 방법 함수에 인자를 전달하기 위해 메모리의 일부인 스택(stack)을 사용 함수 정의에서 배열 인자를 선언하는 방법 배열을 함수에 전달할 때 배열의 주소가 전달되므로 함수 정의에서 이 주소를 저장하기 위한 포인터 변수를 사용.
배열을 함수의 인자로 전달하는 방법 함수 내에서 배열의 크기(또는 끝)를 알 수 있는 방법 함수의 매개변수로 배열의 시작을 가리키는 포인터와 함께 배열의 크기를 동시에 전달한다. 프로그래머가 특별히 약속된 표시를 배열의 끝에 첨가하고 이 값이 발견 되면 배열의 마지막을 나타낸다. sum1(x, 12) 배열의 주소, 배열의 크기 문자열일 경우 ‘\0’ 같은 기호 사용
배열을 함수의 인자로 전달하는 방법 1차원 배열의 전달 #include <stdio.h> /* 배열의 크기 지정 */ double sum1(double *px, int n); /* 포인터 관점 */ double sum2(double x[ ], int n); /* 배열 관점 */ void main(void) { double x[ ] = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}; printf(“sum1=%f\n”, sum1(x, sizeof(x)/sizeof(double))); printf(“sum2=%f\n”, sum2(x, sizeof(x)/sizeof(double))); /* 배열의 크기를 계산 */ printf(“sum1(x, 5) = %f\n”, sum1(x, 5)); /* 배열 요소 [0]-[5] */ printf(“sum1(x +5, 5) = %f\n”, sum1(x+5, 5)); /* 배열 요소 [5]-[9] */ printf(“sum2(&x[5], 5) = %f\n”, sum2(&x[5], 5)); } double sum1(double *px, int n) { double sum = 0; while(n --> 0) sum += *px++; /* 포인터 관점 */ return sum; } double sum2(double x[ ], int n) { int i; double sum = 0; for(i = 0; i < n; i++) sum += x[i]; /* 배열 관점 */ return sum; }
포인터 배열 포인터 배열이란 포인터 변수가 배열 요소인 배열을 말한다. 포인터 배열의 선언 선언 형식 : 데이터형 *포인터 배열명[개수];
포인터 배열 2차원 문자 배열에 의한 문자 배열의 구현과 문제점 2차원 배열의 메모리 구조 포인터 배열의 메모리 구조
명령 라인 인수 명령 라인 인수는 프로그램을 실행할 때 프로그램명 다음에 전달하는데이터를 말한다. 명령 라인 인수 형식 main( ) 함수의 형식 C:\> 프로그램명 인수_1 인수_2 인수_3 .... 인수_n void main(int argc, char *argv[ ]) argc: 명령 라인 인수의 수(정수)로 프로그램명을 포함하며 적어도 1 이상이다. argv: 명령 라인 인수들에 대한 포인터 배열로 첨자 범위는 0~(argc-1) 까지이다. argv[0]: 경로까지 포함한 프로그램명을 가리킨다. argv[1]: 프로그램명 다음의 첫 번째 인수를 가리킨다. argv[2]: 두 번째 인수를 가리킨다.
명령 라인 인수 예제 #include <stdio.h> #include <string.h> void main(int argc, char *argv[]) { if (argc == 2 ) { /* 먼저 인수의 개수가 맞는지 검사 */ if (strcmp(argv[1], "test") == 0) printf("암호가 맞습니다\n"); else { printf("암호가 틀립니다\n"); } printf("인수의 개수가 틀립니다!!"); 실행 결과 C:\> test 암호가 틀립니다. C:\> test ttt 암호가 틀립니다. C:\> test test 암호가 맞습니다. → 실행됨
명령 라인 인수 예제 #include <stdio.h> void main(int argc, char *argv[]) { int i; printf("argc = %d\n", argc); for (i = 0; i < argc; ++i) printf("argv[%d] = %s\n", i, argv[i]); } argc = 5 argv[0] = my_echo argv[1] = a argv[2] = is argv[3] = for argv[4] = apple.