[C프로그래밍 새내기를 위한 첫 C 언어 책] Chapter10(포인터) 정리 및 연습문제
내용 정리
드디어 C 프로그래밍의 애증의 포인터이다.
내 생각에 아직도 C언어가 널리 쓰이는 이유는
포인터 덕분인것 같다 ㅎㅎㅎ
아직도 어렵다... 꼭 정복하겠다.
※ 포인터 개념 정리: 포인터 정리 및 포인터와 배열명의 공통점 및 차이점
포인터를 사용하는 이유
- 참조 불가능한 변수를 간접적으로 참조
- 프로그램의 성능을 개선하고 기억 공간을 효율적으로 사용할 수 있다.
- 동적 할당이 가능하다. (트리, 연결리스트에서 자세히)
포인터 사용 순서
1. 포인터 변수 선언
2. 포인터 변수에 다른 변수의 주소 대입
3. 간접 참조 연산자(*) 사용하여 포인터 변수 이용

위의 그림에서 *pa는
배열 a의 시작 주소를 가리키는 포인터 변수이다.
앞에서 배열명인 a는 배열 a의 시작 주소라고 했다.
결국 *pa = a이다.
*pa가 int형인 이유는 int형 변수의 주소를 저장하기 때문이다.
예제를 보자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
int main()
{
int *pa;
int a[5] = { 10, 20, 30, 40, 50 };
int i;
pa = a; //또는 pa = &a[0];
printf("pa의 값(a의 시작주소 들어있음):%u\n", pa);
printf("배열 a의 주소:%u\n\n", &a);
printf("간접참조 연산자 이용해서 배열의 값 얻기\n\n");
printf("배열 첫번째 원소 얻기\n");
printf("*pa = %d\n", *pa);
printf("a[0] = %d\n", a[0]);
printf("*pa = a[0]\n\n");
printf("배열 두번째 원소 얻기\n");
printf("*(pa+1) = %d\n", *(pa+1));
printf("a[1] = %d\n", a[1]);
printf("*(pa+1) = a[1]\n\n");
printf("배열 세번째 원소 얻기\n");
printf("*(pa+2) = %d\n", *(pa + 2));
printf("a[2] = %d\n", a[2]);
printf("*(pa+2) = a[2]\n\n");
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
pa에 배열 a의 시작 주소를 넣어주어
pa가 간접적으로 배열을 참조할 수 있다는 것을 보여준다.
아래 결과를 보면 이해할 수 있다.
포인터는 그림으로 이해하면 훨씬 이해하기 쉽다.
실제 메모리 내부의 상황을 그림으로 나타내었다.
더 간략하게 표현하면 이런 모습
포인터 변수 pa는 배열 a가 시작하는 주소를 기억장치에 저장하고 있고
int형 배열인 a는 원소마다 저장된 값을 가지고 있다.
아래 그림은 위의 결괏값이랑 다른 주소를 나타낸다.
다시 실행했기 때문에 다른 것이므로 이는 무시하고
pa+1이 a[1]의 주소를 가진다고 보면 된다.
pa가 int 형이기 때문에
만약 pa에 위의 그림과 같이 7797696이 저장되어 있다면
pa+1은 int형 크기인 4byte가 더해져
7797000이 된다.
그래서 *(pa+1)은 a[1]과 같게 되는 것이다.
=======================================
위에서 정리한 내용을 보면
포인터 = 배열명
이라고 하는 공식(?)이 저명하다고 생각된다.
하지만 엄밀히 말하면 다르다!
다르다
다르다.
※ 값에 의한 호출, 주소에 의한 호출
값에 의한 호출과 주소에 의한 호출은 함수 부분에서 잠깐 정리했었는데
포인터의 개념을 배웠으니 더 확실하게 정리해보겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include <stdio.h>
void swap_value(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
printf("swap_value함수 내부의 값: x = %d, y = %d\n\n", x, y);
}
void swap_address(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
printf("swap_address함수 내부의 값: *x = %d, *y = %d\n\n", *x, *y);
}
int main()
{
int x = 100, y = 200;
printf("main에서 함수 사용 전 값: x = %d, y = %d\n\n", x, y);
swap_value(x, y);
printf("main애서 swap_value함수 사용 후: x = %d, y = %d\n\n", x, y);
swap_address(&x, &y);
printf("main애서 swap_address함수 사용 후: x = %d, y = %d\n\n", x, y);
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
결론은
값에 의한 인자 전달은 함수에서는 값이 바뀌지만
main으로 돌아오면 그대로
주소에 의한 인자 전달은 함수에서 값 변경되어서
main으로 돌아와도 유지
이제 이유를 차근차근 살펴볼 차례
이때 swap value함수는 변경한 값을 돌려주지 못한다.
void함수이고, return을 할 수 있는 int 함수로 변경한다고 해도
return 값은 1개이므로 변경된 2개의 값을
main으로 전달할 수 없다.
3. swap_value 함수 종료 후 기억 장소 리턴, main에 아무런 영향 주지 못하고 사라짐
4. swap_address함수 호출한 후 메모리
5. swap_address함수 내부 실행
6. swap_address함수 종료 후 기억장소 리턴, 사라짐
7. 메모리의 마지막 상태
※ 포인터와 문자열
C에서는 문자열 변수가 없다.
배열 or 포인터로 문자열을 표현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include <stdio.h>
#include <string.h>
int main()
{
char str1[10] = "language";
char *str2 = "language";
printf("배열 문자열 변경\n");
printf("%s->", str1);
strcpy(str1, "program");
printf("%s\n\n", str1);
printf("포인터 문자열 변경\n");
printf("%s->", str2);
str2 = "Cprogramming";
printf("%s\n\n", str2);
return 0;
}
|
str1은 배열의 시작 주소인 포인터 상수
주소변경이 불가능하므로
따라서 str1 = "문자열" 이 불가능하다.
str2는 문자열을 가리키는 포인터 변수
원래 "language"가 저장되어 있는 공간을 가리켰는데
str2="문자열"로 다른 문자열이 저장되어있는 공간을 가리키게 할 수 있다.
str1과 같은 배열의 문자열은
문자열 함수(strcpy)만을 이용해 전체 문자열을 변경하거나, 원소 하나하나 만을 변경할 수 있다.
기억공간의 차이
배열 문자열은 미리 개발자가 배열의 크기를 정해야 한다.
만약 배열의 크기가 20인데
"abc"라는 문자열을 저장한다면
'\0'문자를 포함하여 총 4개의 원소만 사용하고, 나머지 16개의 공간은 낭비가 된다.
포인터 문자열은 "abc"를 대입하면 '\0'을 포함하여
문자의 길이에 맞게 동적으로 할당된다.
동적 할당은 나중에 자세히
예제 하나 더
포인터, 배열 문자열 이용하여 문자열 대문자, 소문자화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include <stdio.h>
#include <ctype.h> //toupper, tolower위해서
#include <string.h>
int main()
{
char s[100], *p;
int i;
printf("문자열을 입력하시오 : ");
gets(s);
p = s;
//char형 포인터 이용해서 문자열 대문자로 변경
while (*p != '\0')
{
*p = toupper(*p);
p++;
}
printf("대문자로 변환한 결과 : %s\n", s);
for (i = 0; i < strlen(s); i++)
{
s[i] = tolower(s[i]);
}
printf("소문자로 변환한 결과 : %s\n", s);
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
쉬우니까 설명은 생략
※ char형 포인터 배열, 포인터와 다차원 배열
포인터와 1차원 배열은 전에 포스팅했다.
그럼 2차원 배열, 3차원 배열은 어떻게 될까
똑같은 원리이다.
A[3]에서 A은
배열 A의 시작 주소라고 했다.
A는 주소 100을 나타내는 포인터 상수라 했다.


실제 메모리의 구조는 아니지만 이것도 이해하기 쉽게 그리면
B[0]은 B[0][0], B[0][1], B[0][2] => B[0]으로 시작하는 원소들을 포인팅한다. (B[0][0] 주소(100) 포인팅)
B[1]은 B[1][0], B[1][1], B[1][2] => B[1]으로 시작하는 원소들을 포인팅한다. (B[1][0] 주소(112) 포인팅)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
//프로그램 10-15
#include <stdio.h>
#include <string.h>
#define SIZE 5
int main()
{
char silver[SIZE][10] = { "나태희", "유빈", "나원빈", "문건영", "소지법" };
char temp[10];
int pass, i;
printf("** 은메달 리스트 : ");
for (i = 0; i < SIZE; i++)
{
printf("%s, ", silver[i]);
}
printf("\b\b**\n\n");
//버블정렬 사용하여 정렬
for (pass = 1; pass < SIZE; pass++)
{
for (i = 0; i < SIZE-pass; i++)
{
if (strcmp(silver[i], silver[i + 1]) > 0)
{
strcpy(temp, silver[i]);
strcpy(silver[i],silver[i+1]);
strcpy(silver[i + 1], temp);
}
}
}
printf("** 정렬한 리스트 : ");
for (i = 0; i < SIZE; i++)
{
printf("%s, ", silver[i]);
}
printf("\b\b**\n\n");
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
2차원 배열에 문자열을 저장하여
2차원 배열의 SIZE원소는 문자열 1개를 포인팅 한다.
그림 그리기 귀찮다.

* 저작권에 문제가 된다면 수정하겠습니다.
연습 문제
17, 20번을 풀어보겠다.
문제17: sales 배열에 대리점 10곳의 월 판매량이 저장되어 있다. 이 배열에서 사용자가 입력한 n에 대해 상위 n개의 판매량을 출력하는 프로그램을 작성하시오.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#include <stdio.h>
#define N 10
void top_n(int *sale)
{
int i,j;
int temp;
//내림차순 정렬
for (i = 1; i < N; i++)
{
for (j = 0; j < N - i; j++)
{
if (sale[j] < sale[j + 1])
{
temp = sale[j];
sale[j] = sale[j + 1];
sale[j + 1] = temp;
}
}
}
}
int main()
{
int sales[N] = { 203, 105, 302, 200, 289, 175, 130, 120, 267, 312 };
int n; //상위 n개의 판매량 출력
int i;
printf("상위 몇개의 판매량 출력: ");
scanf("%d", &n);
top_n(sales);
printf("%d개의 판매량 출력:", n);
for (i = 0; i < n; i++)
{
printf("%4d", sales[i]);
}
printf("\n");
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
문제의 조건이 까다롭고, 뭔 말인지 모르겠다.
그냥 함수에서 내림차순 sorting 해서
입력받은 값까지 출력하면 되는 것 아닌가?
어려운 문제인지 알고 풀었는데 아니다..
문제20: 1~45 범위의 난수 6개로 구성된 로또 번호를 생성하는 프로그램을 작성하시오. 난수 6개는 no배열에 저장하되 6개의 난수가 서로 달라야 하며 화면에 출력될 때는 오름차순으로 정렬하여 표시한다. 사용자에게 생성한 로또 번호를 보여준 후에는 새로운 로또 번호의 생성 여부를 물어 사용자가 [ESC] 키를 누르면 더 이상 생성하지 않고 실행을 끝낸다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 6
void random_no(int *arr)
{
int i,j;
int flag=0; // 중복된 번호인지 확인
srand(time(NULL)); //시드값 설정
for (i = 0; i < N; i++)
{
do
{
flag = 0; //flag 0으로 초기화
arr[i] = (rand() % 45) + 1; //번호 할당
for (j = 0; j < i; j++)
{
if (arr[i] == arr[j]) //이전 번호와 중복이면
{
flag = 1; //flag 1로 설정
break; //조건문 탈출, 위의 for문 탈출
}
}
} while (flag == 1); //flag가 1인동안 계속 반복
}
}
void sort(int *arr)
{
int i, j;
int temp;
for (i = 0; i < N - 1; i++)
{
for (j = 0; j < N - i; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j + 1] = temp;
}
}
}
}
void print_lotto(int *arr)
{
int i;
printf("생성된 로또 번호:");
for (i = 0; i < N; i++)
{
printf("%3d", arr[i]);
}
printf("\n");
}
int main()
{
int no[N]; //로또 번호 배열
do{
random_no(no);
print_lotto(no);
sort(no);
print_lotto(no);
printf("그만하려면 [ESC]키를, 새 로또 번호를 보려면 다른 키를 누르세요.\n");
} while (getch() != 0X1B);
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
왜 처음에 이상한 값 뜨는 거지?
random_no 함수가 조금 생각해야 하는 부분
flag변수를 두어
배열에 같은 값이 있으면
flag를 1로 설정하여 while문을 계속 반복하면서
새로운 값을 부여하고
다시 확인하기 반복
로또 당첨되고 싶다. 일단 사지도 않는데 무슨 ㅋㅋ