420 likes | 757 Views
제 5 장 배열과 포인터. 포인터와 주소. 포인터 기억 장소의 위치 ( 주소 ) 를 저장하는 변수 포인터 변수의 선언 type * pointer-variable ; & 연산자 피연산자의 주소를 의미 반드시 기억 장소에 존재하는 객체를 피연산자로 가져야 함 상수 , 수식 , 레지스터 변수를 피연산자로 가질 수 없음. 포인터의 개념. p = &v;. 참조 연산자. * 연산자 포인터가 가리키는 대상을 접근하는데 사용 예 ) int x = 1, y = 2, z[10];
E N D
포인터와 주소 • 포인터 • 기억 장소의 위치(주소)를 저장하는 변수 • 포인터 변수의 선언 • type *pointer-variable; • & 연산자 • 피연산자의 주소를 의미 • 반드시 기억 장소에 존재하는 객체를 피연산자로 가져야 함 • 상수, 수식, 레지스터 변수를 피연산자로 가질 수 없음
포인터의 개념 • p = &v;
참조 연산자 • * 연산자 • 포인터가 가리키는 대상을 접근하는데 사용 • 예) int x = 1, y = 2, z[10]; int *ip; /* ip is a pointer to int */ ip = &x; /* now ip points to x */ y = *ip; /* y is now 1 */ *ip = 0; /* x is now 0 */ ip = &z[0]; /* now ip points to z[0] */
포인터 변수의 연산 • void형의 포인터를 제외한 모든 포인터는 가리키는 대상의 데이타형이 있음 • 예) 정수형 포인터 ip가 있을 때 *ip는 정수형 변수가 사용될 수 있는 모든 수식에 사용가능 *ip += 1; ++*ip; • void형 포인터: generic pointer • (*ip)++ 과 *ip++의 차이 • 포인터는 그 자체가 변수이므로 다른 포인터 변수의 값을 지정하는 지정문에도 사용됨 • 예) int i, *ip, *iq; ip = &i; iq = ip;
포인터를 이용한 인자 전달 • 호출 함수에서 피호출 함수이 변경한 변수의 결과를 사용하려면 값을 변경시키려는 인자를 포인터로 전달하여야 함 • swap 함수 void swap(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; }
포인터와 배열 • 포인터 • int *pa; • pa = &a[0]; • pa는 a의 첫번째 원소를 가리킨다 pa a : • pa+i는 pa에서 i 만큼 뒤에 있는 원소를 가리킨다. • pa가 a[0]을 가리키고 있다면 *(pa+i)는 a[i]를 참조
포인터와 배열(con’d) • 배열의 이름과 포인터의 유사점 • 배열의 이름은 배열의 첫번째 원소의 주소와 같다 • pa = &a[0]; 은 pa = a; 과 동일 • 배열 a의 원소 a[i]를 *(a+i)로 나타내는 것도 가능 • &a[i]와 a+i는 동일한 의미를 가짐 • pa[i]는 *(pa+i)와 같은 의미를 나타냄 • 배열의 이름과 포인터의 차이점 • 포인터는 변수이고 배열의 이름은 변수가 아니다 • 포인터의 경우는 다음과 같은 연산이 가능 pa = a; pa++; • 배열의 이름은 다음과 같은 연산이 허용되지 않음 a = pa; a++;
배열의 함수 이름 전달 • 배열의 이름을 함수의 인자로 전달 • 배열의 처음 원소의 주소가 전달이 된다 • 피호출 함수에서 배열 이름을 나타내는 인자는 포인터이다 int strlen(char *s) { int n; for (n = 0; *s != ''; s++) n++; return n; } • 함수를 정의할 때 형식 인자 char s[]와 char *s는 동등 • 그러나 char *s라고 선언하는 것이 더 좋음
주소 계산 • p가 포인터라면 p++는 기억 장소에서 바로 다음 원소를 가리키도록 p를 증가 • type *p; 일때 p++은 포인터는 1만큼 증가하지만 실제로 기억 장소의 주소는 sizeof(type)만큼 증가됨 • 포인터와 정수는 연산에서 호환되지 않음 • 0은 예외적으로 지정 또는 비교에 사용 • <stdio.h>의 심볼 상수 NULL은 포인터와 관련하여 0 대신에 사용 int *p; p = NULL; /* make p point to nothing */ if (p == NULL) ..... /* test if p is NULL pointer */ • 포인터 p에 NULL을 지정하면 포인터는 기억 장소의 어뗜 위치도 가리키고 있지 않다는 것을 의미
주소 계산(con’d) • 포인터에 대해 허용되는 연산 • 같은 형의 포인터에 의한 지정 • 포인터의 증감 • 포인터끼리의 비교 연산 • 포인터끼리의 뺄셈 int strlen(char *s) { char *p = s; while (*p != '') p++; return p-s; }
char형 포인터 • 문자열 상수 • 문자 배열 • 함수의 인자로 사용되는 경우: 문자열의 시작 주소가 인자로 전달 printf("hello, world"); • char형 포인터의 초기화에 사용 char *pmessage; pmessage = "now is the time"; /* 문자열 복사와 다름 */
char형 포인터(con’d) • 배열의 초기화와 포인터의 초기화 char amessage[] = "now is the time"; /* 배열 */ char *pmessage = "now is the time"; /* 포인터 */ amessage: pmessage: now is the time\0 now is the time\0
예제(strcpy 함수) /* strcpy: version using array subscript */ void strcpy(char *s, char *t) { int i = 0; while ((s[i++] = t[i++]) != '') ; } /* strcpy: version using pointer */ void strcpy(char *s, char *t) { while ((*s++ = *t++) != '') ; }
포인터 배열 • 포인터 배열 • 원소가 포인터인 배열 • 예) 문자열 저장
포인터 배열의 예제 #include <stdio.h> main() { static char *week[] = { "Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday"}; int k; for (k=0;k<7;k++) printf("week[%d]=%c, %s\n",k,*week[k],week[k]); }
다차원 배열 • 다차원 배열(multi-dimensional array) • 선언 방법 • type array-name[d1][d2] ......[dn]; • C는 다차원 배열을 행-열 순서로 저장 • 예를 들어 2차원 배열 int m[3][3]는 다음과 같이 저장됨
다차원 배열(con’d) • 다차원 배열을 인자로 갖는 함수의 형식 인자 선언 • 허용되는 인자 선언 void a(int m[3][3]); void a(int m[][3]); void b(f[4][2][3]); void b(f[][2][3]); void a(int (*m)[3]); => int (*m)[3]는 pointer to array of size 3을 의미 void b(int (*f)[2][3]); => int (*f)[2][3]는 pointer to array of 2d array of [2][3]을 의 미
포인터와 2차원 배열 • 2차원 배열 int a[10][20]; • a는 2차원 배열로서 10×20개의 정수를 저장할 수 있는 기억 공간을 차지 • a[row][col]에 해당하는 원소를 참조하기 위해 20×row+col의 위치 계산 • 포인터 int *b[10]; • b는 10개의 포인터만을 저장할 수 있는 기억 공간을 할당받음 • 각 행이 가리키는 배열의 길이가 각기 다를 수 있다
포인터와 2차원 배열(con’d) • 포인터와 2차원 배열의 기억 장소 사용 char *name[] = "Christoper", "Michael", "John", "Tom" ; char aname[][11] = "Christoper", "Michael", "John", "Tom" ;
포인터와 2차원 배열(con’d) char aname[][11] = {"Christoper", "Michael", "John", "Tom”} ;
명령행(command line) 인자 • 명령행 인자를 처리하기 위한 main함수의 형태 • main(int argc, char *argv[]) • argc는 명령행에서 입력된 토큰의 갯수를 의미 • argv는 입력된 각 토큰들의 포인터를 저장하고 있는 배열 • argc는 항상 1이상의 값을 갖는다. • argv[0]는 프로그램의 이름을 가리키 기 때문 • 명령행 입력: prog arg1 arg2 arg3 • argc는 4, argv는 다음과 같이 구성
명령행 인자 사용의 예 #include <stdio.h> main(int argc, char *argv[]) { int i; for (i = 0; i < argc; i++) printf("argv[%d] = %s%s", i, argv[i], (i < argc-1) ? ", " : ""); }
구조체 • 구조체(structure) • 여러개의 서로 연관된 변수들을 하나의 단위로 묶어서 처리 • 구조체의 예 학생
구조체의 선언 • 선언 struct tag-name { type variable1; type variable2; ........... } ; • 예 struct student { int number; char dept[MAXDEPT]; char name[MAXNAME]; int year; char address[MAXADDR]; char phone[MAXPHONE]; } ;
구조체 변수 선언 • 구조체 변수 선언 • 구조체의 선언은 형(type)을 정의 • 구조체 선언의 오른쪽 • struct tag-name x, y, z ; • 태그(tag)의 사용 • 변수들의 리스트가 없는 구조체 선언은 메모리 할당을 하지 않고 구조체의 구성요소만을 기술 • 태그를 사용한 경우에는 추후 변수 선언 가능 struct point { int x; int y; } ; struct point pt;
구조체 변수 선언(con’d) • 태그를 사용하지 않은 경우 • 추후 변수 선언 불가능 struct { int x; int y; } pt; • 구조체 변수의 초기화 • 초기값에 의한 초기화 struct point pt = { 640, 480 } ; • 구조체 변수에 의한 지정이나 함수의 리턴값으로 초기화 가능
구조체 멤버 • 구조체 멤버의 참조 • structure-name.member • 예) printf("x = %d, y = %d", pt.x, pt.y); • 구조체의 중첩 정의 예) struct rect { struct point pt1; struct point pt2; } rectangle; • 멤버의 참조: rectangle.pt1.x, rectangle.pt1.y
구조체에 대한 연산 • 지정문에서 하나의 단위로 복사 • & 연산자를 사용하여 주소를 얻음 • 멤버를 참조 • 함수의 인자로 전달, 함수의 리턴값: 복사에 의해 전달 • 구조체는 비교에 사용될 수 없음
구조체와 함수 • 구조체를 리턴하는 함수 struct point makepoint(int x, int y) { struct point temp; temp.x = x; temp.y = y; return temp; } struct point pt1; pt1 = makepoint(0, 0);
구조체와 함수(con’d) • 구조체의 인자 전달 struct point addpoint(struct point p1, struct point p2) { p1.x += p2.x; p1.y += p2.y; return p1 } • 구조체의 포인터 • 구조체 전체를 복사하여 전달하는 것보다 포인터를 전달하는 것이 효율적 • 구조체의 포인터의 선언 struct point *pp; • 멤버의 참조 (*pointer-to-structure).member-of-structure pointer-to-structure->member-of-structure
구조체와 함수(con’d) • 구조체 포인터 사용의 예 struct point pt, *pp; pt = makepoint(0,0); pp = &pt; printf("Point is ( %d, %d )",(*pp).x, (*pp).y); struct rect r, *rp = &r; r.pt1.x, rp->pt1.x, (r.pt1).x, (rp->pt1).x => 동일한 참조
구조체와 배열 • 선언의 예 struct ename { char last[30]; char mi; char first[20]; } ; struct person { struct ename name; int serialno; char sex; } ; struct person employee[MAX]; • 원소의 접근 • employee[i].name.last • employee[i].serialno
자기 참조 구조체 • 구조체는 자기자신을 멤버로 가질 수 없음 • 자기 참조 구조체(self-referential structure) • 자기자신을 가리키는 포인터를 멤버로 가지는 구조체 • 리스트(list)나 트리(tree)와 같은 자료 구조를 구현에 사용 struct book { /* 자기 참조 구조체 */ char title[80]; char author[80]; struct book *nextbook; } ;
sizeof • sizeof 연산자 • 컴파일시 대상의 크기를 계산하는 연산자 • sizeof object또는 sizeof(object) • 데이타형의 크기를 바이트 단위로 계산한 정수값을 의미 • ex) int n; sizeof(n) 또는 sizeof(int) => int형의 바이트 크기
typedef • 새로운 데이타형을 정의하는데 사용 • typedef int Length; • int형과 동일한 Length라는 새로운 데이타형을 만드는 것 Length len, maxlen; Length *lengths[]; • typedef char * String; • char형의 포인터형에 해당하는 String형을 정의 String p, lineptr[10];
typedef(con’d) • typedef와 #define의 차이점 • #define String char * • String p, lineptr[10]; => char * p, lineptr[10]의 의미를 나타냄 • typedef의 예 typedef struct { double real; /* 실수부 */ double imag; /* 허수부 */ } COMPLEX; COMPLEX complex_add(COMPLEX a, COMPLEX b) { a.real += b.real; a.imag += b.imag; return a; }
공용체 • 선언 union tag-name { type variable1; type variable2; ........... } ; • 구조체와 다른점: 공용체내의 멤버들은 동일한 기억 장소를 공유 • 공용체의 멤버를 참조하는 방법은 구조체와 동일 union-name.member union-pointer->member
공용체 사용예 #include <stdio.h> #define STRING 0 #define INTEGER 1 #define FLOATING 2 struct { int type; union { char *sval; int ival; float fval; } u; } val;
공용체 사용예(con’d) main() { ............... if (val.type == STRING) printf("Value = %s",val.u.sval); else if (val.type == INTEGER) printf("Value = %d",val.u.ival); else if (val.type == FLOATING) printf("Value = %f",val.u.fval); else printf("bad type : %d",val.type); ................ }