내용 정리

 

드디어 C 프로그래밍의 애증의 포인터이다.

 

내 생각에 아직도 C언어가 널리 쓰이는 이유는 

 

포인터 덕분인것 같다 ㅎㅎㅎ

 

아직도 어렵다... 꼭 정복하겠다.

 

 

 

※ 포인터 개념 정리: 포인터 정리 및 포인터와 배열명의 공통점 및 차이점

 

 

포인터를 사용하는 이유
- 참조 불가능한 변수를 간접적으로 참조
- 프로그램의 성능을 개선하고 기억 공간을 효율적으로 사용할 수 있다.
- 동적 할당이 가능하다. (트리, 연결리스트에서 자세히)

포인터 사용 순서
1. 포인터 변수 선언
2. 포인터 변수에 다른 변수의 주소 대입
3. 간접 참조 연산자(*) 사용하여 포인터 변수 이용

출처https://ko.wikipedia.org/wiki/%ED%8F%AC%EC%9D%B8%ED%84%B0_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)

 

위의 그림에서 *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= { 1020304050 };
    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;
    *= *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으로 돌아와도 유지

이제 이유를 차근차근 살펴볼 차례

 

1. swap value함수 호출한 후 메모리

 

2. swap value함수 내부 실행

 

이때 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 (*!= '\0')
    {
        *= 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을 나타내는 포인터 상수라 했다.

 

 

실제 메모리의 구조는 아니지만 이해하기 쉽게 그리면

 

A는 배열 A를 포인팅 하는 포인터 상수이다.
 
그럼 2차원 배열

 

B[2][3]은 어떻게 될까?
 
B는 배열 B의 시작주소일 것이다.

실제 메모리의 구조는 아니지만 이것도 이해하기 쉽게 그리면
 

 

B는 전체 배열을 포인팅 하며
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] = { 203105302200289175130120267312 };
    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문을 계속 반복하면서

새로운 값을 부여하고
다시 확인하기 반복

로또 당첨되고 싶다. 일단 사지도 않는데 무슨 ㅋㅋ

 

내용 정리

 

함수는 크게 라이브러리 함수와

 

사용자 지정 함수로 나뉜다

 

라이브러리 함수는 6장의 링크를 참조하여 사용할 때 찾아보면 익숙해질 것이다.

 

 

이번 chapter에서는 사용자 지정 함수에 대해 공부해보자

 

 

 

간단한 예제를 통해서 사용자 지정 함수가 어떠한 것인지

 

함수는 어떻게 생겼는지 보자

 

 

 

사용자 지정 함수

 

예제로 간단한 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
//함수
 
//두 수중 큰 값 구하기
#include <stdio.h>
 
int find_larger(int first, int second)
{
    int larger;
    
    if (first > second)
        larger = first;
    else
        larger = second;
 
    return larger;
}
 
