함수 만들기 실습 기초 – 합계 구하기, 소수 판별, 소문자 판별, 대문자 판별, 소문자 변환, 대문자 변환

안녕하세요. 언제나 휴일의 언휴예요.

자주 사용하는 알고리즘을 함수로 만들어 사용합니다.

함수
함수

함수를 만들고 사용하는 것은 많은 연습을 필요합니다.

이번에는 기초적인 함수들을 만들어 보는 실습을 해 볼게요.

특정 범위 내의 정수 합계를 구하는 함수

함수를 만들 때 제일 먼저 해야 할 일은 무엇을 만들어야 하는지 정확히 분석하는 거예요.

이러한 작업은 도메인 분석이라고도 부르죠.

함수를 만들 때는 전달해야 할 인자가 무엇이고 어떠한 결과를 나와야 하는지 결정할 수 있어야 합니다.

특정 범위 내의 정수 합계를 구하는 함수를 예를 든다면 입력 인자로 특정 범위를 전달해야 할 거예요. 범위의 시작과 범위의 끝을 전달하면 되겠죠. 그리고 결과는 합계입니다.

이렇게 해야 할 일이 무엇인지 결정했다면 함수 이름과 입력 매개변수 목록 및 리턴 타입을 표현해 보세요. 이는 함수 선언문으로 표현할 수 있겠죠.

/******************************************
특정 범위의 정수 합계를 구하는 함수
    입력 매개변수 목록:
       start: 범위의 시작
       end: 범위의 끝
    반환 값: 합계                  
*******************************************/
int CalculateSum(int start, int end); 

함수를 만들기 전에 호출할 때 원하는 결과를 얻었는지 테스트 코드를 먼저 작성한다면 보다 나은 습관을 갖을 수 있어요.

다음은 CalculateSum 함수를 호출하여 사용하는 예제 코드입니다. 만약 이러한 예제 코드를 작성하지 못한다면 도메인 분석을 잘못한 것일 수도 있어요.

#include <stdio.h>
/******************************************
특정 범위의 정수 합계를 구하는 함수
    입력 매개변수 목록:
       start: 범위의 시작
       end: 범위의 끝
    반환 값: 합계                  
*******************************************/
int CalculateSum(int start, int end);
 
int main()
{
    int re = 0;
 
    re = CalculateSum(1,10); //함수 호출문
    if(re == 55)
    {
        printf("CalculateSum(1,10)- 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(1,10) 결과는 55여야 합니다. 반환 값:%d\n",re);
    }
    re = CalculateSum(5,100); //함수 호출문
    if(re == 5040)
    {
        printf("CalculateSum(5,100)- 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(5,100) 결과는 5040여야 합니다. 반환 값:%d\n",re);
    }
    return 0;
}
int CalculateSum(int start, int end)
{
    return 0;
}

위처럼 테스트 코드 작성도 숙련도에 따라 assert 함수 등을 사용하여 보다 나은 테스트를 할 수도 있어요. 이에 관한 사항은 별도로 다루기로 할게요.

마지막으로 실제 함수를 구현하는 일이겠죠.

함수 내에서 수행할 알고리즘을 고민합니다. 손 코딩도 하나의 방법이고 플로우 챠트나 다이어그램 등을 사용할 수도 있겠죠. 여기에서는 의사코드(pseudo code)로 표현할게요.

*의사코드는 프로그램을 작성할 때 알고리즘을 표현하기 위한 코드입니다. 이 때 사용하는 코드는 프로그래밍 언어 문법에 얽매이지 않고 개발자들 끼로 소통하기 쉬운 방법으로 표현합니다.*

CalculateSum( start:시작, end:끝)
    합계:=0
    반복: lcnt:=start-> end, setp:1
         합계:= 합계 + lcnt
    반환 합계    

이제 구체적으로 구현한 후 테스트 및 코드를 수정합니다.

