본문 바로가기
Programming/C Programming Modern Approach (K.N.K)

[KNK 정리] 6장: Loops

by hyeyoo 2021. 8. 21.
※ 이 블로그의 글은 글쓴이가 공부하면서 정리하여 쓴 글입니다.
※ 최대한 내용을 검토하면서 글을 쓰지만 틀린 내용이 있을 수 있습니다.
※ 만약 틀린 부분이 있다면 댓글로 알려주세요.

loop

요약/정리

반복문(Loop)는 정해진 구문을 반복적으로 실행하는데 사용된다. C에는 반복문으로 for, while, do가 있다.

while statement

형식 : while ( expression ) statement

while문은 expression이 거짓이 될때까지 statement를 실행한다. 먼저 expression을 evaluate하고, 참이라면 statement를 실행한다. 일반적으로 statement를 실행하다보면 특정 시점에서 expression이 거짓이 되어 while문이 종료된다. 그렇지 않은 경우에는 무한 루프에 빠지거나, break나 goto로 빠져나와야 한다. 일부러 expression에 1처럼 상수를 넣어서 무한루프를 만드는 경우도 있다.

 

do statement

다른데선 do-while문이라고 배웠던 것 같은데 KNK는 do문이라고 부른다.

형식 : do statement while ( expression ) ;

형식은 while과 크게 다르지 않다. do문은 먼저 statement를 실행하고, 그 다음 expression을 evaluate한다. 따라서 맨 처음 한 번은 반드시 statement를 실행하게 되며, 그 이후에는 expression이 거짓이 될때까지 반복한다.

 

for statement

형식: for ( expr1 ; expr2 ; expr3 ) statement

 

대부분의 상황에서 위의 for문은 아래의 while문으로 대체할 수 있다. (statement에 continue가 포함된 경우에는 expr3이 evaluate되지 않으므로 제외)

 

expr1;

while ( expr2 ) {

    statement

    expr3;

}

 

따라서 일반적으로 expr1은 for문을 처음 실행할 때 변수를 초기화하는 역할을 하고, expr2는 언제까지 for문을 반복할 것인지를 정하고, expr3은 iteration마다 실행되어야 하는 내용을 명시한다.. 책을 보면서 생각해보니, C89에서 for (int i = 0; i < 10; i++) 처럼 expr1에 int i = 0이 들어가는 것을 지원하지 않은 것이 이해가 된다. 왜냐하면 변수의 선언은 expression이 아니라 statement이기 때문이다. C99부터는 expr1에 변수의 선언이 오는 것을 허용한다. 또한 for문에서는 expression을 생략할 수 있다. expr1, expr2, expr3 모두 생략할 수 있으며, expr2가 생략된 경우에는 break, goto, return 등을 사용하지 않는한 반복문은 종료하지 않는다.

comma operator

형식: expr1 , expr2

comma 연산자는 expression 2개를 하나로 묶어준다. comma 연산자를 여러 번 사용하면 expr1, expr2, expr3 이런 식으로 여러 개의 expression을 묶어줄 수도 있다. 여러 개의 statement를 하나의 statement로 묶어주는 compound statement와 비슷하다. 이때, (expr1, expr2)의 값은 expr2가 된다. 따라서 expr1은 side effect가 있어야 의미가 있는데, 어차피 expr1의 값 자체는 사용되지 않고 사라지기 때문이다.

 

exiting loops

종종 반복문을 돌다가 특정 조건을 만족했을 때 반복문을 종료해야할 때가 있다. 그럴때 break, continue, goto가 사용된다.

break statement

switch문에서 break문으로 switch문을 빠져나왔듯, while, for문에서 break문을 실행하면 반복문을 빠져나간다. 다만 반복문이 중첩되는 경우가 있는데, 그럴 땐 가장 안쪽의 반복문을 빠져나온다.

continue statement

이 절의 이름이 exiting loops인데, continue는 반복문을 빠져나오는 용도는 아니다. 다만 continue를 실행하면 loop body의 마지막 바로 전으로 돌아간다. 현재 iteration을 종료하고 다음 iteration으로 넘어갈 때 사용한다.

goto statement

break랑 continue는 사실상 jump statement이다. 다만 그 용도가 제한되어있다. 이에 비해 goto는 제약이 적고, 자유자재로 statement와 statement 사이를 점프할 수 있다. (꼭 반복문을 탈출하는 용도로만 사용되지는 않는다.) goto를 사용하려면 label을 먼저 정의해야한다.

 

label 정의하는 형식: identifier  : statement

goto의 형식: goto identifier ;

 

