Post

C & C++ 기초 개념 정리(2) - Flow Control/File I/O

C & C++ 기초 개념 정리(2) - Flow Control/File I/O

Flow Control

프로그래밍에서 Flow of Control은 프로그램이 특정한 순서로 실행되도록 하는 것입니다. 이를 위해 조건문과 반복문을 사용하며, C++에서는 if-else, switch, while, do-while, for 등의 구조를 활용합니다

불리언 표현식(Boolean Expressions)

프로그래밍에서 조건을 평가할 때 Boolean Expression을 사용합니다. true(참) 또는 false(거짓)를 반환하는 표현식입니다.

비교 연산자

연산자의미
==같음
!=다름
<작음
>
<=작거나 같음
>=크거나 같음

논리 연산자

연산자의미
&&논리 AND (둘 다 참일 때만 참)
||논리 OR (하나라도 참이면 참)
!논리 NOT (참이면 거짓, 거짓이면 참)
1
2
3
bool result = (5 > 3) && (10 < 20); // true
bool isEqual = (4 == 4); // true
bool isNotEqual = (7 != 8); // true

이때 &&의 우선순위가 ||보다 높기 때문에 이를 유의해야 합니다. 이로 인해 실행 결과가 완전히 달라질 수 있습니다.

1
2
bool result1 = true || false && false; // true
bool result2 = (true || false) && false; // false

위의 예에서 result1&&가 먼저 평가되어 false && falsefalse가 되고, 이후 true || falsetrue가 됩니다. 반면, result2는 괄호로 인해 true || false가 먼저 평가되어 true가 되고, 이후 true && falsefalse가 됩니다.

연산자 우선순위 (Operator Precedence)

C++에서 연산자는 특정한 우선순위를 가지며, 이는 표현식이 평가되는 순서를 결정합니다. 연산자 우선순위를 이해하면 코드의 동작을 예측하고 오류를 방지할 수 있습니다.

우선순위연산자설명결합 방향
1()괄호좌에서 우
2++, --전위 증가/감소우에서 좌
3*, /, %곱셈, 나눗셈, 나머지좌에서 우
4+, -덧셈, 뺄셈좌에서 우
5<, <=, >, >=비교 연산자좌에서 우
6==, !=동등성 비교좌에서 우
7&&논리 AND좌에서 우
8\|\|논리 OR좌에서 우
9=대입 연산자우에서 좌

일반적으로 연산 $\rightarrow$ 논리 $\rightarrow$ 대입의 순서대로 우선순위를 갖습니다.

조건문 (Branching Mechanisms)

조건문은 특정 조건이 참인지 거짓인지에 따라 코드의 실행 흐름을 결정합니다.

if-else 문

if-else 문을 사용하여 조건을 평가하고 실행할 코드를 결정할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
int hrs = 45;
double rate = 10.0;
double grossPay;

if (hrs > 40) {
    grossPay = rate * 40 + 1.5 * rate * (hrs - 40);
} else {
    grossPay = rate * hrs;
}
// grossPay == rate * 40 + 1.5 * rate * 5 == 475.0

중첩된 if-else

if 문 안에 또 다른 if-else 문을 포함할 수 있습니다.

1
2
3
4
5
6
7
if (speed > 110) {
    if (speed > 130) {
        cout << "You're really speeding!";
    } else {
        cout << "You're speeding.";
    }
}

switch 문

switch 문은 여러 개의 선택지를 가지는 조건문으로, if-else보다 더 직관적인 구조를 제공합니다.

1
2
3
4
5
6
7
8
9
10
switch (변수) {
    case 1:
        // 값1일 때 실행되는 코드
        break;
    case 2:
        // 값2일 때 실행되는 코드
        break;
    default:
        // 어떤 값에도 해당되지 않을 때 실행되는 코드
}

이때 break;를 작성해주지 않으면 다음 case로 실행이 넘어가게 됩니다. 이를 fall-through라고 하며, 의도적으로 사용하는 경우도 있지만, 대부분의 경우에는 break;를 명시적으로 작성하여 이를 방지해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
char grade = 'B';

switch (grade) {
    case 'A':
        cout << "Excellent!";
        break;
    case 'B':
        cout << "Good Job!";
        // break;가 없으면 아래 default로 넘어감
    default:
        cout << "Try harder!";
}
// Good Job!Try harder!

위 코드에서 grade'B'일 경우, Good Job! 출력 후 Try harder!도 출력됩니다. 이를 방지하려면 case 'B':break;를 추가해야 합니다.

반복문 (Loops)

반복문은 특정 조건이 만족될 때까지 코드 블록을 반복 실행합니다.

