7장
키워드 : if, else, switch, continue, break, case, default, goto
if와 if else문의 사용법과 중첩
점프 : break, continue, goto
C의 논리연산자/조건연산자
C의 문자 입출력 함수: getchar( ), putchar( )
ctype.h 헤더파일로 제공되는 문자 함수들
문제 01
- 최저 기온들의 목록을 섭씨로 읽어들이고, 입력 항목들의 개수와 영하로 떨어진 기온들이 전체 목록에서 차지하는 백분율을 출력한다.
- 이 프로그램은 값을 읽기 위해 루프에 scanf()함수를 사용한다. 매번 루프 사이클이 실행되는 동안, 입력 항목들의 개수를 추적하는 카운터를 증가시킨다.
- if문은 영하로 내려간 기온을 탐지하고, 전체 목록에서 기온이 영하로 떨어진 날 수를 별도로 추적한다.
/* 영하로 내려간 날들의 백분율 */
#include <stdio.h>
int main(void)
{
const int FREEZING = 0;
float temperature;
int cold_days = 0;
int all_days = 0;
printf("최저 기온들의 목록을 입력하세요.\n");
printf("섭씨 단위를 사용하세요. (끝내려면 q입력).\n");
while (scanf("%f", &temperature) == 1)
{
all_days++;
if (temperature < FREEZING)
cold_days++;
}
if (all_days != 0)
printf("전체 %d일 중에 영하의 기온은 %.1f %%였습니다.\n",
all_days, 100.0 * (float) cold_days / all_days);
if (all_days == 0)
printf("입력된 데이터가 없습니다.\n");
printf("\n");
return 0;
}
while (scanf("%f", &temperature) == 1)
- scanf()가 수치형이 아닌 입력을 만났을 때 루프를 끝낼 수 있도록 scanf()의 리턴값을 사용한다.
- 변수 temperature에 int형 대신 float형을 사용했기 때문에, 8뿐만 아니라 -2.5와 같은 입력도 받아들일 수 있다.
if (temperature < FREEZING)
cold_days++;
- scanf()로 읽은 값이 0보다 작으면 cold_days를 1씩 증가시킨다.
- temperature가 0보다 작지 않으면 cold_day++;를 수행하지 않고 건너뛰고 루프를 진행하여 그 다음 기온 값을 읽는다.\
if (all_days != 0)
printf("전체 %d일 중에 영하의 기온은 %.1f %%였습니다.\n",
all_days, 100.0 * (float) cold_days / all_days);
if (all_days == 0)
printf("No data entered!\n");
- 프로그램은 출력을 제어하기 위해 if문을 두 번 더 사용한다.
데이터가 있으면 결과를 출력하고, 없으면 없다고 출력한다. - 정수 나눗셈을 피하기 위해, 이 예제는 백분율이 계산될 때 캐스트를 사용하여 float형으로 변환한다.
if문
if문은 둘 중 하나를 선택해야 하는 갈림길을 제공하기 때문에 분기문 또는 선택문이라 불린다.
형식
- C 언어에서 조건문은 if ( ) 형식으로 사용하며 괄호안에는 조건식이 들어간다.
- { } (중괄호) 안에는 조건식이 True일 때 실행할 코드를 입력한다. True가 아니면 이 부분은 실행하지 않고 건너뛴다.
while문과 비교
- while 루프에서와 마찬가지로 단일문이나 복합문 모두 가능하다.
- while문과 주된 차이는 if문에서는 조건이 참일 때 실행이 한 번만 이루어지는 반면에, while 루프에서는 조건 검사와 문장 실행이 여러 번 반복될 수 있다는 것이다.
if else
프로그램을 만들다 보면 True, False로만 분기하는 것은 한계가 있다. 실제로는 두 가지 이상의 다양한 조건 분기가 필요한 상황이 발생한다.
if else문은 조건식을 여러 개 지정하고 각 조건 마다 다른 코드를 실행하여 두 개의 문장 사이에서 선택하는 것을 가능하게 한다.
/* 문제01 */
if (all_days != 0)
printf("전체 %d일 중에 영하의 기온은 %.1f %%였습니다.\n",
all_days, 100.0 * (float) cold_days / all_days);
if (all_days == 0)
printf("입력된 데이터가 없습니다.\n");
- 위 코드에서 첫 번째 if문이 all_days가 0과 같지 않다는 조건을 검사하므로, all_days가 0과 같은지 다시 검사하는 두 번째 if문은 필요 없다.
- 이런 경우에 if else문을 사용하여 앞의 코드를 다음과 같이 다시 작성할 수 있다.
if (all_days != 0)
printf("전체 %d일 중에 영하의 기온은 %.1f %%였습니다.\n",
all_days, 100.0 * (float) cold_days / all_days);
else
printf("입력된 데이터가 없습니다.\n");
- 여기서는 검사가 단 한 번만 이루어진다. if 검사 표현식이 참이면 온도 데이터가 출력되고, 거짓이면 입력된 데이터가 없다는 메시지가 출력된다.
문제02 #getchar() #putchar()
- 입력으로부터 읽은 한 라인을 스페이스는 그대로 스페이스로 출력하고, 그렇지 않으면 ASCII 코드에서 그 다음 순서의 문자로 바꾸어 출력하는 프로그램
/* 스페이스는 유지하고 다른 입력은 변경하기 */
#include <stdio.h>
#define SPACE ' ' // '공백'
int main(void)
{
char ch;
ch = getchar(); // 문자 하나 읽기
while (ch != '\n') // 엔터 입력 아닌 동안
{
if (ch == SPACE) // 스페이스는
putchar(ch); // 유지하고
else
putchar(ch + 1); // 다른 입력은
ch = getchar(); // 다음 문자를 얻는다
}
putchar(ch); // 출력
printf("\n");
return 0;
}
- 이 문장은 scanf("%c", %ch);와 같은 동작을 한다.
- 입력 문자를 읽고, 그 값을 변수 ch에 대입한다.
- 이 문장은 printf("%c", ch);와 같은 동작을 한다.
- 전달인자를 출력한다. 위 문장은 이전에 ch에 대입된 값을 문자로 출력한다.
- 이 함수들은 문자만 다루기 때문에 범용으로 사용되는 scanf()와 printf()보다 빠르고 간결하며, 포맷 지정자가 필요없다.
- 일반적으로 두 함수는 stdio.h 파일에 정의되어 있다.
while (ch != '\n')
{
if (ch == SPACE)
putchar(ch);
else
putchar(ch + 1);
ch = getchar();
}
- 루프를 끝내야 하는 시점을 결정하기 위해 입력 항목의 값 자체를 사용하기 위해서 루프 앞에 입력 문장이 있고 매 루프 사이클의 끝에 또 하나의 입력문이 있다.
- C의 신택스는 유연하기 때문에, 입력과 검사를 다음과 같이 하나의 표현식으로 결합할 수 있다.
while ((ch = getchar()) != '\n')
{
…
}
while (
(ch = getchar()) / ①
!= '\n') ②
- 여기서 두 개의 동작은, ① 입력 값을 ch에 대입하고 ② 그 값을 개행 문자와 비교하는 것이다.
- ①번 자체를 != 연산자의 왼쪽 피연산자로 만든다.
- 이 표현식을 평가하기 위해 컴퓨터는 먼저 getchar() 함수를 호출하고, 그 함수의 리턴값을 ch에 대입한다.
- 대입 표현식의 값은 좌변의 값이기 때문에 ch = getchar()의 값은 ch가 방금 새로 얻은 값이다.
- 그러므로 ch를 읽은 후의 검사 조건은 ch != '\n'이 된다.
ctype.h 계열의 문자함수
문제02의 출력은 마침표(.)가 슬래시(/)로 변환된 것을 보여주는데, 슬래시 문자의 ASCII 코드가 마침표 문자의 코드보다 하나 더 크기 때문이다. 그러나 이 프로그램의 목적이 알파벳 문자만 변환하는 것이라면, 스페이스만 변경하지 않고 그대로 두는 것이 아니라 알파벳 문자가 아닌 모든 것들을 그대로 두는 것이 훨씬 좋을 수 있다.
ctype.h 헤더 파일에 그 함수들의 프로토타입이 들어있다. 이 함수들은 하나의 문자를 전달인자로 사용하고 그 문자가 특정 범주에 속하면 0이 아닌 값(True)을 리턴하고, 그렇지 않으면 0(False)을 리턴한다.
형식
if (조건식)
{
코드1
}
else if (조건식)
{
코드2
}
문제03
- ctype.h 계열 함수를 사용하여 문제02를 일반화 한 것으로 단축형 루프 구조를 사용한다.
// cypher2.c -- alters input, preserving non-letters\
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char ch;
while ((ch = getchar()) != '\n')
{
if (isalpha(ch)) // if a letter,
putchar(ch + 1); // display next letter
else // otherwise,
putchar(ch); // display as is
}
putchar(ch); // display the newline
return 0;
}
ctype.h 계열의 문자 검사 함수
함수 | True를 리턴하게 만드는 전달인자 |
isalnum( ) | 알파벳이나 숫자 |
isalpha( ) | 알파벳 |
isblank( ) | 표준 블랭크 문자(스페이스, 수평 탭, 개행) 또는 부가적으로 이 역할을 하도록 정의된 로케일 문자 |
iscntrl( ) | ctrl+B와 같은 제어문자 |
isdigit( ) | 숫자 |
isgraph( ) | 스페이스가 아닌 출력 가능한 문자 |
islower( ) | 영어 소문자 |
isprint( ) | 출력 가능한 문자 |
ispunct( ) | 구두점 문자(스페이스 또는 알파벳이나 숫자 문자가 아닌 출력 가능한 문자) |
isspace( ) | 화이트스페이스 문자(스페이스, 개행, 폼 피드, 캐리지 리턴, 수직 탭, 수평 탭, 기타 로케일 문자) |
isupper( ) | 영어 대문자 |
isxdigit( ) | 16진수 숫자 |
ctype.h 문자 맵핑 함수
함수 | 수행 동작 |
tolower( ) | 전달인자가 대문자이면 소문자로 변환한 버전을 리턴한다. 전달인자가 소문자이면 원래의 전달인자를 그대로 리턴한다. |
toupper( ) | 전달인자가 소문자이면 대문자로 변환한 버전을 리턴한다. 전달인자가 대문자이면 원래의 전달인자를 그대로 리턴한다. |
문제03
- 입력으로부터 읽은 한 라인을 스페이스는 그대로 스페이스로 출력하고, 그렇지 않으면 ASCII 코드에서 그 다음 순서의 문자로 바꾸어 출력하는 프로그램
// 알파벳 문자가 아닌 것들은 유지하고, 입력을 변경한다.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char ch;
while ((ch = getchar()) != '\n')
{
if (isalpha(ch)) // 문자이면
putchar(ch + 1); // 다음 문자를 출력한다
else // 그렇지 않으면
putchar(ch); // 변경하지 않고 그대로 출력한다
}
putchar(ch);
return 0;
}
- 스페이스와 구두점은 변경되지 않고 영어 대문자와 소문자들은 다음 문자를 출력한다.
else if
프로그램을 만들다보면 True, False만으로 분기하는 것은 한계가 있다. 다수에서 하나를 선택해야 하는 좀 더 복잡한 구현을 위해 else if를 사용한다.
else if는 else인 상태에서 조건식을 지정할 때 사용한다. 단, else if는 단독으로 사용할 수 없다.
형식
if (조건식)
{
코드1
}
else if (조건식)
{
코드2
}
- else는 가장 가까운 if와
문제04
- 전력회사는 고객이 사용한 전력량에 따라 요금을 부과한다. 어떤 전력회사가 킬로와트시(kWh)를 단위로, 다음과 같은 요율로 전기 요금을 부과한다고 가정한다.
처음 360 kWh까지 : kWh당 $0.13230
그 다음 108kWh까지 : kWh당 $0.15040
그 다음 252kWh까지 : kWh당 $0.30025
720 kWh 초과 : . kWh당 $0.34025 - 문제04는 에너지 관리와 비용 절약을 위해, 전기요금을 계산하는 프로그램이다.
// electric.c -- calculates electric bill
#include <stdio.h>
#define RATE1 0.13230 // rate for first 360 kwh
#define RATE2 0.15040 // rate for next 108 kwh
#define RATE3 0.30025 // rate for next 252 kwh
#define RATE4 0.34025 // rate for over 720 kwh
#define BREAK1 360.0 // first breakpoint for rates
#define BREAK2 468.0 // second breakpoint for rates
#define BREAK3 720.0 // third breakpoint for rates
#define BASE1 (RATE1 * BREAK1)
// cost for 360 kwh
#define BASE2 (BASE1 + (RATE2 * (BREAK2 - BREAK1)))
// cost for 468 kwh
#define BASE3 (BASE1 + BASE2 + (RATE3 *(BREAK3 - BREAK2)))
//cost for 720 kwh
int main(void)
{
double kwh; // kilowatt-hours used
double bill; // charges
printf("Please enter the kwh used.\n");
scanf("%lf", &kwh); // %lf for type double
if (kwh <= BREAK1)
bill = RATE1 * kwh;
else if (kwh <= BREAK2) // kwh between 360 and 468
bill = BASE1 + (RATE2 * (kwh - BREAK1));
else if (kwh <= BREAK3) // kwh betweent 468 and 720
bill = BASE2 + (RATE3 * (kwh - BREAK2));
else // kwh above 680
bill = BASE3 + (RATE4 * (kwh - BREAK3));
printf("The charge for %.1f kwh is $%1.2f.\n", kwh, bill);
return 0;
}
- 상수들을 편리하게 한 곳에 모으기 위해 요율을 위한 기호 상수들을 사용한다. 요율의 구분점도 기호 상수로 나타낸다.
→ 요율들이 한 곳에 모여 잇어야 프로그램을 수정하기가 쉽다. - BASE1과 BAE2는 기호 상수로 나타낸 요율과 구분점으로 다시 나타낸다.
→ 요율이나 구분점을 수정하면 BASE1과 BASE2도 자동으로 수정된다. - 전처리기는 계산을 하지 않는다. 프로그램에 BASE1이 나타난 자리가 0.13230 * 360.0으로 대체되고 이것을 컴파일러가
수치값(47.628)으로 평가한다. 따라서 최종적인 프로그램 코드는 계산하지 않고 47.628을 사용한다. - 이 프로그램의 핵심 부분을 다음과 같이 작성할 수 있다.
if (kwh <= BREAK1)
bill = RATE * kwh;
else
if (kwh <= BREAK2)
bill = BASE1 + (RATE2 * (kwh-BREAK1));
else
if (kwh <= BREAK3)
bill = BASE2 + (RATE3 * (kwh-BREAK2));
else
bill = BASE3 + (RATE4 * (kwh-BREAK3));
- C99 표준은 최소 127레벨까지 중첩을 허용하도록 요구하고 있다.
문제05
- 입력으로부터 읽은 한 라인을 스페이스는 그대로 스페이스로 출력하고, 그렇지 않으면 ASCII 코드에서 그 다음 순서의 문자로 바꾸어 출력하는 프로그램
/* 중첩된 if문들이 어떤 수의 약수들을 출력한다 */
#include <stdio.h>
#include <stdbool.h> // 1과 0 대신에 True / False를 사용할 수 있다.
int main(void)
{
unsigned long num; // 검사를 위해 주어지는 수 (입력 받는수)
unsigned long div; // 잠정적인 약수
bool isPrime; // 소수 플래그 / 소수 구별 bool형 ( bool형을 지원하지 않는다면 int형으로 선언하면 1과0으로 사용하면 된다.)
printf("검사할 정수를 하나 입력하세요.; ");
printf("(끝내려면 q).\n");
while (scanf("%lu", &num) == 1) // 사용자 입력깂이 정수인지 구분하여 리턴값 1이 아닐때 까지 루프
{
for (div = 2, isPrime = true; (div * div) <= num; div++) //
{
if (num % div == 0)
{
if ((div * div) != num)
printf("%lu , %lu : 둘 다 %lu의 약수다.\n",
num, div, num / div);
else
printf("%lu : %lu의 약수다.\n",
num, div);
isPrime= false; // 소수가 아니다
}
}
if (isPrime)
printf("%lu: 소수다.\n", num);
printf("검사할 또 다른 정수를 하나 입력하세요; ");
printf("(끝내려면 q)).\n");
}
printf("- 끝 The End -\n\n");
return 0;
}
문제06
- 입력으로부터 읽은 한 라인을 스페이스는 그대로 스페이스로 출력하고, 그렇지 않으면 ASCII 코드에서 그 다음 순서의 문자로 바꾸어 출력하는 프로그램
/* 논리연산자 AND */
#include <stdio.h>
#define PERIOD '.'
int main(void)
{
char ch;
int charcount = 0;
while ((ch = getchar()) != PERIOD)
{
if (ch != '"' && ch != '\'')
charcount++;
}
printf("따옴표를 제외하고, 문자 %d개가 들어있습니다.\n", charcount);
return 0;
}
- 마침표가 문장의 끝을 나타내기 때문에 이 프로그램은 문자 하나를 읽어 그 문자가 마침표인지 검사하는 것으로써 동작을 개시한다.
- 그 다음에 새로운 것이 하나 나오는데, 논리곱(AND) 연산자 &&를 사용하는 if문이다. 그 if문을 다음과 같이 해석할 수 있다. "그 문자가 큰 따옴표가 아니고(AND) 그 문자가 작은따옴표가 아니면, charcount를 1만큼 증가시켜라"
- 전체 표현식이 True가 되려면 두 조건이 모두 True이어야 한다. 논리 연산자는 관계 연산자보다 연산순위가 낮다. 그래서 괄호로 부표현식을 묶을 필요가 없다.
논리 연산자
종류
논리 연산자 | 의미 |
&& | 논리곱 AND |
|| | 논리합 OR |
! | 논리부정 NOT |
- exp1과 exp2가 cat > rat이나 debt == 1000과 같은, 간단한 두 관계 표현식이라 가정하면 다음과 같이 말할 수 있다.
- exp1 && exp2는 exp1과 exp2가 둘 다 True일 때만 True이다.
- exp1 || erxp2는 exp1과 exp2 둘 중 하나가 True이거나 둘 다 True일 때 True이다.
- !exp1은 exp1이 False이면 True이다. 그리고 exp1이 True이면 False이다
핀치히터
- C는 미국 표준 키보드를 사용하는 시스템 상에서 개발되었다. 그러나 전 세계의 모든 키보드가 미국 키보드와 동일한 기호를 사용하는 것은 아니다. 그래서 C99 표준은 논리 연산자를 다르게 표현할 수 있는 핀치히터를 추가했다.
- 이들은 iso646.h 헤더파일에 정의되어 있다. 이 헤더 파일을 포함시키면 && 대신에 and, || 대신에 or, ! 대신에 not을 사용할 수 있다.
- if (ch != '"' && ch != '\n') 를 다음과 같이 표현할 수 있다. if (ch != '"' and ch != '\n')
charcount++; → charcount++;
우선순위
우선순위 | 연산자 | 설명 |
1 | ++ | 후위 증가 연산자 |
-- | 후위 감소 연산자 | |
() | 함수 호출 | |
[] | 첨자 연산자 | |
. | 참조에 의한 선택 | |
-> | 포인터를 통한 선택 | |
2 | ! | 논리 NOT 연산자 |
~ | 비트 NOT 연산자 | |
+ | 양의 부호 (단항 연산자) | |
- | 음의 부호 (단항 연산자) | |
++ | 전위 증가 연산자 | |
-- | 전위 감소 연산자 | |
(타입) | 타입 캐스트 연산자 | |
* | 참조 연산자 (단항 연산자) | |
& | 주소 연산자 (단항 연산자) | |
sizeof | 크기 | |
3 |
* | 곱셈 연산자 |
/ | 나눗셈 연산자 | |
% | 나머지 연산자 | |
4 |
+ | 덧셈 연산자 (이항 연산자) |
- | 뺄셈 연산자 (이항 연산자) | |
5 | << | 비트 왼쪽 시프트 연산자 |
>> | 부호 비트를 확장하면서 비트 오른쪽 시프트 | |
6 | < | 관계 연산자(보다 작은) |
<= | 관계 연산자(보다 작거나 같은) | |
> | 관계 연산자(보다 큰) | |
>= | 관계 연산자(보다 크거나 같은) | |
7 | == | 관계 연산자(와 같은) |
!= | 관계 연산자(와 같지 않은) | |
8 | & | 비트 AND 연산자 |
9 | ^ | 비트 XOR 연산자 |
10 | | | 비트 OR 연산자 |
11 | && | 논리 AND 연산자 |
12 | || | 논리 OR 연산자 |
13 | ? : | 삼항 조건 연산자 |
14 | = | 대입 연산자 및 복합 대입 연산자 (=, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=) |
15 | , | 쉼표 연산자 |
- a > b && b > c || b > d → (a > b) && (b > c) || (b > d)
- 논리 연산자의 우선순위를 완벽하게 기억하지 못하더라도 괄호를 사용하는 습관을 들이면 의미를 분명히 나타낼 수 있다.
평가순서
- 두 연산자가 하나의 피연산자를 공유하는 경우를 제외하고, 일반적으로 C가 복잡한 표현식의 어느 부분을 먼저 평가하는지 보장할 수 없다.
- 예를 들어, 다음과 같은 문장에서 부표현식 5+3이 부표현식 9+6보다 먼저 평가될 수도 있고, 나중에 될 수도 있다.
apples = (5+3) * (9+6); - 이러한 모호함은 컴파일러 설계자가 특정 시스템에 맞는 가장 효율적인 선택을 할 수 있도록 배려하는 규칙이다. 이 규칙에서 논리 연산자들의 처리는 한가지 예외 또는 규칙의 미비이다.
- 예를 들어, 다음과 같은 문장에서 부표현식 5+3이 부표현식 9+6보다 먼저 평가될 수도 있고, 나중에 될 수도 있다.
- C는 논리 표현식들을 왼쪽에서 오른쪽으로 평가한다고 보장한다.
- &&과 || 연산자는 시퀀스 포인트이다. 그래서 프로그램이 하나의 피연산자에서 다음 피연산자로 넘어가기 전에 모든 부작용이 완수된다.
- 게다가 표현식을 전체적으로 거짓이라고 판단할 수 있는 요소가 발견되는 즉시 평가를 중지한다.
- 따라서, 코드를 다음과 같이 작성하는 것이 가능하다.
while ( (c = getchar() ) != ' ' && c != '\n')
문제07
- 입력을 읽고 카운트한 단어의 수를 보고하는 프로그램을 만들기 위해 포함시켜야 하는 것은 다음과 같다.
- 첫째, 프로그램은 한 문자씩 입력을 읽어야 한다. 그리고 언제 멈추어야 하는지 알 수 있는 방법을 가져야 한다.
- 둘째, 문자 수와 라인 수, 단어 수를 카운트할 수 있어야 한다.
- 의사 코드로 표현하면 다음과 같다.
문자 하나를 읽는다
읽지 않은 입력이 아직 남아있는 동안
문자 카운트를 증가시킨다
한 라인을 다 읽었으면, 라인 카운트를 증가시킨다
한 단어를 다 읽었으면, 단어 카운트를 증가시킨다
다음 문자를 읽는다 - 이와 같은 작업을 수행하는 입력 루프의 모델은 다음과 같다. 여기서 STOP은 입력의 끝을 알리기 위해 ch에 대입되는 어떤 값을 나타낸다.
while ((ch=getchar()) != STOP)
{
…
} - 루프의 몸체를 생각해보면, 프로그램이 입력을 위해 getchar()를 사용하므로 매 루프 사이클마다 카운터를 증가시킴으로써 문자 수를 카운트할 수 있다. 라인 수를 카운트하기 위해 프로그램은 개행 문자를 검사할 수 있다.
어떤 문자가 개행 문자이면, 프로그램은 라인 카운트를 증가시켜야 한다. 한 가지 결정 사항은 STOP 문자가 라인의 중간에 나타나는 경우에는 어떻게 해야할 지 인데, STOP 문자 바로 전에 읽은 마지막 문자가 개행이 아니면 문자들로 구성되어 있지만 개행이 없는 라인, 즉 불완전 라인으로 카운트하는 것이다. - 가장 까다로운 부분은 단어 인식이다. 단어가 무엇을 의미하는지부터 정의해야 한다. 화이트스페이스가 포함되지 않은 문자들의 한 시퀀스를 '단어'라고 정의한다. 프로그램이 화이트스페이스가 아닌 첫 문자를 만나면 하나의 단어가 시작된다고 판단하고 만나면 그 단어는 끝이다.
** 화이트스페이스가 아닌 문자를 찾는 가장 간단한 검사 표현식은 다음과 같다.
c != ' ' && c != '\n' && c != '\t' // c가 화이트스페이스가 아니면 True
** 화이트스페이스 문자를 찾는 가장 간단한 검사 표현식은 다음과 같다.
c == ' ' || c == '\n' || c == '\t' // c가 화이트스페이스이면 True - 그러나 ctype.h 계열의 isspace() 함수를 사용하는 것이 훨씬 더 간단하다. 이것은 c가 화이트스페이스면 True를 반환한다.
!isspace(c)는 c가 화이트스페이스가 아니면 True이다. - 어떤 문자가 여전히 한 단어에 속하는지 추적하기 위해, 한 단어의 첫 문자를 읽었을 때 (inword라고 부르는) 플래그를 1로 설정한다. 또한 그 지점에서 단어 카운트를 증가시킬 수 있다. 그렇게 하면 inword가 1 또는 True로 유지되는 동안 계속 ㅇ어지는 화이트스페이스가 아닌 문자들은 단어의 시작을 나타내지 않는다. 다음 번 화이트스페이스를 만났을 때, 플래그를 0 또는 False로 재설정해야 한다. 그러면 프로그램은 다음 단어를 읽을 준비를 한다. 이것을 의사코드로 표현하면 다음과 같다.
c가 화이트스페이스가 아니고 inword가 False이면
inword를 True로 설정하고 단어를 카운트한다.
c가 화이트스페이스이고 inword가 True이면
inword를 False로 설정한다. - 이와 같은 접근은 inword를 각 단어의 시작 지점에서 1 또는 True로 설정하고, 끝나는 지점에서 0 또는 False로 설정한다.
- 단어들은 플래그 설정이 0에서 1로 바뀔 때만 카운트된다.
/* 문자 수, 단어 수, 라인 수를 카운트한다 */
#include <stdio.h>
#include <ctype.h> // isspace()를 사용하기 위해 선언
#include <stdbool.h> // bool, true, false를 사용하기 위해 선언
#define STOP '|'
int main(void)
{
char c; // 현재 읽은 문자
char prev; // 바로 전에 읽은 문자
long n_chars = 0L; // 문자 수
int n_lines = 0; // 라인 수
int n_words = 0; // 단어 수
int p_lines = 0; // 불완전 라인 수
bool inword = false; // c가 여전히 한 단어 안에 속해 있으면 true
printf("분석할 영문 텍스트를 입력하세요. (끝내려면 |를 입력하세요.):\n");
prev = '\n'; // 완전 라인을 식별하는 데 사용한다.
while ((c = getchar()) != STOP)
{
n_chars++; // 문자를 카운트 한다.
if (c == '\n')
n_lines++; // 라인을 카운트 한다.
if (!isspace(c) && !inword)
{
inword = true; // 새 단어를 시작한다.
n_words++; // 단어를 카운트한다.
}
if (isspace(c) && inword)
inword = false; // 단어의 끝에 도달했다.
prev = c; // 문자의 값을 보관한다.
}
if (prev != '\n')
p_lines = 1;
printf("문자 수 : %ld, 단어 수 : %d, 라인 수 : %d, ",
n_chars, n_words, n_lines);
printf("불완전 라인 수 = %d\n\n", p_lines);
return 0;
}
조건 연산자 ?:
특징
- C는 if else문의 한 형식을 간단하게 표현하는 방법을 제공한다.
- 조건 연산자 ?:를 사용하기 때문에, 그것을 조건 표현식이라고 부른다.
- 조건 연산자는 두 부분으로 나뉘어 세 개의 피연산자를 사용하는 연산자다.
- 피연산자가 하나인 연산자를 단항 연산자, 피연산자가 두 개인 연산자를 이항 연산자 라고 부른것과 마찬가지로, 피연산자가 세 개인 연산자를 삼항 연산자라고 부른다. 조건 연산자는 C에서 유일한 삼항 연산자이다.
조건 표현식
표현
- 다음은 어떤 수의 절대값을 구하는 조건 표현식의 한 예이다.
x = (y<0) ? -y : y;
- =과 세미콜론 사이에 있는 모든 것이 조건 표현식이다.
- 이 문장의 의미는 "y가 0보다 작으면 x = -y; 이고 그렇지 않으면 x = y;" 이다.
- if else 신택스를 사용하면 그 의미를 다음과 같이 표현할 수 있다.
if (y<0)
x = -y;
else
x = y;
형식
- 조건 표현식의 일반 형식은 다음과 같다.
expression1 ? expression2 : expression3
- expression1 부분이 0이 아니면(True) 전체 조건 표현식은 expression2 부분과 같은 값을 가진다.
- expression1 부분이 0이면(False) 전체 조건 표현식은 expression3 부분과 같은 값을 가진다.
- 조건 표현식은, 두 가지 가능한 값 중 어느 하나를 변수에 대입하기를 원할 때 사용할 수 있다.
- 조건 표현식의 대표적인 사용 예는 어떤 변수에 두 값 중 큰 값을 대입하는 것이다.
max = (a > b) ? a : b;
- a가 b보다 크면 max에 a를 대입하고, 그렇지 않으면 b를 대입한다.
장점
- 일반적으로 if else문을 사용해도 조건 연산자와 같은 일을 수행할 수 있다. 그러나 조건 연산자를 사용하는 거이 더 간결하다.
- 그리고 컴파일러에 따라서는 더 간결한 기계어 코드를 만든다.
문제08
- 이 프로그램은 제곱미터로 주어지는 면적을 페인트로 전부 칠하는 데 얼마나 많은 페인트 통이 필요한지 계산하는 프로그램이다. 기본 알고리즘은 제곱미터로 주어지는 면적을 페인트 한 통으로 칠할 수 있는 면적으로 나누면 된다.
- 여기서 결과가 1.7통으로 나온다고 가정하면 페인트 가게는 통 단위로 팔기 때문에 2통을 사야한다. 즉, 결과가 소수부를 가진 통 수로 나오면 다음 정수로 무조건 올려야 한다.
- 이 프로그램은 이러한 상황을 다루기 위해 조건 연산자를 사용하고, "can"과 "cans" 중 적당한 것을 선택하여 출력하기 위해 조건 연산자를 사용한다.
/* 조건 연산자 사용 */
#include <stdio.h>
#define COVERAGE 350 // 페인트 한 통으로 칠할 수 있는 평방피트
int main(void)
{
int sq_feet;
int cans;
printf("페인트로 칠할 면적을 평방피트 단위로 입력하세요: \n");
while (scanf("%d", &sq_feet) == 1)
{
cans = sq_feet / COVERAGE;
cans += ((sq_feet % COVERAGE == 0)) ? 0 : 1;
printf("페인트 %d통(%s)을 구입해야 합니다.\n", cans,
cans == 1 ? "can" : "cans");
printf("다음 값을 입력하세요 (끝내려면 q 입력): \n\n");
}
return 0;
}
일반적으로 루프의 몸체 안으로 들어간 프로그램은, 다음 번 루프 검사를 하기 전까지 몸체 안에 있는 모든 문장들을 실행한다.
continue문과 break문은 루프 몸체 안에서 이루어지는 검사 결과에 따라 루프의 나머지를 건너뛰거나 루프를 아예 끝낼 수 있게 한다.
continue문
- continue문은 세 가지 루프 형식이 모두 사용할 수 있다.
- continue문을 만나면 프로그램은 해당 루프 사이클의 나머지를 모두 건너뛰고, 다음 루프 사이클을 시작한다.
- continue문이 중첩된 구조 속에 들어 있는 경우, 그 continue문을 포함하고 이는 내부 구조만 영향을 받는다.
= 가장 가까운 반복문에 영향을 끼친다.
반복문과 continue의 동작 순서도
continue로 코드 실행 건너뛰기
- continue를 사용하여 일부 코드를 실행하지 않고 건너뛸 수 있다. 다음은 1부터 100까지 숫자 중 짝수만 출력한다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++) // 1부터 100까지 증가하면서 100번 반복
{
if (i % 2 != 0) // i를 2로 나누었을 때 나머지가 0이 아니면 홀수
continue; // 아래 코드를 실행하지 않고 건너뜀
printf("%d\n", i);
}
return 0;
}
// 실행 결과
// ...
// (생략)
// 92
// 94
// 96
// 98
// 100
- for를 사용하여 1부터 100까지 반복한다.
- 그리고 if를 사용하여 i가 홀수이면 continue를 실행한다. (i를 2로 나누었을 때 나머지가 0이면 짝수 0이 아니면 홀수)
- 마지막으로 printf를 사용하여 i의 값을 출력한다.
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 100) // i가 100보다 작거나 같을 때 반복. 1부터 100까지 증가하면서 100번 반복
{
i++; // i를 1씩 증가시킴
if (i % 2 != 0) // i를 2로 나누었을 때 나머지가 0이 아니면 홀수
continue; // 아래 코드를 실행하지 않고 건너뜀
printf("%d\n", i);
}
return 0;
}
// 실행결과
// ...
// (생략)
// 92
// 94
// 96
// 98
// 100
- i가 짝수이면 printf가 실행되어 숫자가 출력되고, 홀수이면 continue가 실행되어 printf를 실행하지 않는다.
- 즉, 반복문 안에서 continue를 실행하면 continue 아래의 코드는 실행하지 않고 건너뛴 뒤 다음 반복을 시작한다.
- for뿐만 아니라 while(do while)에서도 continue의 동작은 같다.
- 여기서는 반복 횟수를 정한 뒤 continue를 사용했지만 무한 루프에서 continue를 사용하면 짝수만 계속 출력될 뿐 반복문은 끝나지 않는다.
continue 사용 판단
- continue문의 사용을 피할 수 있는 두 가지 방법이 있다.
- continue를 생략하고 루프의 나머지 부분을 else 블록으로 만드는 것이다.
if (score < 0 || score > 100) 또는 if (score >=0 && score <= 100)
/* printf()문 */ {
else /* 문장들 */
{ }
/* 문장들 */
}
이 경우에 continue를 사용함으로써 얻어지는 장점은, 문장들의 주 그룹에서 들여쓰기 한 단계를 줄일 수 있다는 것이다.
이러한 간결성은 문장들이 길거나 여러 번 중첩되어 있을 때 코드를 읽기 쉽게 해준다. - continue의 또 하나의 용도는 깃발 역할을 하는 플레이스홀더(placeholder)로 사용할 수 있다는 것이다.
예를 들어 다음과 같은 루프는 한 라인의 끝까지(끝을 포함하여) 입력을 읽고 그냥 버린다.
while (getchar() != '\n') vs. while (getchar() != '\n')
; continue;
이것은 한 라인으로부터 원하는 것을 이미 읽었으므로 다음 라인의 시작 위치로 건너뛸 필요가 있을 때 편리하다.
continue를 사용했을 때 코드가 훨씬 읽기 쉬워진다.
- continue를 생략하고 루프의 나머지 부분을 else 블록으로 만드는 것이다.
- continue를 사용했을 때 코드가 간단해지지 않고 오히려 복잡해진다면 사용하지 않아야 한다.\
- 이 루프는 탭은 건너 뛰고 개행 문자를 만날 때만 루프를 탈출한다.
while ( (ch = getchar() ) != '\n' )
{
if ( ch == '\t')
continue;
putchar(ch);
} - 이 루프를 좀 더 경제적으로 다음과 같이 표현할 수 있다. 이와 같이, if 검사로 다시 바꾸면 continue를 없앨 수 있다.
while ( (ch = getchar() ) != '\n' )
{
if ( ch != '\t')
putchar(ch);
}
- 이 루프는 탭은 건너 뛰고 개행 문자를 만날 때만 루프를 탈출한다.
루프가 다시 시작하는 지점
- while 루프와 do while 루프의 경우, continue문 이후에 수행하는 다음 동작은 루프 검사 표현식을 평가하는 것이다.
count = 0;
while (count < 10)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
coutn++;
}
- 이 루프는 10개의 문자(ch가 개행 문자일 때 count++;을 건너뛰기 때문에 개행은 카운트에서 제외된다)를 읽어 출력한다.
- 이때 개행은 제외한다. continue문 이후에 평가될 다음 표현식은 루프 검사 조건이다.
- for 루프의 경우 continue문 이후에 수행할 다음 동작은 갱신 표현식을 평가하고, 이어서 루프 검사 표현식을 평가하는 것이다.
for (count = 0; count < 10; count++)
{
ch = getchar();
if (ch == '\n')
continue;
putchar(ch);
}
- 이 경우 continue문이 실행되었을 때, 먼저 count가 증가된다. 그러고 나서 그 증가된 count가 10과 비교된다.
- 그러므로 이 루프는 while 예제와는 약간 다르게 행동한다. 개행 문자가 아닌 문자들만 에코하는 것은 while 예제와 같다.
- 그러나 이번에는 개행 문자도 카운트에 포함되므로 개행 문자까지 포함하여 10개의 문자를 읽는다.
문제09
- continue문의 사용법을 보여주는 간단한 프로그램
/* continue를 사용하여 루프의 일부를 건너뛴다 */
#include <stdio.h>
int main(void)
{
const float MIN = 0.0f;
const float MAX = 100.0f;
float score;
float total = 0.0f;
int n = 0;
float min = MAX;
float max = MIN;
printf("첫 번째 스코어를 입력하세요. (끝내려면 q 입력) : ");
while (scanf("%f", &score) == 1)
{
if (score < MIN || score > MAX)
{
printf("%0.1f : 유효한 값이 아닙니다. 다시 입력하세요: ", score);
continue; // while 루프 테스트 조건으로 점프한다.
}
printf("%0.1f: 유효한 값입니다.\n", score);
min = (score < min)? score: min;
max = (score > max)? score: max;
total += score;
n++;
printf("다음 스코어를 입력하세요. (끝내려면 q 입력) : ");
}
if (n > 0)
{
printf("스코어 %d개의 평균은 %0.1f 입니다.\n", n, total / n);
printf("최저점 : %0.1f, 최고점 : %0.1f\n", min, max);
}
else
printf("유효한 값을 전혀 입력하지 않았습니다.\n");
return 0;
}
while (scanf("%f", &score) == 1)
{
if (score < MIN || score > MAX)
{
printf("%0.1f : 유효한 값이 아닙니다. 다시 입력하세요: ", score);
continue; // while 루프 테스트 조건으로 점프한다.
}
…
- 사용자가 수치형이 아닌 데이터를 넣을 때까지 입력을 읽는다.
- 루프 안에 있는 if문이 유효하지 않은 스코어 값들을 걸러낸다.
→ 188 입력 시 "188.0 : 유효한 값이 아닙니다. 다시 입력하세요"를 출력하고 루프의 나머지를 건너뛰고 입력 값을 읽기 위해 다음 루프 사이클을 시작한다.
break문
루프 안에 있는 break문은, break가 들어 있는 그 루프로부터 반복을 종료하고 다음 단계로 진행하게 만든다.
break로 반복 종료하기
- 먼저 무한 루프에서 숫자를 증가시키다가 100이 나오면 반복문을 끝낸다.
#include <stdio.h>
int main()
{
int num1 = 0;
while (1) // 무한 루프
{
num1++; // num1을 1씩 증가시킴
printf("%d\n", num1);
if (num1 == 100) // num1이 100일 때
break; // 반복문을 끝냄. while의 제어흐름을 벗어남
}
return 0;
}
// 실행 결과
// ...
// (생략)
// 96
// 97
// 98
// 99
// 100
- while에 1을 지정하여 무한 루프를 만들고 그 안에서 num1을 1씩 증가시키고 if를 이용하여 num1이 100이 될 때 break를 실행한다. while(do while)뿐만 아니라 for에서도 break의 동작은 같다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (;;) // 무한 루프
{
num1++; // num1을 1씩 증가시킴
printf("%d\n", num1);
if (num1 == 100) // num1이 100일 때
break; // 반복문을 끝냄. for의 제어흐름을 벗어남
}
return 0;
}
// 실행 결과
// ...
// (생략)
// 96
// 97
// 98
// 99
// 100
- 여기서는 무한 루프를 예로 들었지만 반복 횟수가 정해져 있더라도 break를 사용하면 반복문은 바로 끝난다.
반복문과 break의 동작 순서도
문제10
- 사각형의 면적을 구하는 루프를 사용한다. 사용자가 사각형의 길이(length)와 너비(width)에 대한 값으로 수치형이 아닌 것을 입력하면 루프가 끝난다.
/* 루프를 벗어나기 위해 break를 사용한다 */
#include <stdio.h>
int main(void)
{
float length, width;
printf("사각형의 길이를 입력하세요 : \n");
while (scanf("%f", &length) == 1)
{
printf("길이 = %0.2f:\n", length);
printf("너비를 입력하세요 : \n");
if (scanf("%f", &width) != 1)
break;
printf("너비 = %0.2f:\n", width);
printf("넓이 = %0.2f:\n", length * width);
printf("사각형의 길이를 입력하세요 : \n");
}
printf("-종료-\n\n");
return 0;
}
- 루프를 다음과 같이 제어할 수 있다.
while (scanf("%f %f", &length, &width) == 2)
- 그러나 break를 사용하면 각각의 입력값을 에코하는 것이 간단하다.
- continue의 경우와 마찬가지로 break를 사용했을 때 오히려 코드가 복잡해진다면 사용하지 않아야 한다.
- 두 개의 검사를 한 곳에 모아 놓으면 논리가 더 명확해진다.
while ( (ch = getchar() ) != '\n' && ch != '\t')
putchar(ch);
- break문은 루프 바로 다음에 오는 문장으로 곧장 실행을 옮긴다.
- for 루프에서 continue가 하는 행동과는 다르게, 루프 제어부의 갱신 부분도 그냥 건너뛴다.
- 중첩된 루프 안에 있는 break는, 그 break문 포함하고 있는 내부 루프만 벗어나게 한다.
- 외부 루프에서도 벗어나려면 또 하나의 break가 필요하다.
int p, q;
scanf("%d", &p);
while (p > 0)
{
printf("%d", p);
scanf("%d", &q);
while (q > 0)
{
printf("%d\n", p*q);
if (q>100)
break; // 내부 루프에서 벗어난다
scanf("%d", &q);
}
if (q>100)
break; // 외부 루프에서 벗어난다
scanf("%d", &p);
}
문제11 #한 라인의 첫 문자만 읽기
- 조건 연산자와 if else 구조는 양자 택일을 해야 하는 프로그램을 쉽게 작성할 수 있또록 한다.
- 그러나 때로는 다수에서 어느 하나를 선택해야하는 경우가 있다.
- if els if ... else를 사용하여 이것을 처리할 수 있는데 C의 switch문을 사용하는 것이 훨씬 편리하다.
- 문제11은 switch문이 어떻게 동작하는 지 보여준다.
- 이 프로그램은 하나의 글자를 읽고, 그 글자로 시작하는 동물 이름을 출력한다.
/* switch문을 사용한다 */
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char ch;
printf("알파벳 글자를 입력하세요 : ");
printf("그러면 내가\n 그 글자로 시작하는 동물 이름을 하나 대겠습니다.\n");
printf("글자를 하나 입력하세요. (끝내려면 #) : \n");
while ((ch = getchar()) != '#')
{
if('\n' == ch)
continue;
if (islower(ch)) /// 소문자만 허용한다.
switch (ch)
{
case 'a' :
printf("argali, 아시아의 야생 양 \n");
break;
case 'b' :
printf("babirusa, 말레이 반도의 야생 돼지 \n");
break;
case 'c' :
printf("coati, 미국 너구리 \n");
break;
case 'd' :
printf("desman, 물에 사는, 두더지랑 비슷한 동물 \n");
break;
case 'e' :
printf("echidna, 바늘 두더지 \n");
break;
case 'f' :
printf("fisher, 갈색을 띤 담비 \n");
break;
default :
printf("에이, 너무 어렵다! \n");
} /* end of switch */
else
printf("나는 소문자만 알아듣는다.\n");
while (getchar() != '\n')
continue; /* 입력 라인의 나머지를 건너뛴다 */
printf("다른 글자를 하나 입력하세요. (끝내려면 #) : \n");
} /* while 루프의 끝*/
printf("-종료-\n");
return 0;
}
while (getchar() != '\n')
continue; /* 입력 라인의 나머지를 건너뛴다 */
- 문제11의 특징 중 하나는 입력을 읽는 방법이다. dab라고 했을 때, 첫 문자만 처리되었다. 이 동작은 단일 문자 입력을 기대하는 대화식 프로그램에서 아주 바람직하다.
- 이 루프는 엔터키를 눌렀을 때 발생하는 개행 문자까지 포함하여 입력으로부터 거기까지 문자들을 읽는다.
- 여기서는 함수의 리턴값이 ch에 대입되지 않는다는 것이 포인트이다.
- 문자들은 단순히 읽히고 버려진다.
- 버려지는 마지막 문자가 개행 문자이기 때문에, 다음 번에 읽을 문자는 다음 라인의 첫 문자다.
- 외부 while 루프에 있는 getchar()가 그것을 읽고 ch에 대입한다.
while ((ch = getchar()) != '#')
{
if('\n' == ch)
continue;
- 프로그램이 만나는 첫 문자가 개행문자가 되도록, 사용자가 엔터키를 누르는 것으로 시작한다고 가정할 때 그런 상황을 해결하는 코드이다.
switch문
- switch 문은 주어진 조건 값의 결과에 따라 프로그램이 다른 명령을 수행하도록 하는 조건문이다.
- if / else 문보다 가독성이 더 좋으며, 컴파일러가 최적화를 쉽게 할 수 있어 속도 또한 빠른 편이다.
- 하지만 switch 문의 조건 값으로는 int형으로 승격할 수 있는(integer promotion) 값만이 사용될 수 있다.
- 즉, C언어에서는 char형, short형, int형 변수나 리터럴, 열거체까지 사용할 수 있다.
- 따라서 if / else 문보다는 사용할 수 있는 상황이 적은 편이다.
문법
switch (조건 값)
{
case 값1:
조건 값이 값1일 때 실행하고자 하는 명령문;
break;
case 값2:
조건 값이 값2일 때 실행하고자 하는 명령문;
break;
...
default:
조건 값이 어떠한 case 절에도 해당하지 않을 때 실행하고자 하는 명령문;
break;
}
- default 절은 조건 값이 위에 나열된 어떠한 case 절에도 해당하지 않을 때 실행된다.
- 이 구문은 반드시 존재해야 하는 것은 아니며 필요할 때만 선언하면 된다.
- 이 구문의 위치가 반드시 switch문의 맨 마지막이지 않아도 된다.
- 각 case 절 및 default 절은 반드시 break 키워드를 포함하고 있어야 한다.
- break 키워드는 조건 값에 해당하는 case 절이나 default 절이 실행된 뒤 전체 switch문을 빠져나가게 해준다.
- 만약 break 키워드가 없다면 조건에 해당하는 switch 문의 case 절 이후의 모든 case절이 실행된다.
조건 값으로 여러 개의 char형 문자를 검사하는 예제
char ch = 'a';
switch (ch)
{
case 'a':
case 'A':
printf("이 학생의 학점은 A입니다.\n");
break;
case 'b':
case 'B':
printf("이 학생의 학점은 B입니다.\n");
break;
case 'c':
case 'C':
printf("이 학생의 학점은 C입니다.\n");
break;
case 'd':
case 'D':
printf("이 학생의 학점은 D입니다.\n");
break;
case 'f':
case 'F':
printf("이 학생의 학점은 F입니다.\n");
break;
default:
printf("학점을 정확히 입력해 주세요!(A, B, C, D, F)");
break;
}
- switch 문의 조건으로 여러 개의 case 절을 사용하여 여러 조건을 한 번에 검사할 수 있다.
문제12
- 하나의 문장에 다수의 case 레이블을 대응시킬 수 있다.
/* 한 문장에 다수의 레이블을 사용한다 */
#include <stdio.h>
int main(void)
{
char ch;
int a_ct, e_ct, i_ct, o_ct, u_ct;
a_ct = e_ct = i_ct = o_ct = u_ct = 0;
printf("간단한 영문 텍스트를 입력하세요.(끝낼려면 # 입력) : \n");
while ((ch = getchar()) != '#')
{
switch (ch)
{
case 'a' :
case 'A' : a_ct++;
break;
case 'e' :
case 'E' : e_ct++;
break;
case 'i' :
case 'I' : i_ct++;
break;
case 'o' :
case 'O' : o_ct++;
break;
case 'u' :
case 'U' : u_ct++;
break;
default : break;
} // switch의 끝
} // while 루프의 끝
printf("모음의 수 : A E I O U\n");
printf(" %4d %4d %4d %4d %4d\n",
a_ct, e_ct, i_ct, o_ct, u_ct);
return 0;
}
- 예를 들어 ch가 글자 i라면, switch 문은 case 'i' : 레이블로 간다.
- 그 레이블에 연결된 break문이 없기 때문에 프로그램 흐름은 그 다음 문장 i_ct++;로 간다.
- ch가 글자 I라면, 프로그램 흐름은 i_ct++;문으로 곧장 간다.
- 결과적으로 두 레이블은 같은 문장을 참조하게 된다.
- 엄밀히 말하면 case 'U'를 위한 break 문은 없어도 된다. 왜냐하면 그것이 없더라도 프로그램 흐름은 switch에 있는 그 다음 문장, 즉 default를 위한 break 문으로 간다.
- 코드를 줄이기 위해 case 'U'를 위한 break 문을 없앨 수도 있다.
- 그러나 나중에 다른 경우(case)들을 더 추가해야 한다면 break를 그 자리에 미리 넣어 두는 것이 실수를 방지할 수 있다.
- 문제12의 경우에 검사하기 전에 모든 글자를 대문자로 변환하는 toupper()함수를 사용하면 한 문장에 다수의 레이블을 사용하는 것을 피할 수 있다.
while ((ch = getchar()) != '#')
{
ch = toupper(ch);
switch(ch)
{
case 'A' : a_ct++;
break;
case 'E' : e_ct++;
break;
case 'I' : i_ct++;
break;
case 'O' : o_ct++;
break;
case 'U' : u_ct++;
break;
default : break;
} // switch문의 끝
} // while 루프의 끝
- 또는 ch의 값을 변경하지 않은 상태로 남기고 싶다면, 다음과 같이 toupper()함수를 switch의 검사 표현식으로 사용할 수 있다.
switch (toupper(ch))
'C언어 > 스터디' 카테고리의 다른 글
ch10 배열과 포인터 (0) | 2024.08.11 |
---|---|
ch09 함수 (0) | 2024.08.10 |
ch6 제어문_루프 문제 (0) | 2024.08.08 |
ch6 제어문_루프 (2) | 2024.08.08 |
ch5 연산자, 표현식, 문장 (0) | 2024.08.07 |