h, b, c, s를 각 각 입력받고, 문제 설명에 나온 공식대로 입력값을 모두 곱하면 소리 파일의 저장 용량을 계산할 수 있다.
그러나 계산한 값은 비트 단위이기 때문에 출력 예시처럼 MB 단위로 바꿔야 한다.
문제 설명의 단위들을 보면 비트를 1024로 두번 나누고, 8로 한번 나누면 MB단위로 만들 수 있다.
이때 자료형은 int 자료형과 float 자료형을 사용하면 계산 과정에서 자료형의 범위를 초과할 수 있기 때문에 정수는 long long 자료형과 실수는 double 자료형을 사용하였다. long long 자료형은 서식문자로 %lld를 사용하고, double 자료형은 서식문자로 %lf를 사용한다.
또한 소숫점 한자리만 출력해야 하기 때문에 %.1lf로 서식문자를 사용한다.
정답은 다음 코드와 같다.
1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
int main()
{
longlongint h, b, c, s; // 입력받을 변수
double result; // 계산결과를 저장할 변수
scanf("%lld %lld %lld %lld", &h, &b, &s, &c); // 입력
result = h * b * s * c; // 계산한 결과 (bit 단위)
printf("%.1lf MB", result/(8*1024*1024)); // 계산 결과를 MB단위로 바꾸어 출력
while 반복문은 조건식이 만족하지 않으면 반복을 하지 않고 넘어간다. do while 반복문은 조건이 만족하지 않아도 코드를 최소 한 번은 실행한다.
do while 반복문은 초기식이 반복문 바깥에 있고, do로 시작하여 중괄호 안에 반복할 코드와 변화식이 들어가고, 중괄호가 끝나는 부분에 조건식을 지정한다.
초기식
do
{
반복할 코드
변화식
} while (조건식);
초기식의 값과는 상관없이 중괄호 안의 코드와 변화식을 한번 실행하고, 조건식을 판단하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
29.1 do while 반복문 사용하기
#include <stdio.h>
int main()
{
int i = 0;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i < 100);
return 0;
}
반복문에 사용할 변수를 선언한 뒤 0으로 초기화 한다. do 아래에 중괄호를 열고, 반복할 코드와 변화식을 넣는다. 중괄호를 닫은 뒤 while에 조건식을 지정하고 세미클론을 붙여준다.
do 다음에 오는 코드는 조건식과 관계 없이 무조건 한 번은 실행한다. 따라서 printf가 실행되고 i++가 실행된다. 이후 조건식에서 i가 100보다 작은지 검사한다. 100보다 작으면 계속 반복하고, 100이되면 i < 100은 거짓이 되므로 반복문이 종료된다.
중괄호 안에는 반드시 변화식이 있어야 한다. 변화식이 없으면 무한루프 현상이 발생하므로 주의해야 한다.
29.2 초깃값을 1부터 시작하기
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i <= 100);
return 0;
}
i를 1로 초기화 했기 때문에 조건식을 i <= 100으로 지정하여 100번 반복하도록 하였다. i가 101이 되면 i<=100이 거짓이 되므로 반복문이 종료된다.
29.3 초깃값을 감소시키기
#include <stdio.h>
int main()
{
int i = 100;
do
{
printf("Hello, world! %d\n", i);
i--;
} while (i > 0);
return 0;
}
i를 100으로 초기화하고, i--로 i를 1씩 감소시키면서 i > 0으로 100번 반복시켰다. i가 0 이되면 반복문을 끝낸다.
29.4 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 0;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i < count);
return 0;
}
scanf 함수로 입력값을 받아 count변수에 저장하여 조건식에서 i < count로 count의 값만큼 반복하도록 하였다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
do
{
printf("Hello, world! %d\n", count);
count--;
} while (count > 0);
return 0;
}
위 코드와 같이 입력 받은 값을 초기식으로 사용할 수 도 있다. 변화식을 count--로 하여 반복할 때마다 count값을 1씩 감소시키고, 조건식에서 count 가 0보다 클때까지 반복하여 count에 들어있는 값만큼 반복하였다.
29.5 반복 횟수가 정해지지 않은 경우
while 반복문처럼 do while 반복문도 반복 횟수가 정해지지 않았을 경우에 주로 사용한다. do while반복문은 최소 한번은 반드시 실행한다는 점에서 차이가 있다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
int i = 0;
do
{
i = rand() % 10;
printf("%d\n", i);
} while (i != 3);
return 0;
}
위 코드는 0~9까지 무작위로 생성되는 숫자중 조건식 i != 3에 따라 3이 생성되면 반복을 종료하고 3이 생성되지 않으면 계속 반복하는 코드이다.
29.6 do while 반복문으로 무한루프 만들기
#include <stdio.h>
int main()
{
do
{
printf("Hello, world!\n");
} while (1);
return 0;
}
do 다음에 반복할 코드를 작성하고 조건식 부분에 1을 지정하면 된다. 조건식이 없으므로 변화식도 필요 없다.
stdbool.h 헤더를 사용하고, 조건식에 true를 지정해도 된다.
29.7 코드를 한 번만 실행하기
#include <stdio.h>
int main()
{
do
{
printf("Hello, world!\n");
} while (0);
return 0;
}
조건식에 0을 지정하면 do부분의 코드가 한 번만 실행된다. 조건식이 거짓이므로 반복을 하지 않고 끝내기 때문이다.
stdbool.h 헤더를 사용하고, 조건식에 false를 지정해도 된다.
29.8 퀴즈
정답은 b, c, e이다.
정답은 c 이다.
정답은 c이다.
29.9 연습문제 : do while 반복문 사용하기
정답은 c1 != 'q' 이다. 입력값이 q가 아니면 코드를 계속 반복한다.
29.10 심사문제 : 숫자의 합 구하기
정답은 다음 코드와 같다.
sum += i;
i++;
Unit 30. break, continue로 반복문 제어하기
break는 for,while,do while,switch문법에서 제어 흐름을 벗어나기 위해 사용한다. switch의 경우로 예를 들면 다음과 같다.
continue는 break와 비슷하지만 break는 제어 흐름을 중단하고 빠져나오지만, continue는 제어 흐름을 유지한 상태에서 코드의 실행만 건너뛰는 역할을 한다.
30.1 break로 반복문 끝내기
#include <stdio.h>
int main()
{
int num1 = 0;
while (1)
{
num1++;
printf("%d\n", num1);
if (num1 == 100)
break;
}
return 0;
}
while에 1을 지정하여 무한루프를 만들고 반복하는 코드에서 num1의 값을 1씩 증가시킨후 num1의 값을 출력하고, if를 이용하여 num1 이 100이 되면 break로 반복문을 종료시킨다. break를 실행하게 되면 반복문이 바로 종료된다.
다음과 같이 for문에서도 반복문의 동작은 같다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (;;)
{
num1++;
printf("%d\n", num1);
if (num1 == 100)
break;
}
return 0;
}
반복 횟수가 정해져 있어도 break를 사용하면 반복문은 바로 끝난다.
30.2 continue로 코드 실행 건너뛰기
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
1 부터 100까지 반복하며 if를 사용하여 i가 홀수면 continue를 실행해서 아래 코드를 건너뛰고 다음 반복을 시작한다.
while에서도 continue의 동작은 같다.
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 100)
{
i++;
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
무한 루프에서 사용하면 위 코드의 경우 짝수만 계속 출력되고, 반복문은 끝나지 않는다.
30.3 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 1;
while (1)
{
printf("%d\n", i);
if (i == count)
break;
i++;
}
return 0;
}
무한 루프인 반복문 상태에서 i의 값을 출력하고, i의 값이 입력한 값과 같으면 반복문을 종료하고, 다르면 i의 값을 1씩 증가시킨다.
30.4 입력한 숫자까지 짝수만 출력하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = 1; i <= count; i++)
{
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
입력 받은 값 만큼 반복하고, 반복문 안에서 i 가 홀수면 continue를 실행하여 다음 코드를 실행하지 않고 건너뛰고, i가 짝수면 printf로 숫자를 출력한다.
i를 10으로 나눴을때 나머지가 7이면 아래 코드를 건너뛰고, i가 num2 보다 커지면 반복문을 종료한다.
Unit 31. 계단식으로 별 출력하기
31.1 중첩 루프 사용하기
콘솔은 2차원 평면이기 때문에 별을 일정한 모양으로 출력하려면 반복문을 두 개 사용하는 것이 편하다.
반복문 안에 반복문이 들어가는 구조를 중첩 루프라 한다. 반복문의 루프 인덱스 변수는 index의 첫 글자를 따서 i를 사용하고 반복문 안에 반복문이 들어갈 때는 i, j, k 처럼 i 부터 알파벳 순서대로 사용한다. 2중 for문은 다음과 같은 구조이다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
printf("j:%d ", j);
}
printf("i:%d\\n", i);
printf("\n");
}
return 0;
}
바깥쪽 루프에서 시작하여 안쪽 루프가 가로 방향으로 j 값을 출력하고, 가로 방향 출력이 끝나면 바깥쪽 루프에서 i값과 개행문자를 출력하여 세로 방향을 처리한다.
중첩루프는 2차원 평면을 다룰 수 있으므로 이미지 처리, 영상처리 , 좌표계 처리 등에 사용된다.
31.2 사각형으로 별 출력하기
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
안쪽의 반복문에서 printf로 별을 출력하면 가로 방향으로 별이 하나씩 출력된다. printf에서 제어문자를 사용하지 않으면 줄바꿈을 하지 않는다. 바깥쪽 반복문에서 안쪽 반복문이 끝나면 제어문자 \n을 출력함으로 줄을 바꾸고 다시 안쪽 반복문으로 별을 출력하여 사각형을 그릴 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 7; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
위와 같이 코드를 수정하면 안쪽 반복문은 3번 반복하고, 바깥쪽 반복문은 7번 반복하므로 가로 방향으로 별을 7개 그리고, 세로 방향으로 3줄을 출력한다.
31.3 계단식으로 별 출력하기
위와 같이 계단식으로 별을 출력하려면 별을 출력하지 않는 부분이 있으므로 조건문으로 출력을 제어해야 한다.
첫번째 줄에는 별이 한개, 두번째 줄에는 별이 두 개 와 같은 규칙으로 줄의 위치와 별의 개수가 동일하다. 따라서 가로 줄을 출력할 때 세로 줄의 크기 만큼 반복하여 출력하도록 하면 된다. 이 조건을 고려하여 소스코드를 작성하면 다음과 같다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (j <= i)
printf("*");
}
printf("\n");
}
return 0;
}
안쪽 반복문에서 별을 줄 위치 숫자 만큼 출력하기 위해 조건문으로 if(j <= i)를 사용하였다.
31.4 대각선으로 별 출력하기
별을 대각선으로 출력하는 코드는 다음과 같다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == i)
printf("*");
else
printf(" ");
}
printf("\n");
}
return 0;
}
가로 방향 변수와 세로 방향 변수가 같을 경우 별을 출력하고 같지 않으면 공백을 출력하여 대각선으로 별이 출력되도록 하였다. 콘솔에서는 문자 출력을 하지 않으면 항상 가장 왼쪽에서 시작하므로 별을 출력하지 않는 부분은 공백을 출력해야 별이 원하는 위치에 오게 된다.
31.5 퀴즈
정답은 a,d,e 이다.
정답은 b이다.
31.6 연습문제 : 역삼각형 모양으로 별 출력하기
정답은 다음 코드와 같다.
if(j < i)
{
printf(" ");
}
else
{
printf("*");
}
첫줄에는 별 5개로 시작하여 하나씩 줄어들기 때문에 i가 늘어날 때 마다 출력되는 공백의 수가 하나씩 늘어나서 역삼각형 모양의 삼각형이 출력된다.
31.7 심사문제 : 산 모양으로 별 출력하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
int height;
scanf("%d", &height);
for (int i = 0; i < height; i++)
{
for (int j = height - 1; j >= 0; j--)
{
if (j > i)
printf(" ");
else
printf("*");
}
for (int j = 0; j < height - 1; j++)
{
if (j < i)
printf("*");
}
printf("\n");
}
return 0;
}
입력받은 높이로 세로 방향 반복문을 만들고, 가로 방향은 출력할 값들을 반으로 나눠서 반복문을 두개로 처리한다. 첫번째 안쪽 반복문에서는 j의 초기값을 입력값-1로 하고, j를 1씩 감소시키면서 j가 세로줄 위치보다 크면 공백을 출력하고, 아니면 별을출력하여 별이 다음과 같은 모양인 삼각형을 출력한다.
두번째 안쪽 반복문은 반대로 j가 세로줄 위치보다 작으면 별을 출력하여 다음과 같은 형태의 삼각형을 출력하여 위의 삼각형과 붙여지도록 한다.
삼각형이 붙여지는 것은 전체가 붙여지는 것이 아니라 한 줄씩 붙여지는 것이고, 안쪽의 두 반복문이 마치면 개행문자를 출력하므로 줄을 구분한다.
Unit 32. goto로 프로그램의 흐름을 원하는대로 바꾸기
goto의 동작은 다음과 같이 goto로 레이블을 지정하고 코드 안에서 지정한 레이블이 있는 위치로 코드를 건너뛴다.
goto를 남발하는 것은 코드의 가독성도 떨어뜨리고 유지보수를 힘들게 하여 좋지 않지만, goto를 적절히 활용하면 중복되는 코드를 없앨 수 있고, 코드를 좀 더 간결하게 만들 수 있다. 특히 에러처리에 유용하기 때문에 리눅스 커널에서 자주 사용된다.
32.1 goto와 레이블 사용하기
goto는 레이블을 지정하여 사용한다. 레이블은 :(클론)을 붙이고, 레이블 이름을 짓는 규칙은 변수와 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1)
goto ONE;
else if (num1 == 2)
goto TWO;
else
goto EXIT;
ONE:
printf("1입니다.\n");
goto EXIT;
TWO:
printf("2입니다.\n");
goto EXIT;
EXIT:
return 0;
}
goto에 레이블을 지정하면 중간에 있는 코드는 무시하고 레이블로 즉시 이동한다. 위 코드에서는 레이블로 이동하고 다시 goto로 EXIT레이블로 이동하여 프로그램을 끝낸다. 위 코드를 if 문으로 표현하면 다음과 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1)
printf("1입니다.\n");
else if (num1 == 2)
printf("2입니다.\n");
return 0;
}
if문을 이용한 코드가 더 간결하기 때문에 이와 같은 경우 goto문을 쓰는 것은 좋지않다.
32.2 중첩 루프 빠져나오기
#include <stdio.h>
#include <stdbool.h>
int main()
{
int num1 = 0;
bool exitOuterLoop = false;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20)
{
exitOuterLoop = true;
break;
}
num1++;
}
if (exitOuterLoop == true)
break;
}
printf("%d\n", num1);
return 0;
}
위 코드는 num1이 20이 되면 반복문을 끝내는데 num1은 안쪽 반복문에서 증가시키고 있다.
break는 현재 루프만 끝내기 때문에 안쪽 루프에서 break를 사용하면 안쪽 루프만 끝내고, 반대쪽 루프는 계속 반복한다. 그렇기 때문에 바깥쪽 루프도 끝내려면 break를 두 번 실행해야 한다.
이럴 때는 다음과 같이 goto문을 사용하여 간단하게 루프를 빠져나올 수 있다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20)
goto EXIT;
num1++;
}
}
EXIT:
printf("%d\n", num1);
return 0;
}
위 코드는 num1이 20이 되면 EXIT레이블로 즉시 이동하여 반복문을 한 번에 빠져나온다.
이와 같이 goto는 다중 루프를 빠져나올 때 유용하다.
32.3 goto와 에러처리 패턴
다음은 가상의 통지서를 집집마다 방문하여 자가주택을 소유한 30대 남자에게 전달하는 코드이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2) // 여자
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
if (age < 30) // 30세 미만
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
if (isOwner == 0) // 전월세
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
return 0;
}
문을 열고 자가주택을 소유한 30대 남자가 아니라면 문을 닫고 나온다. 위와 같은 경우 주어진 조건에 해당하지 않으면 인사를 하고 문을 닫는 코드가 경우마다 중복되고 있다. 만약 조건이 더 늘어나면 중복되는 코드도 더 많아지게 된다.
이때 다음과 같이 goto문을 사용하면 에러 상황때 실행하는 중복코드를 하나로 모을 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2)
goto EXIT;
if (age < 30)
goto EXIT;
if (isOwner == 0)
goto EXIT;
EXIT:
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
에러가 발생하면 EXIT로 이동시켜 에러 코드를 처리하게 한다.
위와 같이 종료 직전에 항상 실행해야 하는 에러 코드들을 한 곳에 모아두고 goto를 사용하면 중복 코드를 없앨 수 있고, 코드의 의도도 명확해진다.
32.4 퀴즈
정답은 d,e 이다. 변수 생성 규칙과 동일하다.
정답은 b, d, e이다.
32.5 연습문제 : switch에서 반복문 빠져나오기
2번에 레이블을 만들고 1번에 goto문으로 바로 2번으로 이동하도록 하여 정답은 다음과 같다.
goto EXIT;
EXIT:
32.6 심사문제 : 중첩 루프 빠져나오기
200과 300이 출력되므로 EXIT2 레이블로 이동해야 하므로 EXIT2;가 답이다.
Unit 33. FizzBuzz
FizzBuzz는 간단한 프로그래밍 문제로 규칙은 다음과 같다.
1에서 100까지 출력
3의 배수는 Fizz 출력
5의 배수는 Buzz 출력
3과 5의 공배수는 FizzBuzz 출력
33.1 1부터 100까지 숫자 출력하기
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
printf("%d\n", i);
}
return 0;
}
위 코드는 for 반복문을 이용하여 1부터 100까지 숫자를 출력하는 코드이다.
33.2 3의 배수일때와 5의 배수일 때 처리하기
다음은 1부터 100까지 숫자를 출력하면서 3의 배수일 때는 Fizz, 5의 배수 일때는 Buzz를 출력하는 코드이다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
i를 3으로 나눴을 때 나머지가 0이면 3의 배수이기 때문에 Fizz를 출력하고, 5로 나눴을 때 나머지가 0이면 5의 배수 이므로 Buzz를 출력한다. 아무것도 해당하지 않으면 숫자를 출력한다.
33.3 3과 5의 공배수 처리하기
3과 5의 공배수를 처리할 때는 다음과 같이 논리연산자 &&를 사용하면 된다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if(i % 3 == 0 && i % 5 == 0)
printf("FizzBuzz\n");
else if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
i를 3으로 나눴을 때 나머자 0 이고, 5로 나눴을 때도 나머지가 0이면 3과 5의 공배수 이므로 FizzBuzz를 출력한다.
3의나 5의 배수인지를 먼저 검사하면 그 조건문에서 처리하고 아래 조건식은 검사하지 않으므로 공배수를 처리하는 조건식은 가장 위인 if에 들어가야 한다. 공배수를 먼저 검사하고 3의 배수인지, 5의 배수인지를 검사해야 한다.
33.4 논리연산자를 사용하지 않고 3과 5의 공배수 처리하기
3 과 5의 최소공배수는 15이므로 15로 나눴을 때 나머지가 0이면 3 과 5의 공배수이다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if(i % 15 == 0)
printf("FizzBuzz\n");
else if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
15가 3과 5의 최소공배수라는 것은 코드를 읽는 사람이 값 속의 뜻을 알아내야 하므로 실무에서는 i % 3 == 0 && i % 5 == 0 처럼 계산식과 논리연산자를 사용하여 뜻을 명확하게 드러내는 것이 좋다.
33.5 코드 단축하기
다음과 같이 삼항연산자를 사용하여 코드를 매우 단축할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; ++i <= 100;)
printf(i % 3 ? i % 5 ? "%d\n" : "Buzz\n" : i % 5 ? "Fizz\n" : "FizzBuzz\n", i);
return 0;
}
반복문의 조건식 안에 변화식도 함께 작성하였다. 반복할 때 마다 ++를 수행하여 i의 값을 1씩 증가시킨 후 100보다 작거나 같은지 검사한다.
삼항연산자를 사용하여 각 조건들을 모두 처리하여 출력한다.
i % 3이 0이 아니면 3의 배수가 아니고, i % 5로 간다. i % 5가 0이 아니면 5의 배수도 아니므로 %d로 숫자를 출력한다.
i % 5 가 0 이면 Buzz를 출력한다.
i % 3이 0이면 다시 i % 5를 계산하여 0이 아닌 값이 나오면 Fizz를 출력하고 0이 나오면 3과 5의 공배수 이므로 FizzBuzz를 출력한다.
~ 연산자는 비트 NOT연산자로 0은 1로, 1은 0으로 비트를 뒤집는다. 또는 비트 반전 이라고도 한다.
1010 1010의비트를 뒤집으면 0101 0101이 되고 이는 10진수로 93이다.
23.3 시프트 연산자 사용하기
C에서는 비트의 논리 연산 뿐 아니라 >>, << 연산자로 각 자리를 이동시킬 수도 있다.
#include <stdio.h>
int main()
{
unsigned char num1 = 3; // 3: 0000 0011
unsigned char num2 = 24; // 24: 0001 1000
printf("%u\n", num1 << 3); // 24: 0001 1000: num1의 비트 값을 왼쪽으로 3번 이동
printf("%u\n", num2 >> 2); // 6: 0000 0110: num2의 비트 값을 오른쪽으로 2번 이동
return 0;
}
시프트 연산은 변수 << 이동할 비트수 또는 변수 >> 이동할 비트수 형식으로 사용한다. 비트를 이동시키고 남는 공간은 0으로 채운다.
0000 0011 (3)의 경우 num1 << 3 으로 왼쪽으로 3번 이동하여 0001 1000 (24) 가 되었다.
num2 >> 2는 0001 1000 (24)가 오른쪽으로 2번 이동하여 0000 0110 (6)이 되었다.
한번씩 이동할 때 마다 2의 거듭제곱으로 곱하거나 나누면 된다. 3 << 3 은 3 x 2^3 = 24 이고, 24 >> 2 는 24 / 2^2 = 6이다.
부호있는 자료형의 첫번째 비트는 부호비트라고 하는데, 이 비트가 1이면 음수, 0이면 양수다.
부호 있는 자료형에 저장된 -125는 1000 0011로 첫번째 비트가 1이라 음수이며 10진수로는 -125 이다. 이 비트들을 오른쪽으로 5번 이동시키면 모자라는 공간은 모두 부호비트의 값으로 채워지기 때문에 1111 1100 (-4) 이 된다. 부호없는 자료형은 비트를 오른쪽으로 이동하면 모자란 공간은 모두 0으로 채워진다.
부호 있는 자료형에서 부호비트가 0인 양수에 >> 연산을 하면 모자라는 공간은 부호비트인 0으로 채운다.
부호있는 자료형에서 양수를 왼쪽으로 두번 이동시키면 부호비트 0을 1이 덮어써서 음수가 될 수 도 있다. 반대로 음수의 비트를 왼쪽으로 이동시켜도 부호비트 1을 0이 덮어쓰게 되면 양수가 될 수도 있다. 부호있는 자료형에 시프트 연산을 할 때는 부호비트에 위치한 숫자에 따라 음수, 양수가 결정되므로 의도치 않은 결과가 나올 수 있으니 주의해야 한다.
24.4 비트 연산자로 플래그 처리하기
플래그는 깃발에서 유래한 용어이다. 깃발을 위로 올리면 on, 아래로 내리면 off가 되는 것 처럼 비트가 1이면 on, 비트가 0이면 off이다.
다음과 같이 8비트 크기의 자료형은 비트가 8개 들어가므로 8가지 상태를 정할 수 있다. 다음은 두번째 비트와 8번째 비트가 켜진 상태이다.
0100 0001
int와 같은 4바이트 크기의 자료형은 32비트 이기때문에 32개의 상태를 저장할 수 있다.
플래그는 적은 공간에 정보를 저장해야 하며 빠른 속도가 필요할 때 사용한다. CPU는 내부 저장공간이 매우 작기 때문에 각종 상태를 비트로 저장한다.
비트는 다음과 같이 |=로 킨다.
#include <stdio.h>
int main()
{
unsigned char flag = 0;
flag |= 1; // 0000 0001 마스크와 비트 OR로 여덟 번째 비트를 켬
flag |= 2; // 0000 0010 마스크와 비트 OR로 일곱 번째 비트를 켬
flag |= 4; // 0000 0100 마스크와 비트 OR로 여섯 번째 비트를 켬
printf("%u\n", flag); // 7: 0000 0111
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
return 0;
}
플래그로 사용할 변수에 |= 연산자와 숫자로 특정 비트를 킨다. 비트를 조작하거나 검사할때 사용하는 숫자를 마스크(mask)라고 부른다. 여기서는 1,2,4가 마스크이다.
플래그 비트를 켜는것은 OR연산의 특징을 이용하여 해당 비트가 꺼져있으면(0이면) 비트를 키고(1로 만들고) 켜져있으면 유지한다. 특정 비트가 꺼져있는지 확인하려면 &연산자를 사용하면 된다. 플래그에 저장된 값 0000 0111과 1(0000 0001)을 & 연산하면 0000 0001이 된다. 연산 결과가 마스크 값이 나오면 비트가 켜져있고, if 조건을 만족시키며 연산 결과가 0이 나오면 비트가 꺼져있음을 의미하고 if 조건을 만족시키지 못한다.
다음은 플래그의 비트를 끄는 방법이다.
#include <stdio.h>
int main()
{
unsigned char flag = 7; // 7: 0000 0111
flag &= ~2; // 1111 1101 마스크 값 2의 비트를 뒤집은 뒤 비트 AND로 일곱 번째 비트를 끔
printf("%u\n", flag); // 5: 0000 0101
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
return 0;
}
마스크 값을 ~로 뒤집은 뒤 &= 연산자를 사용하여 특정 비트를 끈다. 2의 비트를 뒤집으면 1111 1101 이 되어 AND연산 하면 2번째 비트만 0이된다. AND연산을 하면 원하는 비트가 켜져있든, 꺼져있든 항상 끄게 된다.
다음은 비트가 켜져있으면 끄고, 꺼져있으면 키는 방법이다. 다른 말로는 토글(toggle)이라고도 한다. ^= 연산자를 이용한다.
#include <stdio.h>
int main()
{
unsigned char flag = 7; // 7: 0000 0111
flag ^= 2; // 0000 0010 마스크와 비트 XOR로 일곱 번째 비트를 토글
flag ^= 8; // 0000 1000 마스크와 비트 XOR로 다섯 번째 비트를 토글
printf("%u\n", flag); // 13: 0000 1101
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
if (flag & 8)
printf("0000 1000은 켜져 있음\n");
else
printf("0000 1000은 꺼져 있음\n");
return 0;
}
XOR 연산은 두 비트가 다르면 1, 같으면 0이 되기 때문에 마스크의 플래그의 비트가 1이면 마스크의 비트 1과 같아서 0이되고, 플래그의 비트가 0이면 마스크의 비트 1과 달라서 1이 된다.
24.5 퀴즈
정답은 C 이다.
정답은 a 이다.
정답은 d이다.
정답은 b이다.
정답은 b이다. 끄는것은 c 보기처럼 해야 한다.
24.6 연습문제 : 시프트 연산과 플래그 활용하기
1 << 7은 1000 0000(128) 이다. 이걸 4로 만드려면 첫번째 비트를 끄고, 여섯번째 비트를 켜야 한다. 근데 위 코드에서 마스크가 시프트로 되어있으므로 1 << 2로 여섯번째 비트를 키고, ~(1 << 7) 연산으로 첫번째 비트를 끈다.
flag2는 0000 0000으로 만들어야 하는데 1 << 3은 0000 1000(8)이므로 1 << 3을 마스크로 하면 플래그와 마스크가 같기 때문에 모든 비트가 0이된다. 따라서 정답은 1 << 2, ~(1 << 7), 1 << 3 이다.
24.7 심사문제 : 시프트 연산과 플래그 활용하기
정답은 다음 코드와 같다.
flag |= num1 << 3;
flag &= ~(num2 >> 2);
flag ^= 1 << 7;
Unit 25. 연산자 우선순위 알아보기
C언어도 수학처럼 곱셈/나눗셈이 덧셈/뺄셈보다 우선순위가 높다. 또한 다양한 다른 연산자들의 우선순위는 다음과 같다.
논리연산자의 우선순위는 높은 순서로 !, &&, ||이다. 따라서 괄호 먼저 계산하면 false && !false || false 가 되고 이후 !를 계산하면 false && true || false 가 되고, &&를 계산하면 false || false가 되서 최종 결과는 false로 0이 출력된다.
다음과 같이 비교연산자에도 우선순위가 있다.
#include <stdio.h>
int main()
{
int num1;
num1 = 5 == 5 < 10;
printf("%d\n", num1);
return 0;
}
비교 연산자는 == 보다 <가 우선순위가 높다. 따라서 5 < 10이 참이므로 1이 나오고 5 == 1은 거짓이므로 num1에는 0이 저장된다.
#include <stdio.h>
int main()
{
int num1 = 1;
int num2 = 2;
int num3;
num3 = num1 << 2 + num2 << 1;
printf("%d\n", num3);
return 0;
}
산술 연산자가 시프트 연산자보다 우선 순위가 높기 때문에 2 + num2 가 먼저 계산 된 후 num1 << 4 << 1을 계산하여 32가 나온다.
num3 = (num1 << 2) + (num2 << 1);
위 코드와 같이 시프트 연산자를 괄호로 묶으면 먼저 계산되서 num3에는 8이 저장된다.
25.5 퀴즈
정답은 c이다.
정답은 b이다.
정답은 true이다.
정답은 a이다.
25.6 연습문제 : 괄호 사용하기
주어진 순서대로 괄호로 묶으면 정답은 2*((1<<num1)+(2>>num2)) 이다.
25.7 심사문제 : 괄호 사용하기
정답은 다음 코드와 같다.
((num1 + num2) * 10) - num3
Unit 26. switch 분기문으로 다양한 조건 처리하기
switch 분기문은 조건이 많아도 손쉽게 처리할 수 있다. switch문의 형태는 다음과 같다.
switch 분기문은 항상 case와 함께 사용한다. 변수의 값이 case에 지정한 값과 일치하면 해당 코드를 실행하고, case에 일치하는 것이 하나도 없으면 default의 코드를 실행한다. switch 분기문은 형식이 균일하며 처리해야 할 조건이 많을 때 사용한다.
파이썬에서는 switch 분기문이 없다.
26.1 사용자가 입력한 값에 따라 문자열 출력하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
printf("1입니다.\n");
break;
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
1을 입력하면 case1 에 해당하는 코드가 실행되고, case에 없는 숫자를 입력하면 default에 해당하는 코드가 실행된다.
switch의 괄호에는 값을 판단할 변수를 지정한다. case 다음에는 반드시 값(리터럴)이 와야 하며 변수나 조건식은 올 수 없다. case에 값을 지정하면 :(클론)을 붙이고, 다음줄 부터 실행할 코드를 입력한다. 코드의 마지막에는 break를 입력해야 한다. default에는 아무 case에도 해당하지 않을 때 실행할 코드를 입력한다.
조건식이 바뀌지 않고, 값만 바뀔 때는 switch문이 적합하며, 값과 조건식 모두 바뀔 때는 else if가 적합하다.
26.2 case에서 break를 사용하지 않을 때의 동작 알아보기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
printf("1입니다.\n");
case 2:
printf("2입니다.\n");
default:
printf("default\n");
}
return 0;
}
위 코드와 같이 break가 없으면 1을 입력했을 때 case 1:에 해당하는 코드만 실행하는 것이 아니라 case 2:, default: 에 해당하는 코드 모두 실행되었다. case를 작성할 때는 break로 중단해야 해당 case만 실행된다. break로 중단하지 않으면 그 다음에 있는 case, default의 코드가 모두 실행된다.
26.3 case에서 break 생략 응용하기
case에서 break를 생략하는 것은 버그같지만 실제로도 의도적으로 많이 사용하는 방식이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
case 2:
printf("1 또는 2입니다.\n");
break;
case 3:
case 4:
printf("3 또는 4입니다.\n");
break;
default:
printf("default\n");
}
return 0;
}
위 코드와 같이 case 1: 과 case 2:를 연달아 지정하면 num1의 값이 1일때와 2일 때 모두 코드가 실행된다. num1의 값이 3또는 4일때도 마찬가지다. case에서 break를 생략하면 여러가지 값으로 같은 코드를 실행할 수 있다.
이 코드는 다음과 같은 if 조건문을 표현할 수 있다.
if (num1 == 1 || num1 == 2)
printf("1 또는 2입니다.\n");
else if (num1 == 3 || num1 == 4)
printf("3 또는 4입니다.\n");
else
printf("default\n");
if조건문은 일일히 조건식을 나열해줘야 하므로 처리해야할 숫자가 많아지면 번거로워지기 때문에 이런 경우에는 case에서 break를 생략하는 것이 유리하다.
26.4 case 안에서 변수 선언하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
int num2 = num1;
printf("%d입니다.\n", num2);
break;
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
위와 같은 코드는 컴파일러에 따라 에러를 발생할 수 도 있고, 발생하지 않을 수도 있다. case부분을 중괄호로 묶지 않았기 때문이다.
case안에서 변수를 선언하려면 다음과 같이 해당 case를 중괄호로 묶어야 한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
{
int num2 = num1;
printf("%d입니다.\n", num2);
break;
}
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
변수를 선언한 case를 중괄호로 묶어주면 컴파일 에러가 발생하지 않고, 변수를 선언할 수 있다. 중괄호 안에 사용한 변수는 case 1:안에서만 사용할 수 없고 case 2:나 switch바깥에서는 사용할 수 없다.
26.5 switch에서 판별할 수 있는 자료형 알아보기
switch에서 판별할 변수는 정수 자료형만 사용할 수 있고, 실수 자료형은 사용할 수 없다. 문자 자료형은 정수 자료형이므로 switch에서 사용할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char c1;
scanf("%c", &c1);
switch (c1)
{
case 'a':
printf("a입니다.\n");
break;
case 'b':
printf("b입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
작은 따옴표를 이용하여 문자 자료형을 사용할 수 있다. 그러나 case "hello": 처럼 문자열은 사용할 수 없다.
26.6 퀴즈
정답은 e이다. case 뒤에는 클론이 붙어야 한다.
정답은 e이다.
정답은 a,e이다.
26.7 연습문제 : switch 분기문 사용하기
비트 시프트 연산을 사용하므로 정답은 1,2,3 이다.
26.8 심사문제 : 음료수 자판기 만들기
정답은 다음 코드와 같다.
case 'f':
printf("환타\n");
break;
case 'c':
printf("콜라\n");
break;
case 'p':
printf("포카리스웨트\n");
break;
default:
printf("판매하지 않는 메뉴\n");
break;
Unit 27. for 반복문으로 Hello, world! 100번 출력하기
대부분의 프로그래밍 언어는 반복되는 작업을 간단하게 처리하기 위해 반복문이라는 기능을 제공한다. 반복문은 반복 횟수, 반복 및 정지 조건을 자유 조재로 제어할 수 있다.
C의 for 반복문은 다음과 같이 괄호 안에 초기식, 조건식, 변화식을 지정하며 이 부분을 루프 선언문 이라고 부른다. 그리고 중괄호 안에 반복할 코드를 작성하는데 이 부분을 루프 본체라 부른다.
for (초기식; 조건식; 변화식)
{
반복할 코드
}
초기식 부터 시작하여 조건식을 판별하여 조건식이 참이면 반복할 코드를 실행하고, 변화식을 수행한다. 다시 조건식을 검사하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
조건식 -> 루프본체 -> 변화식 -> 조건식으로 순환하는 것을 루프라 한다.
파이썬에서는 for문에 in 키워드를 사용하여 지정한 숫자만큼 반복하거나, range 함수를 이용하여 지정한 범위만큼 반복한다. 초기식, 조건식, 변화식이 따로 정해져 있지 않다.
27.1 for 반복문 사용하기
다음과 같이 for 반복문으로 Hello, world!를 100번 출력할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 100; i++)
{
printf("Hello, world!\n");
}
return 0;
}
초기식은 int i = 0;으로 반복에 사용할 변수를 선언하고 0으로 초기화 했다. 조건식은 i < 100; 으로 i가 100보다 작을 때 까지만 반복하겠다는 의미이다. 변화식은 i++로 반복 할 때 마다 i를 1씩 증가시키겠다는 의미이다.
반복문이 처음 시작하면 i에 0이 들어가고, i 가 100보다 작은지 검사하여 100보다 작으면 중괄호 안의 코드를 실행하고 i를 1 증가시킨다. i가 100이 되면 조건식이 거짓이 되므로 반복문을 끝낸다.
초기식에서 변수를 선언하는 것은 C99방식이다. 이전의 방식은 초기식에서 선언할 수 없고 밖에서 초기식에 사용할 변수를 선언해야 한다. C99방식에서 초기식에 선언한 변수는 for문 안에서만 사용할 수 있고, 옛날 방식은 for문 밖에서 선언했기 때문에 for문 안과 밖에서 모두 사용할 수 있다.
27.2 초기값의 변화 알아보기
#include <stdio.h>
int main()
{
int i;
for (i = 0; i < 10; i++)
{
printf("Hello, world!\n");
}
printf("%d\n", i);
return 0;
}
i가 0부터 1씩 증가하여 10이 되면 조건이 거짓이 되어 반복문이 종료되기 때문에 반복문이 끝난 뒤 변수 i의 값을 보면 10이 나온다.
27.3 초깃값을 1부터 시작하기
프로그래밍에서 반복문의 초기값은 보통 0부터 시작하지만 원하는 반복 횟수를 채울 수 있다면 어떤 초기값을 사용해도 상관 없다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
위 코드에서 초기식이 1부터 시작하지만 i <= 100이므로 100보다 작거나 같을때까지 반복하여 반복 횟수는 100번이다. i가 101이 되면 조건식이 거짓이 되어 반복문을 끝낸다.
27.4 초깃값을 감소시키기
다음과 같이 초깃값을 크게 주고, 변수를 감소시키면서 반복할 수 도 있다.
#include <stdio.h>
int main()
{
for (int i = 100; i > 0; i--)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
초깃값은 100이고, 변화식에서 i--로 i를 1씩 감소시켰다. 조건식이 0이므로 i가 0이 되면 반복문이 끝나서 반복 횟수는 총 100번이다.
27.5 for 반복문과 세미클론
for 반복문도 if문과 마찬가지로 루프 선언문 뒤에 세미클론을 붙이면 안된다. 세미클론을 붙이면 for문과 아래 코드는 관계 없는 코드가 되어 반복을 하지 않는다.
27.6 for 반복문에서 중괄호 생략하기
다음과 같이 반복할 코드가 한 줄이면 중괄호를 생략할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 100; i++)
printf("Hello, world!\n");
return 0;
}
for 문에서 반복할 코드가 두 줄 이상일 때 중괄호를 생략하면 첫번째 줄만 반복하게 되므로 반복할 코드가 두 줄 이상이면 반드시 중괄호를 사용해야 한다.
27.7 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = 0; i < count; i++)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
입력값을 받아서 count에 저장하고, i가 count보다 작을때까지 반복했다. 3을 입력했으므로 Hello world!는 3번 출력된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = count; i > 0; i--)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
위 코드와 같이 입력값을 초기값으로 사용할 수도 있다.
또한 다음과 같이 i를 따로 선언하지 않고 입력받은 값을 그대로 사용할 수 도 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (; count > 0; count--)
{
printf("Hello, world! %d\n", count);
}
return 0;
}
for 반복문에 사용할 변수와 초기값이 준비되어 있으면 초깃값 부분은 생략할 수 있다. 그리고 두 조건식과 변화식을 입력받은 변수를 기준으로 만들면 된다.
27.8 for 반복문에서 변수 두 개 사용하기
#include <stdio.h>
int main()
{
for (int i = 0, j = 0; i < 10; i++, j += 2)
{
printf("i: %d, j: %d\n", i, j);
}
return 0;
}
위 코드는 for 초기식에서 변수를 두 개 선언하고, 변화식에서 i를 1씩 증가하고, j는 2씩 증가시켜 10번 반복 하도록 하였다. 변화식에서는 ++, -- 뿐 아니라 +=, -=, *=, /=등의 연산자도 사용할 수 있다.
27.9 for 반복문으로 무한 루프 만들기
#include <stdio.h>
int main()
{
for (;;)
{
printf("Hello, world!\n");
}
return 0;
}
for문에서 초기식, 조건식, 변화식을 모두 생략하면 무한루프 이다. 무한루프는 반복문이 종료되지 않기 때문에 ctrl+c를 눌러 강제로 종료시켜야 한다.
27.10 퀴즈
정답은 c,d 이다.
정답은 c, e이다.
정답은 b이다. for문 뒤에는 세미클론이 오면 안된다.
정답은 for(;;)이다.
27.11 연습문제 : for 반복문에서 변수 두 개 사용하기
정답은 inti=2,j=5;i<=32;i*=2,j-- 이다.
27.12 심사문제 : 알파벳 순서로 출력하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
char c1;
scanf("%c", &c1);
for(int i = c1; i <= 122; i++)
{
printf("%c", i);
}
printf("\n");
return 0;
}
z는 아스키코드로 122이므로 i가 122가 될때까지 1씩 더하며 반복하며 i 값을 문자로 출력하였다.
Unit 28. while 반복문으로 Hello, world! 100번 출력하기
while 반복문은 괄호 안에 조건식만 들어가고, 초기식은 반복문 바깥에 있다. 중괄호 안에는 반복할 코드와 변화식이 들어간다.
초기식
while (조건식)
{
반복할 코드
변화식
}
초기식부터 시작하여 조건식을 판별한다. 조건식이 참이면 반복할 코드와 변화식을 수행한다. 그리고 다시 조건식을 판별하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
파이썬에서 while 반복문은 조건식 뒤에 클론(:)을 붙이고, 반복할 코드와 변화식은 들여쓰기로 구분한다.
28.1 while 반복문 사용하기
#include <stdio.h>
int main()
{
int i = 0;
while (i < 100)
{
printf("Hello, world!\n");
i++;
}
return 0;
}
반복문애 사용할 변수 i를 선언하고, 0으로 초기화 한다. while에는 조건식만 지정하고, 변화식은 중괄호 안에 지정한다. 중괄호 안에 변화식이 없다면 반복문이 끝나지 않는 무한루프 현상이 발생한다.
위 코드는 조건식이 i < 100 이기 때문에 i가 100이 되면 반복문이 종료되고, 변화식으로 i++를 사용했기 때문에 반복할 때 마다 i의 값이 1 씩 증가하여 0부터 99까지 100번 반복하게 된다.
28.2 초깃값을 1부터 시작하기
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 100)
{
printf("Hello, world!\n");
i++;
}
return 0;
}
위 코드는 i를 1부터 시작했기 때문에 조건식을 i <= 100으로 지정하여 i가 101이 되면 반복문이 종료된다.
28.3 초깃값을 감소시키기
다음과 같이 초깃값을 크게 주고, 변수를 감소시키면서 반복문을 진행할 수 도 있다.
#include <stdio.h>
int main()
{
int i = 100;
while (i > 0)
{
printf("Hello, world! %d\n", i);
i--;
}
return 0;
}
i가 100이고, 변화식에서 i--를 지정하여 반복을 할 때마다 i의 값을 1씩 감소시킨다. i > 0 이 조건식이므로 100부터 1까지 100번 반복하고, i가 0이되면 반복문이 종료된다.
28.4 while 반복문과 세미클론
while문도 끝에 세미클론을 붙이면 안된다. while 끝에 세미클론을 붙이면 while문은 중괄호 안의 반복할 코드와 변화식과는 무관한 코드가 되기 때문에 중괄호 안에 있는 변화식도 실행할 수 없어서 무한루프 현상이 발생한다. 그러나 중괄호 안의 코드는 아직 실행 전이기에 출력되는값은 아무것도 없다.
28.5 입력한 횟수대로 반복하기
다음 코드와 같이 입력한 값을 반복문에 사용할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 0;
while (i < count)
{
printf("Hello, world! %d\n", i);
i++;
}
return 0;
}
scanf 함수로 값을 입력 받아서 count변수에 저장하여 count에 들어간 값만큼 반복한다. 위에서는 3을 입력했기 때문에 3번 반복한다.
다음은 초깃값을 입력받은 것이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
while (count > 0)
{
printf("Hello, world! %d\n", count);
count--;
}
return 0;
}
위 코드에서는 i를 선언하지 않고 count를 바로 사용하여 변화식은 count--로 반복할 때 마다 count의 값을 1씩 감소시킨다. count가 0이 되면 반복문이 끝난다.
28.6 반복 횟수가 정해지지 않은 경우
while 반복문은 반복 횟수가 정해지지 않았을 때 논리 조건에 따라 반복 여부를 결정할 때 주로 사용된다.
다음은 while반복문 안에서 무작위로 정수를 생성한 뒤 3이 나오면 반복을 끝낸다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
int i = 0;
while (i != 3)
{
i = rand() % 10;
printf("%d\n", i);
}
return 0;
}
stdlib.h는 srand, rand 함수를 사용할 수 있는 헤더파일이고, time.h는 time함수를 사용할 수 있는 헤더파일이다. 무작위로 정수를 생성하려면 srand, rand, time 함수가 필요하다.
srand함수는 난수를 발생시킬 초기값 시드(seed)를 설정한다. 보통은 현재 시간값을 사용한다. rand함수는 난수를 발생시킨다. time 함수는 정수 형태로 된 현재 시간값을 반환한다.
다음 코드로 현재 시간값을 시드로 설정한다.
srand(time(NULL));
while 반복문에서 i != 3으로 i가 3이 아니면 계속 반복한다. rand() % 10은 rand의 반환값이 크기 때문에 10으로 나눠서 나머지로 1 ~ 9까지만 사용한다. 1 ~ 9까지의 숫자가 랜덤으로 출력되다가 3이 출력되면 반복문이 종료된다. while 반복문은 반복 횟수가 정해져 있지 않을 때 유용하다.
28.7 while 반복문으로 무한루프 만들기
#include <stdio.h>
int main()
{
while (1)
{
printf("Hello, world!\n");
}
return 0;
}
while 조건식에 1을 지정하면 무한루프가 만들어 진다. 조건식 자체가 없으므로 변화식도 필요 없다. 무한루프를 종료시키려면 ctrl + c를 누르면 된다.
다음과 같이 1 대신 불 값인 true를 넣기도 한다.
#include <stdio.h>
#include <stdbool.h>
int main()
{
while (true)
{
printf("Hello, world!\n");
}
return 0;
}
1 대신 true를 사용하는 것은 코드의 의도를 좀 더 명확하게 하기 위함이다.
28.8 while 반복문에서 중괄호 생략하기
#include <stdio.h>
int main()
{
while (1)
printf("Hello, world!\n");
return 0;
}
while 반복문에서 수행할 코드가 한줄이라면 중괄호를 생략할 수 있지만, while문은 보통 본체에 변화식이 함께 들어가므로 중괄호를 생략할 일은 많지 않다.
28.9 퀴즈
정답은 a, d, e이다.
정답은 b이다. i < 10 이여야 한다.
정답은 while(1)이다.
28.10 연습문제 : while 반복문 사용하기
i 변수는 8비트 이므로 i는 1에서 1칸씩 계속 왼쪽으로 시프트하다보면 1000 0000은 128 이고, 0000 0000이 되므로 i는 0일때 반복문을 종료시키면 된다. 답은 i != 0 이다.