int main()
{
    int n1, n2, max;
 
    printf("첫 번째:");
    scanf("%d"&n1);
 
    printf("두 번째:");
    scanf("%d"&n2);
 
    max = find_larger(n1, n2);
    printf("%d, %d중큰 수는:%d\n",n1,n2,max);
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

 

main함수를 실행하다 도중에

find_larger(n1,n2)를 만나

n1, n2를 인자로 사용하여 find_larger을 호출한다.

 

find_larger함수가 실행되며, n1값은 first로 n2값은 second로 전달되어 저장된다.

 

이때n1 == first는 아니다!

전달하는 시점에 값을 전달해주는 것뿐이다.

 

find_larger은 int형 변수인 larger을 반환해주면서 함수가 끝나고

다시 함수를 호출했던 부분인 main함수로 돌아와서 계속 main함수를 진행한다.

 

 

 

※ 함수의 인수 전달 (값에 의한 전달, 주소에 의한 전달)

 

 

값에 의한 호출로는
함수를 호출할 때 전달하는 인자와
호출한 함수의 변수(전달받은 인자가 저장된 값)는 다른 변수이기 때문에

호출한 함수 내부에서 값을 변경하더라도
main함수에서는 그대로임...

예를 들어

 

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
#include <stdio.h>
 
void sort(int first, int second)
{
    int temp;
 
    if (first > second)
    {
        temp = first;
        first = second;
        second = temp;
    }
    printf("\nsort함수의 정렬 결과 first: %d, second: %d\n\n", first, second);
    printf("first의 기억장소 주소: %u\n"&first);
    printf("second의 기억장소 주소: %u\n\n"&second);
}
 
int main()
{
    int n1 = 5000, n2 = 4000;
    
    printf("함수 호출 전 n1: %d, n2: %d\n", n1, n2);
    sort(n1, n2);
    printf("함수 호출 후 n1: %d, n2: %d\n\n", n1, n2);
 
    printf("n1의 기억장소 주소: %u\n"&n1);
    printf("n2의 기억장소 주소: %u\n"&n2);
    
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
 
값에 의한 호출 방식으로 함수의 인자를 전달하면
main함수의 변수 값을 다른 함수에서 변경 할 수 없다.

결과를 확인하면 sort함수에서는 숫자가 오름차순으로 정렬이 되었으나
sort함수를 호출 하고 나서 main함수의 숫자는 정렬이 안된것을 확인 할 수 있다.

이유는 변수가 전혀 다른 변수이기 때문이다.
전혀 다른 변수라는것은 변수가 저장 되어있는 기억장소의 주소를 확인하면 알 수 있다.
n1와 first의 변수의 주소가 다르다.

 

따라서 다른 함수에서 호출 한 함수의 변수를 변경하고 싶으면
변수의 주소를 함수에 전달하여야 한다!

 

 

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
#include <stdio.h>
 
void sort(int *first, int *second)
{
    int temp;
 
    if (*first > *second)
    {
        temp = *first;
        *first = *second;
        *second = temp;
    }
    printf("\nsort함수의 정렬 결과 first: %d, second: %d\n\n"*first, *second);
    printf("first의 기억장소 주소: %u\n", first);  //&(*first)도 같은 표현
    printf("second의 기억장소 주소: %u\n\n", second);
}
 
int main()
{
    int n1 = 5000, n2 = 4000;
 
    printf("함수 호출 전 n1: %d, n2: %d\n", n1, n2);
    sort(&n1, &n2);
    printf("함수 호출 후 n1: %d, n2: %d\n\n", n1, n2);
 
    printf("n1의 기억장소 주소: %u\n"&n1);
    printf("n2의 기억장소 주소: %u\n"&n2);
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

main에서 sort(&n1,&n2)로 변수의 주소 전달
sort함수는 main주소를 전달받았으므로
간접 참조 연산자*를 이용해 주소가 아닌 값을 받아올 수 있다.


결과를 보면 main함수의 변수들이 정렬된 것을 볼 수 있다.
주소를 확인하면 main함수의 변수와 sort함수의 변수가 같은 것을 확인할 수 있다.
주소를 전달받았기 때문이다.

 

추후 포인터를 공부한다면 좀 더 쉽게 이해할 수 있을 것이다.

 

 

 

※ 배열을 인자로 함수에 전달하기

 

 

배열명도 포인터이므로 포인터처럼 사용하여 인자로 전달하면 된다.
결론이지만
이해해보자

배열의 원소 하나를 함수의 인자로 전달하는 방식으로 모든 원소를 함수에 넣는다.
이 방법 번거롭고 많은 함수 호출이 일어난다.

일단 예제

 

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
//함수
//배열의 원소 하나하나를 인자로 전달
 
#include <stdio.h>
#define N 10
 
int find_larger(int first, int second);
 
int main()
{
    int freeze[N] = { 150-20-3050-5-120-510-12 };
    int i, max;
 
    max = freeze[0];
 
    for (i = 1; i < N; i++)
    {
        max = find_larger(max, freeze[i]);
    }
 
    printf("어는 점 목록:");
    for (i = 0; i < N; i++)
    {
        printf("%d ", freeze[i]);
    }
    printf("\n가장 높은 어는 점:%d\n", max);
 
    return 0;
}
 
int find_larger(int first, int second)
{
    if (first > second)
        return first;
    else
        return second;    
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

max = find_larger(max, freeze[i]);
함수 호출할 때 freeze배열의 원소 하나하나 find_larger함수에 넣어서 최댓값을 찾는다.

처음에 max = find_larger[0] 로 지정해 놓았기 때문에
함수에는 1번 원소부터 넣어서

find_larger(max,freeze[1]) => 0~1중 큰 값이 리턴
find_larger(max,freeze[2]) => 0~2중 큰 값이 리턴
find_larger(max,freeze[3]) => 0~3중 큰 값이 리턴
find_larger(max,freeze[4]) => 0~4중 큰 값이 리턴
...
find_larger(max,freeze[9]) => 0~9중 큰 값이 리턴

총 9번 함수를 호출하여 큰 값을 구한다.

 

 

원소 하나하나 인자로 전달하는 방법은 상당히 비효율적이다.
=> 배열 전체를 인자로 전달한다.

포인터를 배우지 않았지만, 배열명 = 포인터라고 알아두자

배열명인 freeze를 인자로 전달하면 배열 전체를 전달할 수 있다.

 

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
//함수
//배열의 전체를 인자로 전달
#include <stdio.h>
#define N 10
 
int find_larger(int arr[]);
 
int main()
{
    int freeze[N] = { 150-20-3050-5-120-510-12 };
    int max,i;
 
    max = find_larger(freeze);
 
    printf("어는 점 목록:");
    for (i = 0; i < N; i++)
    {
        printf("%d ", freeze[i]);
    }
 
    printf("\n가장 높은 어는 점:%d\n", max);
 
    return 0;
}
 
int find_larger(int arr[])
{
    int larger;
    int i;
 
    larger = arr[0];
    for (i = 1; i < N-1; i++)
    {
        if (larger < arr[i])
            larger = arr[i];
    }
    return larger;    
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

max = find_larger(freeze);
freeze배열 전체를 인자로 전달하였다.

 

 

배열 이름인 freeze는 변수를 저장하고 있는 변수가 아니라
배열의 시작 부분을 가리키고 있는 주소와 같다.
위 그림을 보면 이해가 가능하다.

freeze를 인자로 전달하여 freeze [0~9]까지 접근이 가능하게 된다.

 

 

-정리-
원소 하나하나를 전달하는 방법에 비해 훨씬 효율적인 방법
배열명을 인자로 전달하면 된다.
배열명은 배열의 첫 시작 원소의 주소이다.

 

 

※ 난수 구하기

 

난수구하기 은근히 자주 사용하는데 이참에 정리해보자
JAVA,C++,C#,C,PYTHON 배울 때 모두 사용한 거 같다
좀 자세히 정리해봐야지

C언어의 난수 생성함수
rand() : 0~32767 범위 안의 임의의 값 반환
헤더파일 stdlib.h(standard library 맞나?) 필요

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int i, random;
    for (i = 1; i <= 5; i++)
    {
        random = rand();
        printf("%d번째 난수:%5d\n", i, random);
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

계속 같은 결과 나온다. 41,18467,6334,26500,19169 계속 이 순서...
난수 맞니??

 

 

씨드 설정 함수인 srand를 설정하여 난수가 순서를 변경한다.
=> 씨드가 같으면 생성되는 난수의 순서가 동일하다.

default값은 1로
위에서 씨드를 설정하지는 않았지만 srand(1)로 설정이 되어있는 상태인 것이다.

하지만 씨드값을 프로그램을 실행할 때 매번 다르게 할 수 없다.
time함수를 이용하여 현재 시간으로 씨드값을
설정하면 매번 다르게 자동적으로 씨드값을 설정할 수 있다.

srand(time(NULL))  time 함수 사용하기 위해 time.h 헤더파일 필요

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int i, random;
 
    srand(time(NULL));
 
    for (i = 1; i <= 5; i++)
    {
        random = rand();
        printf("%d번째 난수:%5d\n", i, random);
    }
    return 0;
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

 

매번 다르다!! 이 정도는 돼야 난수라고 인정하지

 


스케일링
난수를 원하는 범위로 설정하는 방법
간단하다

rand() : 0~32767 이라고 했다.

rand()%6 을 하면 0부터 5까지 나오겠지
그럼 (rand()%6)+1 을 하면 1부터 6까지의 범위가 나온다. (주사위)

5~10은
(rand%6)+5

일반화
min~max는
(rand%(max-min+1)) + min

간단한 게임 만들기
컴퓨터가 숨긴 수 맞추기

 

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
//컴퓨터가 숨긴 1~100 중의 정수를 맞히는 게임
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main()
{
    int begin = 1end = 100;
    int count = 0;
    int computer, user;
 
    srand(time(NULL)); //씨드 설정
 
    computer = (rand() % (end - begin + 1)) + begin//컴퓨터가 숨기는 수 생성
    
    do
    {
        printf("%d~%d중의 값입니다. 얼마일까요?"beginend);
        scanf("%d"&user);
 
        count++;
 
        if (user < computer)
            begin = user + 1;
        else
            end = user - 1;
    } while (user != computer);
 
    printf("\n%d를 %d번만에 맞췄습니다.\n", computer, count);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

 

computer = (rand() % (end - begin + 1)) + begin;
이것만 이해하면 끝

 

 

 

 

 

 


연습 문제

 

8, 9번을 풀어보겠다.

 

문제8: 한 달 동안의 음성 통화 시간이 voice분이고 문자 전송 건수가 text건일 때 휴대폰 사용료 계산 방법을

사용해 휴대폰 사용료 charge를 구하는 프로그램을 작성하시오

 

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
#include <stdio.h>
 
int voice(int time)
{
    int charge; //음성통화 요금
    
    if (time > 100//100분 초과
        charge = (100 * 100+ ((time - 100* 80);
    else
        charge = 100 * time;
 
    return charge;
}
 
int message(int num)
{
    int charge; //문자 요금
 
    if (num > 20//문자 20건 초과
        charge = (num - 20* 20;
    else
        charge = 0;
 
    return charge;
}
 
int main()
{
    int voice_time; //음성 통화 시간
    int message_num; //문자 전송 건수
    int voice_charge; //음성 통화 요금
    int message_charge; //문자 전송 요금
    int sum_charge; //총 요금
    int surtex; //부가세
 
    printf("음성 통화 시간은(분)? ");
    scanf("%d"&voice_time);
    printf("문자 전송 건수는? ");
    scanf("%d"&message_num);
 
    //함수 호출
    voice_charge = voice(voice_time);
    message_charge = message(message_num);
 
    sum_charge = voice_charge + message_charge + 10000;
    surtex = sum_charge*0.1;
 
    //출력
    printf("휴대폰 사용 요금 청구서\n");
    printf("=========================================\n");
    printf("음성 통화 시간 %d\n",voice_time);
    printf("문자 전송 건수 %d건\n", message_num);
    printf("-----------------------------------------\n");
    printf("기본요금\t\t\t%6d원\n"10000);
    printf("음성 통화료 %d분\t\t%6d원\n",voice_time,voice_charge);
    printf("문자 전송료 초과 %d건(20건 무료)%6d원\n", message_num-20, message_charge);
    printf("-----------------------------------------\n");
    printf("합계\t\t\t\t%6d원\n", sum_charge);
    printf("부가세(10%%)\t\t\t%6d원\n", surtex);
    printf("=========================================\n");
    printf("이번 달 요금\t\t\t%6d원\n", sum_charge+ surtex);
    
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

그냥 텍스트 줄 맞추는데 힘들었다.

 

 

 

문제9: 학생 N명의 퀴즈 점수 10,8,7,9,6,10,9,8,7이 저장된 배열 class에 대해 사용자의 점수가 입력되면 순위를 출력하는 프로그램을 작성하시오. 입력된 점수 my_score의 순위를 구하는 함수 rank를 정의해 사용하시오.

 

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
#include <stdio.h>
#define N 9
 
int rank(myscore, otherscore)
{
    if (myscore < otherscore) //더 높은 점수있으면 등수 1개 낮추기
        return 1;
    else                       //내가 더 높으면 등수 그대로
        return 0;
}
 
int main()
{
    int score;
    int my_rank = 1;
    int i;
    int class[N] = { 10879610987 };
 
    printf("사용자의 점수를 입력하세요: ");
    scanf("%d"&score);
 
    for (i = 0; i < N; i++//배열 원소 하나씩 함수에 전달
    {
        my_rank += rank(score, class[i]); //리턴 받은 수를 누적시키기
    }
    for (i = 0; i < N; i++)
    {
        printf("%3d"class[i]);
    }
    printf("과 비교\n");
    printf("내 등수 = %d등\n", my_rank);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter

 

포인터로 배열 전체를 함수에 인자로 전달하면 더 쉬울 것을 하나하나 전달하니까
변수 생각하는데 더 머리가 아프다.
아직 포인터 진도 안나갔으니 하나하나 전달하여 해결

 

 

내용 정리

 

배열에 대한 간단한 내용은 빠르게 스킵하고

 

1차원, 2차원, 3차원 배열의 구조를 정리한후, 버블정렬에 대해 정리하려 한다.

 

 

배열은 배열명만으로 모든 배열 원소를 한번에 입출력하거나 저장할 수 없으며,

배열 원소 단위로 입출력하거나 저장해야 한다.

 

→ 표준 입력 함수에서는 입력된 자료를 저장할 기억장소의 주소를 명시해야 하므로 배열 원소 앞에 '&'붙인다.

ex) scanf("%d",&quiz[0]);

 

 

 1차원 배열

 

예제로 간단한 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
//프로그램 7-3 설문조사 결과 구하기
 
#include <stdio.h>
#define PERSONS 30
#define STARS 6
 
int main()
    int i;
 
    //배열 초기화
    int survey[PERSONS] = { 132532123452
        332145235134231423 };
 
    int vote[STARS] = { 0 }; //각 연예인의 투표수 0으로 초기화
 
    for (i = 0; i < PERSONS; i++)
    {
        vote[survey[i]]++;  //선택 된 연예인 득표수 증가
    }
 
    printf("연예인 득표수\n");
    printf("================\n");
    for (i = 1; i < STARS; i++)
    {
        printf(" %d번   %d표\n", i, vote[i]);
    }
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

위의 예제 실행결과

 

 

 2차원 배열

 

2차원 배열 (출처: 네이버 지식백과)

 

 

2차원 배열은 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
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
//프로그램 7-7 2*3행렬 A와 B의 덧셈 결과인 행렬 C구하기
 
#include <stdio.h>
#define N 2 //행 수
#define M 3 //열 수
 
int main()
{
    int A[N][M], B[N][M], C[N][M];
    int i, j;
 
    printf("행렬A 입력\n");
    for (i = 0; i < N; i++)
    {
        for (j = 0; j < M; j++)
        {
            printf("%d행 %d열:",i+1,j+1);
            scanf("%d"&A[i][j]);
        }
    }
 
    printf("\n행렬B 입력\n");
    for (i = 0; i < N; i++)
    {
        for (j = 0; j < M; j++)
        {
            printf("%d행 %d열:", i + 1, j + 1);
            scanf("%d"&B[i][j]);
        }
    }
 
    printf("\n행렬A \n");
    for (i = 0; i < N; i++)
    {
        printf("[");
        for (j = 0; j < M; j++)
        {
            printf("%3d",A[i][j]);
        }
        printf("]\n");
    }
 
    printf("\n행렬B \n");
    for (i = 0; i < N; i++)
    {
        printf("[");
        for (j = 0; j < M; j++)
        {
            printf("%3d", B[i][j]);
        }
        printf("]\n");
    }
 
    printf("\n행렬C(A+B)\n");
    for (i = 0; i < N; i++)
    {
        printf("[");
        for (j = 0; j < M; j++)
        {
            C[i][j] = A[i][j] + B[i][j];
            printf("%3d", C[i][j]);
        }
        printf("]\n");
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 

 

1차원 배열과 별반 다를게 없다. 배열 선언, 출력, 입력시에 차원 수만 다르다.

 

 

 

 

 3차원 배열

 

2차원에서 한 차원이 더 늘어났으므로, 3차원 입체 공간이다.

표가 여러 면이 겹쳐저 있다고 생각하면 된다.

 

3차원 배열의 논리적 구조(출처: https://kr.mathworks.com/help/matlab/math/multidimensional-arrays.html)

 

A[면][행][열] 이런 식이다.

 

 

 

※ char형 배열을 이용한 문자열 처리

 

문자열이 "Seoul"이면 배열의 원소 자리는 6자리 필요

끝에 널(\0)문자 포함!

 

배열명은 포인터! 이 부분은 포인터 챕터에서 포스팅.

따라서 입력받을 때는 &가 필요없다.

 

 

 

※ 버블정렬

 

배열에서 이웃하는 두 원소씩 차례대로 정렬하는 작업을

반복하여 배열 전체를 정렬하는 방법.

 

버블 정렬 (출처:  http://blog.naver.com/PostView.nhn?blogId=justant&logNo=20204028286&parentCategoryNo=&categoryNo=33&viewDate=&isShowPopularPosts=false&from=postView)

 

그림을 보면 버블정렬의 정의가 쉽게 이해 된다.

 

 

{5,4,3,2,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
//버블정렬
//1. 단순 버블 정렬
 
#include <stdio.h>
 
int main()
{
    int a[5= { 54321 };
    int i, j, k, temp;
 
    for (i = 0; i < 5 - 1; i++)
    {
        printf("step%d\n", i+1);
 
        for (j = 0; j < 5 - 1; j++)
        {
            if (a[j] > a[j + 1])
            {
                temp = a[j + 1];
                a[j + 1= a[j];
                a[j] = temp;
            }
            printf("%d번째 비교:", j + 1);
            for (k = 0; k < 5; k++)
            {
                printf("%d", a[k]);
            }
            printf("\n");
        }
 
        printf("\n");
    }
    return 0;
}

버블 정렬 과정

 

step1

1번째 4와 5를 비교하여 5를 뒤로이동.

2번째 5와 3을 비교하여 5를 뒤로 이동.

step1이 끝나면 맨 앞의 5가 맨 뒤로 이동하는 것을 볼 수 있다.

 

나머지 step도 마찬가지.

 

 

그러나 이 방법은 비효율적이며 배열의 원소 수가 많을 경우 연산 낭비이다.

 

step2의 4번째

step3의 3번째 4번째

step4의 2번째 3번째 4번째

원소의 순서가 변하지 않는 낭비 연산임을 알 수 있다.

 

 

원소의 순서가 변경될 때만 출력해보면

확실하게 절반 정도의 연산이 낭비임을 알 수 있다.

 

원소의 순서가 변경 될 때만 출력

 

맨 위의 그림처럼 정렬이 완료된 마지막 원소를 제외하고

정렬을 하면 효율적인 알고리즘이 될 것이다.

 

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
//버블정렬
//2. 정렬된 원소를 제외한 나머지 원소 정렬
 
#include <stdio.h>
 
int main()
{
    int a[5= { 54321 };
    int i, j, k, temp;
 
    for (i = 0; i < 5 - 1; i++)
    {
        printf("step%d\n", i + 1);
 
        for (j = 0; j < 5 - (i+1); j++)
        {
            if (a[j] > a[j + 1])
            {
                temp = a[j + 1];
                a[j + 1= a[j];
                a[j] = temp;
            }
            printf("%d번째 비교:", j + 1);
            for (k = 0; k < 5; k++)
            {
                printf("%d", a[k]);
            }
            printf("\n");
        }
 
        printf("\n");
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

단순 버블정렬과 다른 부분은

for (j = 0; j < 5 - (i+1); j++)

고작 이부분.

 

반복 횟수인 i번을 추가로 빼주는 것이다.

 

의미는

 

step1에서는 원소가 5개 이므로 4번 모두 비교를 하고

step2에서는 step1에서 정렬이 된 마지막 원소를 제외하고 3번 비교를 한다.

...

step4에서는 맨 앞 두 원소만 비교하여 정렬한다.

 

효율성을 개선한 버블정렬

 

하지만 이 알고리즘도 완벽하게 효율적인 알고리즘이 아니다.

 

완전하게 내림차순인 경우의 배열만 이 알고리즘이 효율적이다.

 

만약 배열에 {1,2,3,5,4}가 저장되어 있다면

 

 

사진 설명을 입력하세요.

step1에서 {1,2,3,4,5} 로 정렬이 되고

나머지 step는 연산 낭비이다.

 

이는 한 step진행 후

원소 순서에 변화가 생기는지 여부를 확인 할 수 있는

flag변수를 사용해서 비효율을 줄일 수 있다.

 

이부분은 생략한다.


 

연습 문제

 

11, 12번을 풀어보겠다.

 

 

문제 11: 2*3 행렬 A와 3*4 행렬 B를 곱한 결과인 2*4 행렬 C를 구하여 다음과 같이 출력하시오.

 

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
//연습문제 11
 
#include <stdio.h>
 
int main()
{
    int A[2][3= { { 123 }, { 456 } };
    int B[3][4= { { 3456 }, { 1243 }, { 4261 } };
    int C[2][4= { 0, };
    int i, j, k;
    int sum = 0;
 
    printf("행렬A\n");
    for (i = 0; i < 2; i++){
        printf("[ ");
        for (j = 0; j < 3; j++)
        {
            printf("%d ", A[i][j]);
        }
        printf("]\n");
    }
    printf("\n");
 
    printf("행렬B\n");
    for (i = 0; i < 3; i++){
        printf("[ ");
        for (j = 0; j < 4; j++)
        {
            printf("%d ", B[i][j]);
        }
        printf("]\n");
    }
    printf("\n");
 
    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 4; j++)
        {
            for (k = 0; k < 3; k++)
            {
                sum += A[i][k] * B[k][j];
            }
            C[i][j] = sum;
            sum = 0;
        }
    }
 
    printf("행렬C=A+B\n");
    for (i = 0; i < 2; i++){
        printf("[ ");
        for (j = 0; j < 4; j++)
        {
            printf("%d ", C[i][j]);
        }
        printf("]\n");
    }
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

3중 for문을 사용하여 곱셈하는데

약간 복잡하지만 충분히 생각해보면 이해가 된다.

 

 

 

 

문제 12: 10개 문항이 있는 학생 10명의 시험 답안지로부터 채점을 한 후 결과를 다음과 같이 출력하시오. 학생 10명의 시험 답안지는 다음과 같이 2차원 배열을 선언하면서 초기화한다.

 

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
#include <stdio.h>
#define STUDENT 10
#define N 10
 
int main()
{
    int paper[STUDENT][N] = 
    { { 1323423143 }, { 1222423142 },
    { 4323423142 }, { 1322433442 },
    { 1323423341 }, { 1124433243 },
    { 1323423142 }, { 1323323142 },
    { 2333324142 }, { 3442412142 } };
    
    int answer[N] = { 1323423142 };
    int score[STUDENT] = { 0, }; //각 학생의 점수배열
    int order[STUDENT] = { 0, }; //각 학생의 등수
    int high = 0//자신보다 높은 점수 있으면 1개씩 증가시키기
    int i,j;
 
    printf("\t문항별 채점 결과\n");
    printf("=========================================\n");
    
    printf(" 문항");
    for (i = 0; i < N; i++)
    {
        printf("%3d",i);
    }
    printf("  점수\n");
    printf("=========================================\n");
    
    for (i = 0; i < STUDENT; i++//점수 및 맞고 틀린 문제 확인
    {
        printf("%3d번", i+1);
        for (j = 0; j < N; j++)
        {
            if (paper[i][j] == answer[j])
            {
                printf("%3c",'o');
                score[i]++;
            }
            else
                printf("%3c",'x');
        }
        printf("  %d점\n",score[i]);
    }
 
    for (i = 0; i < STUDENT; i++)
    {
        for (j = 0; j < STUDENT; j++)
        {
            if (score[i] < score[j])
                high++
        }
        order[i] = high + 1;
        high = 0//자신 점수보다 높은 학생 수 0으로 초기화
    }    
 
    printf("\n\n\t  정렬 및 석차\n");
    printf("\t=================\n");
    printf("\t번호 점수 석차\n");
    printf("\t=================\n");
    for (i = 0; i < STUDENT; i++)
    {
        for (j = 0; j < STUDENT; j++)
        {
            if (order[j] == i+1)
                printf("\t%2d번%3d점%3d등\n", j + 1, score[j], order[j]);
        }
    }
    printf("\t=================\n");
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

 

내용 정리

 

전처리기는 하드웨어 작업에 사용할 꺼 같다. 
기본 코딩때 사용되는 #define와 #include만 정리하였다.

 

 

 #define 지시자
특정 기호 상수를 프로그래머가 정의한 치환 문자열로 대체하는 역할을 한다.

ex) #define PI 3.1415   (실수형 상수)
      #define MSG "잘못된 입력입니다."   (문자열 상수)
      #define SQUARE ((x)*(x))  (매크로 함수)

 

 

 

 #include 지시자
파일을 포함하는 지시자로 헤더 파일을 참조하기 위해 사용
printf, scanf를 사용할 수 있는 이유는
해당함수의 원형이 선언되어 있는 stdio.h 헤더파일 때문이다.

 

 


자세한 함수들은 다른 블로그 참고

https://whiteme7.blog.me/110043694088

 

[C] 표준 라이브러리 함수

---------------------------------------------------------------- 데이터 변환 함수 문자나 문자열을 수...

blog.naver.com

 

이번 chapter은 연습문제 생략

 

내용 정리

 

※ 조건문(if, switch)

 

 

if문과 switch문의 사용 방법은 간단하므로 생략하고

자주 실수하는 사항에 대해서만 정리하려 한다.

 


if문
if 와 else 쌍에 주의한다. 괄호가 필요하다.

 

1
2
3
4
5
6
scanf("%d",&x);
if(x>0)
    if(x%2 == 0)
        printf("양의 짝수 입니다.\n");
else
    printf("음수입니다.\n");

 

위처럼 작성하면 위의 맨 마지막 else문은

컴파일러는 바로 위의 if 문과 쌍으로 생각한다.

 

결국 else문은 (x>0) && (x%2 !=0) 일 때 조건으로 생각된다.

 

 

따라서 아래와 같이 괄호를 사용하여

컴파일러가 알 수 있게 확실히 명시한다.

 

1
2
3
4
5
6
7
8
scanf("%d",&x);
if(x>0)
{
    if(x%2 == 0)
        printf("양의 짝수 입니다.\n");
}
else
    printf("음수입니다.\n");

 

다시 정리하자면

컴파일러는 else와 짝을 이루는 if를 찾을 때 들여쓰기를 무시하고

else와 짝을 이루지 않는 상위의 if들 중 가장 가까운 if와 짝이 된다고 해석한다.

→ 괄호 사용 습관화

 

 

switch문

 

간단한 실수 이지만 break문을 빼고 자주 사용한다.

오류가 발견될 때 보통 입력받은 케이스 이후 케이스가 동작한다면

break문을 빠트린 것을 알 수 있다.

 

아래는 오류 케이스

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    int a;
    scanf("%d"&a);
    switch (a)
    {
    case 1printf("입금하기 화면으로 이동합니다.\n");
    case 2printf("출금하기 화면으로 이동합니다.\n");
    case 3printf("송금하기 화면으로 이동합니다.\n");
    default:printf("잘못된 입력입니다.\n");
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

2를 입력하면

 

입력받은 케이스 case2 이외의 3, default 경우까지 모두 실행이 된다.

이 경우 break 문이 빠진것을 알 수 있으며

break문 case끝마다 추가해 주어야 한다.

 

아래와 같이 수정하면 문제없이 동작한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    int a;
    scanf("%d"&a);
    switch (a)
    {
    case 1printf("입금하기 화면으로 이동합니다.\n"); break;
    case 2printf("출금하기 화면으로 이동합니다.\n"); break;
    case 3printf("송금하기 화면으로 이동합니다.\n"); break;
    default:printf("잘못된 입력입니다.\n");
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

case2의 break문이 실행되어 switch문을 벗어나게 된다.

 

 

 

※ 반복문(for, while, do~while)

 

 

for, while, do~while을 어떠한 경우에 사용해야 하는지 헷갈리는 경우가 많았다.

사용 방법은 간단하지만 많은 알고리즘 문제를 해결하다 보면 자연스럽게 익혀진다.

 

for와 while문의 차이를 자세히 알아보면

 

for문: 반복 횟수가 명확할 때 이용하면 편리

while문: 반복 횟수는 모르지만 어떤 조건은 만족 할 때까지 반복할지 아는 경우

do~while문: 무조건 한번은 실행하고, 조건 검사

 


 

연습 문제

 

11, 15, 22, 23, 24번을 풀어보겠다.

 

 

문제 11: 양의 정수 하나를 입력받고, 입력받은 수가 소수(prime number)인지 판단하는 프로그램을 작성하시오.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    int num;
    int i;
    printf("양의 정수:");
    scanf("%d"&num);
 
    for (i = 2; i < num; i++)
    {
        if (num%i == 0)
        {
            printf(">>%d는 소수가 아닙니다.\n", num);
            break;
        }
    }
    if (num == i)
        printf(">>%d는 소수입니다.\n", num);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

2~자기자신까지 나누어 봐서 0으로 나누어 떨어지면

그 순간 소수 아니라고 판단하고, 종료.

만약 모두 0으로 나머지가 나누어 떨어지지 않는다면

소수.

 

 

 

문제 15: 어떤 수를 입력받고 입력받은 수보다 작은 소수를 전부 출력하는 프로그램을 작성하시오.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
int main()
{
    int max;
    int n = 2;
    int i;
 
    printf("최대 몇까지의 소수를 출력?");
    scanf("%d"&max);
 
    while (n < max)
    {
        for (i = 2; i < n; i++)
        {
            if ((n%i) == 0)
                break;
        }
        if (n == i)
            printf("%d ", n);
        n++;
    }
    return 0;
}

 

앞의 11번과 유사, 반복문 하나 더 추가

 

 

문제 22: 반복문을 이용해 다음과 같은 패턴을 출력하는 프로그램을 작성하시오.

 

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
//1번
#include <stdio.h>
 
int main()
{
    int i,j;
    
    for (i = 1; i <= 9; i++)
    {
        if ((i == 1|| (i == 9))
        {
            for (j = 1; j <= 9; j++)
            {
                printf("*");
            }
            printf("\n");
        }
        else
        {
            for (j = 1; j <= 9; j++)
            {
                if ((j == 1|| (j == 9))
                    printf("*");
                else
                    printf(" ");
            }
            printf("\n");
        }
    }
    return 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
32
33
34
35
36
//2번
#include <stdio.h>
 
int main()
{
    int i, j;
    
    for (i = 1; i <= 5; i++//1~5줄
    {
        for (j = 4; j >= i; j--)
        {
            printf(" ");
        }
        
        for (j = 1; j <= i * 2 - 1; j++)
        {
            printf("*");
        }
        printf("\n");
    }
    
    for (i = 1; i <= 4; i++//6~9줄
    {
        for (j = 1; j <= i; j++)
        {
            printf(" ");
        }
 
        for (j = 1; j <= 9- (i * 2) ; j++)
        {
            printf("*");
        }
        printf("\n");
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//3번
#include<stdio.h>
 
int main()
{
    int i, j;
    for (i = 1; i <= 9; i++)
    {
        for (j = 1; j <= i; j++)
        {
            printf("%d", j);
        }
        printf("\n");
    }
    return 0;
}
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//4번
#include <stdio.h>
 
int main()
{
    int i, j;
    for (i = 9; i >= 1; i--)
    {
        for (j = 1; j <= i; j++)
        {
            printf("%d", j);
        }
        printf("\n");
    }
    return 0;
}

 

 

문제 23: 원하는 피보나치의 개수를 입력해서 그 개수만큼 수열이 출력되는 프로그램을 작성하시오.

 

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
#include <stdio.h>
 
int main()
{
    int num,i;
    int fibo;
    int fibo_1 = 0;
    int fibo_2 = 1;
 
    printf("피보나치 개수 입력:");
    scanf("%d"&num);
 
    printf("\n*** 피보나치 수열 ***\n");
 
    printf("%d,%d", fibo_1, fibo_2);
 
    for (i = 2; i < num; i++)
    {
        fibo = fibo_1 + fibo_2;  //앞의 두 피보나치값 더해서 현재 피보나치 구함
        printf(",%d", fibo);
        fibo_1 = fibo_2;  //앞의 피보나치를 구할 때 2번째 값을 1번째 값으로 변경
        fibo_2 = fibo;  //현재 피보나치 값을 다음 피보나치를 구하기 위한 2번째 값으로 변경
    }
    printf("입니다.\n");
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

 

 

문제 24: 자연수 두 개 입력받은 후, 최대 공약수를 구하는 프로그램을 while문을 이용해 작성하시오.

 

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
#include <stdio.h>
 
int main()
{
    int x, y, r, temp;
    printf("두 개의 정수를 입력하시오:");
    scanf("%d %d"&x, &y);
 
    if (x < y) //y가 더 크면 큰 수를 x로 변경
    {
        temp = x;
        x = y;
        y = temp;
    }
    while (1)
    {
        if (y == 0)
        {
            printf("최대 공약수는 %d입니다.", x);
            break;
        }
        r = x % y;
        x = y;
        y = r;
    }
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

 

무한루프 사용해서 y값이 0이 될때까지 x와 y값을 조절하여

y==0일 때 x값이 최대공약수, 무한루프 탈출

내용 정리

이번 장은 정리할 내용이 별로 없다

 

 

※ 산술 연산자


+, -, *, / 는 정수형, 실수형 모두 연산가능

 

%(나머지 연산자)는 정수형만 가능하다
5%2.5는 에러

연산자의 우선순위따지는것 보다 괄호사용 하는것이 간편

논리연산자, 비트연산자는 하드웨어 부분에서 많이 쓰는듯...
생략

 

 


형변환 연산자


자동 형 변환 : 컴파일러가 자동으로 처리
강제 형 변환: (자료형)피연산자 
     
형 변환은 기억공간의 크기를 변경하는 것이 아니라, 일시적으로 다른 형으로 변경


연습문제 풀이

 

10, 13, 16번을 풀어보겠다.

 

 

문제 10: 초 단위의 시간을 입력 받아 몇 시간, 몇 분, 몇 초인지를 계산하는 프로그램을 작성하시오.

 

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
 
int main()
{
    int sec;
    printf("초(sec)입력: ");
    scanf("%d"&sec);
    printf(">>입력한 %d초는 %d시간 %d분 %d초입니다.\n", sec, (sec / 3600), ((sec % 3600/ 60), (sec % 60));
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

 

 

문제 13: 조건 연산자를 이용해 세 수를 입력받아 가장 큰 수 를 구하는 프로그램을 작성하시오.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    int a, b, c;
    int max;
    printf("세 개의 정수:");
    scanf("%d %d %d"&a, &b, &c);
 
    (a > b) ? ((a > c) ? (max = a) : (max = c)) : ((b > c) ? (max = b) : (max = c));
    printf(">>가장 큰 수 = %d\n", max);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

조건연산자 내부에 조건연산자 통해서 해결가능

 

 

 

문제 16: 연도를 입력받아 윤년인지 평년인지를 판단하는 프로그램을 작성하시오.

 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
int main()
{
    int y;
    printf("년도를 입력하세요.:");
    scanf("%d"&y);
 
    if ((y % 4 == 0&& (y % 100 != 0|| (y % 400 == 0))
        printf("윤년\n");
    else
        printf("평년\n");
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

윤년의 조건이 전부인 문제.

 

 

 

 

 

 

 

내용 정리

 

※ 필드 폭, 정밀도

필드폭: 자료를 출력하는 데 사용할 전체 칸   
%+필드폭d: 부호 출력
%-필드폭d: 왼쪽 정렬 (사용 안 하면 오른쪽 정렬)

정밀도: 소수점 아래 자리 수 지정
%. 정밀도 f: 정밀도 3이라면 소수 4자리에서 반올림하여 나타낸다.

 

아래의 예제를 확인하면 확실히 이해할 수 있을 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main()
{
    double avg = 83.768;
 
    printf("avg = %5.1f\n", avg);
    printf("avg = %7.3f\n", avg);
    printf("avg = %.2f\n", avg);
    printf("avg = %5f\n", avg);
    return 0;
}

 

 

출력함수

printf
주의점: "(큰 따옴표) 출력하기 위해 이스케이프 문자인 사용


 ex) printf("My name is 'C'.");  → 작은 따옴표는 이스케이프 문자 필요 없음
     printf("You said \"Hello!\".");   큰 따옴표 이스케이프 문자 필요함!

문자 저장에는 작은 따옴표도 이스케이프 문자 필요
ex) char ch = '\'';

 

 

- putchar

문자 전용 출력 함수

변환 명세 필요 없음, 실행 속도 빠르다.  문자 출력 용이

 

ex) char grade = 'A';

putchar(grade);

putchar('+');

 

 A+ 출력, 개행하려면 putchar('\n');

 

 

- puts

문자열 전용 출력함수

변환 명세 필요 없음, 개행 문자 자동출력

puts("Hello!"); == printf("Hello!\n");

printf("% s\n", "Hello!");

 

 

입력함수

- scanf

scanf함수를 사용할 때는 키보드에서 입력되는 값을 주기억장치 몇 번지에 저장할지 명시해야 한다.

 기억 장소를 구해주는 연산자 &(앰퍼센트)를 사용

 

하지만 문자열을 저장하는 변수명이나 배열명은 그 자체가 문자열을 저장할 주기억장치의 시작 주소이므로 &를 붙이지 않는다.

 

문자열은 스페이스바, 탭키 전까지의 문자열이 변수에 저장

 

 

- getchar

문자 전용 입력함수

getchar() 형태로만 사용, 한 문자 입력을 받는다.

 

 

- gets

문자열 전용 입력함수

변환 명세 필요 없으며, 엔터키를 입력하기 전까지 모든 문자가 문자열로 저장.(공백 포함)

 

 

※fflush(stdin) 문자 입력과 버퍼 지우기

버퍼에 남아있는 개행 문자 때문에 문자를 입력할 때 오류 발생!

버퍼 안의 내용을 모두 지운 뒤 입력을 받아야 한다.

 

문자, 문자열 자료형 앞에 사용  공백도 입력문자로 사용하기 때문(숫자는 상관없음)

 

예제를 통해 입력 함수와 버퍼를 지워야 하는 이유를 확인해 보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
 
int main()
{
    char a, b, c;
    int d, e;
 
    printf("1.문자입력:");
    a = getchar();
    printf("2.숫자입력:");
    d = scanf("%d"&d);
    //fflush(stdin);
    printf("3.문자입력:");
    a = getchar();
 
    return 0;
}

 

14열의 입력 함수가 처리될 때 버퍼에 있는 개행 문자가 삽입되어

입력을 수행하지 않고 메인 함수가 마무리된다.

 

따라서 12행의 주석처리를 해제하면 정상적으로 작동한다.


연습문제 풀이

 

7,8,9,10번을 풀어보겠다.

 

 

문제 7: 키보드에서 문자를 한 개 입력받은 후, 그 문자에 해당하는 ASCII코드 값을 구하는 프로그램을 작성하시오.

 

 

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
//연습문제7
int main()
{
    char ch;
    printf("문자를 입력하세요.:");
    ch = getchar();
    printf("%c의 ASCII코드 값은 %d입니다.\n", ch, ch);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

문자, 숫자간 변환은 변환명세만 신경쓰면 된다.

 

 

 

문제 8: 키보드에서 8진수 정수와 16진수 정수를 하나씩 입력받은 후, 이 입력 값들과 두 정수의 합을 10진 정수로 출력하는 프로그램을 작성하시오.

 
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
//연습문제8
int main()
{
    int oct, hex;
    printf("8진수 16진수를 순서대로 입력하세요.:");
    scanf("%o %x"&oct, &hex);
    printf("%o(8) + %x(16) = %d", oct, hex, oct + hex);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

문제 8 실행 결과

 

역시 변환명세만 신경쓰면 진수변경 용이하다.

 

 

 

문제 9: 반지름을 입력받아 원의 둘레와 면적, 그리고 구의 부피를 구한 후 세 개의 결과 값의 소수 자리를 세로로 맞추어 출력하시오.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
//연습문제9
int main()
{
    int r;
    double pi = 3.141592;
    printf("반지름(cm)을 입력하세요: ");
    scanf("%d"&r);
    printf("둘레= %7.2lf cm\n",pi* r* 2);
    printf("면적= %7.2lf cm2\n", pi* r * r);
    printf("부피= %7.2lf cm3\n", (4.0/3.0)*pi*r*r*r);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
 

필드폭, 정밀도 문제이다. 맨 위의 예제에서 자세히 다뤘으니 설명은 패스

 

 

 

문제 10: 키와 몸무게를 입력받아 키에 맞는 표준 체중을 구한 후 자신의 몸무게와 표준 체중과의 차이를 출력하는 프로그램을 작성하시오.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
 
//연습문제10
int main()
    double height, weight;
 
    printf("키(cm)를 입력히세요.:");
    scanf("%lf"&height);
    printf("몸무게(kg)를 입력히세요.:");
    scanf("%lf"&weight);
 
    printf("당신의 키는 %.1lfcm (%.1lffeet)입니다.\n",height,height/30.48);
    printf("당신의 체중은 %.1lfkg (%.1lflb)입니다.\n",weight,weight*2.20459);
    printf("표준 체중 %.1lf과의 차이는 %+.1lfkg입니다.\n", (height - 100)*0.9, weight - ((height - 100)*0.9));
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

문제 10 실행결과

조금 더 살을 찌워보자...

내용 정리

 

※프로그램

 

변수를 사용해 자료를 저장

 

변수: 자료 값을 저장하는 주기억장치에 있는 공간, 자료에 따라 다양한 변수 사용.

→ 신뢰성 있고 효과적인 프로그램 작성 위함.

 

 

 

프로그램 기본구조

 

처음: #include <stdio.h>등 전처리기 지시

main: 콘솔 응용 프로그램을 실행하면 처음으로 실행되는 함수, 반드시 하나만 존재

return: main함수의 결과를 main함수를 호출했던 곳으로 되돌려줌

 

 

 

자료의 표현방법

 

1. 문자

ASCII코드 사용

2. 정수

10진수, 2진수, 8진수, 16진수

표현범위는 8 비트면 28개의 정보 표현가능 ((-2^7)~(2^7-1)범위 표현 가능)

컴퓨터에서 음수는 2의 보수 +1로 처리

3. 실수

32비트 - 소수점 이하 7자리

64비트 - 소수점 이하 15자리

 

 

 

상수의 표현

 

상수(constant): 프로그램에서 자료의 값을 나타내는 데 사용하는 숫자나 문자, 실행되는 도중에 값이 변경될 수 없다.

 

 

 

변수

 

저장 공간 = 변수

저장공간을 확보하는 행위가 변수선언

의미 있게 작명

첫 글자 특수문자 x, 예약어 사용 x, 공백 포함 x

 

 

 

자료형

 

1. 문자형

char(1바이트): -128 ~ 127

singed char(1바이트): -128 ~ 127

unsigned char(1바이트): 0~255 => 크기가 작은 양수의 자료를 저장할 때 용이

2. 실수형

float(4바이트): 소수점 이하 6자리

double(8바이트): 소수점 이하 15자리

 

 

 


연습문제 풀이

 

4,5,6,7번을 풀어보겠다.

 

 

문제 4:float height=177.5;와 같이 선언하면 경고 메시지가 나타난다. 나타나는 이유와 해결방법은?

 

변수 height는 float형, 실수인 177.5는 실수의 기본형인 double형 이므로

프로그램 실행에는 문제가 없지만, 아래와 같은 경고 메시지가 나타난다.

 

 

문제 4 경고메시지

 

float 변수에 float임을 알려주는 f를 추가하여 문제를 해결한다.

float height=177.5f; 로 수정하면 경고 메시지가 사라진다.

 

 

 

 

문제 5: 25인치의 길이를 센티미터로 계산하여 출력하는 프로그램을 완성하시오.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
 
int main()
{
    int inch = 25;
    float cm;
 
    cm = inch * 2.54f;
 
    printf("%dinch는 %fcm입니다.\n", inch, cm);
 
    return 0;
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

문제 5 실행결과

 

 

 

문제 6:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    double celsius, fahrenheit;
 
    fahrenheit = 72.0// 실수형으로 저장
    celsius = (5.0 / 9.0* (fahrenheit - 32);
 
    printf("화씨온도 %lf도는 섭씨온도 %lf도입니다. \n", fahrenheit, celsius);
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

5/9인 정수 나눗셈이면 해가 0으로 나와서 잘못된 결과가 나온다.

→ 아래와 같이 5.0 / 9.0인 실수 나눗셈으로 수정해야 한다.

 

celsius = (5.0 / 9.0) * (fahrenheit - 32);

 

문제6 실행결과

 

 

 

문제 7:

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
 
int main()
{
    int num;
    num = 02013;
    printf("num(8진수) = %o\n", num);
    printf("num(10진수) = %d\n", num);
 
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter

 

문제7 실행결과

 

변환 명세

%o: 8진수

%x: 16진수

%d: 10진수

+ Recent posts