10 포인터

Download Report

Transcript 10 포인터

Part 10 포인터
©우균, 창병모
1
이 장의 내용







포인터란
포인터 인수 전달
배열과 포인터
더 복잡한 선언문
typedef
void 포인터
함수 포인터
2
10.1 포인터란
3
메모리 구조

컴퓨터 메모리
메모리 셀이 연속적으로 나열
된 형태
 각 메모리 셀에는 주소가 부여
되어 있음
 데이터는 각 데이터 크기에 필
요한 만큼 메모리 셀을 차지함


메모리 셀 자체 정보
어떤 데이터 a가 차지하는 메
모리 셀 개수는 sizeof(a)로
알 수 있음
 메모리 셀 주소는?

4
포인터

포인터(pointer)란?
데이터를 저장하기 위해 할당된 메모리 공간의 주소
 포인터 상수(pointer constant): 메모리 주소 값
 포인터 변수(pointer variable): 주소 값을 저장할 수 있는 변수
 데이터에는 자료형이 연관되어 있으므로 포인터에도 자료형이 연관됨
(예: int 포인터, double 포인터 등)


간접참조(indirection, dereferencing)
포인터가 가리키는 곳을 따라가 연관된 데이터 혹은 그 데이터가 저장된
공간을 참조하는 것
 간접참조한 데이터의 자료형은 포인터 자료형을 이용하여 판단함

포인터
간접참조
int 포인터 상수
1024
int형 데이터
double 포인터 변수
1052
double형 데이터
5
포인터 선언과 사용

포인터 선언 형식
자료형 *포인터변수;

포인터 사용 예
int one = 1;
//
int *to_one;
//
to_one = &one;
//
one = one + 1;
//
one = *to_one + 1; //
*to_one = one + 1; //

int 변수
int 포인터 변수
to_one은 one을 가리킴
one ≡ 2
one ≡ 3
one ≡ 4
포인터와 일반 변수를 함께 선언
one
1
4
3
2
to_one
int one, *to_one;
int one, *to_one = &one; // 선언과 함께 초기화!
6
toOne.c

간접참조 수식 *to_one의 의
미는 대입 연산자의 어느 쪽에
있느냐에 따라 정해짐
오른편: to_one이 가리키는
곳에 저장된 값
 왼편: to_one이 가리키는 곳
에 할당된 변수(int 변수)

실행결과:
one
one
one
one
=
=
=
=
1
2
3
4
7
포인터 제1법칙(law1.c)
포인터 제1법칙
*(& a) ≡ a
포인터 제1법칙의 의미
 주소연산을 취한 결과
에 간접참조연산을 취
하면 원래 변수와 같다
실행결과:
숫자 n을 입력해 주세요. 275
n
= 275
*&n
= 275
8
10.2 포인터 인수전달
9
포인터 인수전달

포인터를 인수로 전달하면



포인터를 간접참조함으로써 해당 포인터가 가리키는 데이터 값
을 변경할 수 있다
호출된 함수에서 호출한 함수의 변수 값을 변경할 수도 있다
포인터 인수 전달을 사용하는 이유




호출된 함수의 ‘부수효과(side-effect)’로서 호출한 함수의 변수 값
을 변경하기 위해
인수로 전달할 데이터의 크기가 매우 큰 경우에 인수전달 효율을
높이기 위해
인수 데이터 크기가 큰 경우의 예: 배열, 구조체
사실 C에서 배열은 항상 포인터를 통해 전달된다.
10
변수 값 교환 프로그램(swap0.c)
이 부분을 함수로 만들어 봅시다!
실행결과:
변수 값을 순서대로 출력하면 2, 3 입니다.
변수 값을 순서대로 출력하면 3, 2 입니다.
11
변수 값 교환 함수?(swap1.c)
교환되지 않은 이유?
swap의 a, b는 main의 a,
b가 아니다. main의 a, b
의 사본일 뿐이다.
실행결과:
변수 값을 순서대로 출력하면 2, 3 입니다.
변수 값을 순서대로 출력하면 2, 3 입니다.
12
변수 값 교환 함수(swap2.c)
swap에서 main의
a, b의 주소를 알고
있으므로 이들 변수
값을 변경할 수 있
다.
실행결과:
변수 값을 순서대로 출력하면 2, 3 입니다.
변수 값을 순서대로 출력하면 3, 2 입니다.
13
10.3 배열과 포인터
14
배열과 포인터