while 문

while 문은 조건이 참인 동안 반복 실행됩니다.

1
2
3
4
5
int count = 0;
while (count < 3) {
    cout << "Hello! ";
    count++;
}

do-while 문

do-while 문은 최소 한 번 실행된 후 조건을 평가합니다.

1
2
3
4
5
int count = 0;
do {
    cout << "Hello! ";
    count++;
} while (count < 3);

while 문과 do-while 문은 반복문으로 사용되지만, 실행 흐름에서 차이점이 있습니다.

구분while 문do-while 문
실행 조건조건이 참일 때만 실행최소 한 번은 실행된 후 조건을 평가
조건 평가 시점반복문 실행 전에 조건을 평가반복문 실행 후에 조건을 평가
문법조건 뒤에 세미콜론;이 필요 없음조건 뒤에 세미콜론;이 필요함

for 문

  • 기본 문법
    1
    2
    
      for (Init_Action; Bool_Exp; Update_Action)
          Body_Statement
    
    • Init_Action
      반복문이 실행될 때 초기화되며 실행되는 코드. 주로 반복 변수를 초기화합니다.
    • Bool_Exp
      반복문이 실행될 조건을 평가하는 표현식. 조건이 참일 경우 반복문이 실행됩니다.
    • Update_Action
      반복문이 실행된 후 수행되는 코드로, 주로 반복 변수의 값을 변경합니다. 생략하더라도 실행이 가능합니다.
    • Body_Statement
      조건이 참일 때 실행되는 코드 블록입니다.
      1
      2
      3
      4
      
        for (int i = 0; i < 3; i++) {
        cout << "Hello! ";
        }
        // Output: Hello! Hello! Hello!
      

Loop 사용시 주의사항

무한 루프 (Infinite Loops)

반복문을 사용할 때, 종료 조건이 잘못 설정되거나 누락되면 무한 루프가 발생할 수 있습니다. 이는 프로그램이 종료되지 않고 계속 실행되는 상태를 의미합니다.

1
2
3
4
5
int i = 0;
while (i < 5) {
    cout << i << " ";
    // i++; // 주석 처리로 인해 무한 루프 발생
}

위 코드에서 i++가 누락되어 i의 값이 계속 0으로 유지되므로 무한 루프가 발생합니다. 이를 방지하려면 반복문 내에서 조건을 변경하는 코드를 반드시 포함해야 합니다.

조건 변경에 대한 코드가 있더라도 무한루프에 빠지는 오류를 범할 수도 있습니다.

1
2
3
4
5
int i = 0;
while (i < 5);/* ; 로 인해 Body_Statement 실행불가! */  {
    cout << i << " ";
    // i++; // 주석 처리로 인해 무한 루프 발생
}

위와 같이 조건 표현식 바로 뒤에 ;를 사용하게 되면 해당 지점에서 반복이 일어나게 되고, Body_Statement가 실행되지 않아 무한루프에 빠지게 됩니다.
임베디드 시스템 등에서는 무한루프를 의도적으로 사용할 수도 있습니다.

break와 continue

  • break
    반복문을 즉시 종료하고 반복문 이후의 코드로 실행 흐름을 이동시킵니다.
    1
    2
    3
    4
    5
    
    for (int i = 0; i < 10; i++) {
        if (i == 5) break;
        cout << i << " ";
    }
    // Output: 0 1 2 3 4
    
  • continue
    현재 반복을 건너뛰고 다음 반복을 실행합니다.
    1
    2
    3
    4
    5
    
    for (int i = 0; i < 10; i++) {
        if (i % 2 == 0) continue;
        cout << i << " ";
    }
    // Output: 1 3 5 7 9
    

반복문 중첩 (Nested Loops)

반복문 안에 또 다른 반복문을 포함할 수 있습니다. 이때, 내부 반복문이 종료된 후 외부 반복문이 실행됩니다.

1
2
3
4
5
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 2; j++) {
        cout << "i: " << i << ", j: " << j << endl;
    }
}

위 코드의 출력은 다음과 같습니다:

1
2
3
4
5
6
i: 1, j: 1
i: 1, j: 2
i: 2, j: 1
i: 2, j: 2
i: 3, j: 1
i: 3, j: 2

중첩된 반복문은 복잡한 데이터 구조를 처리하거나 다차원 배열을 순회할 때 유용하게 사용됩니다.

중괄호를 생략한 경우

C++에서는 조건문이나 반복문에서 실행할 코드가 한 줄일 경우 중괄호 {}를 생략할 수 있습니다. 하지만 이는 코드의 가독성을 떨어뜨리거나 의도하지 않은 동작을 유발할 수 있으므로 주의가 필요합니다.

