-Summary-
연산자 우선 순위, switch for while do while 반복문, break continue, 계단식 별 출력, goto, FizzBuzz
02-19 (Unit 24.1 ~ Unit 27.12)
-2의 거듭제곱-
시프트 연산자를 사용한다면 2의 거듭 제곱을 빠르게 구할 수 있다.(아마 컴퓨터 입장에서 연산하기 쉬운 2진수로 연산해서 그런 것 같다.) **으로 제곱 연산을 할 수 있는 파이썬과 달리, math.h를 include하지 않는 이상 c언어는 거듭제곱 연산자를 제공하지 않으므로, 이 시프트 연산자를 이용해서 거듭제곱을 하면 된다.
위 코드처럼 1을 왼쪽으로 지수만큼 시프트 연산을 수행하면 해당 지수만큼 2가 거듭제곱되서 출력된 것을 볼 수 있다.
2^2 = 1 << 2
2^8 = 1 << 8
-시프트 연산에서 자릿수가 넘어갔을 경우-
앞서 시프트를 실습하면서 정리했었는데 시프트 연산에서 자릿수가 넘어갈 경우, 자릿수가 넘어간 값은 사라지게 된다.
예를 들어 2 >> 2 같은 경우 0000 0010가
1. 0000 0001(0)
2. 0000 0000(10)
이렇게 2번째에 자릿수를 넘어가게 되어 사라진다.
c언어에서 직접 2 >> 2를 해본다면 결과가 0으로 같은 것을 볼 수 있다.
<<의 경우도 마찬가지로 0000 0000 이 범위를 넘긴다면 사라진다.
즉 첫째 자리나 마지막 자리를 넘어서는 비트는 사라지는 것이다.
-MSB, LSB-
비트에서 첫 번째 비트를 Most Significant Bit의 약자인 MSB라 부르고, 마지막 비트는 Least Siginificant Bit의 약자인 LSB라고 부른다.
-signed의 비트 연산-
지금까지는 unsigned 형 값으로만 비트 연산을 했었다. unsigned가 아닌 signed형 값으로 비트 연산을 할 때는 부호 비트를 조심해야 된다고 한다.(실제로 비트 파트를 들어갔을 때, signed로 실습했다가, 값이 이상하게 나와서 혼란을 조금 겪었었다.)
마땅한 실습 값을 못찾아서 코딩도장의 예제로 했다. 보면 unsigned char a는 131이고 char b는 -125인데 똑같이 >>5 연산을 해주면 4, -4로 연산 결과가 나온 것을 볼 수 있다. 분명 절대 값은 달랐는데, 결과를 보면 절대 값이 같아진다.
그 이유는 부호 있는 자료형의 MSB(첫 번째 비트)는 부호로 사용되기 때문이다. 1은 음수, 0은 양수를 의미한다.
위가 131과 -125 >> 5 연산 과정이다. 처음에는 2진수의 음수 표현을 잘 몰라서 30분 가량 구글링을 했다. 이해가 안되는 부분이 1000 0011이 도대체 왜 -125인지를 몰랐지만 2의 보수를 이해하니 알게 됐다. 125의 2진수는 0111 1101다. -125는 125의 2의 보수이므로 먼저 0111 1101을 not 연산한 후(1000 0010) 그 상태에서 1을 더해주면(1000 0011) 그게 바로 -125인 것이다.
어쨌든 131을 >> 5하면 비트가 오른쪽으로 이동하면서 MSB부터 0으로 채워지는 반면, 음수의 경우 MSB가 1이므로 0이 채워지는 대신 1이 채워져 그 1이 계속해서 오른쪽으로 이동하는 것이다. 즉 5번 >> 연산을 끝내면 1111 1100이 된다.
이 1111 1100또한 2의 보수를 해본다면 0000 0100 즉 4다. 즉 1111 1100은 -4인 것이다.
부호가 0, 즉 양수인 상태에서 >> 연산을 한다면 부호 없는 수와 똑같이 MSB가 0으로 채워지므로 부호 없는 수의 >>와 결과가 같다.
<<의 경우도 부호 있는 수와 부호 없는 수의 차이가 있다. 113을 왼쪽으로 2번 시프트 할 경우 0111 0001이 1100 0100으로 변한다. 부호 없는 수는 196이 나오는데, 부호 없는 수인 경우 MSB가 부호로 취급되므로 음수가 된다. 즉 1100 0100의 2의 보수는 0011 1100 이므로 60 즉 - 60이다.
결론적으로 부호 있는 수를 시프트 연산한다면 MSB가 부호 값을 가지므로 부호 없는 수의 시프트 연산 결과와 같지 않을 수 있다.
-flag(플래그)-
플래그는 깃발에서 유래된 용어로 깃발을 올리면 on, 깃발을 내리면 off를 의미한다. c언어에서는 1이면 on, 0이면 off다. 예를들어 0000 0011은 7번째와 8번째 비트가 on 상태인 것이다.
8비트(1바이트) 크기의 자료형은 1, 0을 8개 저장할 수 있으므로 8가지 상태를 저장할 수 있다. 4바이트 크기인 int 같은 경우는 4 * 8 = 32, 즉 32개의 상태를 저장할 수 있는 것이다.
-|=
|=는 비트 단위 or 연산을 해주는 비트 연산자다. 이를 이용해서 특정 비트의 값을 켜줄 수 있는데, 예를 들면 0000 0000 이렇게 8비트가 존재한다면 0000 0000 |= 1을 해주면 0000 0000 or 0000 0001이므로 0000 0001이 된다. LSB가 켜진 것이다. 7번째 비트를 켜고 싶다면 해당 값인 2로 |= 해주면 0000 0000 or 0000 0010이 되므로 0000 0010, 즉 일곱 번째 비트가 켜진 상태가 된다. 이런식으로 |=을 이용해서 특정 비트의 값을 켜주는 것이다.
-&
&은 비트 단위 and 연산을 해주는데, 이를 이용하면 특정 비트가 켜져 있는지 검사할 수 있다.
예를들어 0000 0001이 있을 때 0000 0001 & 1을 해주면 0000 0001 and 0000 0001, 즉 1이 나오므로 LSB는 on 상태인 것이다. 0000 0100 같은 경우는 0000 0100 & 4 = 0000 0100 and 0000 0100, 즉 1이 나오므로 6번째 비트는 켜져 있는 것이다.
위는 |=과 &을 이용해서 flag를 키고, 해당 비트가 켜졌는지 검사하는 코드다. 먼저 unsigned char형 변수 flag에 0000 0000을 넣고 |= 1, |= 8을 해주면 0000 0101, 즉 LSB와 5번째 비트가 on이 된다. 그리고 if 조건식 flag & 1, flag & 2, flag & 3으로 LSB, 7번째 bit, 5번째 bit가 켜져있는지 검사하면 LSB와 5번째 bit가 켜져있다는 것을 확인할 수 있다.
-&= ~
&= ~를 사용한다면 해당 플래그의 비트를 off로 바꿀 수 있다. 예를 들어 0000 0101 이렇게 LSB와 6번째 비트가 켜져 있을 때, 0000 0101 &= ~1을 해주면 0000 0101 and 1111 1110이 되므로 0000 0100이 결과로 나온다. LSB의 비트가 꺼진 것이다. 6번째 비트도 0000 0100 &= ~4를 해주면 0000 0100 and 1111 1011이 되므로 0000 0000이 결과가 나온다. 6번째 비트가 꺼진 것이다. 이런식으로 해당 플래그의 비트를 off로 바꿀 수 있다.
<비트> &= ~<해당 비트>
|=으로 LSB, 5번째 비트를 켜줬을 때 값이 9가 나오는데, 이 상태에서 &= ~1, &= ~8 연산을 해주니 LSB와 5번째 비트가 off로 변해서 값이 0으로 다시 돌아간 것을 볼 수 있다.
-^=
^=은 비트 단위 xor 연산을 해주는 비트 연산자다. 이를 이용하면 해당 비트가 on이라면 off로, off라면 on으로 바꿔줄 수 있다. 예를 들어 0000 0100이 있을 때 0000 0100 ^= 4를 해준다면 0000 0100 xor 0000 0100이 되므로 0000 0000이 된다. 6번째 비트가 켜져 있으므로 꺼지게 한 것이다. 이 상태에서 다시 ^= 4를 해준다면 0000 0000 xor 0000 0100이 되므로 0000 0100이 된다. 6번째 비트가 꺼져 있으므로 킨 것이다.
마치 off -> on, on -> off 기능을 가진 토글 같다해서, 토글이라고도 부른다.
^=으로 on -> off, off -> on으로 바꾸는 실습을 해봤다. 0000 0000인 flag에 |= 1을 써줘서 LSB가 on이 됐다. 이 상태에서 ^= 1을 써주니 LSB가 다시 off가 된 것을 볼 수 있고, 이 상태에서 이번에는 ^= 8, 즉 5번째 비트를 xor해주니 off 였던 5번째 비트가 on이 된 것을 볼 수 있다.
시프트 연산자로 8192를 만드는 연산식을 찾는 것이다.
암산으로는 계산할 수 없으므로 직접 코드를 짜서 확인했다. 확인 결과 1 << 13 연산식이 8192가 되는 것을 볼 수 있다.
답 : c
0011 1000을 << 3 연산했을 때의 결과는
1. 0111 0000
2. 1110 0000
3. 1100 0000
1100 0000이다.
답 : a
1000 0000 >> 2의 결과는
1. 0100 0000
2. 0010 0000
0010 0000인 것 같지만 unsigned가 아닌 그냥 char이므로 MSB는 부호를 의미한다. 따라서
1. 1100 0000
2. 1110 0000
이렇게 부호가 밀려와서 1110 0000가 된다.
이 역시 unsinged가 아닌 signed char에 저장됐다. 0011 000 >> 3의 결과는
1. 0001 1000
2. 0000 1100
3. 0000 0110
0000 0110이다.(부호가 0이므로 0이 계속 남는 값을 채워준다.)
답 : b
b : 특정 비트 끄기는 flag &= ~mask다.
빈 칸을 채워 4 0이 출력되게 만드는 것이 목적이다. 4 0은 각각 flag1, flag2의 값이므로 시프트와 flag 전환을 적절히 사용하면 될 것 같다.
#include <stdio.h>
int main()
{
unsigned char flag1 = 1 << 7; //1000 0000(128)
unsigned char flag2 = 1 << 3; //0000 1000(8)
flag1 |= 1 << 2;
flag1 &= ~(1 << 7);
flag2 ^= 1 << 3;
printf("%u %u\n", flag1, flag2);
return 0;
}
초기값으로 flag1 = 1000 0000, flag2 = 0000 1000이 들어간다. 출력해야할 값은 4, 0 즉 0000 0100, 0000 0000이다. flag1 |=은 flag를 키는 연산이므로 4가 되기 위해서 일단 6번째의 비트가 on이어야 한다. 그래서 1 << 2(4)로 |= 해줬고, 첫 번째 플래그를 끄기 위해서 flag1 &= ~(1 << 7), 즉 flag &= ~1000 0000으로 첫 번째 비트를 꺼줬다. flag2는 0000 0000이 되야하므로 5번째의 비트를 ^=로 1 << 3(8)을 사용해 off로 만들었다.
두 정수의 입력을 받고 아래와 같은
- num1의 비트를 왼쪽으로 3번 이동한 값으로 flag의 비트 켜기
- num2의 비트를 오른쪽으로 2번 이동한 값으로 flag의 비트 끄기
- flag의 첫 번째 비트 토글하기
flag 연산을 해서 결과를 출력시키는 것이 목적이다.
#define __USE_MINGW_ANSI_STDIO 1 // Dev-C++(MinGW)에서 %hhu를 사용하기 위한 설정
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
unsigned char flag = 16;
unsigned char num1, num2;
scanf("%hhu %hhu", &num1, &num2);
flag |= num1 << 3;
flag &= ~(num2 >> 2);
flag ^= 128;
printf("%u\n", flag);
return 0;
}
%hhu 서식 지정자가 낯설긴 했지만 그냥 무시하고 풀었다.
num1의 비트를 왼쪽으로 3번 이동한 값으로 flag의 비트를 켜기 위해 flag |= num1 << 3을 썻다.
num2의 비트를 오른쪽으로 2번 이동한 값으로 flag의 비트 끄기 위해 flag &= ~(num2 >> 2)를 썻다.
flag의 첫 번째 비트를 토글하기 위해 flag ^= 128 (1000 0000)을 썻다.
-연산자 우선순위-
위 사진은 코딩도장 Unit 25.0에 정리돼 있는 c언어 우선순위 표다. 보면 예전부터 당연하게 써오던 ()의 우선순위가 1순위임을 알 수 있고, +- 연산보다 * / %의 우선순위가 더 높은 것을 알 수 있다.
이를 외울 필요는 없고 그저 우선순위를 높여야할 연산식에 ()를 감싸주면 된다는 점만 알고 있음 될 것 같다.
-()
2 * 3 + 4의 연산식은 당연히 2 * 3이 먼저 연산된 결과 값 + 4가 된다. 하지만 괄호로 2 * (3+4)를 써줄 경우 괄호 식이 먼저 계산되서 2 * 7이 된다.
지금까지 당연하게 써왔지만, 이는 사실 위 표에 정리된 c언어의 우선순위에 따라서 먼저 연산됐던 것이다.
-++x, --x 등...
표를 보면 대부분 결합 방향이 -> 인데 ++x, --x 등은 방향이 <-인 것을 볼 수 있다. 이는 무엇을 의미하냐면 예를들어
int a = 3;
int b = ++a;라는 식이 있으면 += 1된 a의 값이 b에 들어간다.
반면
int a = 3;
int b = a++; 라는 식이 있으면 a의 값이 b에 들어간 후 a의 값이 ++되는데 이게 결합 방향에 따른 차이다.
<-는 왼쪽부터 계산, 즉 왼쪽의 ++a를 1 증가시켜준 후 int b = a 연산을 수행해 주는 것이다.
반면 a++은 ->이므로 오른쪽부터 int b = a 연산을 수행한 후 ++을 해주는 것이다.
위는 코딩 도장의 연산 순서 문제를 조금 변형시켜서 실습해본 것이다. 처음 풀었을 때는 나조차도 잘못된 답을 골랐다.
연산 순서는 다음과 같다.
1. 괄호안의 10 - 5 계산 : 1 + 2 * 5 / ++a
2. ++a 계산 : 1 + 2 * 5 / 2
3. 2 * 5 계산 : 1 + 10 / 2
4. 10 / 2 계산 : 1 + 5
5. 1 + 5 계산 : 6
답 : 6
++a의 연산 방향이 <-이라 10 -5 계산 후 ++a 연산 후 (10 - 5) / ++a를 먼저 계산할 줄 알았지만 생각해보니 *, /의 순서는 ->이라 앞의 2 * (10-5)부터 계산되는 것이었다. 2 * (10-5)가 계산된 후 그 다음 순서인 /가 연산되고 +가 연산되서 6이 나온 것이다.
이 식 역시 결과로 1이 나올 것 같지만, ==보다 >의 연산이 좀더 빠르므로 1 > 2의 결과인 거짓이 나온다.
위 시프트 역시 +보다 우선순위가 낮으므로 ()로 시프트 식을 감싸 것과 안감쌋을 때의 결과는 다르게 나온다.
즉 정리하자면 우선순위에 따라 연산결과가 잘못되거나, 조건식의 참, 거짓 여부가 의도와는 다르게 나올 수 있으므로 ()로 감싸주는 것이 가독성면에서도 좋고, 우선순위로 인한 잘못된 연산 예방 차원에서 좋으니 ()를 써주자.
c언어 연산자 우선순위 표에 따르면 |가 가장 우선순위가 낮다.
답 : c
먼저 괄호안의 식 5 % 2가 연산되서 num2 = 32 >> 2 * ++num1 + 1이 되고 그 다음에는 ++num1이 연산되서 32 >> 2 * 2 + 1이 된다. 그 다음에는 *가 연산되서 32 >> 4 + 1이 된다. 그 다음에는 +가 연산되서 32 >> 5가 된다.
32 >> 5를 코드를 짜 계산하니 1이 나온다.
답 : b
연산 순서에 따르면 () -> ! -> && -> || 순으로 계산된다. 따라서 (false || false)가 연산되서 bool b1 = !false && true || false가 되고, 그 다음에는 !가 연산되서 true && true || false가 된다. 그 다음에는 &&이 연산되서 true || false가 되서 답은 true가 된다.
답 : true
num2 = num1 << num1 < num2 + 1;에서 우선순위는 + -> << -> < 순이다. 먼저 +가 연산되서 num2 = num1 << num1 < 5가 된다. 그 다음 <<가 연산되서 num2 = 8 < 5가 되는데 8은 5보다 크므로 false 값이 0, 즉 num2는 0이 된다.
답 : a
1. 시프트, 2. 덧셈, 3. 곱셈 순으로 연산이 진행되게끔 해서 6을 출력하는 것이 목적이다. 연산 순서를 고려해서 ()를 적절히 활용하면 될 것 같다.
#include <stdio.h>
int main()
{
int num1 = 1;
int num2 = 1;
int num3;
num3 = 2 * ((1 << num1) + (2 >> num2));
printf("%d\n", num3);
return 0;
}
먼저 시프트 연산이 수행되도록 1 << num1, 2 >> num2를 ()로 감싸줬고 그 다음 덧셈을 연산해야하므로 (1 << num1) + (2 >> num2) 전체를 괄호로 또 한번 감싸줬다. 그러면 시프트 연산 수행 -> 덧셈 -> 곱셈 순으로 연산이 진행된다.
세 개의 정수를 입력 받고, 해당 값들을
- num1과 num2를 더하기
- 1번 결과에 10을 곱하기
- 2번 결과에서 num3을 빼기
순으로 연산한 뒤 결과를 출력하는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
int num2;
int num3;
scanf("%d %d %d", &num1, &num2, &num3);
printf("%d\n", (num1 + num2) * 10 - num3);
return 0;
}
1번 순서인 num1 + num2를 ()로 감싸줘서 맨 먼저 연산이 수행되게 했고 그 다음에 * 10 - num3를 써줬다. * 10 -num3 부분은 어쩌피 곱하기가 먼저 연산되므로 따로 괄호로 감싸줄 필요가 없다.
-switch-
switch는 if와 비슷하게 조건에 따른 특정 코드를 실행할 때 사용한다. 지금까지 if문을 사용해서 조건에 따른 분기를 했는데, c언어에는 파이썬에 없는 switch라는 것이 존재한다. if문 같은 경우 참과 거짓, 이 두가지만 처리 가능해서 여러 조건을 처리할 시 else if로 계속해서 조건식을 지정해줬는데, switch 같은 경우 형식이 비슷할 경우, 조건이 많아도 if문에 비해 비교적 간단하게 처리가 가능하다.
switch(<조건에 기준이 될 변수>)
{
case <값1> :
<변수가 해당 case의 값1일 때 실행할 코드>
break;
case <값2> :
<변수가 해당 case의 값2일 때 실행할 코드>
break;
case <값3> :
<변수가 해당 case의 값3일 때 실행할 코드>
break;
case <값4> :
<변수가 해당 case의 값4일 때 실행할 코드>
break;
default :
<위 case에 해당되는게 없을 시 실행할 코드>
break;
}
이런식으로 if문처럼 <변수> == <값> 이렇게 조건을 여러 개 안써줘도 switch를 사용한다면 간단하게 구현이 가능하다.
먼저 switch 뒤의 괄호에 조건에 기준이 될 변수를 넣고 case에 지정한 값과 일치하면 해당 case의 코드를 실행한다. 주의할점이 해당 case의 코드가 끝났을 때는 반드시 break;를 써줘야한다. default는 if문의 else와 같은 역할로 case문에 해당되는게 없을 시 default 코드를 실행한다.
위는 switch문을 실습해본 것이다. 먼저 입력 값을 받을 int형 변수 a를 선언해서 입력 값을 담았다. 그리고 switch (a)를 써줘서 조건의 기준이 될 변수를 a로 정했다. 입력을 2로 했으므로 case 0에 해당되지 않으니 pass, case 1에도 해당되지 않으니 pass, a == 2므로 case 2에 만족한다. 만족하니 case 2의 코드인 printf("2\n");을 실행하고 break;한다.
만약 입력 값을 1을 준다면 1이 출력되고 0을 준다면 0이 출력될 것이다. 아니면 0,1,2에 해당하지 않는 값을 준다면 else의 역할인 default 코드가 실행되어 "default"가 출력될 것이다.
이 switch문을 if문으로 작성하면 다음과 같다.
if(a == 0)
printf("0\n");
else if(a == 1)
printf("1\n");
else if(a == 2)
printf("2\n");
else
printf("default");
if, else if를 조건 하나당 하나씩 써줘야하고 조건식도 일일히 하나하나 다 써줘야한다. 조건식이 바뀌지 않고 값이 바뀔 경우에는 if문 보다는 switch문이 더 적합하다. 물론 조건식이 바뀔 경우에는 if문을 써줘야한다.
위에서 case의 코드가 끝날 때마다 break를 써줘야한다고 했는데, 만약 이 break를 써주지 않는다면 위 사진과 같은 결과가 나온다. 분명 0을 입력했는데 case 0 코드만 실행되는게 아닌, case 1, 2, default까지의 코드가 모두 실행됐다. 위 현상을 fall through라고 부르는데, 이 현상을 막기 위해서는 위에서처럼 break를 case의 코드가 끝날 때 마다 써줘서 switch문을 중단 시켜줘야한다. 여기서 break는 파이썬과 비슷한 기능이다. 파이썬에서 반복문을 탈출할 때 break를 사용한 것처럼, 이 c언어의 switch문에서도 해당 조건의 코드가 실행된 후 탈출 시켜주기 위해서 break를 써주는 것 같다.
default의 경우 switch 실습 코드에도 break를 안적어줬는데, 이는 default 아래에는 더 이상 조건이 존재하지 않기 때문에, break를 안써줘도 실행될 코드가 없기 때문이다.
위 처럼 의도적으로 break를 안써줄 수 있다. if문의 ||과 비슷한데, 위처럼 작성하면 a가 0, 1일 때는 printf("0,1\n")과 break가, a가 2,3일 때는 printf("2,3\n"); break가 실행된다. 이를 if문으로 바꿔쓰면 이렇다.
if(a == 0 || a == 1)
printf("0, 1\n");
else if(a == 2 || a == 3)
printf("2, 3\n");
else
printf("default");
이렇게 ||가 쓰일 때도 switch문으로 처리해주면 간결하게 작성 가능하다. 하지만 case에는 조건식을 적을 수 없으므로 &&은 switch로 구현하기 힘들 것 같다.
또한 switch는 판별할 변수로 정수 자료형만 사용할 수 있고, float, double 같은 실수 자료형은 사용할 수 없다.
위 사진처럼 char 자료형 역시 정수 자료형이므로 문자형으로도 switch문의 판별할 변수에 사용할 수 있다.
-case 코드 내 변수 선언
내가 사용하고 있는 visual studio 버전(2019)에서는 case 코드 내에서 변수 선언이 가능하지만(다만 빨간줄이 그어진다.) 2013 버전 이하부터는 컴파일 에러가 발생한다고 한다.
보면 빨간줄이 그어지지만 컴파일은 잘 되는 것을 볼 수 있다.
컴파일은 잘 되지만 빨간줄이 거슬리므로 올바른 case 내 변수 선언 방법도 알아둬서 나쁠건 없다.
case 내에서 변수 선언을 하려면 그냥 case 코드를 { }로 묶어주면 된다.
위처럼 {}로 case 코드를 묶어주니, 빨간줄 없이 변수 선언이 되는 것을 볼 수 있다. 2013 이하 버전에서는 {}로 묶어준다면 오류없이 컴파일이 될 것이다.
단 이 중괄호 안에서 선언된 함수는, 파이썬에서 배운 것처럼 해당 case의 스택 프레임에 속하므로 이 case문을 벗어나서는 사용이 불가능하다.
e : case 1 뒤에 ;가 아닌 :를 써줘야한다.
switch 문을 통해 c1의 값으로 분기를 가른다. c1에는 'b'가 들어있으므로 case 'b'의 코드가 실행된다. 따라서 printf("b\n");이 실행되는데, 보면 break;가 안써져 있다. 그러면 그 다음 case 'c'의 코드도 실행되는데, case 'c'에도 break가 없으므로 그 다음이 default 코드까지 실행된다. 따라서 출력 결과는
b
c
default
다.
답 : e
switch는 정수형만 판별할 수 있다. 따라서 float, double 형은 판별할 수 없다.
답 : a, e
2를 입력했을 때 "2", 4를 입력했을 때 "4", 8를 입력했을 때 "8"을 출력하는 것이 목적이다. 보면 case의 값을 보면 각각 <<로 값을 정한다.(번거롭게;;) 따라서 1번 칸에는 2, 2번칸은 4, 3번칸은 8이 되도록 << 연산을 이용하면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1 = 0;
scanf("%d", &num1);
switch (num1)
{
case 1 << 1:
printf("2\n");
break;
case 1 << 2:
printf("4\n");
break;
case 1 << 3:
printf("8\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
사실상 1 << <지수>는 2^<지수>, 즉 2의 지수 제곱이다. 따라서 2는 1 제곱이므로 1을, 4는 2 제곱이므로 2, 8을 3 제곱이므로 3을 넣어줬다.
입력으로 f, c, p 문자중 하나가 입력되는데, 입력 값이 f면 환타, c면 콜라, p면 포카리 스웨트를 출력하는 것이 목적이다. 만약 입력 값이 f,c,p에 해당하지 않으면 "판매하지 않는 메뉴" 문구를 출력하면 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char memu;
scanf("%c", &memu);
switch (memu)
{
case 'f':
printf("환타");
break;
case 'c':
printf("콜라");
break;
case 'p':
printf("포카리스웨트");
break;
default:
printf("판매하지 않는 메뉴");
}
return 0;
}
switch문으로 문제를 해결했다. 먼저 case 'f': 를 써줘서 입력 값이 f인 경우 환타가 출력되게 했다.(break는 필수) 그리고 case 'c'를 써줘서 입력 값이 c인 경우 콜라가 출력되게 했고, case 'p'를 써줘서 입력 값이 p인 경우 포카리스웨트가 출력되도록 작성했다. 마지막으로 입력 값이 f, c, p에 해당하지 않는 경우(case 'f', 'c', 'p'에 해당하지 않는 경우) default를 써줘서 "판매하지 않는 메뉴"를 출력하도록 작성했다.
-for-
c언어의 for는 파이썬의 for와 비슷한 기능이다. for는 반복문으로 사용자가 원하는 만큼 반복을 할 수 있도록 해준다.
파이썬은 range라는 기능이 있어서 100번 반복하고 싶다 하면
for i in range(100)을 써주면 되는 반면, c언어는 range라는 키워드가 존재하지 않고, for의 형식은 초기식, 조건식, 변화식으로 이뤄진다.
for(<초기식>; <조건식>; <변화식>)
{
<반복할 코드>
}
먼저 초기식으로 시작하고(초기식은 맨 처음 한 번만 실행한다. 조건식으로 조건에 맞는지 판별, 만약 거짓이라면 반복문을 끝내고 참이라면 반복을 해준다. 여기서 변화식은 반복을 한번 돌 때마다 실행준다.
위는 코딩도장에 나와 있는 for문의 과정이다. 위에서 정리한 것 처럼, 초기식 -> 조건식(거짓이면 탈출) -> 반복 코드 실행 -> 변화식이다.
아래는 for문을 이용해서 printf("A\n"); 코드를 10번 반복한 것이다. 결과를 보면 A가 10번 출력된 것을 볼 수 있다.
for문의 초기식으로 int i = 0을, 조건식으로 i < 10을, 변화식으로 i++을 적어줬는데, 과정은 맨 처음 int형 변수 i를 0으로 초기화하며 선언하고, i가 10보다 작은지 검사한다. 당연히 10보다 작으므로 printf("A\n");을 실행하고 i++, 즉 i에 1을 더해준다. 그 다음 다시 i가 10보다 작은지 검사하고, printf 실행하고 i++을 실행하는 것이 i가 10보다 작지 않을 때까지 반복되는 것이다.
이를 디버깅 표로 적으면
i | i < 10 | 출력
0 | true | A
1 | true | A
2 | true | A
3 | true | A
4 | true | A
5 | true | A
6 | true | A
7 | true | A
8 | true | A
9 | true | A
10 | false
end
이렇게 된다. i < 10이 될 때까지 반복하는 것이다. 파이썬은 단순히 range나 반복가능한 객체를 in 뒤에 써두면 원하는 만큼, 혹은 객체의 길이만큼 반복이 가능하지만, c언어는 좀 수동적으로 직접 몇번 반복할지 조건을 정해줘야한다.
참고로 반복문에서 사용한 i는 for문 스택프레임에서 생성됐으므로, for문 밖에서는 사용이 불가하다.
if문과 마찬가지로 이 for문 반복식 괄호 뒤에 ;를 써주면 안된다.
for (int i = 0; i < 10; i++);
{
printf("A\n");
}
이렇게 작성하면 for문과 printf는 관계가 없어지므로 for문과는 상관없이 printf문이 일반 코드처럼 딱 한번 실행되고 끝난다.
즉
for (int i = 0; i < 10; i++)
{
}
printf("A\n"); 과 같은 코드가 되는 것이다.
또한 if문 처럼 실행 코드가 1줄인 경우 { }를 생략해줄 수 있다.
for (int i = 0; i < 10; i++);
{
printf("A\n");
}의 경우
for (int i = 0; i < 10; i++);
printf("A\n");
근데 개인적으로 나는 for문은 {}를 써주는 편이다. if문은 괜찮은데 for문은 {}가 없으면 뭔가 햇갈리는 느낌이 들기 때문이다.
위 코드에서는 for의 초기식에서 int i = 0으로 int 형 변수 i를 새로 선언했는데, for문 밖에서 선언한 변수를 초기식에다 적어도 된다. 이렇게 하면 당연히 i를 for문 밖에서도 접근할 수 있다.
예)
int i;
for(i = 0; i < 10; i++)
{
<반복할 코드>
}
보통 이 반복문에 사용하는 변수명은 index의 약자인 i로 많이 짓는다.
i를 for문 전에 미리 선언하고 for 반복문이 끝난 후 i를 출력해보았다. i의 값이 9일거라고 생각할 수도 있는데, 위에 디버깅표로 정리했듯이 i가 10이 되고 나서 조건이 거짓이 되므로 10이 된 후 반복문을 끝낸다. 위 코드의 결과를 보면 i는 9가 아닌 10인 것을 볼 수 있다.
for문의 초기, 조건, 변경식도 입맛대로 바꿀 수 있다. 위는 i의 초기값을 1로 설정했고, 조건식을 i <= 10으로 바꿨다.
디버깅 표를 작성해보면
i | i <= 10 | 출력
1 | true | 1
2 | true | 2
3 | true | 3
4 | true | 4
5 | true | 5
6 | true | 6
7 | true | 7
8 | true | 8
9 | true | 9
10 | true | 10
11 | false
end
이렇게 11부터 조건이 거짓이 되므로 0~9까지가 아닌 1~10까지 출력이 된다.
이런식으로 변화식을 i--로 바꾼다면 i의 값이 1씩 줄어들게 할 수 있다. 이럴시 초기식과 조건식은 이에 맞게 바꿔줘야한다. 위는 i = 10, i > 0, i-- 이렇게 초기값은 10, 조건은 i가 0보다 클 때, 그리고 i는 - 1로 작성했다.
디버깅 표를 작성해보면
i | i <= 10 | 출력
10 | true | 10
9 | true | 9
8 | true | 8
7 | true | 7
6 | true | 6
5 | true | 5
4 | true | 4
3 | true | 3
2 | true | 2
1 | true | 1
0 | false
end
이렇게 i의 값이 1씩 감소하면서 i가 0이 됐을 때 조건식이 false가 되어 반복이 끝난다.
-scanf, for
scanf와 for를 응용한다면 입력한 값 만큼 반복하도록 할 수 있다.
코딩도장에서는 for(int i = 0; i < count; i++)로 했지만, 나는 조금 다르게 for(int i = count; i > 0; i--)로 작성했다.
동작원리는 먼저 count 변수에 입력 값이 들어가고 i의 초기값으로 count의 값을 주는데, 이 i가 0보다 클 때까지 반복하고 한 번 반복할 때마다 1씩 감소하도록 해준다. 만약 입력 값으로 3이 들어온다면,
i | i > 0 | 출력
3 | true | 3
2 | true | 2
1 | true | 1
0 | false
end
이런식으로 입력한 값만큼 반복하는 것이다.
이런식으로 아에 초기식을 안써준다면 count 값만으로도 해당 값만큼 반복이 가능하다. count 값이 0보다 큰지 검사하고 매 반복마다 -1을 해줌으로써 0이 되는 순간까지 반복하게 하는 것이다.
이렇게 하면 변수 하나를 더 선언할 필요 없이 입력 값만큼 반복이 가능하다.
이런식으로 초기식에 scanf를 넣어줄 수 있다. 이렇게 한다면 for문 시작과 동시에 사용자의 입력값을 count 변수에 저장하고, 조건식, 변화식을 거쳐 해당 값만큼 반복을 시켜주는 것이다.
-for문에서 변수 두 개 사용
초기식에 변수를 두 개 선언하면 두 개의 변수를 사용할 수 있다.
이런식으로 초기식에서 변수 두 개를 선언하고 해당 변수를 조건식이나 변화식에 써줄 수 있다. 위 코드는 i = 0, j = 10으로 초기식에서 선언하고, 조건은 i < 5 그리고 i는 ++, j는 --를 해준 것이다. 이럴 경우 i < 5가 거짓이 될 때까지 i는 +1, j는 -1이 되는 것이다.
위처럼 || 논리 연산자를 사용해서 두 변수 모두 조건식으로 활용할 수 있다. 원래는 i가 5가 되면 멈추는 반복문이었지만 i < 5 || j > 0을 써주니 i가 5보다 작거나, j가 0보다 크거나가 되어 두 조건 모두 거짓이 될 때까지 반복된다.
-for 무한루프
무한루프가 안됐던 걸로 기억하는 파이썬의 for와 달리 c언어 for는 무한루프가 가능하다.
위처럼 for의 조건 부분에 ;;만 써준다면 무한루프가 된다. 사진을 보면 for문을 무한루프로 작성해서 A가 끝없이 출력되는 것을 볼 수 있다. 이를 멈추는 방법은 ctrl + c를 눌러주면 된다.
a : 0 ~ 8까지 9번 반복된다. X
b : 1 ~ 8까지 8번 반복된다. X
c : 0 ~ 9까지 10번 반복된다. O
d : 1 ~ 10까지 10번 반복된다. O
e : 1 ~ 9까지 9번 반복된다. X
for문으로 i를 10부터 1까지 감소시키는 방법은 int i = 10을 초기식으로, i > 0 또는 i >= 1을 조건식, i -- 또는 i -= 1을 변화식으로 하면 된다.
답 : c, e
b : for의 반복식에는 ;를 써주면 안된다.(써줘도 컴파일 에러는 없지만, 반복이 안되므로 for를 써준 의미가 없다.)
무한 루프는 for(;;)로 만들어줄 수 있다.
답 : for(;;)
2 5, 4 4, 8 3, 16 2, 32 1이 각 줄에 출력되게 만드는 것이 목적이다. 출력문을 보면 i, j이 두 변수로 출력하므로 for에서 i, j 두 변수를 선언하여 해당 값들이 출력되게 하면 될 것 같다.
#include <stdio.h>
int main()
{
for (int i = 2, j = 5; i <= 32; i *= 2, j--)
{
printf("%d %d\n", i, j);
}
return 0;
}
i = 2, j = 5로 선언해주고, 조건은 i <= 32(i가 32보다 작거나 같을 때까지) 그리고 변화식은 i는 * 2씩 값이 늘어나므로 i *= 2, j는 -1씩 값이 감소하므로 j--를 써줬다.
알파벳 문자가 하나 입력되는데, 이 문자부터 z까지 순서대로 출력하는 것이 목적이다. 각 알파벳의 아스키 코드는 앞뒤 순서로 1씩 차이난다는 점을 이용하면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char i;
for (scanf("%c", &i); i <= 122; i++)
{
printf("%c", i);
}
return 0;
}
먼저 char형 변수 i로 문자를 for문 초기식에서 입력받는다. for문의 조건식은 i <= 122로 z의 아스키 값이 122이므로 122까지 출력하도록 한 것이다. 당연히 알파벳은 순서대로 출력해야하므로 변화식은 i++로 작성했다. 생각해보니 i <= 122는 i <= 'z'로 바꿔주면 가독성 면에서 더 좋을 것 같다. 어쨌든 이렇게 작성하면 입력한 알파벳의 아스키 코드부터 z의 아스키 코드 122까지 1씩 증가하며 해당 아스키 값의 문자를 출력하기 때문에 입력한 알파벳부터 z까지 순서대로 알파벳 문자를 출력할 수 있다.
02-20 (Unit 28.1 ~ Unit 30.7)
-while-
파이썬과 마찬가지로 c언어에서의 while 역시 반복문으로 지정한 만큼 특정 코드를 반복해주는 역할을 한다. for와 달리 while은 파이썬과 c언어에서 형식이 거의 흡사하다.
while <조건식>
<반복할 코드>
while (<조건식>)
{
<반복할 코드>
}
위 형식으로 사용하는데, (), {} 유무만 빼면 파이썬과 거의 똑같은 것을 볼 수 있다. for문과 달리 변화식을 반복식에 넣어줄 수 없으므로 반복할 코드에 변화식을 넣어줘야지 무한루프 없이 반복을 끝낼 수 있다.
마찬가지로 반복할 코드가 1줄인 경우에는
while (<조건식>)
<반복할 코드>
로 작성할 수는 있지만 for문과 달리 while은 수동적으로 변화식을 줘야하는데, 1줄만 작성하면 변화식이 들어갈 자리가 없어진다.
for문과 if문과 똑같이 while의 반복식을 적고 ;를 써주면 반복이 안된다.
while(<조건식>);
{
<반복할 코드>
}
위처럼 써준다면
while(<조건식>)
{
}
<반복할 코드>
이 코드와 다를바 없으므로 { } 안의 코드가 반복되지 않는다.
while의 동작 과정이다.
조건식(거짓일 경우 반복 끝) -> 반복할 코드 실행 -> 조건식(거짓일 경우 반복 끝) -> 반복할 코드 실행...
이렇게 동작한다. 따로 변화식을 while문에서 제공하지 않으므로 변화식을 임의로 넣어 특정 때에 조건식이 거짓이 되도록 해서 반복을 끝내야한다.
위 코드가 while문의 가장 기본적인 형태다. 먼저 조건에 사용할 변수인 i 선언 및 초기화. while의 조건은 i < 10, 이렇게 i가 10보다 작을 때까지 반복하도록 작성했고, 반복할 코드는 printf와 i++을 적어줬다.
먼저 i는 0이므로 i < 10의 조건이 참이다. 따라서 printf, i++을 실행해서 i의 값인 0이 출력되고, 그 후 i의 값이 + 1된다. i의 값이 1이 됐지만 i < 10의 조건에 만족하므로 다시 printf, i++를 실행한다.
이런식으로 i가 10보다 작을 때까지 반복하는데, i++코드로 i가 10이 된 순간, 반복을 멈춘다. 결과를 보면 i가 9일 때 printf한 후 i++된 i(10)는 i < 10에 만족하지 못하므로 반복이 끝난 것을 알 수 있다.
위 코드를 for문으로 작성하면
for(int i = 0; i < 10; i++)
printf("A\n");
이런식으로 좀더 간결하게 사용할 수 있다.
당연하지만 for문처럼 조건에 사용될 변수나, 조건, 변화식을 원하는대로 바꿔도 된다.
위는 i의 초기 값을 20으로 정하고 조건을 i > 0, 즉 i가 0보다 클 때까지로 바꾼 후, 변화식도 i++에서 i -= 2로 바꿔준 모습이다. 이렇게 되면 while 조건식에서 i가 0보다 큰지를 검사하고 작거나 같다면 반복문 종료를, 참이라면 printf문을 실행하고 i의 값 -2를 해준다. 20부터 0까지 짝수만 출력되게 하는 것이다.
while의 무한 루프 구현 방법은 파이썬과 비슷하다. 파이썬에서는
while True:
<반복할 코드>
로 무한루프를 구현했는데, c언어에서는 True 대신 참을 의미하는 정수인 1을 써주고 ()로 감싸준다.
while(1)
<반복할 코드>
위처럼 while 문 안에 printf("A\n");를 적고 while의 조건식에 1을 넣으면 A가 끝없이 출력되는 것을 볼 수 있다. 이를 멈추는 방법은 for문과 똑같이 ctrl + c를 눌러주면 된다.
-scanf, while
마찬가지로 scanf와 while을 응용하면 입력한 값만큼 반복하도록 할 수 있다.
이런식으로 사용자가 입력한 값을 변화식을 통해 -= 1 해주면서 조건은 입력한 값이 0보다 클 때까지로 지정하면 된다. 이렇게 하면 사용자가 입력한 값이 10이다 가정하면 10부터 변화식을 통해 -= 1이되고 i가 0이 된 순간 i > 0 조건에 만족하지 않으므로 즉시 반복문이 종료된다.
또한 조건을 바꿔줄 수도 있다. 1부터 입력 값까지 출력하고 싶다면 반복문 조건에 사용할 변수 i, 입력 값을 저장할 변수를 선언해서 조건을 i < <입력 값> 으로 지정하고 변화식은 i++로 해주면된다.
-srand, rand, time, while
반복 횟수를 정하지 않고 랜덤 함수를 이용해 특정 값이 되면 반복을 종료하도록 할 수 있다.
srand : 난수를 발생시킬 초기 seed 값 설정
rand : 난수 발생
time : 현재 시간 값 반환(정수형)
위는 랜덤함수를 이용해 i가 특정 값이 아닐 때 계속해서 랜덤 값을 뽑도록 작성한 모습이다. 먼저 랜덤 함수인 rand를 쓰려면 난수를 발생시킬 초기 seed 값이 필요한데, 그냥 공식처럼 알고 있으면 된다.
처음에는 srand(time(NULL))로 현재 시간을 난수 seed 값으로 설정하고, i를 선언한 후 i = rand() % 10 (0 ~ 9까지)로 랜덤한 수가 i에 들어가도록 한다. 조건은 i != 5, 즉 rand 함수로 i에 5가 할당되지 않는한 계속해서 반복하는 것이다.
rand() % 10이 0 ~ 9가 되는 이유는 rand로 발생하는 난수는 자리수가 큰데, 이를 10으로 나눴을 때 나머지는 항상 1의 자리만 나올 수 밖에 없다. rand로 9999가 나왔다 치면, 10으로 나눴을 때 나머지는 9인데, 그럼 이 9가 랜덤 값이 되는 것이다. 즉 이런식으로 % <범위>를 통해 랜덤 값의 범위를 지정할 수 있다.
이 while문 보다 솔직히 for문이 편하다고 생각할 수도 있는데, 내 생각에는 용도가 다른 것 같다. 반복할 횟수가 정해진 경우에는 for문이 while보다 좋지만, for문은 지정 횟수만큼만 반복이 가능하므로, 몇 번 반복할지 감이 안오는 경우 while을 써주는게 좋을 것 같다.
b : 초기식은 while문 전에 작성해야한다.(int i = 0; 같은) X
c : for문과 달리 while은 반복 횟수가 정해져 있지 않아도 사용 가능하다. X
답 : a, d, e
b : 조건이 잘못됐다. i > 10으로 정하면 i의 초기값이 0이기 때문에 반복 코드 실행조차 못한다. 따라서 조건을 i > 10 -> i < 10으로 바꿔줘야한다.
답 : b
무한루프는 while의 조건식에 어떤 경우라도 참이 되는 식 혹은 값을 넣어주면 된다. 따라서 참을 의미하는 정수인 1를 써주면 무한루프가 구현된다. 참고로 0이 아닌 정수는 모두 참이므로 1만 가능한게 아니라 0이 아닌 수를 넣어주면 무한루프 구현이 가능하다.
답 : while(1)
1, 2, 4, 8, 16, 32, 64, 128을 각 줄에 출력하는 것이 목적이다. 출력해야할 수들을 보면 2에 제곱으로 진행됨을 알 수 있다. 보면 적을 부분이 while의 조건식이므로 128에서 멈추도록 하는 조건을 넣어주면 될 것 같다.
#include <stdio.h>
int main()
{
unsigned char i = 1;
while (i <= 128 && i > 0)
{
printf("%u\n", i);
i <<= 1;
}
return 0;
}
먼저 128이 되면 멈춰야되니 i <= 128이라는 조건을 써줬는데, 이러면 i가 128인 경우 <<= 1을 했을 때 값이 0이되어 조건에 만족하게 된다. 그래서 무한루프가 발생하는데, 여기서 i > 0 조건을 &&으로 추가해줘서 i <= 128이면서 i > 0일 때만 반복할 수 있도록 작성했다.
지금 생각해보면 i <= 128 조건 필요 없이 i > 0만 써줘도 될 것 같다.
금액이 입력되는데, 그 금액을 반복해서 1200보다 작아질 때까지 -1200 해서 출력하는 것이 목적이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 0;
scanf("%d", &i);
while (i >= 1200)
{
i -= 1200;
printf("%d\n", i);
}
return 0;
}
먼저 입력 값을 받고, 조건 판별 변수로 사용할 i 변수를 선언하고 scanf로 입력 값을 받는다. 그리고 while로 반복을 시키는데 조건은 i가 1200보다 같거나 큰가? 이다. i가 1200보다 작아진다면 1200을 - 못해줌으로 이럴경우 반복을 끝내줘야하기 때문이다. 반복 코드는 먼저 i -= 1200으로 i - 1200을 해준다. 이유는 출력결과를 본다면 10000부터 출력하는게 아닌 -1200 된 값부터 출력되기 때문이다. -1200을 해준 다음 -1200 된 값을 출력하도록 작성했다.
-do while-
do while 반복문은 파이썬에는 존재하지 않는 반복문이다. 이름에서도 느껴지듯이 while과 비슷한 역할을 한다. do while 역시 원하는 만큼 반복을 해주는 역할을 하는데, while과 다른점은 while은 조건식에 맞지 않는 경우 반복 코드를 실행하지 않고 넘어갔지만, 이 do while은 조건식을 확인하기전에 먼저 반복코드를 실행하므로 조건식에 맞지 않는다 하더라도 최소 1번은 반복코드가 실행이 된다.
do
{
<반복할 코드>
} while(<조건식>);
위처럼 do {} 안에다가 반복할 코드를 넣고 while은 {} 없이 ;를 붙여서 사용하면 된다. 반복할 코드를 while의 { }가 아닌 do {} 안에 써주는데, 이러면 조건식 보다 먼저 do 내의 코드를 실행하고 그 다음에 while의 조건식을 검사해서 만족한다면 다시 do로 돌아가서 do 코드를 실행, 만족하지 않는다면, 반복을 하지 않는다.
do 내의 코드 실행 -> 조건식 확인(거짓이라면 반복 종료) -> (참이라면)do 내의 코드 실행 -> 조건식 확인(거짓이라면 반복 종료) 조건식이 참이라면 이 과정이 반복되는 것이다.
do while은 처음 한 번은 실행해야하고, 그 이후에만 조건에 따라 반복해야하는 코드를 구현할 때 간단하게 사용할 수 있다.
while의 조건을 i > 10으로 작성했지만, i의 값은 0이다. 원래의 while문이었다면 조건에 만족하지 않으므로 아무것도 출력하지 않고 프로그램이 종료되겠지만, do while은 조건 만족을 확인하기 전에 do의 코드를 먼저 실행하므로 i의 값인 0이 출력되고 프로그램이 종료된다.
이렇게 while처럼 10번 반복하게 만들 수도 있다.
while과 마찬가지로 조건, 초기 값, 변화식도 마음대로 바꿀 수 있다. 위는 i의 초기값, 조건식, 변화식을 수정하여 10 ~ 1까지 출력되도록 작성한 것이다.
while과 같이 true를 의미하는 숫자 1을 do while의 조건식에 넣어주면 무한루프가 발생하는 것을 볼 수 있다. 반복할 코드에 printf("A");를 넣으니 끝도 없이 A가 출력되고 있다..
위처럼 do while의 조건에 0을 넣어준다면 반복할 코드를 딱 한 번 실행하게 할 수 있다. 솔직히 처음 봤을 때는 쓸데가 없어보였지만 매크로에서 유용하게 사용된다고 한다.
-scanf, do while
while과 마찬가지로 scanf와 같이 사용하면 입력한 값만큼 반복하도록 작성할 수 있다.
위 코드는 i로 입력 값을 받아 변화식을 통해 i의 값을 감소시켜, i가 0보다 큰지를 확인해서 반복해주는 기능을한다. 10을 입력하니 10 ~ 1까지 출력되는 것을 볼 수 있다.
반대로 변수를 하나 더 써서 1~입력값까지 출력되도록 조건식과 변화식을 바꿔줄 수도 있다.
Unit 29.5의 srand, rand, time을 이용해서 무작위 값으로 do while을 돌리는 부분은 while 문과 똑같은 방법이므로 생략하겠다.
a : while문과 마찬가지로 반복 횟수가 정해져 있지 않더라도 사용 가능하다. X
d : 0이 아닌 수를 지정해야지 무한 루프가 된다.
답 : b, c, e
3번 출력하기 위한 조건으로는 i < 3을 써주면 된다.
답 : c
do while 반복문의 코드를 한 번만 실행하는 방법은 do { } while(0)을 써주면 된다. 그 이유는 do while문은 먼저 반복할 코드를 한 번 실행하고 조건식을 판별하는데, 딱 한 번만 실행하는 것이 목적이므로, 이때의 조건식이 거짓이면 딱 한 번만 실행할 수 있다. 따라서 거짓을 의미하는 0을 조건식에 넣어줬다.
답 : c
문자 q를 입력하면 프로그램을 종료하는 것이 목적이다. 문자 입력은 do while문에서 반복하면서 c1을 통해 받는다. while 문의 조건으로 c1이 q일시 반복을 끝내면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char c1;
do
{
scanf("%c", &c1);
} while (c1 != 'q');
printf("프로그램 종료\n");
return 0;
}
c1 != 'q' 이렇게 입력 문자가 q가 아닐시 반복하고 q면 반복을 종료하도록 했다.
0부터 입력된 숫자까지의 합을 구하는 것이 목적이다. do의 코드가 비어 있고, 주어진 줄은 2줄이다. 그중 1줄은 변화식을 넣어줘야 하므로(무한 루프가 되지 않기 위해서) 나머지 1줄을 합을 담는 변수인 sum에 입력 값까지 1씩 증가하는 i를 더해주도록 하면 될 것 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
unsigned int num1;
unsigned int sum = 0;
scanf("%d", &num1);
unsigned int i = 0;
do
{
sum += i;
i++;
} while (i <= num1);
printf("%d\n", sum);
return 0;
}
sum += i로 매 반복마다 sum에 i의 값을 더해줌으로써 값이 누적된 sum을 이용해 합을 구하도록 작성했고, i++를 써줘서 반복이 한 번 돌때마다 i의 값도 +1 되도록 작성했다.
-break-
파이썬과 똑같이 break는 반복문을 탈출 시켜주는 역할을 한다.
while(1)
{
}
위와 같이 무한 루프의 반복문이 있어도 break를 써준다면 반복문에서 빠져나갈 수 있다.
c언어에서 break는 파이썬과 똑같이 반복문을 탈출 시켜주기도 하지만 switch 등과 같은 제어흐름을 벗어나기 위해도 사용한다.
위는 while 조건식에 1을 넣어줘, 무한루프를 발생시키는데, 이 안에 if문으로 i > 9라면 break를 실행하도록 했다. i는 매 반복마다 += 1로 1씩 증가시켜준다. 이렇게 작성하고 실행하니 무한루프가 아닌 0~10까지 출력해주고 break를 만나 반복이 끝나는 것을 볼 수 있다.
for문에서도 사용할 수 있다. for의 반복식에 ;;를 써주면 무한루프가 발생하는데 이 for문안에 if 조건문으로 i > 9일시 break를 하도록 작성하자 i가 매 반복마다 + 1씩 되면서 i > 9가 만족하는 순간 반복이 끝나게 된다. while과 원리는 똑같다.
-continue-
continue는 파이썬의 continue와 같은 역할을 한다. 반복문이 돌아가는중 continue를 만난다면 반복할 코드의 아랫 부분은 실행하지 않고 스킵해서 다시 반복할 코드의 윗부분으로 돌아간다.
위 코드는 코딩도장의 짝수 출력코드를 홀수 출력 코드로 바꿔본 것이다. 먼저 i를 0으로 초기화 후 선언하고 while의 조건은 i < 10으로 작성했다. while 내의 코드를 보면 먼저 i++을 하고 if문으로 i % 2==0을 검사하는데 맞다면 continue를 시켜준다. 그 아래에는 printf로 i를 출력시켜준다. 앞서 말했듯이 continue는 반복문 내의 코드 맨 윗부분으로 다시 돌아가게 해준다. 이 코드에서는 i % 2== 0이 참이라면(짝수라면) 아래의 printf문을 실행하지 않고 위로 올라가 다시 i < 10 조건을 검사한 후 반복문 내의 코드를 실행하는 것이다.
while의 동작이 조건식 검사(거짓이라면 반복 종료) -> 반복문 내의 코드들 실행 이었다면 반복문 내의 코드 실행과정에서 continue를 실행하는 순간 continue 뒤의 코드는 실행을 안하고 조건식 검사 단계로 돌아가는 것이다.
-scanf, break-
scanf와 break를 응용한다면 위에서 작성했던 방법과는 다른식으로 사용자가 입력한 값만큼 반복시킬 수 있다.
입력 값을 담을 변수 i를 선언하고 scanf로 입력 값을 저장한다. while의 조건은 1로 무한루프를 발생시키도록 했는데, 이 안의 조건문으로 i가 0보다 작거나 같아진다면 break로 반복문을 끝내도록 작성했다. 그 다음 코드는 printf로 i의 값을 출력하고 i -1 해주는건데, 이러면 반복문이 돌아갈 때마다 i의 값이 출력되고, i의 값이 -1된다. 이 과정이 반복되다가 i가 0보다 작거나 같은 순간이 되면 반복을 끝내주는 것이다.
이 방법 말고도 변수 하나를 더 선언해 입력 값을 담고, i는 0부터 +1되게 하는데, 반복 하다가 입력 값과 i가 같게 된다면 반복문을 끝낼 수도 있다.
위 코드가 위에서 말한 방법으로 작성한 것이다. 위는 코딩도장에서 입력한 값만큼 짝수를 출력하는 것을 while문으로 홀수를 출력하도록 바꿔서 작성한 것이다. 먼저 i와 count를 선언해주는데, 이 count에는 입력 값을 저장해준다. 그리고 while은 조건을 1을 줘서 무한루프를 발생시키는데, 반복 첫 줄에서 i에 + 1을 해주고, 만약 i가 count의 값보다 커진다면 break를 해줌으로써 반복을 종료한다. 만약 아니라면 i % 2 == 0을 검사하는데, i 나누기 2의 나머지가 0이라면 짝수이므로 continue를 실행해 printf를 건너띄어 준다. 만약 나머지가 0이 아니라면 홀수이므로 printf를 실행해 i를 출력시켜줬다.
b : break는 반복문(while, for 등)을 끝내준다. X
c : continue는 반복문에서 실행안한 코드들을 건너띄고 반복문 맨 위의 조건 검사 부분으로 보내주는 것인데, switch는 반복문이 아니므로 조건 검사 부분으로 보내줄 수 없다. X
e : continue는 루프를 중단하지 않는다. X
답 : a, d
1 ~ 10부터 출력하는 것이면 i가 10이 됐을 때 반복을 끝내줘야한다. 따라서 저 빈칸에 반복문을 탈출시켜주는 break를 써주면 된다.
답 : a
짝수일 경우 printf를 실행하면 안되므로 코드를 건너띄어 주는 continue를 써주면 된다.
답 : b
3으로 끝나는 숫자를 출력하는 것이 목적이다. 3으로 끝나지 판단 방법은 해당 숫자를 10으로 나눴을 때 나머지가 3이면 3으로 끝나는 것이다. 3으로 끝나지 않는 경우 continue로 printf문을 실행시키지 않게 하고, 반복문 카운트인 i가 103보다 커진다면 break로 반복문을 끝내줘야한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int i = 1;
for (;;)
{
if (i % 10 != 3)
{
i++;
continue;
}
if (i > 103)
break;
printf("%d ", i);
i++;
}
return 0;
}
먼저 i % 10의 나머지가 3이 아닐시 i에 1을 더해주고(더해주지 않는다면 i의 증가 없이 계속 continue가 되기 때문에 무한루프가 발생한다.) continue로 나머지 반복 코드들을 스킵한다. 만약 나머지가 3일시 i > 103을 검사하고 만약 103보다 i가 크다면 break로 반복문을 종료한다. 아닐시에는 해당 값을 printf로 출력하도록 작성했다. 원래 내가 전체를 코딩하는 거였으면 if(i > 103)을 if(i%10 !=3)보다 위에 뒀을 것 같다. 그 이유는 i의 값이 103이 넘어선 이후에 i % 10이 3이 아니여야 i > 103의 조건을 확인해 break를 해주기 때문에 i의 값이 104에서 끝나는게 아닌 113에서 끝나게 된다.
이 문제에서는 칸수 제한 때문에 i > 103을 아래에 배치한 것이다.
정수 두 개가 입력되는데 해당 정수 두 개를 각각 시작 값과 끝 값으로 했을 때 그 사이에 7로 끝나지 않는 수를 출력하는 것이 목적이다. 7이 끝나지 않는 수는 10으로 나눴을 때 나머지가 7이 아닌 수들이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
int num2;
int i;
scanf("%d %d", &num1, &num2);
i = num1;
while (1)
{
if (i > num2)
break;
if (i % 10 == 7)
{
i++;
continue;
}
printf("%d ", i);
i++;
}
return 0;
}
먼저 i > num2 조건을 세우고 만약 이 조건에 만족할시 i의 값이 끝 값을 넘어선 것이므로 break로 반복문을 종료한다.
만약 i > num2 조건이 거짓이라면 i % 10 == 7 조건을 확인하는데, 이 조건을 만족할 시에는 i가 7으로 끝나는 수이므로 i++ 후 continue로 해당 i의 값을 출력하지 못하도록 한다. 만약 i가 7으로 끝나는 수가 아니라면 printf로 해당 i의 값을 출력해주도록 작성했다.
02-21 (Unit 31.1 ~ Unit 33.8)
-중첩 루프-
중첩 루프란 반복문 안에 반복문이 들어가는 형태를 말한다.
for(<초기식>, <조건식>, <변화식>)
{
for(<초기식>, <조건식>, <변화식>)
{
<반복식>
}
}
위처럼 for문 안에 for문이 들어가는 중첩 루프를 작성했을 때의 동작은 바깥쪽 for문이 한 번 실행될 때마다 안쪽의 for문이 정해놓은 횟수만큼 반복을 한다. 예를들어 바깥, 안쪽 모두 5번 반복이다 가정했을 때, 바깥쪽이 1번 반복할 때 안쪽은 5번 반복하는 것이다.
위 사진처럼 바깥, 안쪽 모두 5번씩 반복하는 for문을 작성했을 때 바깥쪽 for문이 한 번 돌때마다 안쪽의 for문은 5번씩 돈다는 것을 결과로 볼 수 있다.
-별 출력
위의 중첩 루프를 응용한다면 위 사진처럼 5X5의 별을 출력할 수 있다. 바깥쪽 for문은 행을, 안쪽 for문은 열을 담당하는 것이다. 1행에 5개의 별이 출력되야하므로 바깥쪽 for문이 한 번 돌동안(한 행을 내릴 때 마다) 안쪽 for문은 5개의 별을 출력해야하기 때문에 안쪽 for문에 printf("★");을 써줬다. 그리고 별 5개를 다 출력했을 때 바깥쪽 for문에서 printf("\n");로 한 행을 내려준다.
여기서 각각 i < 5, j < 5이 조건식을 수정한다면 4X6, 3X7, 2X2 등의 여러 크기의 사각형을 그릴 수 있다.
바깥쪽 for문은 행을 의미, 안쪽 for문은 열을 의미.
위 코드에 간단한 조건문을 추가하면 계단식으로 별을 출력할 수 있다.
위처럼 i <= j 조건식이 만족할 때 별을 출력해준다면 위 결과처럼 계단식으로 별을 출력할 수 있다.
저 조건식은 i(행)이 j(별)보다 작거나 같으면 별을 출력해주는건데, 맨 처음에 i에는 0이 들어가고 j에도 0이 들어간다, 그러면 저 식은 참이므로 별을 출력하고 j가 1,2,3,4로 변해도 여전히 저 식은 참이므로 별이 5개 출력된다.
i에 1이 들어가면 처음에 j는 0이 들어가니 별을 출력하지 않는다. 그다음 j가 1~4일 때는 i <= j를 만족하므로 별을 4개 출력한다.
그 다음 i에 2가 들어가면 j가 0,1일 때는 별을 출력하지 않는다. j가 2~4일 때는 i <= j를 만족하므로 별을 3개 출력한다. 그 다음 i에 3이 들어가면 j가 0~2일 때 별을 출력하지 않는다. j가 3~4일 때는 i <= j를 만족하므로 별을 2개 출력한다. 그 다음 i에 4가 들어가면 i가 0~3일 때 별을 출력하지 않는다. j가 4일 때만 i < = j를 만족하므로 별을 1개만 출력한다.
위 과정을 거쳐 저 결과화면처럼 출력되는 것이다.
i <= j 조건을 i >= j로 바꾼다면 코딩도장처럼 첫 째줄에는 1개, 둘 째줄에는 2개 이런식으로 별을 위 결과와 반대 방향의 계단식을 출력할 수 있다,.
안쪽 for문의 조건식을
if (i == j)
printf("★");
else
printf(" ");로 바꾼다면 대각선으로 별을 출력할 수 있다.
i가 0이고 j가 0일 때는 i == j에 만족하므로 별을 출력하고, j가 1~4일 시에는 만족하지 않으므로 else의 코드인 printf(" ");, 즉 공백이 출력된다.
i가 1이고 j가 0일 때는 i == j에 만족하지 못하니 공백을 출력하고, j가 1일 때는 만족하니 별을 출력한다. 즉 공백을 한 칸 출력한 후 별을 출력하게 되는 것이다.
i가 2고 j가 0~1일 때는 i == j에 만족하지 못하니 공백을 2칸 출력하고, j가 2일 때는 만족하니 별을 출력한다. 즉 공백을 두 칸 출력한 후 별을 출력하게 되는 것이다.
i가 3이고 j가 0~2일 때는 i == j에 만족하지 못하니 공백을 3칸 출력하고, j가 3일 때는 만족하니 별을 출력한다. 즉 공백을 세 칸 출력한 후 별을 출력하게 되는 것이다.
i가 4고 j가 0~3일 때는 i == j에 만족하지 못하니 공백을 4칸 출력하고, j가 4일 때는 만족하니 별을 출력한다. 즉 공백을 네 칸 출력한 후 별을 출력하게 되는 것이다.
이런 과정을 거쳐 대각선으로 별이 출력되는 것이다.
b : i가 있는 반복문은 세로 방향을 처리한다. X
c : j가 있는 반복문은 가로 방향을 처리한다. X
답 : a, d, e
별을 1행에는 1개, 2행에는 2개 이런식으로 계단식으로 출력하려면 조건식을 i >= j로 작성하면 된다.
답 : b
아까 별찍기에서 실습으로 정리했던 문제다. 안쪽 for문 안의 코드는 i가 j보다 작거나 같을 시 별을 출력하고 클 경우 공백을 출력하면 된다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if(i <= j)
printf("*");
else
printf(" ");
}
printf("\n");
}
return 0;
}
위에서 이미 정리했으므로 자세히 정리하진 않겠다. i가 j보다 작을 경우 *를 출력시키기 위해 i <= j라는 식을 세우고 해당 조건식 아래에는 printf("*");을 써줬다. 만약 i <= j에 어긋날시에는 else의 코드 printf(" ");로 공백을 출력해주도록 작성했다.
입력된 높이 만큼 산 모양으로 *를 출력하는 것이 목적이다.
이 문제는 사실 파이썬 코딩도장 Unit 19.6에서 이미 나왔던 문제다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int height;
scanf("%d", &height);
for (int i = 1; i <= height; i++) {
for (int j = i; j < height; j++) {
printf(" ");
}
for (int j = 0; j < i * 2 - 1; j++) {
printf("*");
}
printf("\n");
}
return 0;
}
파이썬은 편한 기능이 많이 쉽게 작성했지만 c언어는 파이썬과 달리 reversed 같은게 지원이 안되서 예전에 학교에서 배웠던 피라미드 별 알고리즘으로 작성했다.
원리는 먼저 height로 높이를 입력 받고 바깥쪽 for문은 i의 초기 값을 1로(왜냐하면 안쪽 2번째 for문의 조건이 j < i * 2 -1인데 i가 0일 경우 j < -1이 되기 때문에, 이를 방지해주기 위해서다.) 해주고, 조건은 height와 작거나 같을 때까지로 설정한다. 당연히 i는 1씩 증가하도록 한다.
두 번째 for문은 int j = i; j < height; j++로 적어줌으로써 i의 값 ~ height 만큼 반복하여 공백을 출력해준다.(높이가 5고 i가 1인 경우 1~4이므로 공백을 4번 출력한다.)
세 번째 for문은 j = 0; j < i * 2 -1; j++로 설정하는데, 그 i * 2 - 1이 조건이 되는 이유는 출력 결과를 보면 맨 마지막 행의 별의 개수는 항상 높이 * 2 - 1이 되는 것을 알 수 있다. 마지막에서 한 행 위는 (높이-1) * 2 -1이라는 규칙이 있는데, 이 for문에서는 i가 행의 역할을 하므로 i * 2 -1 만큼 별을 출력해주는 것이다.
이렇게 작성한다면 산(피라미드) 모양으로 별을 출력할 수 있다.
-goto-
goto는 원하는 코드(레이블)로 이동해 주는 역할을 한다. 마치 어셈블리어에서 jmp 같은 역할을 하는 것이다.
goto를 사용하면 상당히 편하고 코드를 간결히 만들 수 있지만, 과도하게 사용한다면 프로그램 흐름이 복잡해지고 유지보수가 힘드므로 적당히 사용하는 것이 좋다고 한다. goto와 레이블을 사용하면 신기하게 코드를 거슬러 올라갈 수도 있다!
goto <레이블 명>;
위처럼 goto 다음에 레이블 명을 써주면 해당 레이블로 이동하게 된다.
<레이블 명>:
레이블 명은 위처럼 레이블 명을 써준 다음 옆에 :를 붙여주면 된다.
위는 레이블을 여러 개 생성한 후 goto를 이용해 각 조건에 따른 레이블로 이동하는 코드다.
a == 1일 경우 AONE 레이블로, a == 2일 경우 ATWO, a != 1 && a != 2일 경우 ATHREE로 이동해서 각각 1,2,3을 출력한다. 각 레이블로 이동한 후에는 각 조건에 따라 1,2,3만 출력해야하므로 출력 후 return 0이 있는 QUIT 레이블로 goto한다.(이렇게 해주지 않는다면 AONE으로 이동했다치면 printf("1") printf("2") printf("3") 이렇게 다른 레이블의 코드도 실행된다.)
솔직히 위 코드는 if문으로 쓰는게 훨씬 간단하다.
if(a == 1)
printf("1");
else if(a == 2)
printf("2");
else
printf("3");
위와 같이 더 간단하게 쓰일 수 있는 코드가 있다면 goto를 사용하지 않는 것이 바람직하다.
위와 같은 상황에서 goto를 쓰는 것이 바람직하다. 중첩 루프시 안쪽 반복문에서 break를 쓴다면 해당 반복문만 탈출되고 바깥쪽의 반복문은 탈출이 안되는데, 이럴 경우 goto를 써주면 편하다.
i == 3이고 j == 3일 시 루프를 탈출 시켜주려고 할 때 break를 써준다면 안쪽 for문만 탈출되므로 루프에서 탈출할 수 없다. 하지만 goto를 써주고 해당 레이블을 루프 바깥에 위치하도록 하면 루프 바깥으로 이동하게 되므로 루프에서 탈출할 수 있게 되는것이다.
위처럼 조건에 맞을 때 실행할 코드가 중복된다면 위처럼 조건에 맞을 시 해당 레이블로 이동해서 실행하게 할 수도 있다. 위는 15세 영화 관람 조건을 확인하는 프로그램으로 입력 나이가 15보다 아래거나 돈이 10000 아래라면 CANT 레이블로 가서 영화 관람 실패를 출력한다. 만약 이 조건에 모두 거짓일시에는 영화관람 성공을 출력하고 return 0이 있는 QUIT 레이블로 이동한다.
레이블은 변수명처럼 숫자로 시작하면 안되고 영문자로 시작해야한다. 또한 레이블 명 뒤에는 :를 붙여줘야한다.
답 : d, e
a : goto는 goto와 레이블 사이의 코드를 스킵하고 레이블로 이동한다. X
c : goto는 이동할 레이블을 지정해줘야한다. X
답 : a, c, e
"1입니다."가 한 번 출력되도록 만드는 것이 목적이다. 칸을 채우지 않고 실행한다면 for문이 switch를 감싸고 있어서 10번 1입니다.가 출력되게 된다. break를 써줘도 바깥의 for문은 탈출할 수 없으므로 goto를 사용하면 된다.
2번 칸에 원하는 레이블 명을 정하고 1번 칸에서 goto <레이블 명>;을 써주면 될 것 같다.
#include <stdio.h>
int main()
{
int num1 = 1;
for (int i = 0; i < 10; i++)
{
switch (num1)
{
case 1:
printf("1입니다.\n");
goto QUIT;
default:
break;
}
}
QUIT:
return 0;
}
2번 칸의 레이블 명을 QUIT으로 정한 후 1번 칸에 goto QUIT;를 써줘서 "1입니다."가 단 한 번만 출력되도록 했다.
너~무 간단한 문제다. goto에 레이블을 지정해 200, 300을 출력하는 것이 목적이므로 더 설명할 것도 없이 그냥 EXIT2로 이동하면 200, 300을 출력할 수 있다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (num1 == 10)
goto EXIT2
num1++;
}
}
EXIT1:
printf("100\n");
EXIT2:
printf("200\n");
EXIT3:
printf("300\n");
return 0;
}
EXIT2를 써줬다.
-FizzBuzz-
파이썬에서 했던 FizzBuzz 문제가 c언어에서도 나왔다.
FizzBuzz 문제의 규칙은 다음과 같다.
- 1에서 100까지 출력
- 3의 배수는 Fizz 출력
- 5의 배수는 Buzz 출력
- 3과 5의 공배수는 FizzBuzz 출력
이번 문제는 강의를 듣기전 스스로 FizzBuzz 문제를 풀어본 후, 강의를 들으며 고쳐야할 점, 아쉬운 점을 정리할 것이다.
#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);
}
}
위가 내가 작성한 FizzBuzz 문제 코드다. for문을 이용해 i의 값이 1 ~ 100이 되도록 반복을 해주고, 맨 먼저 i % 15조건식을 검사해서 i가 3과 5의 공배수인지 검사한다. 15로 나눴을 때 나머지를 검사하는 이유는 15가 3과 5의 최소 공배수기 때문에 3과 5의 배수는 무조건 15의 배수가 된다. 따라서 15를 기준으로 잡아준 것이다. 만약 이 조건에 만족한다면 FizzBuzz를 출력해준다.
만약 i가 3과 5의 공배수가 아니라면 i % 3 == 0, 이렇게 3의 배수인지 검사하고 맞다면 Fizz를 출력한다.
만약 i가 3과 5의 공배수가 아니고 3의 배수도 아니라면 i % 5 == 0, 이렇게 5의 배수인지 검사하고 맞다면 Buzz를 출력한다.
만약 i가 3과 5의 공배수, 3의 배수, 5의 배수가 아니라면 해당 i를 그대로 출력해준다.
공배수 조건식을 맨 먼저 써준 이유는 3, 5의 배수부터 검사한다면 앞에서 3, 5의 배수를 찾아서, 공배수 조건식을 확인하지 않고 그냥 넘어갈 수도 있기 때문이다.
-개선할 점 1.
단순히 알고리즘 풀이 관점에서 문제를 풀 때는 위처럼 3, 5의 공배수를 검사할 때 i % 15 == 0 조건식을 써주는게 좋지만, 실무에서는 직관적으로 i % 3 == 0 && i % 5 == 0 이렇게 써주는게 좋다 한다.
무조건 고쳐야할 점은 아니다.
-개선할 점 2
#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 <= 100; 이렇게 조건식 안에 변화식을 작성하니 for 반복식이 단축됐다.
미처 삼항 연산자를 생각 못하고 있었다.
i % 3 ? i % 5 ? "%d\n" : "Buzz\n" : i % 5 ? "Fizz\n" : "FizzBuzz\n", i
i % 3이 참이라면(나머지가 1이라면) i % 5를 검사해 1일 경우 해당 i를 그대로 출력, i % 5가 0이면 Buzz를 출력한다. 만약 i % 3이 거짓(나머지가 0이라면) 뒤쪽의 i % 5 조건식을 검사하는데 참이라면(나머지가 1) Fizz를 출력, 거짓이라면(나머지가 0이라면) FizzBuzz를 출력한다.
삼항 연산자를 이렇게 중첩해서 쓰는 건 생각을 못했는데, 확실히 이렇게 작성하면 좀 더 코드가 단축되겠다.
다만 실무에서는 알아보기 힘들기 때문에 자제해야된다고 한다.
i가 4의 배수인지 확인하는 방법은 i를 4로 나눴을 때 나머지를 확인하면 된다. 0이라면 배수고, 1이라면 배수가 아니다.
답 : c
i가 4와 8의 공배수인지 확인하는 방법은 4와 8의 최소 공배수인 8로 나눠서 나머지가 0인지 확인하는 방법이랑, 4와 8로 각각 나누고 && 논리 연산자로 연결해서 둘 다 나머지가 0인지 확인하는 방법이 있다.
답 : c, e
2의 배수면 Fizz, 7의 배수면 Buzz, 2와 7의 공배수면 FizzBuzz를 출력하는 것이 목적이다. 주어진 칸에 적절한 조건식을 넣으면 될 것 같다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 2 == 0 && i % 7 == 0)
printf("FizzBuzz\n");
else if (i % 2 == 0)
printf("Fizz\n");
else if (i % 7 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
첫 번째 조건식에서는 2와 7의 공배수인지 확인해야하므로 i % 2 == 0 && i % 7 == 0인지 검사하여 공배수인지 확인하고 조건에 만족하면 FizzBuzz를 출력한다.
두 번째 조건식에서는 2의 배수인지 확인해야하므로 i % 2 == 0인지 확인하여 2의 배수인지 확인했다. 조건에 만족하면 Fizz를 출력한다.
세 번째 조건식에서는 7의 배수인지 확인해야하므로 i % 7 == 0인지 확인하여 7의 배수인지 확인했다. 조건에 만족하면 Buzz를 출력한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int start, end;
scanf("%d %d", &start, &end);
for (;start <= end; start++)
{
printf(start % 5 ? start % 11 ? "%d\n" : "Buzz\n" : start % 11 ? "Fizz\n" : "FizzBuzz\n", start);
}
}
앞서 배운 중첩 삼항 연산자로 코드를 단축해서 풀었다. start와 end를 시작 값과 끝 값으로 입력을 받아 for문의 조건식으로 ;start <= end; start ++를 써줘서 시작 값부터 끝 값까지 start의 값이 1씩 증가하면서 반복하도록 했고 삼항 연산자로 start % 5가 참이라면(나머지가 1) start % 11 조건식으로 검사하고, 이 역시도 참이라면(나머지가 1) 해당 start의 값을 출력한다. start % 11이 거짓이라면(나머지 0) "Buzz"를 출력한다. start % 5가 거짓이라면(나머지가 0), 끝의 start % 11로 검사하여 참이라면(나머지 1) "Fizz"를 거짓이라면(나머지 0) "FizzBuzz"를 출력하도록 작성했다.
'Old (2021.01 ~ 2021.12) > Programming' 카테고리의 다른 글
c언어 코딩도장 Unit 36 ~ Unit 38 (0) | 2021.03.06 |
---|---|
c언어 코딩도장 Unit 34 ~ Unit 35 (0) | 2021.03.01 |
c언어 코딩도장 Unit 12 ~ 23 (4) | 2021.02.17 |
c언어 코딩도장 Unit 1 ~ 11 (0) | 2021.02.16 |
파이썬 코딩도장 Unit 38, 39, 43 (0) | 2021.02.14 |