사용자 정의 함수
“cough”라고 세 번 말하는 C 프로그램을 작성하고 싶다면 아래처럼 작성하면 된다.
#include <stdio.h>
int main(void)
{
printf("cough\n");
printf("cough\n");
printf("cough\n");
}
printf 를 세 번 반복하면 되지만, 동일한 작업을 반복하는 것이기 때문에
사용자 정의 함수를 이용하면 아래 코드와 같이 더 단순화 할 수 있다.
루프 중에서 For을 사용하여 코드를 다시 짜보면,
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 3; i++)
{
printf("cough\n")
}
}
어렵지 않게 cough을 3번 출력해보았다.
#include <stdio.h>
void cough(void)
{
printf("cough\n")
}
int main(void)
{
for (int i = 0; i < 3; i++)
{
cough();
}
}
void를 입력하고 원하는 함수명(cough)을 적은 뒤 괄호 안에 void를 적어준다.
그리고 printf로 cough를 출력하는 코드를 작성한다.
지금까지 사용하던 함수는 우리가 직접 구현할 필요가 없었습니다.
과거의 어떤 사람들이 모두 구현해두었기 때문이다.
함수의 이름을 cough라고 정했다.
이제 int main(void)의 안에 cough(함수명)를 사용해보자.
그럼 정의해둔 대로 cough가 출력된다.
하지만 여기에도 문제가 있다.
함수를 1개가 아닌 여러개를 만들수록 main 함수는 아래로 내려가기 때문이다.
메인 함수가 아래에 위치한 것 보다는 위에 위치한 것이 더 보기 좋겠지?
그러나 main 함수를 위로 올리고 cough 함수를 아래에 작성하면 오류가 발생한다.
main 함수에서 cough() 함수를 사용했지만 cough함수는 아래에 있다.
C는 오래되었고 똑똑하지 않기 때문에 아래에 cough라는 함수가 있을 것이라 생각하지 못한다...
이 것을 해결하려면 다시 cough함수를 위로 올려야한다.
그러나 이 과정을 영구적으로 반복할 수는 없는 노릇...
하지만 방법이 있다.
#include <stdio.h>
void cough(void);
int main(void)
{
for (int i = 0; i < 3; i++)
{
cough();
}
}
void cough(void)
{
printf("cough\n");
}
void cough(void)를 세미콜론과 함께 위로 올리는 것이다.
이전에 cough를 봤던 것처럼 C를 속이는 방법이다.
cough함수를 전부 본 적은 없어도 이름은 본적이 있으니 main 함수에 나올 때까지 코드를 계속 읽도록 하는 것이다.
이번에는 원하는 횟수만큼 cough를 출력할 수 있도록 해보자.
#include <stdio.h>
void cough(int n);
int main(void)
{
cough(3);
}
void cough(int n)
{
for (int i = 0; i < n; i++)
{
printf("cough\n");
}
}
맨 아래의 void cough(int n){...} 은 cough 라는 이름의 함수를 직접 정의한 것이다.
cough( ) 안의 int n 은 함수가 입력값을 받아서 int 형식을 갖는 n이라는 변수에 저장하겠다는 의미이다.
그리고 { } 안의 내용을 보면 n번 동안 cough를 출력하는 for 루프가 있다.
다시 main 함수 안으로 돌아가보면 cough(3) 이라는 한 줄의 코드를 통해서 3이라는 값을 cough 함수에 전달하고,
궁극적으로는 cough를 세 번 출력할 수 있게 된다.
여기서 main 함수를 우리가 정의한 cough 함수보다 위에 위치시키고 싶다면, 예시에서와 같이 void cough(int n);를 먼저 입력해서 cough 라는 함수가 정의되어있음을 알려줘야 한다.
#include <cs50.h>
#include <stdio.h>
int get_positive_int(void);
int main(void)
{
int i = get_positive_int();
printf("%i\n", i);
}
int get_positive_int(void)
{
int n;
do
{
n = get_int("Positive Integer: ");
}
while (n < 1);
return n;
}
여기 get_postive_int라는 함수가 있는데 입력을 받지 않았다.
괄호 안에 아무것도 넣을 필요가 없다. 아무 양의 정수나 받으면 된다.
하지만 이 전에 사용했던 함수처럼 어떤 값을 받아와서 변수에 저장하는 것처럼 이 함수가 뭔가를 반환하게 하고 싶다.
그래서 int get_positive_int(void) 파란색 글씨는 void가 아니고 int가 된다.
함수 왼쪽에 있는 단어는 출력의 종류를 의미 한다.
int get_positive_int(void) 괄호 안의 빨간색 단어(void)는 입력의 종류를 뜻한다.
만약 입출력이 없다면 void를 적어주면 된다.
그리고 int n; 이라고 하는 처음 보는 것이 있는데
이것은 컴퓨터에게 n이라고 하는 변수를 달라는 일종의 힌트이다.
그 안에 어떤 값을 저장할지 아직 모르기 때문에 그냥 int n;만 적는 것이다.
아직은 아무것도 할당할 필요가 없다.
그럼 n은 쓰레기 값(Garbage Value)이라고 부르는 값을 가지게 된다.
n에 무엇이 들었는지는 모르지만 중요하지 않다. 나중에 제대로 넣으면 된다.
do-while의 루프
불리언 표현 while(n<1); 이 참일때 다음을 수행하라는 뜻이다.
만약 n이 1보다 작다면 계속해서 질문을 반복하는 것이다.
while을 단독으로 사용하면 while의 조건이 참이어야만 수행한다.
하지만 do-while은 do에서 무조건 한 번은 먼저 수행하게 해준다.
물론 이것은 여러가지 표현 방법 중 하나이다.
중첩 루프
마리오 게임에서 흔히 볼 수 있는 블록처럼 화면에 여러 개의 이미지를 가로나 세로로 여러 개 이어서 출력하고 싶으면 어떻게 해야 할까?
아래처럼 for 루프를 사용할 수 있다.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
int n;
do
{
n = get_int("Size: ");
}
while (n < 1);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
printf("#");
}
printf("\n");
}
}
먼저 int n; 으로 정수 값을 갖는 변수 n을 정의하고
do{ …}while()을 이용해서 while( )의 조건이 만족할때 까지 get_int 함수로 사용자가 입력값을 받아 n에 저장한다.
do{ }while()을 사용하면 조건과 상관없이 최소한 한 번은 { }안의 내용을 실행할 수 있다.
그리고 for 루프를 두 번 중첩해서 돌면서 “#”을 출력한다. 첫 번째 루프에서는 변수 i를 기준으로 n번 반복하고,
그 안의 내부 루프에서는 변수 j를 기준으로 n번 반복한다. 내부 루프에서는 “#”을 출력하고, 내부 루프가 끝날 때마다 줄바꿈을 수행한다. 따라서 최종적으로는 가로가 n개, 세로가 n개인 “#”이 출력되게 된다.
하드웨어의 한계
컴퓨터는 RAM(랜덤 액세스 메모리)이라는 물리적 저장장치를 포함하고 있다.
우리가 작성한 프로그램은 구동 중에 RAM에 저장된다.
RAM은 유한한 크기의 비트만 저장할 수 있기 때문에 때때로 부정확한 결과를 내기도 한다.
부동 소수점 부정확성
실수 x, y를 인자로 받아 x 나누기 y를 하는 프로그램이 있다.
#include <cs50.h>
#include <stdio.h>
int main(void)
{
// 사용자에게 x 값 받기
float x = get_float("x: ");
// 사용자에게 y 값 받기
float y = get_float("y: ");
// 나눗셈 후 출력
printf("x / y = %.50f\n", x / y);
}
나눈 결과를 소수점 50자리까지 출력하기로 하고, x에 1을, y에 10을 입력하면 아래와 같은 결과가 나온다.
x: 1
y: 10
x / y = 0.10000000149011611938476562500000000000000000000000
정확한 결과는 0.1이 되어야 하지만, float 에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 내게 된다.
정수 오버플로우
비슷한 오류로, 1부터 시작하여 2를 계속해서 곱하여 출력하는 아래와 같은 프로그램이 있다고 해보자.
#include <stdio.h>
#include <unistd.h>
int main(void)
{
for (int i = 1; ; i *= 2)
{
printf("%i\n", i);
sleep(1);
}
}
우리가 변수 i를 int로 저장하기 때문에, 2를 계속 곱하다가 int 타입이 저장할 수 있는 수를 넘은 이후에는
아래와 같은 에러와 함께 0이 출력될 것이다.
...
1073741824
overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
-2147483648
0
0
...
정수를 계속 키우는 프로그램에서 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어진 것이다.
int에서는 32개의 비트가 다였기 때문이다. 그 이상의 숫자는 저장할 수 없다.
따라서 다루고자 하는 데이터 값의 범위를 유의하며 프로그램을 작성하는 것이 중요하다.
'공부 STUDY > CS' 카테고리의 다른 글
CS50 | 알고리즘 - 검색 알고리즘, 알고리즘 표기법, 선형 검색 (0) | 2022.06.25 |
---|---|
CS50 | 배열 Array (2) - 배열, 문자열과 배열, 문자열의 활용, 명령행 인자 (0) | 2022.06.23 |
CS50 | 배열 Array (1) - 컴파일링, 디버깅, 코드의 디자인 (0) | 2022.06.23 |
CS50 | C 언어 - (1) (0) | 2022.06.21 |
1. 컴퓨팅 사고 Computational Thinking, Scratch (0) | 2022.06.19 |