1
2
3
if (x > 0)
    cout << "Positive number";
    cout << "This line is always executed.";

위 코드에서 if 조건문에 중괄호를 생략했기 때문에 cout << "This line is always executed.";는 조건과 상관없이 항상 실행됩니다. 이를 방지하려면 중괄호를 명시적으로 작성해야 합니다.

1
2
3
4
if (x > 0) {
    cout << "Positive number";
    cout << "This line is always executed.";
}
1
2
3
while (x > 0)
    cout << x;
    cout << x--;

바로 위 예시에선, 들여쓰기 때문에 마치 두 줄이 while 루프 안에 있는 것처럼 보이지만, 실제로는 한 줄만 루프에 포함됩니다. 따라서 x는 루프 내에서 줄어들지 않아서 무한 루프가 발생 가능합니다. 반복문에서도 중괄호를 명시해야합니다.

1
2
3
4
while (x > 0){
    cout << x;
    cout << x--;
}

파일 입출력

파일 입출력 (File Input/Output)

C++에서는 파일을 읽고 쓰기 위해 <fstream> 헤더를 사용합니다. 주요 클래스는 다음과 같습니다:

  • ofstream: 파일에 데이터를 쓰기 위한 클래스
  • ifstream: 파일에서 데이터를 읽기 위한 클래스
  • fstream: 파일 읽기와 쓰기를 모두 지원하는 클래스

파일 쓰기 (Writing to a File)

파일에 데이터를 쓰기 위해 ofstream 객체를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ofstream outFile("example.txt");
    if (outFile.is_open()) {
        outFile << "Hello, File!" << endl;
        outFile << "This is a test." << endl;
        outFile.close();
    } else {
        cout << "Unable to open file for writing." << endl;
    }
    return 0;
}

위 코드는 example.txt 파일에 데이터를 작성합니다. 파일이 성공적으로 열리지 않으면 오류 메시지를 출력합니다.

파일 읽기 (Reading from a File)

파일에서 데이터를 읽기 위해 ifstream 객체를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ifstream inFile("example.txt");
    string line;
    if (inFile.is_open()) {
        while (getline(inFile, line)) {
            cout << line << endl;
        }
        inFile.close();
    } else {
        cout << "Unable to open file for reading." << endl;
    }
    return 0;
}

위 코드는 example.txt 파일의 내용을 한 줄씩 읽어와 출력합니다.

파일 읽기와 쓰기 (Reading and Writing)

fstream 객체를 사용하면 파일 읽기와 쓰기를 동시에 수행할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <fstream>
#include <iostream>
using namespace std;

int main() {
    fstream file("example.txt", ios::in | ios::out | ios::app);
    if (file.is_open()) {
        file << "Appending this line to the file." << endl;
        file.seekg(0); // 파일의 시작으로 이동
        string line;
        while (getline(file, line)) {
            cout << line << endl;
        }
        file.close();
    } else {
        cout << "Unable to open file." << endl;
    }
    return 0;
}

위 코드는 파일에 데이터를 추가한 후, 파일의 내용을 읽어 출력합니다.

파일 열기 모드 (File Open Modes)

파일을 열 때 사용할 수 있는 모드는 다음과 같습니다:

모드설명
ios::in읽기 모드로 파일 열기
ios::out쓰기 모드로 파일 열기
ios::app파일 끝에 데이터를 추가
ios::trunc파일 내용을 열 때 삭제
ios::binary바이너리 모드로 파일 열기

여러 모드를 조합하여 사용할 수 있습니다. 예를 들어, ios::in | ios::out은 읽기와 쓰기를 동시에 수행합니다.

파일 스트림 상태 확인

파일 스트림의 상태를 확인하기 위해 다음 멤버 함수를 사용할 수 있습니다:

  • is_open(): 파일이 성공적으로 열렸는지 확인
  • eof(): 파일의 끝에 도달했는지 확인
  • fail(): 파일 스트림에 오류가 발생했는지 확인
  • bad(): 심각한 오류가 발생했는지 확인
1
2
3
4
5
6
ifstream file("example.txt");
if (!file.is_open()) {
    cout << "Error opening file." << endl;
} else if (file.fail()) {
    cout << "File stream failed." << endl;
}

파일 입출력은 데이터를 영구적으로 저장하거나 외부 데이터를 처리할 때 매우 유용합니다.


해당 포스트는 서울대학교 전기정보공학부 정교민 교수님의 프로그래밍방법론 25-1학기 강의를 정리한 내용입니다.

This post is licensed under CC BY 4.0 by the author.