int CalculateSum(int start, int end) //함수 정의문
{
    int sum = 0; //합계:=0
    int lcnt = 0; 
    for( lcnt = start; lcnt <= end; lcnt++)//lcnt:=start-> end, setp:1
    {
        sum += lcnt;//합계:= 합계 + lcnt
    }
    return sum;//반환 합계
}

다음은 전체 소스 코드입니다.

#include <stdio.h>
/******************************************
특정 범위의 정수 합계를 구하는 함수
    입력 매개변수 목록:
       start: 범위의 시작
       end: 범위의 끝
    반환 값: 합계                  
*******************************************/
int CalculateSum(int start, int end);
 
int main()
{
    int re = 0;
 
    re = CalculateSum(1,10); //함수 호출문
    if(re == 55)
    {
        printf("CalculateSum(1,10)- 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(1,10) 결과는 55여야 합니다. 반환 값:%d\n",re);
    }
    re = CalculateSum(5,100); //함수 호출문
    if(re == 5040)
    {
        printf("CalculateSum(5,100)- 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(5,100) 결과는 5040여야 합니다. 반환 값:%d\n",re);
    }
    return 0;
}
int CalculateSum(int start, int end) //함수 정의문
{
    int sum = 0; //합계:=0
    int lcnt = 0; 
    for( lcnt = start; lcnt <= end; lcnt++)//lcnt:=start-> end, setp:1
    {
        sum += lcnt;//합계:= 합계 + lcnt
    }
    return sum;//반환 합계
}

특정 정수가 소수인지 판별하는 함수를 정의하여 1에서 100사이에 소수 개수 구하기

이번에는 함수를 정의하고 이를 이용하여 특정 문제를 해결하는 실습을 해 볼게요.

작성할 함수는 특정 정수가 소수인지 판별하는 함수입니다.

그리고 구현해야 할 것은 1에서 100사이에 소수 개수를 구하는 코드입니다.

먼저 소수인지 판별하는 함수의 이름과 입력 매개변수 목록과 반환 형식을 결정합시다.

일반적으로 판별하는 함수의 이름은 Is로 시작합니다.

여기에서는 IsPrimeNumber라고 정할게요.

입력 매개변수 목록은 소수인지 판별하고자 하는 정수 하나를 전달합니다.

그리고 반환 값은 소수 인지 여부인데 C언어에서는 이럴 때 참이면 1을 반환 거짓일 때 0을 반환하는 것이 일반적입니다.

/******************************************
소수인지 판별하는 함수
    입력 매개변수 목록:
       number: 정수
    반환 값: 소수인지 여부 (참일 때 1, 거짓일 때 0)                 
*******************************************/
int IsPrimeNumber(int number);

먼저 테스트 코드를 먼저 작성할게요. 이러한 작업은 초보 개발자 뿐만 아니라 숙련자도 귀찮은 작업입니다. 하지만 규모가 커지면 이러한 단위 테스트를 거쳤을 때 버그 발생 확률을 줄여 개발 비용을 줄여줍니다.

#include <stdio.h>
/******************************************
소수인지 판별하는 함수
    입력 매개변수 목록:
       number: 정수
    반환 값: 소수인지 여부 (참일 때 1, 거짓일 때 0)                 
*******************************************/
int IsPrimeNumber(int number);
 