배열 이름을 포인터에 저장할 수 있음




배열 이름은 배열이 할당된 공간의 주소이므로 포인터 상수임
따라서 배열 이름을 포인터 변수에 저장할 수 있음
이 때, 포인터의 타입은 배열 원소를 가리킬 수 있는 타입이어야
함
포인터 가감연산


포인터에 가감연산을 취하면 포인터가 가리키는 자료의 크기 단
위로 포인터 값이 증감함
포인터 가감 연산 예:
int a[10], *p = a;
p += 2;
포인터 p 값은 2만큼 증가하는 것이 아니라
2 * sizeof(int)만큼 증가함
15
arrayName.c
char형 배열 이름
은 char형 포인터
에 저장할 수 있다.
16
ptrIncr.c
실행결과:
sizeof(int) = 4
a + 0 = 0012FF58
a + 1 = 0012FF5C
b - 2 = 0012FF60
b - 1 = 0012FF64
17
포인터를 통한 배열원소 참조
배열 첨자연산대신
포인터 간접참조연산을
사용할 수 있다.
실행결과:
I am just a poor boy ~
18
포인터 제2법칙(law2.c)
포인터 제2법칙
p[n] ≡ *(p + n)
포인터 제2법칙의 의미
 배열 첨자연산은 본질적
으로 포인터 연산이다.
실행결과:
I am just a poor boy ~
I am just a poor boy ~
19
포인터 상수에도 적용됨

배열이름은
포인터 상수
포인터 제2법칙은 포인터 상수
에도 똑같이 적용된다.
실행결과:
I am just a poor boy ~
I am just a poor boy ~
20
배열 인수 전달 복습
배열 형태로 선언한 매개변수
에는 배열 이름(포인터 상수)
이 전달되므로 사실 포인터임
 따라서 배열 매개변수 선언 시
크기 선언은 중요하지 않음
T p[250] ≡ T p[3] ≡ T p[]

배열 형태지만 사실
은 char 포인터임
실행결과:
I am just a poor boy ~
21
포인터다운 배열 참조 방식

배열 시작 위치를 기억할 필요
가 없다면 포인터를 증가시켜
가며 배열 원소를 참조할 수 있
다.
p가 가리키는 원소를 참조한 후
p를 1 증가시킴
실행결과:
I am just a poor boy ~
p가 가리키는 원소가 '참'이면 반
복. 즉 p가 가리키는 원소가
'\0'이 아니면 반복.
22
10.4 더 복잡한 선언문
23
포인터의 포인터

int 포인터의 포인터를 한 번
따라가면 int 포인터를 얻을
수 있다.
ppi는 포인터 변수
를 가리킬 수 있는 포
인터
실행결과:
i
*pi
**ppi
**ppi
*pi
i
=
=
=
=
=
=
5
5
5
12345
12345
12345
24
C 선언문에 내재된 철학
사용 형태대로 선언한다.

선언문을 다시 살펴봅시다.



int i, a[10], *p;
i는 int형이므로 정수를 저장할 수 있다.
i = 5;
a에 첨자연산을 취한 a[…] 형태는 int형이다.
그래서 a는 int 배열형이다.
a[0] = 5;
p에 간접참조연산을 취한 *p 형태는 int형이다.
그래서 p는 int 포인터형이다.
*p = 5;
25
더 복잡한 선언문 이해

포인터의 배열


int *a[3], *pi; // *(a[3])과 같음
a에 첨자연산을 취한 a[…] 형태가 int 포인터이므로
a는 포인터의 배열이다.
배열 포인터