label을 정의하고 goto를 실행하면 해당 label이 가리키는 statement로 점프한다. 근데 사실 goto가 사용되는 곳은 그렇게 많지 않다. 너무 남발하면 가독성이 떨어지기 때문이다. goto는 위에서 말한, 중첩 반복문의 탈출에 유용하다.

 

C99부터는 goto문에 제약이 있다. 책에서는 "it can't be used to bypass the declaration of a variable-length array"라고 표현하는데, 이 부분은 아직 제대로 이해를 못했다.

null statement

null문은 말 그대로 아무것도 하지 않는 statement이다. 아래 처럼 반복문에 사용할 수 있다. null문을 쓸만한 곳은 그렇게 많지 않다.

for (sum = 0, n = 1; n <= k; sum += n, n++)
	/* empty loop body */ ;

 

Exercises

1. What output does the following program fragment produce?

i = 1;
while (i <= 128) {
    printf("%d ", i);
    i *= 2;
}

답: 1 2 4 8 16 32 64 128 

 

2. What output does the following program fragment produce?

i = 9384;
do {
    printf("%d ", i);
    i /= 10;
while (i > 0);

답: 9384 938 93 9

 

3. What output does the following for statement produce?

for (i = 5, j = i - 1; i > 0, j > 0; --i, j = i - 1)
    printf("%d ", i);

위의 for문은 i > 0은 무시되고, j > 0이 참인 동안 동안 실행된다. i = 5이고, j = i - 1이므로

답: 4 3 2 1

 

4. Which one of the following statements is not equivalent to the other two (assuing that the loops are the same)?

(a) for (i = 0; i < 10; i++) ...

(b) for (i = 0; i < 10; ++i) ...

(c) for (i = 0; i++ < 10; ) ...

 

expr2와 expr3은 evaluate되는 시점이 다르므로 답: c

 

5. Which one of the following statements is not equivalent to the other two (assuing that the loops are the same)?

(a) while (i < 10) { ... }

(b) for (; i < 10; ) { ... }

(c) do { ... } while ( i < 10 )

답: c

 

6. Translate the program fragment of Exercise 1 into a single for statement.

for (i = 1; i <= 128; i *= 2) {
    printf("%d ", i);
    i *= 2;
}

7. Translate the program fragment of Exercise 2 into a single for statement.

for (i = 9384; i > 0; i /= 10) {
    printf("%d ", i);
    i /= 10;
}

8. What output does the following for statement produce?

for (i = 10; i >=1; i /= 2)
    printf("%d ", i++);

답: 10 5 3 2 1

 

9. Translate the for statement of Exercise 8 into an equivalent while statement. You will need one statement in addition to the while loop itself.

i = 10;
while (i >= 1) {
    printf("%d ", i++);
    i /= 2;
}

10. Show how to replace a continue statement by an equivalent goto statement.

for (int i = 0; i < 100; i++) {
    if (...)
        continue;
 }

for (int i = 0; i < 100; i++) {
    if (...)
        goto end_of_loop_body;
    end_of_loop_body: /* null statement */ ;
}

11. What output does the following program fragment produce?

sum = 0;
for (i = 0; i < 10; i++) {
    if (i % 2)
        continue
    sum += i;
}
printf("%d\n", i);

1 + 3 + 5 + 7 + 9 = 25. 답: 25

12. The following "prime-testing" loop appeared in Section 6.4 as an example:

for (d = 2; d < n; d++)
    if (n % d == 0)
        break;

This loop isn't very efficient. It's not necessary to divide n by all numbers between 2 and n - 1 to determine whether it's prime. In fact, we need only check divisors up to the square root of n. Modify the loop to take advantage of this fact. Hint: Don't try to compute square root of n; instead, compare d * d with n.

답: 

for (d = 2; d * d < n; d++)
    if (n % d == 0)
        break;

13. Rewrite the following loop so that its body is empty:

for (n = 0; m > 0; n++)
    m /= 2;

답: 

for (n = 0; m > 0; n++, m /= 2);

 

14. Find the error in the following program fragment and fix it.

if (n % 2 == 0);
    printf("n is even\n");

if의 expression 뒤의 null statement를 지워야 한다.

Programming Projects

1. Write a program that finds the largest in a series of numbers entered by the user. The program must prompt the user to enter numbers one by one. When the user enters 0 or a negative number, the program must display the largest nonnegative number entered:

Enter a number: 60
Enter a number: 38.3
Enter a number: 4.89
Enter a number: 100.62
Enter a number: 75.2295
Enter a number: 0

The largest number entered was 100.62

#include <stdio.h>

int main()
{
    float number, max;
    
    do {
        printf("Enter a number: ");
        scanf("%f", &number);
        
        if (max < number)
            max = number;
    } while (number != 0.0f);
    
    printf("The largest number entered was %.2f\n", max);
}

2. Write a program that asks the user to enter two integers, then calculates and displays their greatest common divisor (GCD):

Enter two integers: 12 28
Greatest common divisor: 4

Hint: The classic algorithm for computing the GCD, known as Euclid's algorithm, goes as follows: Let m and n be variables containing the two numbers. If n is 0, then stop: m contains the GCD. Otherwise, compute the  remainder when m is divided by n. Copy n into m and copy the remainder into n. Then repeat the process, starting with testing whether n is 0.

#include <stdio.h>

int main()
{
    int x, y, r;
    
    printf("Enter two integers: ");
    scanf("%d %d", &x, &y);
    
    do {
        r = x % y;
        x = y;
        y = r;
    } while (y != 0);
    
    printf("Greatest common divisor: %d\n", x);
    
    return 0;
}

3. Write a program that asks the use rto enter a fraction, then reduces the fraction to lowest terms:

Enter a fraction: 6/12

In lowest terms: 1/2

Hint: To reduce a fraction to lowest terms, first compute the GCD of the numerator and denominator. Then divide both the numerator and denominator by the GCD.

#include <stdio.h>

int main()
{
    int a, b;
    int x, y, r;
    
    printf("Enter a fraction: ");
    scanf("%d/%d", &x, &y);
    
    a = x, b = y;
    
    do {
        r = x % y;
        x = y;
        y = r;
    } while (y != 0);
      
    printf("In lowest terms: %d/%d\n", a / x, b / x);
    
    return 0;
}

5. Programming Project 1 in Chapter 4 asked you to write a program that displays a two-digit number with its digits reversed. Generalize the program so that the number can have one, two, three, or more digits. Hint: Use a do loop that repeatedly divides the number by 10, stopping when it reaches 0.

#include <stdio.h>

int main()
{
    int num;
    
    printf("Enter a number: ");
    scanf("%d", &num);
    
    printf("The reversal is: ");
    do {
        printf("%d", num % 10);
        num /= 10;
    } while (num > 0);
    
    return 0;
}

6. Write a program that prompts the user to enter a number n, then prints all even squares between 1 and n. For example, if the user enters 100, the program should print the following:
4
16
36
64
100

#include <stdio.h>

int main()
{
    int n;
    
    printf("Enter n: ");
    scanf("%d", &n);
    
    for (int i = 1; i * i <= n; i++) {
        if (i % 2 == 0)
            printf("%d\n", i * i);
    }
    
    return 0;
}

8. Write a program that prints a one-month calendar. The user specifies
the number of days in the month and the day of the week on which the
month begins:

Enter number of days in month: 31
Enter starting day of the week (1=Sun, 7=Sat): 3

       1  2  3  4  5
6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

Hint: This program isn't as hard as it looks. The most important part is a for statement that uses a variable i to count from 1 to n, where n is the number of days in the month, printing each value of i.  Inside the loop, an if statement tests whether i is the last day of the week; if so, it prints the new-line character.

#include <stdio.h>

int main()
{
    int days_in_month, day_of_week;
    
    printf("Enter number of days in month: ");
    scanf("%d", &days_in_month);
    
    printf("Enter starting day of the week (1=Sun, 7=Sat): ");
    scanf("%d", &day_of_week);
    
    for (int i = 0; i < day_of_week - 1; i++) {
            printf("   ");
    }
    for (int day = 1; day <= days_in_month; day++) {
        
        printf("%2d ", day);
        
        if (day_of_week == 7) {
            printf("\n");
            day_of_week = 1;
        } else {
            day_of_week++;
        }
    }
    
    return 0;
}

9. Programming Project 8 in Chapter 2 asked you to write a program that calculates the remaining balance on a loan after the first, second, and third monthly payments. Modify the program so that it also asks the user to enter the number of payments and then displays the balance after each of these payments.

#include <stdio.h>
int main(void) 
{ 
    float loan, interest_rate, monthly_payment, balance;
    int n;
    
    printf("Enter amount of loan: ");
    scanf("%f", &loan);
    printf("Enter interest rate: ");
    scanf("%f", &interest_rate); /* convert it from yearly to monthly */
    interest_rate = 0.01 * interest_rate / 12;
    printf("Enter monthly payment: ");
    scanf("%f", &monthly_payment);

    printf("Enter n: ");
    scanf("%d", &n);

    balance = loan;
    for (int i = 1; i <= n; i++) {
        balance = balance - monthly_payment + balance * interest_rate;
        printf("Balance remainig after %d-th payment: $%.2f\n", i, balance);
    }
}

졸리다. 남은 두문제는 자고 내일 다시..

댓글