440 likes | 697 Views
컴퓨터 메모리 메모리 셀이 연속적으로 나열된 형태 각 메모리 셀에는 주소가 부여되어 있음 데이터는 각 데이터 크기에 필요한 만큼 메모리 셀을 차지함 메모리 셀 자체 정보 어떤 데이터 a 가 차지하는 메모리 셀 개수는 sizeof (a) 로 알 수 있음 메모리 셀 주소는 ?. 64M 메모리를 가정했을 때 메모리 구성도. 메모리 구조. 포인터. 포인터 (pointer) 란 ? 데이터를 저장하기 위해 할당된 메모리 공간의 주소 포인터 상수 (pointer constant): 메모리 주소 값
E N D
컴퓨터 메모리 메모리 셀이 연속적으로 나열된 형태 각 메모리 셀에는 주소가 부여되어 있음 데이터는 각 데이터 크기에 필요한 만큼 메모리 셀을 차지함 메모리 셀 자체 정보 어떤 데이터 a가 차지하는 메모리 셀 개수는 sizeof(a)로 알 수 있음 메모리 셀 주소는? 64M 메모리를 가정했을 때 메모리 구성도 메모리 구조
포인터 • 포인터(pointer)란? • 데이터를 저장하기 위해 할당된 메모리 공간의 주소 • 포인터 상수(pointer constant): 메모리 주소 값 • 포인터 변수(pointer variable): 주소 값을 저장할 수 있는 변수 • 데이터에는 자료형이 연관되어 있으므로 포인터에도 자료형이 연관됨(예: int포인터, double포인터 등) • 간접참조(indirection, dereferencing) • 포인터가 가리키는 곳을 따라가 연관된 데이터 혹은 그 데이터가 저장된 공간을 참조하는 것 • 간접참조한 데이터의 자료형은 포인터 자료형을 이용하여 판단함 포인터 간접참조 int형 데이터 int포인터 상수 1024 double형 데이터 1052 double포인터 변수
one 1 to_one 포인터 선언과 사용 • 포인터 선언 형식 자료형 *포인터변수; • 포인터 사용 예 int one = 1; // int변수 int *to_one; // int포인터 변수 to_one = &one; // to_one은 one을 가리킴 one = one + 1; // one ≡ 2 one = *to_one + 1; // one ≡ 3 *to_one = one + 1; // one ≡ 4 • 포인터와 일반 변수를 함께 선언 int one, *to_one; int one, *to_one = &one; // 선언과 함께 초기화! 2 3 4
간접참조 수식 *to_one의 의미는 대입 연산자의 어느 쪽에 있느냐에 따라정해짐 오른편: to_one이 가리키는 곳에 저장된 값 왼편: to_one이 가리키는 곳에 할당된 변수(int변수) toOne.c 실행결과: one = 1 one = 2 one = 3 one = 4
포인터 제1법칙의 의미 주소연산을 취한 결과에 간접참조연산을 취하면 원래 변수와 같다 포인터 제1법칙(law1.c) 포인터 제1법칙 *(& a) ≡ a 실행결과: 숫자 n을 입력해 주세요. 275 n = 275 *&n = 275
포인터 인수전달 • 포인터를 인수로 전달하면 • 포인터를 간접참조함으로써 해당 포인터가 가리키는 데이터 값을 변경할 수 있다 • 호출된 함수에서 호출한 함수의 변수 값을 변경할 수도 있다 • 포인터 인수 전달을 사용하는 이유 • 호출된 함수의 '부수효과(side-effect)'로서 호출한 함수의 변수 값을 변경하기 위해 • 인수로 전달할 데이터의 크기가 매우 큰 경우에 인수전달 효율을 높이기 위해 • 인수 데이터 크기가 큰 경우 예: 배열, 구조체
변수 값 교환 프로그램(swap0.c) 이 부분을 함수로 만들어 봅시다! 실행결과: 변수 값을 순서대로 출력하면 2, 3 입니다. 변수 값을 순서대로 출력하면 3, 2 입니다.
교환되지 않은 이유? swap의 a, b는 main의 a, b가 아니다. main의 a, b의 사본일 뿐이다. 변수 값 교환 함수?(swap1.c) 실행결과: 변수 값을 순서대로 출력하면 2, 3 입니다. 변수 값을 순서대로 출력하면 2, 3 입니다.
swap에서 main의 a, b의 주소를 알고 있으므로 이들 변수 값을 변경할 수 있다. 변수 값 교환 함수(swap2.c) 실행결과: 변수 값을 순서대로 출력하면 2, 3 입니다. 변수 값을 순서대로 출력하면 3, 2 입니다.
배열과 포인터 • 배열 이름을 포인터에 저장 • 배열 이름은 배열이 할당된 공간의 주소이므로 포인터 상수임 • 따라서 배열 이름을 포인터 변수에 저장할 수 있음 • 이 때, 포인터의 타입은 배열 원소를 가리킬 수 있는 타입이어야 함 • 포인터 가감연산 • 포인터에 가감연산을 취하면 포인터가 가리키는 자료의 크기 단위로 포인터 값이 증감함 • 포인터 가감 연산 예: int a[10], *p = a; p += 2; 포인터 p 값은 2만큼 증가하는 것이 아니라 2 * sizeof(int)만큼 증가함
arrayName.c char형 배열 이름은 char형 포인터에 저장할 수 있다. 실행결과: 배열 이름을 이용한 출력: Good bye yellow brick road~! 포인터 변수를 이용한 출력: Good bye yellow brick road~!
ptrIncr.c 실행결과: sizeof(int) = 4 a + 0 = 0012FF58 a + 1 = 0012FF5C b - 2 = 0012FF60 b - 1 = 0012FF64
포인터를 통한 배열원소 참조 배열 첨자연산대신 포인터 간접참조연산을 사용할 수 있다. 실행결과: I am just a poor boy ~
포인터 제2법칙의 의미 배열 첨자연산은 본질적으로 포인터 연산이다. 포인터 제2법칙(law2.c) 포인터 제2법칙 p[n] ≡ *(p + n) 실행결과: I am just a poor boy ~ I am just a poor boy ~
포인터 제2법칙은 포인터 상수에도 똑같이 적용된다. 포인터 상수에도 적용됨 실행결과: I am just a poor boy ~ I am just a poor boy ~ 배열이름은 포인터 상수
배열 형태로 선언한 매개변수에는 배열 이름(포인터 상수)이 전달되므로 사실 포인터임 따라서 배열 매개변수 선언 시 크기 선언은 중요하지 않음 T p[250] ≡ T p[3] ≡ T p[] 배열 인수 전달복습 배열 형태지만 사실은 char포인터임 실행결과: I am just a poor boy ~
배열 시작 위치를 기억할 필요가 없다면 포인터를 증가시켜가며 배열 원소를 참조할 수 있다. p가 가리키는 원소를 참조한 후 p를 1 증가시킴 p가 가리키는 원소가 '참'이면 반복. 즉 p가 가리키는 원소가 '\0'이 아니면 반복. 포인터다운 배열 참조 방식 실행결과: I am just a poor boy ~
int포인터의 포인터를 한 번 따라가면 int포인터를 얻을 수 있다. 포인터의 포인터 ppi는 포인터 변수를 가리킬 수 있는 포인터 실행결과: i = 5 *pi = 5 **ppi = 5 **ppi = 12345 *pi = 12345 i = 12345
배열 포인터로 출력한 2차원 배열 포인터의 포인터로 출력한 포인터 배열 배열 포인터와 포인터의 포인터 실행결과: [Array Pointer] 0012FF4C: This 0012FF51: That 0012FF56: Here 0012FF5B: Hour [Pointer Pointer] 0012FF68: This 0012FF6C: is 0012FF70: pointer 0012FF74: array
typedef • typedef는… • 자료형 별칭(type alias)을 정의하는데 사용되는 키워드 • 복잡한 자료형을 간단한 이름으로 지칭할 수 있음 • 선언문 앞에 typedef를 붙이면 선언하는 이름은 타입 이름이 됨 • typedef사용 예 • int에 대한 타입 별칭 int i; // i는 int변수 typedefint iType; // iType은 int타입의 다른 이름 • int포인터에 대한 타입 별칭 int *p; // p는 int포인터 변수 typedefint *pType; // pType은 int포인터 타입 pType q = &i; // q는 pType, 즉 int포인터
따라서 변수 선언은 물론, typedef.c 이렇게 선언한 뒤에는 IntType ≡ int RealType ≡ double 이다. 자료형 변환에도 사용할 수 있다. 실행결과: 5 + 7 = 12 5 / 7 = 0.714286
복잡한 타입을 typedef로… • typedef를 이용하면 복잡한 타입을 쉽게 다룰 수 있음 • 배열 포인터 선언 예 int (*q1)[20]; • typedef를 이용하여 단계적으로 선언하는 예 typedefint AI[20]; AI *q2; // q1과 q2모두 크기 20인 int배열에 대한 포인터임
void는 '없음(nothing)' void*는 '아무것이나 가리키는 포인터(pointer to anything)' 그래서 void*를 '범용 포인터(generic pointer)'라고 부르기도 한다. 이 부분을 함수로 바꾸면… void 포인터 실행결과: i의 주소 = 0012FF74 f의 주소 = 0012FF78
void포인터는 간접참조할 수 없음 간접참조하려면 명시적으로 형변환을 수행해야 함 void 포인터에 대한 간접참조 실행결과: i = 19100829 f = 3.141592
함수 포인터 • 함수 포인터(function pointer)란? • 함수를 가리키기 위한 포인터 • 함수 포인터를 이용하여 함수를 호출할 수 있음 • 함수 포인터로 함수를 호출할 때에는 간접참조하지 않아도 됨 • 리턴타입 뿐만 아니라 매개변수 자료형도 함수의 타입에 해당함 • 함수 포인터 활용 예 int (*fp)(int); int add1(int x) { return x + 1; } void prInt(int x) { printf("%d", x); } int add(int x, int y) { return x + y; } ... fp = add1; // prInt는 저장불가(리턴타입이 다름) // add는 저장불가(매개변수 타입이 다름) two = fp(1); // add1을 호출하게 됨
funPtr.c 실행결과: 자연수 하나를 입력하세요. 64 팔진표기: 100 십진표기: 64
average.c (1/2) 산술평균 조화평균 기하평균 세 함수의 타입이 모두 같다는 사실에 주의하자. a_mean: double × double double h_mean: double × double double g_mean: double × double double
average.c (2/2) 함수 포인터 배열 fun 실행결과: 평균을 구할 두 수를 입력해 주세요. 5 10 어떤 평균을 구하고 싶으신가요? 1. 산술평균 2. 조화평균 3. 기하평균 번호를 입력해 주세요. 1 선택하신 산술평균은 7.500000
▶ 프로그래밍 실습 1 • 명령줄 인수를 처리하는 프로그램 Do.c를 작성하여라. Do.c의 실행 파일을 Do(혹은 Do.exe)라고 하면 Do는 자신을 포함하여 문장을 입력으로 받는다. 그리고 마치 그 문장을 이해하고 답변하는 것처럼 반응한다. 예를 들면, 다음과 같다. > Do you have match? Yes, I have match. > Do you like C language? No, I don't like C language. 긍정적인 답변을 할 것인지 부정적인 답변을 할 것인지는 무작위로 결정하라. 무작위로 결정하기 위해 rand()와 srand()를 활용하라.
▶ 프로그래밍 실습 2 • 암호화 알고리즘 중에는 "증분 k(displacement k)" 알고리즘이 있다. 예를 들어, k = 3인 경우에는 각 알파벳을 3개 뒤로 미룬다. 즉 a는 d로 b는 e로 변경한다. z 다음의 알파벳은 다시 a가 오는 것으로 가정한다. 이런 방식으로 암호화하면 k = 3일 때 문자열 "Joy of C programming" 은 "Mrb ri F surjudpplqj" 이 된다. 사용자로부터 k를 입력받은 후 그 다음 행부터 주어지는 문자열을 암호화하는 증분 k 암호화 프로그램을 작성하라. k는 0이상 25이하의 수라고 가정하자.