int (*q)[3], ai[3];
q에 간접참조연산을 취한 *q 형태가 int 배열이므로
q는 int형 배열에 대한 포인터다.
정수 포인터의 배열
q
26
다시 보자! 매개변수 자료형

매개변수에서 배열 형태는 사실 포인터다.
void print(int a[]);



int ai[10];으로 선언된 ai를 전달할 수 있음
int 배열이름을 전달할 수 있으므로 int 포인터
좀더 복잡한 경우는?
void print(int *x[]);


// int **x;와 같음
int *p[10];으로 선언된 p를 전달할 수 있음
int 포인터 배열을 전달할 수 있으므로 int 포인터의 포인터
void print(int y[][10]); // int (*y)[10];과 같음



int a[5][10];으로 선언된 a를 전달할 수 있음
int 배열의 배열을 전달할 수 있으므로 int 배열 포인터
여기서 두 번째 크기 10은 생략할 수 없다!
27
10.5 typedef
28
typedef

typedef는…




자료형 별칭(type alias)을 정의하는데 사용되는 키워드
복잡한 자료형을 간단한 이름으로 지칭할 수 있음
선언문 앞에 typedef를 붙이면 선언하는 이름은 타입 이름이 됨
typedef 사용 예

int에 대한 타입 별칭
int i;
typedef int iType;

// i는 int 변수
// iType은 int 타입의 다른 이름
int 포인터에 대한 타입 별칭
int *p;
// p는 int 포인터 변수
typedef int *pType; // pType은 int 포인터 타입
pType q = &i;
// q는 pType, 즉 int 포인터
29
typedef.c
이렇게 선언한 뒤에는
IntType ≡ int
RealType ≡ double
이다.
따라서
변수 선언은 물론,
자료형 변환에도 사
용할 수 있다.
실행결과:
5 + 7 = 12
5 / 7 = 0.714286
30
복잡한 타입을 typedef로…


typedef를 이용하면 복잡한 타입을 쉽게 다룰 수 있음
배열 포인터 선언 예
int (*q1)[20];

typedef를 이용하여 단계적으로 선언하는 예
typedef int AI[20];
AI *q2;
// q1과 q2모두 크기 20인 int 배열에 대한 포인터임
31
10.6 void 포인터
32
void 포인터



void는 ‘없음(nothing)’
void*는 ‘아무것이나 가리키는
포인터(pointer to anything)’
그래서 void*를 ‘범용 포인터
(generic pointer)’라고 부르기도
한다.
이 부분을 함수로 바꾸면…
실행결과:
i의 주소 = 0012FF74
f의 주소 = 0012FF78
33
void 포인터에 대한 간접참조


void 포인터는 간접참조할 수 없
음
간접참조하려면 명시적으로 형변
환을 수행해야 함
실행결과:
i = 19100829
f = 3.141592
34
10.7 함수 포인터
35
함수 포인터