int main()
{
    int re = 0; //함수 호출문
    if(re = IsPrimeNumber(1))
    {
        printf("IsPrimeNumber(1) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    else
    {
        printf("CalculateSum(1) - 정상적으로 동작\n");
    }
    if(re = IsPrimeNumber(2))
    {
        printf("IsPrimeNumber(2) - 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(2) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    if(re = IsPrimeNumber(4))
    {
        printf("IsPrimeNumber(4) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    else
    {
        printf("CalculateSum(4) - 정상적으로 동작\n");
    }
    if(re = IsPrimeNumber(7))
    {
        printf("IsPrimeNumber(7) - 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(7) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    return 0;
}
int IsPrimeNumber(int number)
{
    return 0;
}

이제 알고리즘을 결정합시다.

소수는 1과 자기 자신만을 약수로 갖는 자연수입니다.

따라서 2에서 자신 사이의 값 중에 약수가 있으면 소수가 아니겠죠.

하지만 2에서 자기 자신 사이의 값 중에 약수가 없으면 소수입니다.

(자기 자신/2 까지만 확인하더라도 소수인지 판단할 수 있습니다.)

IsPrimeNumber(number: 판별할 정수)
    조건: number가 1보다 작거나 같다면
        거짓(0) 반환
    반복: i:=2-> number/2, setp:1
         조건: i가 number의 약수일 때(나누었을 때 나머지가 0인 수)
            거짓(0) 반환
    참(1)반환

알고리즘을 코드로 구현 및 테스트 합시다.

int IsPrimeNumber(int number)
{
    if(number<=1) //조건: number가 1보다 작거나 같다면
    {
        return 0;//거짓(0) 반환
    }
    for(int i = 2; i<=number/2; i++) //반복: i:=2-> number/2, setp:1
    {
        if(number%i == 0)//조건: i가 number의 약수일 때(나누었을 때 나머지가 0인 수)
        {
            return 0; //거짓(0) 반환
        }
    }
    return 1; //참(1)반환
}

테스트를 포함한 전체 코드는 다음과 같습니다.

#include <stdio.h>
/******************************************
소수인지 판별하는 함수
    입력 매개변수 목록:
       number: 정수
    반환 값: 소수인지 여부 (참일 때 1, 거짓일 때 0)                 
*******************************************/
int IsPrimeNumber(int number);
 
int main()
{
    int re = 0; //함수 호출문
    if(re = IsPrimeNumber(1))
    {
        printf("IsPrimeNumber(1) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    else
    {
        printf("CalculateSum(1) - 정상적으로 동작\n");
    }
    if(re = IsPrimeNumber(2))
    {
        printf("IsPrimeNumber(2) - 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(2) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    if(re =IsPrimeNumber(4))
    {
        printf("IsPrimeNumber(4) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    else
    {
        printf("CalculateSum(4) - 정상적으로 동작\n");
    }
    if(re = IsPrimeNumber(7))
    {
        printf("IsPrimeNumber(7) - 정상적으로 동작\n");
    }
    else
    {
        printf("CalculateSum(7) 결과는 0이어야 합니다. 반환 값:%d\n",re);
    }
    return 0;
}
int IsPrimeNumber(int number)
{
    if(number<=1) //조건: number가 1보다 작거나 같다면
    {
        return 0;//거짓(0) 반환
    }
    for(int i = 2; i<=number/2; i++) //반복: i:=2-> number/2, setp:1
    {
        if(number%i == 0)//조건: i가 number의 약수일 때(나누었을 때 나머지가 0인 수)
        {
            return 0; //거짓(0) 반환
        }
    }
    return 1; //참(1)반환
}

마지막으로 실제 해야 할 1에서 100사이의 소수 개수를 구하는 코드를 작성합니다.

수행 코드는 IsPrimerNumber 함수에 입력 인자로 1에서 100사이의 정수를 전달하는 것을 반복하여 참일 때 의 개수를 누적시킵니다.

전체 코드는 다음과 같습니다.

#include <stdio.h>
/******************************************
소수인지 판별하는 함수
    입력 매개변수 목록:
       number: 정수
    반환 값: 소수인지 여부 (참일 때 1, 거짓일 때 0)                 
*******************************************/
int IsPrimeNumber(int number);
 
int main()
{
    int count = 0;
    for (int i = 1; i <= 100; i++)
    {
        if (IsPrimeNumber(i))
        {
            count++;
        }
    }
    printf("1~100 사이의 소수는 %d개 입니다.\n", count);
    return 0;
    
    return 0;
}
int IsPrimeNumber(int number)
{
    if(number<=1) //조건: number가 1보다 작거나 같다면
    {
        return 0;//거짓(0) 반환
    }
    for(int i = 2; i<=number/2; i++) //반복: i:=2-> number/2, setp:1
    {
        if(number%i == 0)//조건: i가 number의 약수일 때(나누었을 때 나머지가 0인 수)
        {
            return 0; //거짓(0) 반환
        }
    }
    return 1; //참(1)반환
}

소문자인지 판별, 대문자인지 판별, 소문자로 바꾸는 함수와 대문자로 바꾸는 함수

이번에는 소문자인지 판별하는 함수와 대문자인지 판별하는 함수, 소문자로 바꾸는 함수와 대문자로 바꾸는 함수를 만들어 볼게요.

실제 이 함수들은 표준 라이브러리 함수를 제공하고 있습니다.

ctype.h 에 소문자로 변환하는 함수(tolower)와 대문자로 변환하는 함수(toupper)를 제공하고 있어요.

소문자(islower)인지, 대문자(isupper)인지 판별하는 함수도 제공하고 있습니다.

이번에는 이 함수들을 이용하지 않고 같은 형태로 동작하는 함수를 만들어 보기로 할게요.

ASCII 코드 복습

앞에서 ASCII 코드 값에 관하여 소개한 적이 있어요.

간단히 확인하기 위한 코드를 소개할게요.

#include <stdio.h>

int main()
{
    int cnt = 0;
    
    printf("===숫자===\n");
    for (int ch = '0'; ch <= '9'; ch++)
    {
        printf("[%c,%03d] ", ch, ch);        
    }

    printf("\n===대문자===\n");
    for (int ch = 'A'; ch <= 'Z'; ch++, cnt++)
    {
        printf("[%c,%03d] ", ch, ch);
        if (cnt % 10 == 9)
        {
            printf("\n");
        }
    }

    printf("\n===소문자===\n");
    for (int ch = 'a'; ch <= 'z'; ch++,cnt++) 
    {
        printf("[%c,%03d] ", ch, ch);
        if (cnt % 10 == 9)
        {
            printf("\n");
        }
    }

    return 0;
}

실행 결과

숫자===
[0,048] [1,049] [2,050] [3,051] [4,052] [5,053] [6,054] [7,055] [8,056] [9,057] 
대문자===
[A,065] [B,066] [C,067] [D,068] [E,069] [F,070] [G,071] [H,072] [I,073] [J,074] 
[K,075] [L,076] [M,077] [N,078] [O,079] [P,080] [Q,081] [R,082] [S,083] [T,084] 
[U,085] [V,086] [W,087] [X,088] [Y,089] [Z,090] 
소문자===
[a,097] [b,098] [c,099] [d,100] 
[e,101] [f,102] [g,103] [h,104] [i,105] [j,106] [k,107] [l,108] [m,109] [n,110] 
[o,111] [p,112] [q,113] [r,114] [s,115] [t,116] [u,117] [v,118] [w,119] [x,120] 
[y,121] [z,122] 

결과를 보면 숫자 문자 ‘0’부터 ‘9’까지 순차적으로 부여하는 것을 알 수 있어요.

대문자도 ‘A’부터 ‘Z’까지 소문자도 ‘a’부터 ‘z’까지 순차적으로 부여하고 있습니다.

이를 이용하여 대문자인지 소문자인지 판별할 수 있을 거예요.

그리고 ‘A’-‘a’ 혹은 ‘a’-‘A’를 이용하면 소문자로 변환 및 대문자로 변환하는 것도 가능합니다.

소문자 판별, 대문자 판별하는 함수

먼저 소문자인지 판별하는 함수와 대문자인지 판별하는 함수를 만들어 볼게요.

ctype.h 에서 제공하는 함수의 원형을 보면 다음과 같습니다.

int islower(int c);//소문자인지 판별하는 함수
int isupper(int c);//대문자인지 판별하는 함수

이 두 가지 함수를 사용하는 간단한 코드를 먼저 만들어 볼게요.

#include <stdio.h>
#include <ctype.h>
int main()
{
    int ch = 'a';
    if (islower(ch))
    {
        printf("%c : 소문자\n",ch);
    }
    else
    {
        printf("%c : 소문자 아님\n", ch);
    }
    ch = 'A';
    if (isupper(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    ch = '1';
    if (isupper(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    return 0;
}

실행 결과

a : 소문자
A : 대문자
1 : 대문자 아님

이번에는 직접 만들어서 테스트 해 봅시다.

만들 함수 이름은 islower_eh, isupper_eh라고 정할게요.

입력 매개변수와 리턴 형식은 islower, isupper와 똑같이 하기로 합시다.

int islower_eh(int c);//소문자인지 판별하는 함수
int isupper_eh(int c);//대문자인지 판별하는 함수

테스트 코드는 위에서 islower 함수와 isupper함수를 사용한 코드에서 호출 부분만 바꾸기로 할게요.

#include <stdio.h>
int islower_eh(int c);//소문자인지 판별하는 함수
int isupper_eh(int c);//대문자인지 판별하는 함수
int main()
{
    int ch = 'a';
    if (islower_eh(ch))
    {
        printf("%c : 소문자\n",ch);
    }
    else
    {
        printf("%c : 소문자 아님\n", ch);
    }
    ch = 'A';
    if (isupper_eh(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    ch = '1';
    if (isupper_eh(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    return 0;
}int islower_eh(int c)
{
    return 0;
}
int isupper_eh(int c)
{
    return 0;
}

소문자는 ‘a’ 보다 크거나 같고 ‘z’ 보다 작거나 같은 아스키 코드값을 갖습니다.

대문자는 ‘A’ 보다 크거나 같고 ‘Z’ 보다 작거나 같은 아스키 코드값을 갖습니다.

함수 구현도 이러한 비교 연산 결과를 반환하면 끝입니다.

int islower_eh(int c)
{
    return (c>='a')&&(c<='z');
}
int isupper_eh(int c)
{
    return (c >= 'A') && (c <= 'Z');
}

테스트를 포함한 전체 코드는 다음과 같습니다.

#include <stdio.h>
int islower_eh(int c);//소문자인지 판별하는 함수
int isupper_eh(int c);//대문자인지 판별하는 함수
int main()
{
    int ch = 'a';
    if (islower_eh(ch))
    {
        printf("%c : 소문자\n",ch);
    }
    else
    {
        printf("%c : 소문자 아님\n", ch);
    }
    ch = 'A';
    if (isupper_eh(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    ch = '1';
    if (isupper_eh(ch))
    {
        printf("%c : 대문자\n", ch);
    }
    else
    {
        printf("%c : 대문자 아님\n", ch);
    }
    return 0;
}
int islower_eh(int c)
{
    return (c>='a')&&(c<='z');
}
int isupper_eh(int c)
{
    return (c >= 'A') && (c <= 'Z');
}

실행 결과

a : 소문자
A : 대문자
1 : 대문자 아님

소문자로 변환하는 함수, 대문자로 변환하는 함수

이번에는 소문자로 변환하는 함수와 대문자로 변환하는 함수를 만들어 봅시다.

ctype.h 에서 제공하는 함수는 tolower, toupper 함수입니다.

int tolower(int c); //소문자로 바꾸는 함수
int toupper(int c); //대문자로 바꾸는 함수

먼저 tolower 함수와 toupper 함수를 사용하는 코드를 작성합시다.

앞에서 islower 함수 호출 결과 참일 때 toupper 함수를 호출하여 대문자로 변환할게요.

마찬가지로 isupper 함수 호출 결과 참일 때 tolower 함수를 호출하여 소문자로 변환할게요.

#include <stdio.h>
#include <ctype.h>
int islower_eh(int c);//소문자인지 판별하는 함수
int isupper_eh(int c);//대문자인지 판별하는 함수
int main()
{
    int ch = 'a';
    int tch = ch;
    if (islower_eh(ch))
    {
        tch = toupper(ch);
    }
    printf("%c를 대문자로 변환 %c\n", ch, tch);

    ch = 'A';
    tch = ch;
    if (islower_eh(ch))
    {
        tch = toupper(ch);
    }
    printf("%c를 대문자로 변환 %c\n", ch, tch);

    ch = 'a';
    tch = ch;
    if (isupper_eh(ch))
    {
        tch = tolower(ch);  
    }
    printf("%c를 소문자로 변환 %c\n", ch, tch);

    ch = 'A';
    tch = ch;
    if (isupper_eh(ch))
    {
        tch = tolower(ch);
    }
    printf("%c를 소문자로 변환 %c\n", ch, tch);
    return 0;
}
int islower_eh(int c)
{
    return (c>='a')&&(c<='z');
}
int isupper_eh(int c)
{
    return (c >= 'A') && (c <= 'Z');
}

실행 결과

a를 대문자로 변환 A
A를 대문자로 변환 A
a를 소문자로 변환 a
A를 소문자로 변환 a

이번에는 직접 만들어서 테스트 해 봅시다.

만들 함수 이름은 tolower_eh, toupper_eh라고 정할게요.

입력 매개변수와 리턴 형식은 tolower, toupper와 똑같이 하기로 합시다.

int tolower_eh(int c);//소문자로 바꾸는 함수
int toupper_eh(int c);//대문자로 바꾸는 함수

소문자로 바꾸는 함수에서는 대문자일 때 ‘A’를 빼고 ‘a’를 더하면 소문자로 바꿀 수 있어요.

대문자로 바꾸는 함수에서는 소문자일 때 ‘a’를 빼고 ‘A’를 더하면 소문자로 바꿀 수 있어요.

int tolower_eh(int c)
{
    if (isupper_eh(c))
    {
        c = c - 'A' + 'a';
    }
    return c;
}
int toupper_eh(int c)
{
    if (islower_eh(c))
    {
        c = c - 'a' + 'A';
    }
    return c;
}

테스트 코드를 포함한 전체 코드는 다음과 같아요.

#include <stdio.h>
int islower_eh(int c);//소문자인지 판별하는 함수
int isupper_eh(int c);//대문자인지 판별하는 함수
int tolower_eh(int c);//소문자로 바꾸는 함수
int toupper_eh(int c);//대문자로 바꾸는 함수
int main()
{
    int ch = 'a';
    int tch = ch;
    if (islower_eh(ch))
    {
        tch = toupper_eh(ch);
    }
    printf("%c를 대문자로 변환 %c\n", ch, tch);

    ch = 'A';
    tch = ch;
    if (islower_eh(ch))
    {
        tch = toupper_eh(ch);
    }
    printf("%c를 대문자로 변환 %c\n", ch, tch);

    ch = 'a';
    tch = ch;
    if (isupper_eh(ch))
    {
        tch = tolower_eh(ch);  
    }
    printf("%c를 소문자로 변환 %c\n", ch, tch);

    ch = 'A';
    tch = ch;
    if (isupper_eh(ch))
    {
        tch = tolower_eh(ch);
    }
    printf("%c를 소문자로 변환 %c\n", ch, tch);
    return 0;
}
int islower_eh(int c)
{
    return (c>='a')&&(c<='z');
}
int isupper_eh(int c)
{
    return (c >= 'A') && (c <= 'Z');
}
int tolower_eh(int c)
{
    if (isupper_eh(c))
    {
        c = c - 'A' + 'a';
    }
    return c;
}
int toupper_eh(int c)
{
    if (islower_eh(c))
    {
        c = c - 'a' + 'A';
    }
    return c;
}

이 외에도 다양한 함수들을 만들어 보길 권합니다.