3. 포인터란 무엇인가?
프로그램이 기억공간을 참조하는 2가지 방법
1. 변수명을 사용하는 방법
이 4byte의 기억 공간은
지금부터 var라는
이름으로 사용이 가능하다.
변수 선언
int var ;
var
메모리에 기억 공간이 할당된다.
변수명으로 기억공간을 사용한다.
var = 10;
value = var;
할당된 기억 공간에 10을 저장한다.
var 값을 다른 기억 공간에 복사한다.
4. 포인터란 무엇인가?
프로그램이 기억공간을 참조하는 2가지 방법
2. 변수가 할당된 메모리의 실제 주소(포인터)를 사용하는 방법
int var ;
변수 선언
메모리에 4byte의 기억 공간이 할당된다.
var
99
100
101
포인터
102
103
104
105
106
107
108
시작 주소값이다.
포인터를 사용하여 변수명과 같이 할당된 기억 공간에 값을 저장하거나 저장된 값을 꺼내어 쓸 수 있다.
5. 주소 연산자 –
포인터를 구하자.
프로그램에서 변수명은 알 수 있지만 그 변수의 실제 주소값은 알 수 없다.
따라서, 포인터를 사용하기 위해서는 먼저 그 주소값을 알아내는 과정이 필요하다.
특정 변수의 포인터를 구하기 위해서는 주소 연산자(&)를 사용한다.
& var
주소 연산자
포인터를 구해준다. !
변수명
Sample Code
char ch;
int in;
double dbl;
// char형 변수 선언, 크기 1byte
// int형 변수 선언, 크기 4byte
// double형 변수 선언, 크기 8byte
printf(“ch의 포인터 : %un”, &ch);
printf(“in의 포인터 : %un”, &in);
printf(“dbl의 포인터 : %un”, &dbl);
결과를 Memory Map으로
표시하여 보자.
6. 주소 연산자 –
포인터를 구하자
Sample Code 실행 결과 분석 예
:
..052
..051
..050
..049
..048
..047
..046
..045
..044
..043
..042
..041
..040
..039
..038
..037
..036
ch
in
포인터에는 자신이 어떤 자료형으로 부터
만들어졌는지에 대한 정보를 가지고 있다.
포인터
dbl
주소값
정보
&ch
..052
char형 기억 공간의 주소값이다.
&in
..048
int형 기억 공간의 시작 주소값이다.
&db
..040
double형 기억 공간의 시작 주소값이다.
포인터의 크기는 자료형에 상관없이 동일한가?
7. 참조 연산자 – 포인터를 사용하자.
포인터를 통해서 기억 공간을 사용하기 위해서는 참조 연산자(*)를 사용한다.
* &var
참조 연산자
포인터가 가리키는
기억 공간을 사용한다. !
포인터
참조 연산자로 포인터를 사용하는 방법
포인터가 가리키는 기억 공간을 사용한다.
포인터가 가리키는 기억 공간의 값을 사용한다.
Sample Code
int a = 10, b = 20;
*&a = *&b
printf(“a의 값 : %dn”, a);
기억 공간을 사용
// 변수 b에 저장된 값을 변수 a의 기억 공간에 기억
*&a = *&b ;
기억 공간의 값을 사용
8. 포인터 변수 – 포인터를 저장하자.
포인터의 저장
int a;
int ap;
ap = &a;
// 포인터를 구할 변수
// 포인터를 저정할 변수
// a의 포인터를 구하여 ap에 저장한다.
변수 a
변수 ap
99
100
101
102
103
104
105
106
error C2440 : ‘=‘ : cannot convert from ‘int *’ to ‘int’
107
108
109
포인터에는 자료형에
대한 정보가 함께 있다.
포인터 변수
int *ap
가리키는 자료형을
앞에 붙여준다.
포인터 변수 ap는 int형 변수의
시작 주소값만을 저장할 수 있다.
변수명 앞에 별표를 붙여
포인터 변수임을 표시한다.
9. 포인터 변수 – 포인터를 저장하자.
포인터의 저장
int a;
int *ap;
ap = &a;
// 포인터를 구할 변수
// 포인터를 저장할 변수
// a의 포인터를 구하여 포인터 변수 ap에 저장한다.
ap가 포인터를 저장하면
100
100
가리킨다.
포인터 변수 ap
int 형 변수 a
Sample Code
int a ;
int *ap = &a;
*ap = 10;
// int 형 변수 선언
// 포인터 변수 선언과 동시에 초기화
// 포인터 변수가 가리키는 공간에 10 저장
printf(“변수명을 사용한 출력 : %dn”, a);
printf(“포인터 변수를 사용한 출력 : %dn”, *ap);
10. 포인터는 왜 필요한가? (1)
함수들은 독립된 기억 공간을 가진다.
#include <stdio.h>
main 함수영역
void assign();
int main()
{
int var = 0;
}
assign 함수영역
var
var ??
0
assign();
printf(“ 함수 호출 후 var에 저장된 값 : %d n”, var);
return 0;
void assign()
{
var = 100;
}
error C2065 : ‘var‘ : undeclared identifier
void assign()
{
int var ;
var = 100;
}
결과는 ?
11. 포인터는 왜 필요한가? (2)
포인터로 다른 함수의 기억공간을 사용한다.
main 함수영역
#include <stdio.h>
void assign(int *);
int main()
{
int var = 0;
}
assign 함수영역
포인터를 넘겨준다.
200 var
0
200
Ip가 포인터가 저장하여 var를 가리킨다.
(var 변수의 시작 주소값은 200)
assign(&var);
printf(“ 함수 호출 후 var에 저장된 값 : %d n”, var);
return 0;
void assign(int *ip)
{
*ip = 100;
}
ip
void assign()
{
return 100;
}
결과는 동일한가 ?
변수가 여러 개일 경우는?
12. 포인터는 왜 필요한가? (3)
두 변수의 값을 서로 바꾸는 함수를 만들어 보도록 하자.
void exchange(int *, int *) ;
#include <stdio.h>
int main()
{
int var1 = 10, var2 = 20;
}
printf(“바꾸기 전 : %d , %d n”, var1, var2);
exchange(&var1, &var2);
printf(“바꾼 후 : %d, %d n”, var1, var2);
return 0;
void exchange(int *cp, int *mp)
{
// 구현하시오..
}
void exhange(int c, int m)로
구현할 경우 값이 바뀌는가?
바뀌지 않는 이유는 ?
13. 포인터와 포인터 변수의 크기
Sample Code
char ch;
int in;
double dbl;
printf(“char 형 변수의 포인터 크기 : %d n”, sizeof(&ch);
printf(“ int 형 변수의 포인터 크기 : %d n”, sizeof(&int);
printf(“ double 형 변수의 포인터 크기 : %d n”
가리키는 자료형의 크기는
달라도 모든 포인터 변수의
크기는 4 바이트이다.
ch (1byte)
char 형 포인터 변수
in (4byte)
int 형 포인터 변수
dbl (8byte)
double 형 포인터 변수
14. 포인터와 포인터 변수의 형변환
Sample Code
int *ip;
double dbl=6.4;
ip = &dbl;
// int 형 변수를 가리키는 포인터 변수
// double 형 변수
// double형 변수의 포인터를 int형 포인터 변수에 대입
error C2440 : ‘=‘ : cannot convert from ‘double * ‘ to ‘int *’
int *ip;
double dbl=6.4;
ip = (int *) &dbl;
//(double *)를 (int *)로 강제 형변환한다.
16. 배열이란?
배열이란
동일한 자료형으로 된 기억 장소들의 모임
배열을 사용하는 목적은 기억 장소의 일괄 처리에 있다.
배열의 선언
int ages[5];
배열의 형태
배열의 이름
변수의 갯수
배열 ages
블록은 하나의 int형 변수
int
int
int
int
int
다섯 개의 변수가 연속으로 이어짐
17. 배열의 사용
배열의 사용
배열내의 각각에 기억 공간은 별도로 붙여진 이름이 없다.
따라서 개별적인 이름이 아닌 배열에서 차지하는 위치를 이용한다.
배열을 구성하는 기억 공간들을 배열의 요소(element)라고 하고, 각 요소가 배열에서 차지하는
위치를 첨자(index)라고 한다.
기억 공간의 사용 : 배열명 + 배열에서의 위치
배열 요소의 표현
배열명[첨자]
첫번째 배열 요소
두번째 배열 요소
배열 ages
ages[0]
ages[1]
세번째 배열 요소
ages[2]
네번째 배열 요소
ages[3]
다섯번째 배열 요소
ages[4]
* 배열의 첨자는 0부터 시작한다.
18. 배열의 사용
배열요소의 표현은 하나의 변수명과 똑같이 사용된다.
score[3]
하나의 변수명과 같다.
따라서 일반 변수와 같이 사용한다. Ex) scanf(“%d”, &ages[3]);
첨자는 0부터 시작하므로 최대값이 배열 요소의 개수보다 하나 작다.
ages[5]
0
1
2
다섯개만 쓰기로 정의
3
4
score[5] = 34;
5
허락되지 않은
기억 공간을
사용하게 된다.
19. 배열과 반복문
모든 배열요소는 배열명이 같고 첨자가 하나씩 증가하는 규칙을 가지고 있기 때문에
반복문을 이용하면 각 배열 요소를 간단하게 처리할 수 있다.
i=0일때 : scanf(“%d”, &ages[0]);
for(i=0; i<5; i++)
scanf(“%d”, &ages[0]);
}
i=1 일때 : scanf(“%d”, &ages[1]);
i=2 일때 : scanf(“%d”, &ages[2]);
i=3 일때 : scanf(“%d”, &ages[3]);
i=4 일때 : scanf(“%d”, &ages[4]);
배열과 반복문울 이용하여 다섯 명의 나이를 입력 받고,
평균을 구하는 프로그램을 작성하여 보자. (시간 15분)
20. 배열의 초기화
배열을 선언하면 그 안에 쓰레기값이 존재하게 된다.
int nums[5];
int i, total =0;
for(i=0; i<5; i++)
total+=nums[i]
}
printf(“total : %dn”, total);
배열 nums
?
?
?
?
?
+
출력 결과는 ?
결과가 시스템의 상황에 따라 달라진다.
배열은 선언과 동시에 초기화 하는 것이 좋다.
int nums[5] = {1,3,5,15,30};
1
순서대로
저장된다.
3
7
15
30
nums[0] nums[1] nums[2] nums[3] nums[4]
21. 배열의 초기화
배열의 초기화 시 편리한 자동 기능
배열 요소의 수보다 초기화 값이 적으면 남은 공간은 0으로 채워진다.
배열 nums
int nums[5] = {1,3};
1
3
0
0
0
자동으로 0으로 채워진다.
배열 선언시 초기화를 하면 배열 요소를 생략할 수 있다.
배열 nums
int nums[] = {1,3,7,15,30};
1
숫자 생략가능
3
7
15
30
자동으로 다섯 개의 기억 공간이 할당된다.
22. 배열의 크기
배열의 크기를 계산해야 하는 경우가 많이 생긴다.
배열의 크기를 자동으로 계산하는 방법은 ?
전체 20 bytes
int nums[5];
int
int
int
int
int
4byte
배열 요소의 개수 = sizeof(nums) / sizeof(nums[0])
배열 전체의 크기
배열 요소 하나의 크기
23. 배열 예제
int
int
int
int
nums[] = {1,2,3,4,5} ;
total = 0;
i;
size ;
// 배열의 초기화
// 합을 저장한 누적 변수
// 반복 제어 변수
// 배열 요소의 개수를 저장할 변수
size = sizeof(nums) / sizeof(nums[0]);
for(i=0; i<size; i++) {
total += nums[i];
}
배열 요소를 각각 초기화한 경우와 비교해 보자.
size를 사용하지 않은 경우와 비교해 보자.
24. 문자 배열과 null
문자 배열의 선언과 사용
char word[4];
배열명 word
char
char
char
4byte
word[0] = ‘L’;
word[1]
char
= ‘o’;
word[2] = ‘v’;
‘L’
‘o’
‘v’
‘e’
word[3] = ‘e’;
출력은?
for(i=0; i<4; i++)
print(“%c”, word[i]);
}
같은 의미
printf(“%s”, word);
25. 문자 배열과 null
배열의 끝 ?
char word[50]; // 배열 선언
char word[50]; // 배열 선언
word[0] = ‘L’;
word[1] = ‘o’;
word[2] = ‘v’;
word[3] = ‘e’;
word[4] = ‘0’;
printf(“%s”, word); //문자열 출력
word[0] = ‘L’;
word[1] = ‘o’;
word[2] = ‘v’;
word[3] = ‘e’;
printf(“%s”, word); //문자열 출력
word
L
o
v
e
?
?
?
?
여기에 있는 것도 출력할 문자열들인가?
word
L
o
v
e
0
?
?
여기까지가 출력한 문자열이군…
?
26. scanf 함수를 이용한 문자열 입력
char word[50];
char word[4];
printf(“Input String is : “);
printf(“%s”, word);
printf(“Input String is : “);
printf(“%s”, word);
printf(“input string : “);
scanf(“%s”, word);
word
L
i
printf(“input string : “);
scanf(“%s”, word);
n
u
x
/0
L
i
n
?
마지막에 널 문자를 넣어 문자열을 완성한다.
할당 영역
word
?
u
x
/0
?
이웃한 메모리 영역을 침범하게 된다.
?
27. 문자배열의 초기화
문자 배열 초기화 문자열 상수의 대입은 선언과 동시에 초기화하는 경우만 가능
char str[10] = {‘d’, ‘r’, ‘e’, ‘a’, ‘m’, ‘0’};
d
r
e
a
초기화한 문자열
m
0
0
0
0
남는 배열 요소
일반 변수
int val = 10;
val = 20;
// 변수 선언과 동시에 초기화 (0)
//초기화 된 이후 다른 값을 저장 (0)
문자 배열
char str[80] = “dream” ;
str = “world“ ;
// 변수 선언과 동시에 초기화 (0)
//초기화 된 이후 다른 값을 저장 (x)
0
28. 문자배열의 초기화
문자 배열 값 지정
w
o
r
l
d
0
0
0
0
0
한 byte 씩 모두 복사해야 한다.
d
r
e
a
m
0
초기화 시 배열 요소의 개수 생략
char str[] = “I love you”;
몇 byte의 기억 공간이 잡힐까?
0
0
0
0
30. 포인터로 배열요소의 참조
배열은 많은 양의 데이터를 효율적으로 처리할 수 있다.
반복문의 사용
배열은 같은 형태의 변수가 연속된 메모리 공간에 할당된다.
첫번째 배열요소의 위치를 알면 나머지도 알 수 있다.
x의 값을 알면 나머지는 자동으로 구해진다.
x
int ary[5];
int
ary[0]
x+4
int
ary[1]
x+8
int
ary[2]
x+12
int
ary[3]
x+16
int
ary[4]
31. 포인터로 배열요소의 참조 (예제)
Example Code
int ary[5] = {10, 20, 30, 40, 50};
int *ap;
ap=&ary[0];
printf(“ 1st 배열 요소의 주소값 : %u n”, ap);
printf(“ 1st 배열 요소의 값 : %d n”, *ap);
ap=&ary[0]+4 ;
printf(“ 2nd 배열 요소의 주소값 : %u n”, ap);
printf(“ 2nd 배열 요소의 값 : %d n”, *ap);
출력 결과
1st 배열 요소의 주소값 : 1245036
1st 배열 요소의 값 : 10
2nd 배열 요소의 주소값 : 1245052
2nd 배열 요소의 값 : 50
36
// 첫 번째 배열 요소의 포인터를 포인터 변수에 저장
// 첫 번째 요소의 주소값 출력
// 배열 요소에 저장된 값 출력
// 두 번째 배열 요소의 포인터를 구한다.
// 첫 번째 요소의 주소값 출력
// 배열 요소에 저장된 값 출력
52 ??
10
20
30
40
50
ary[0]
ary[1]
ary[2]
ary[3]
ary[4]
32. 포인터로 배열요소의 참조 (예제 계속)
왜 원하지 않는 결과가 나왔는가?
포인터는 어떤 자료형으로 부터 계산되었는지에 대한 정보를 가지고 있다.
따라서 포인터에 정수값을 더할 때는 포인터가 가리키는 자료형의 크기가 곱해지게 된다.
포인터+정수값
포인터 + (정수값+포인터가 가리키는 자료형의 크기)
&ary[0]+4=&ary[0]+(4*sizeof(int)) = 36+16=52
&ary[0] + 0 = 36
10
ary[0]
&ary[0] + 1 = 40
20
ary[1]
&ary[0] + 2 = 44
30
ary[2]
&ary[0] + 3 = 48
40
ary[3]
50
ary[4]
포인터가 가리키는 기억 공간 참조
&ary[0] + 4 = 52
포인터는 4바이트씩 증가한다.
*(&ary[0]+i)
각 배열요소를 가르키는 포인터를 차례로 계산
for(i=0; i<5; i++){
printf(“%dn”, *(ary[0]+i));
}
33. 배열명은 포인터!
C언어는 첫 번째 배열요소의 포인터를 쉽게 사용할 수 있도록 배열명으로 표현한다.
배열명은 첫 번째 배열 요소를 가리키는 포인터이다.
첫번재 기억 공간의 시작주소값
같다.
배열명
ary
36
10
ary[0]
44
48
52
20
30
40
50
ary[1]
ary[2]
ary[3]
ary[4]
포인터표현
배열표현
ary[0]
ary[1]
ary[2]
ary[3]
ary[4]
40
==
*(ary+0)
*(ary+1)
*(ary+2)
*(ary+3)
*(ary+4)
for(i=0; i<5; i++){
printf(“%dn”, *(ary+i));
}
34. 포인터 변수로 배열 요소 참조
int *ap = ary ; 배열명을 포인터 변수에 저장
36
36
포인터변수 ap
40
44
48
52
10
ary ==
20
30
40
50
ary[1]
ary[2]
ary[3]
ary[4]
ary[0]
*(ap + 1) 40번지에 있는 배열 요소를 참조한다.
36+(1*sizeof(int)) = 36+4=40번지
int ary[5] = {10, 20, 30, 40, 50};
int *ap = ary;
int i;
for(i=0; i<5; i++) {
printf(“%5d”, *(ap+1));
}
배열표현 포인터표현
ary[0]
ary[1]
ary[2]
ary[3]
ary[4]
==
*(ap+0)
*(ap+1)
*(ap+2)
*(ap+3)
*(ap+4)
int ary[5] = {10, 20, 30, 40, 50};
int *ap = ary;
int i;
for(i=0; i<5; i++) {
printf(“%5d”, ap[i]);
}
35. 배열 요소 참조 방법 정리
배열요소의 참조 방법
1. 배열명을 사용한 배열 표현
2. 배열명을 사용한 포인터 표현
3. 배열명을 저장한 포인터 변수를 사용한 포인터 표현
4. 배열명을 저장한 포인터 변수를 사용한 배열 표현
ary ==
36
포인터변수 ap
36
10
(1) ary[0]
40
44
48
52
20
30
40
50
ary[1]
ary[2]
ary[3]
ary[4]
(2) *(ary+0) *(ary+1) *(ary+2) *(ary+3) *(ary+4)
(3) *(ap+0)
(4) *(ap[0])
*(ap+1)
*(ap+2)
*(ap+3)
*(ap+4)
*(ap[1])
*(ap[2])
*(ap[3])
*(ap[4])
모두 첫번째 배열 요소를 참조하는 표현식이다.
36. 배열명은 포인터 변수가 아니다.
포인터 주소값을 나타낸다.
상수이므로 변경할 수 없다.
ary = ary + 2;
ary ++ ;
배열명은 변수가 아니므로 자신의 값을
바꿀 수 없다.
포인터 변수
변수이므로 가르키는 대상을 바꿀 수 있다.
포인터
오직 하나만..
포인터 변수
많을수록 ..
37. 배열 처리 함수 – 값 출력
모든 배열요소는 포인터로 참조할 수 있으므로 배열을 처리하는데 필요한 것은 배열에
있는 데이터가 아니라, 배열이 메모리의 어디에 있는지에 대한 주소값이면 충분하다.
int ary[5] = {10, 20, 30, 40, 50};의 모든 값을 출력하는 함수를 만들어 보자.
1. void ary_prn(int *ap)
{
int i;
for(i=0; i<5; i++){
printf(“%d”, *(ap+i));
}
}
호환성 있는 함수를 만들기 위해서는 배열명과 배열요소의 개수를 함께 전달인자로 사용해야 한다.
// ary_prn(ary, sizeof(ary)/sizeof(ary[0]));
2. void ary_print(int *ap, int an)
{
int i;
for(i=0; i<an; i++){
printf(“%5d”, ap[i]);
}
}
38. 배열 처리 함수 – 값 입력
배열의 위치를 알아야 한다.
int ary[5] ;의 값을 입력하는 함수를 만들어 보자.
1.
void ary_input(int *ap)
{
int i;
for(i=0; i<5; i++){
scanf(“%d”, ap+1);
}
}
printf는 배열 요소에 저장된 값을 필요로 하므로 참조 연산자를 사용했지만,
scanf는 참조연산자를 사용할 필요가 없다.
40. 2차원 배열의 선언과 초기화
1차원 배열 복잡하고 많은 데이터 처리에 한계
배열을 하나의 요소로 하는 또 다른 배열 2차원 배열
첫 번째 배열 요소
두 번째 배열 요소
+
배열 요소가 세 개인 새로운 배열.
세 번째 배열 요소
41. 2차원 배열의 선언과 초기화
2차원 배열의 선언
배열의 형태
int score[3][4];
배열의 이름
int 형 변수 4개짜리 1차원 배열이
2차원 배열의 배열 요소가 된다.
변수요소의 갯수
부분 배열 : 2차원 배열은 논리적으로 행렬로 표현된다.
부분배열명
첫번째 부분배열
ages[0]
두번째 부분배열
ages[1]
세번째 부분배열
ages[2]
2차원 배열 SCORE
42. 2차원 배열의 선언과 초기화
2차원 배열 요소의 참조
score[0][0] = 10 ;
첫번째 부분 배열의 첫번째 배열 요소에 10을 저장한다.
score[0]
score[0][0]
score[0][1]
score[0][2]
score[0][3]
43. 2차원 배열의 선언과 초기화
2차원 배열의 처리 2중 for 문
int score[3][4];
int i, j;
i는 0부터 2까지 세 번 반복된다.
for(i=0 ; i< 3) ; i++) {
학생 한 명의 점수를
입력 받는 과정
printf(“네 과목의 점수를 입력하세요 : “);
for( j=0 ; j<4 ; j++) {
scanf(“%d”, &score[i][j]);
}
부분배열명의 첨자를 제어변수로 사용하여
새로운 부분 배열에 점수를 입력 받도록 한다.
}
j 반복문
i 반복문
j가 0 일때
j가 1 일때
j가 2 일때
j가 3 일때
i가 0 일때
score[0][0]
score[0][1]
score[0][2]
score[0][3]
i가 1 일때
score[1][0]
score[1][1]
score[1][2]
score[1][3]
i가 2 일때
score[2][0]
score[2][1]
score[2][2]
score[2][3]
44. 2차원 배열의 선언과 초기화
예제 – 3명의 학생에 대한 4과목 점수 처리 예제
#include <stdio.h>
int main()
{
int score[3][4];
int i, j;
int tot;
double avg;
for(i=0; i<3; i++){
printf(“네 과목의 점수를 입력하시오 : “);
for( j=0; j<4; j++){
scanf(“%d ”, &score[i][j]);
}
}
for(i=0; i<3; i++){
tot=0;
for( j=0; j<4; j++){
tot+=score[i][j];
}
avg=tot/4.0;
//한 명의 총점을 모두 누적한 후에 평균 계산
printf(“총점 : %d, 평균 : %21fn”, tot, avg); //총점, 평균 출력
}
}
return 0;
45. 2차원 배열의 선언과 초기화
2차원 배열의 초기화 – case 1
int nums[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int nums[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
1
3
4
5
6
7
8
9
2차원 배열 nums
2
10
11
12
//논리적 구조에 맞게 행을 구분
첫번째 행이 초기화 된 후에
두 번째, 세 번째 행이 차례로
초기화 된다.
2차원 배열의 초기화 – case 2
기억 공간보다 초기화 값이 적은 경우 남은 공간은 0으로 초기화 된다.
int nums[3][4]={1,2,3,4,5,6};
1
2차원 배열 nums
2
3
4
5
6
0
0
0
0
0
0
첫 번째 기억 공간만 초기화 하면
모든 기억 공간이 0으로 초기화 된다.
int nums[100][200]={0} ;
// 모든 공간이 0으로 초기화
46. 2차원 배열의 선언과 초기화
2차원 배열의 초기화 – case 3
int nums[3][4]={{1},{5,6},{9,10,11}};
0
0
0
5
6
0
0
9
2차원 배열 nums
1
10
11
0
2차원 배열의 초기화 – case 4
int nums[][4]={{1},{2,3},{4,5,6}};
행의 수가 세 개
행 첨자 생략
열 첨자 생략 불가능
2차원 배열 nums
1
0
0
0
2
3
0
0
4
5
6
0
3행 4열의 기억 공간 할당
47. 2차원 배열의 선언과 초기화
2차원 배열의 초기화 – case 5
int nums[][4]={1,2,3,4,5,6};
첫번째 행 초기값
2차원 배열 nums
두 번째 행 초기값
1
2
3
4
5
6
0
0
2행 4열의 기억 공간 할당
48. 2차원 문자 배열
각 행이 하나의 문자열을 저장한다.
char animal[5][20];
//5개의 문자열을 저장할 2차원 문자배열
부분배열명
animal[0]
animal[1]
animal[2]
animal[3]
animal[4]
Scanf(“%s”, animal[4]);
전체가 배열명
//gets함수를 사용하면 gets(animal[4]);
char animal[5][20];
int i;
//2차원 문자 배열 선언
//반복 제어 문자
for(i=0; i<5; i++);{
printf (“문자열을 입력하세요 : ”);
scanf(“%s”, animal[i]);
//i값이 변하면서 각 부분배열명이 된다.
}
49. 2차원 문자 배열의 초기화
2차원 문자 배열의 초기화 case 1
char animal[5][10] = {{‘c’,’a’,’t’,‘0’}, {‘h’,’o’,’r’,’s’,’e’,’0’},
{‘d’,’o’,’g’,’0’}, {‘t’,’I’,’g’,’e’,’r’,’0’},
{‘e’,’l’,’e’,’p’,’h’,’a’,’n’,’t’,’0’}};
2차원 문자 배열의 초기화 case 2
char str[10] = “cat” ; 하나의 문자열 상수는 1차원 문자 배열을 초기화 할 수 있다.
char animal[5][10] = {“cat”, ”horse”, “dog”, “tiger”, “elephant”};
c a t
0 0 0 0 0 0 0
h o r s e
d o g
0 0 0 0 0
0 0 0 0 0 0 0
t i g e r
0 0 0 0 0
e l e p h a n t
0 0
51. 포인터 배열
예제
#include <stdio.h>
int main()
{
char * ptr_ary[5];
int i;
// 포인터 배열 선언
ptr_ary[0]=“dog”;
ptr_ary[1]=“elephant”;
ptr_ary[2]=“horse”;
ptr_ary[3]=“tiger”;
ptr_ary[4]=“lion”;
}
for(i=0; i<5 ; i++) {
printf(“%sn”, ptr_ary[i]);
}
return 0;
ptr_ary
ptr_ary[0]
d o g
ptr_ary[1]
e l e p h a n t
ptr_ary[2]
h o r s e
0
ptr_ary[3]
t i g e r
0
ptr_ary[4]
l
스택 영역
0
i o n
0
0
데이터의변경이 불가능한 메모리 영역
52. 포인터 배열의 초기화
포인터 배열의 초기화 방법
char animal[5][10]
char *ptr_ary[5]
= {“dog”, “elephant”, “horse”, “tiger”, “lion”};
= {“dog”, “elephant”, “horse”, “tiger”, “lion”};
배열의 형태에 따라 문자열을 복사하거나
포인터를 저장한다.
초기화 방법은 같다..
포인터 배열은 2차원 배열인가?
포인터 배열은 첨자를 하나 사용하는 1차원 배열이다. 그러나 배열 요소가 포인터 변수이므로
2차원 배열처럼 활용하는 것이 가능하다.
57. 포인터 응용을 위한 2가지 원칙
첫째, 포인터가 가리키는 자료형은 무엇인가?
int val;
&val
double val;
int형 변수
&val
포인터 &val은 int형을 가리킨다.
double형 변수
포인터 &val은 double형을 가리킨다.
둘째, 포인터는 같은 자료형을 가리키는 포인터 변수에 저장해야 한다.
가리키는 자료형
int * ip ;
ip는 포인터 변수
변수이름
ip = &var;
//포인터를 포인터 변수에 대입
가리키는 자료형은 모두 int 형이다.
58. 다중 포인터
다중 포인터
포인터 변수를 가리키는 포인터
int *ip;
&ip
이중 포인터
(int *)형 변수
// 포인터 &ip는 (int *) 형을 가리킨다.
포인터 변수
이중 포인터 사용한 변수 참조
int val = 10;
int *ip = &val;
// int형 변수의 선언과 초기화
// 포인터 변수의 선언과 초기화
포인터 변수의 시작 주소
(200)
&ip
(200)
100
포인터 변수 ip
변수의 시작 주소
(100)
10
int형 변수 val
59. 다중 포인터
이중 포인터 사용한 변수 참조 (계속)
* * &ip
(200)
#include <stdio.h>
int main()
{
int val=10;
int *ip ;
}
(100)
(10)
//포인터 변수의 선언과 동시에 초기화 가능 (int * ip=&val)
ip = &val;
printf(“변수 val의 값 : %d n”, **&ip);
return 0;
이중 포인터 변수
가리키는 자료형
int* * ipp ;
ipp는 포인터 변수 변수이름
ipp = &var;
//이중포인터를 이중 포인터 변수에 대입
가리키는 자료형은 모두 int * 형이다.
60. 다중 포인터
이중 포인터 변수 (계속)
int val = 10;
int *ip ;
int **ipp ;
// int형 변수의 선언과 초기화
// 포인터 변수의 선언
// 이중 포인터 변수 선언
ip = &val ;
// int 변수의 포인터를 포인터 변수에 저장
ipp = &ip ;
// 포인터 변수의 포인터를 이중 포인터 변수에 저장
printf(“변수 val의 값 : %dn”, **ipp);
이중 포인터 변수 ipp
(300)
200
(200)
포인터 변수 ip
(100)
100
**ipp == ip
10
*ip == val
**ipp == val
int형 변수 val
61. 배열 포인터
배열명의 역할
1) 첫번째 배열 요소를 가리키는 포인터로서의 기능
ex) int nums[5] = {10, 20, 30, 40, 50} ;
int ip* = nums ;
// 배열명은 포인터이므로 포인터 변수에 저장한다.
printf(“%d n”, *nums);
// 첫번째 기억 공간을 참조하여 10이 출력된다.
printf(“%d n”, *(nums+4));
// nums의 값이 100이면 116번지 참조
nums
10
20
30
40
50
2) 배열의 기억공간 전체를 나타내는 논리적 변수의 기능
ex) int ary[5];
ary = 10;
(0)
// 배열명에 직접 값을 대입할 수 없다.
ary[0] = 10;
(x)
// 특정 기억공간을 첨자로 지정하여 값을 대입해야 한다.
ary
int ary[5];
크기 : 20byte
형태 : int형 변수
5개
int
int
int
int
전체 20 byte
int
62. 배열 포인터
배열 포인터
배열 전체를 가리킨다.
&ary
ary
36
int
int
int
int
int
int
ary+1
40
int
&ary
44
int
48
52
int
56
int
60
int
64
int
int형 다섯 개의 배열을
가리키는 배열 포인터
68
int
72
…
int
int
&ary+1
• ary + 1 1245036 + (1*sizeof(ary[0]) 1245036+(1*4) 1245040
• &ary + 1 1245036 + (1*sizeof(ary) 1245036+(1*20) 1245056
……
63. 배열 포인터
첫번째 기억공간
두번째 기억공간
세번째 기억공간
*(ary+0)
*(ary+1)
*(ary+2)
배열요소의 참조는 배열명을 사용한다.
1차원 배열에서는 배열명으로 각 기억공간을 참조하므로 배열포인터를 구하는 것이 의미가 없다.
2차원 배열의 경우
int ary[3][4];
ary[0]
ary[1]
ary[2]
배열명 ary는 첫번째 부분 배열을
가리키는 배열포인터이다. !
ary
int
int
int
int
int
int
int
int
int
int
int
배열 요소는 세 개
각 배욜 요소의 형태는
“int형 변수 네 개의 배열형”
int
부분배열명은 각 부분배열의 첫번째
배열요소를 가리키는 포인터이다. !
64. 배열 포인터
배열포인터 변수
가리키는 자료형
int형 변수 네 개의 1차원 배열
int (* ap) [4] ;
ap는 포인터 변수다.
예제)
변수의 이름
괄호가 생략되면
포인터 배열(포인터 변수들의 배열)을
선언하는 형식이 되므로 주의 !!!
# include <stdio.h>
int main()
{
int ary[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*ap) [4] ; //int형 변수 네 개의 배열을 가리키는 배열 포인터 변수
int i, j;
ap = ary;
for(i=0; i<3; i++) {
for(j=0; j<4; j++) {
printf(“%5d”, ap[i][j]);
}
printf(“n”);
}
return 0;
}
65. 배열 포인터
2차원 배열에서 배열명으로 기억공간을 참조하는 원리
2차원 배열은 논리적으로는 2차원이지만 물리적으로는 1차원의 형태로 기억공간에 할당
ary
배열명 ary의 값은 배열의
시작위치값 100과 같다.
100
ary[0]
1
116
부분 배열명은 각 부분 배열의
시작 위치 값이다.
ary[1]
5
132
ary[2]
9
104
2
120
6
136
10
• ary +1 100 + (1*sizeof(ary[0])) 100 + (1*16) 116
부분 배열명 ary[0]는
변수의 기능을 하며
부분배열 전체의 크기를 계산한다.
• *(ary+1) ary[1]
//두 번째 부분 배열명
108
3
112
4
124
7
140
11
128
8
144
12
66. 함수 포인터
함수명는 포인터
프로그램 실행시 함수가 존재하는 메모리의 위치값이 함수명이다.
함수를 (*sum)(10,20)과 같은 형태로 호출하는 것이 가능하다. 함수명이 포인터라는 증거 !
함수의 정의
함수포인터(함수명)
sum
함수명은 함수를 가리킨다.
int sum(int a, int b)
{
int res;
res = a + b;
return res;
}
함수 포인터 변수
함수의 형태
int sum (int, int) ;
(리턴값의 형태)
(매개변수의 객수와 형태)
int (* fp) (int, int) ;
• 가리키는 함수의 형태
Int형 값 두개를 전달 인자로 받고
Int형 값을 리턴하는 함수
변수의 이름
Fp는 포인터 변수다.
• int *fp(int,int); 괄호가 없으면 포인터를 리턴하는 함수
67. 함수 포인터
사용 예제
#include <stdio.h>
int sum (int, int)
// 함수의 선언
int main()
{
int (*fp) (int, int) ;
// 함수 포인터변수 선언
int res;
// 리턴값을 저장할 변수
fp = sum;
// 함수명을 함수포인터변수에 저장한다.
res = fp(10,20);
// 함수포인터변수로 함수를 호출한다.
printf(“result : %d n”, res);
// 리턴값 출력
return 0;
}
int sum (int a, int b)
{
return a+b;
}
// 함수의 정의
68. 함수 포인터
함수포인터의 사용
형태가 같은 다양한 기능의 함수를 선택적으로 호출하는데 사용
• int sum(int, int) ; // 두 정수값을 더해서 리턴하는 함수
형태가 같다.
• int mul(int, int) ;
// 두 정수값을 곱해서 리턴하는 함수
• int max(int, int) ; // 두 정수값 중에서 큰 값을 리턴하는 함수
• fp = sum ;
int (*fp) (int, int);
정수의 합 계산
(sum)
func( );
//func(sum);
//func(mul);
//fucn(max);
• fp = mul ;
• fp = max ;
정수의 곱 계산
(mul)
모두 사용 가능하다.
큰 값 계산
(max)
필요에 따라 원하는 긴으의 함수명을
전달인자로 주고 호출한다.
void func(int (*fp)(int, int))
{
…
fp(a,b);
…
}
69. 함수 포인터
사용 예제
#include <stdio.h>
void func(int (*)(int, int));
// func 함수의 선언, 매개변수는 함수포인터 변수
int sum(int, int);
// 두 정수값을 더하는 함수의 선언
int mul(int, int);
// 두 정수값을 곱하는 함수의 선언
int max(int, int);
// 두 정수값 중에서 큰 값을 구하는 함수의 선언
int main()
{
int sel ;
// 메뉴 선택 번호를 저장할 변수
printf(“1. 두 정수의 합 n”);
// 메뉴 출력
printf(“2. 두 정수의 곱 n”);
printf(“3. 두 정수중 큰 값 선택 n”);
printf(“ 원하는 작업을 선택하시오 : “ );
scanf (“%d”, &sel);
switch(sel) {
case 1: func(sum); break;
case 2: func(mul); break;
case 3: func(max); break;
}
return 0;
}
70. 함수 포인터
사용 예제 (계속)
void func(int (*fp) (int, int)) {
int a, b;
int res;
printf(“ 두 정수값을 입력 하시요 : “);
scanf(“%d %d”, &a, &b);
res = fp(a, b);
printf(“결과값은 : %d n”, res);
}
왜 함수 포인터를 사용하는가?
int sum(int a, int b) {
프로그램의 유지 보수가 쉽다.
return a+b ;
}
int mul (int a, int b) {
return a*b ;
}
int max (int a, int b) {
if(a >b) return a;
else return b;
}
71. void 포인터
void 포인터
자료형에 대한 정보가 없다. (특별한 경우를 제외하고 사용할 경우가 거의 없다. --;)
다른 포인터에 형변환 연산자를 사용하여 강제로 만들어주어야 한다.
int a ;
(void * ) &a ;
// int형 변수 선언
// int형 변수의 포인터를 void 포인터로 강제 형 변환
void 포인터 변수
가리키는 자료형이 정해져 있지 않다.
void * vp;
int in ;
double db ;
void *vp ;
vp = & in ;
vp = & db ;
변수의 이름.
vp는 포인터 변수
// int형 변수
// double형 변수
// void 포인터 변수
// int형 변수의 포인터를 저장할 수도 있고,
// double형 변수의 포인터도 저장할 수 있다.
vp는 자료형에 대한 정보가 없으므로 사용시 원하는 형태로 변환해야 한다. !!!
printf(“%d n”, *(int *) vp);
vp = (int * ) vp +1;
72. void 포인터
사용 예제
#include <stdio.h>
#include <string.h>
void exchange(char *, void *, void * ) ;
// 함수 선언
int main()
{
int a=10; b=20;
// 정수형 변수 a, b
double da=1.5, db=2.5;
// 실수형 변수 da, db
exchange(“int”, &a, &b);
// 두 정수값의 교환
printf(“정수값 교환 후 : %d, %d n”, a, b);
// 교환 후 결과 출력
exchange(“double”, &da, &db);
printf( “실수값 교환 후 : %.11f,
}
// 두 실수값을 교환한다.
%.11f n”, da, db)
//교환 후 결과 출력
73. void 포인터
사용 예제 (계속)
void exchange (char *type, void *vp1, void *vp2)
// 함수의 정의
{
int itp;
// 정수형 임시 변수
double dtp;
// 실수형 임시 변수
if(strcmp(type, “int”) == 0) {
itp=*(int *) vp1;
*(int *) vp1 = * (int *) vp2;
*(int *) vp2 = itp;
}
* (int *) vp1
if(strcmp(type, “double”) == 0)
vp1을 (int *)형으로 변환.
{
dtp=*(double *)vp1;
*(double *)vp1 = *(double *)vp2;
*(double *)vp2 = dtp;
}
}
변환된 vp1이 가리키는 기억공간 참조