함수 포인터(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을 호출하게 됨
36
funPtr.c
실행결과:
자연수 하나를 입력하세요.
팔진표기:
100
십진표기:
64
64
37
average.c (1/2)
산술평균
조화평균
기하평균
세 함수의 타입이 모두 같다는 사실에 주의하자.
a_mean: double × double  double
h_mean: double × double  double
g_mean: double × double  double
38
average.c (2/2)
함수 포인터 배열 fun
실행결과:
평균을 구할 두 수를 입력해 주세요.
5 10
어떤 평균을 구하고 싶으신가요?
1. 산술평균
2. 조화평균
3. 기하평균
번호를 입력해 주세요. 1
선택하신 산술평균은 7.500000
39
Key Point
40
Key Point 1





포인터는 메모리에 부여된 주소를 나타낸다. 주소 ‘값’을 나타낼 때
는 포인터 상수라고 하고 주소를 저장할 수 있는 ‘변수’를 나타낼 때
는 포인터 변수라고 한다.
연산자 *가 단항 연산자로 사용될 경우에는 간접참조 연산자로 사용
된다. 포인터 p에 대하여 *p는 p가 가리키는 메모리 구획(변수)를 나
타낸다.
임의의 변수 a에 대하여 다음과 같은 법칙이 성립한다.
*(& a) ≡ a
피호출자에서 호출자의 변수를 변경하려면 변경하고자 하는 변수의
포인터를 넘겨주어야 한다.
배열 이름은 포인터 상수이므로 포인터 변수에 저장할 수 있다.
41
Key Point 2




T*형 포인터를 1만큼 증가시키면 실제 포인터 값은 sizeof(T)만
큼 증가한다.
임의의 포인터 p와 정수 n에 대하여 다음 등식이 성립한다.
p[n] ≡ *(p + n)
복잡한 자료형에 대한 선언문은 typedef를 이용하면 이해하기 쉽
다.
void 포인터는 아무 대상이나 다 가리킬 수 있지만 간접참조 할 수
없다. void 포인터를 간접참조하려면 먼저 다른 포인터 형으로 형
변환을 수행해야 한다.
42
Key Point(고급 주제)





포인터의 포인터는 포인터를 가리키는 포인터이므로 포인터의 포인
터를 간접참조하면 포인터를 얻을 수 있다.
C 언어 선언문의 가장 큰 특징은 사용 형태대로 선언한다는 것이다.
자료형 T를 이용한 다음 선언문에서 a와 b는 모두 포인터의 배열이
다.
T *a[M], *(b[N]);
자료형 T를 이용한 다음 선언문에서 a는 배열 T[M]에 대한 포인터
다.
T (*a)[M];
함수 포인터란 함수를 가리키기 위해 사용되는 포인터로서 함수 선
언 형태에서 ‘함수명’ 대신 ‘(*함수포인터명)’을 사용함으로써 선언할
수 있다.
43
요약(1/3)

메모리 구조




포인터



주소를 나타내는 자료 또는 그러한 자료형
포인터가 가리킬 데이터의 자료형 정보도 가지고 있음
포인터 연산자



일정한 크기의 메모리 셀이 연속되어 나열되어 있음
각 메모리 셀에는 주소가 부여되어 있음
데이터는 크기에 따라 필요한 개수만큼 메모리 셀을 차지함
주소 연산자(&): 변수의 주소를 돌려줌
간접참조 연산자(*): 포인터가 가리키는 위치 또는 그 위치의 데
이터를 돌려줌
포인터 제1법칙: *(&v) ≡ v
44
요약(2/3)

포인터 인수 전달



포인터와 배열 연동





포인터를 인수로 전달하면 호출된 함수에서 호출한 함수의 변수
내용을 변경할 수 있음
크기가 큰 데이터(배열, 구조체 등)는 포인터로 전달하면 효율적
임
배열 이름은 포인터 상수이므로 포인터 변수에 저장 가능
포인터는 배열 원소에 대한 포인터형이어야 함
포인터 증감연산은 가리키는 데이터의 크기만큼 증감됨
포인터 제2법칙: p[i] ≡ *(p + n)
C 선언문의 철학


사용하는 형태대로 선언함
예: T *(a[10]);는 포인터의 배열 T (*p)[10];는 배열 포인
터
45
요약(3/3)

typedef



void 포인터



타입 별칭(type alias)을 생성함
typedef를 이용하면 복잡한 타입을 간결하게 표현할 수 있다.
void는 "아무것도 아닌 것(nothing)"을 나타내지만 void*는 "임
의의 것(anything)"을 가리킬 수 있음(generic pointer)
void 포인터는 간접참조할 수 없으므로 void 포인터를 간접참
조하려면 먼저 자료형 변환을 수행해야 함
함수 포인터



함수를 가리키는 포인터
매개변수 개수 및 자료형, 리턴타입 등이 같은 함수만 저장 가능
함수 포인터로 함수를 호출할 때에는 간접참조 연산을 생략할 수
있음
46
프로그래밍 실습
47
▶ 프로그래밍 실습 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()를 활용하라.
48
▶ 프로그래밍 실습 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이하의 수라고 가정하자.
49