HOWTO · C++

C++ 비 void 함수에서 제어 흐름이 끝에 도달하는 오류 해결 방법

C++의 non-void 함수가 닫는 중괄호에 도달할 수 있는 이유와 모든 반환 경로를 처리하는 방법, 그리고 대체 반환값이나 컴파일러 경고가 도움이 되는 상황을 알아보세요.

이 페이지의 내용

C++에서 반환형이 void가 아닌 함수는 main의 끝에 도달하면 암묵적으로 0을 반환하는 경우를 제외하고, 실행 가능한 모든 경로에서 값을 반환해야 합니다. 다른 비 void 함수에서 실행 가능한 경로가 return 값 없이 닫는 중괄호에 도달하면 동작은 정의되지 않습니다. 코드와 경고 설정에 따라 컴파일러가 해당 경로에 진단을 표시할 수도 있습니다. 문제를 해결하려면 각 분기를 따라가며 함수의 목적에 맞는 값을 반환하도록 하세요. (비 void 함수)

오류 이해하기

반환값이 있는 함수는 선언된 형식의 값을 반환해야 합니다. 흔히 “control reaches end of non-void function”이라고 불리는 컴파일러 진단은 해당 값을 반환하지 않은 채 닫는 중괄호에 도달할 수 있는 경로를 가리킵니다. 문제는 단순히 함수 끝에 문장이 빠진 것만이 아닙니다. 앞쪽의 조건문 때문에 일부 입력에서 결과가 반환되지 않을 수 있습니다. 예를 들어 int add(int a, int b) { if (a > b) return a + b; }는 a <= b일 때 값을 반환하지 않습니다. main을 제외한 비 void 함수가 이런 식으로 끝에 도달하면 동작은 정의되지 않으며, 특정한 충돌이나 결과가 보장되는 것은 아닙니다.

다음 예제를 실행해 보세요.

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int main() {
    std::cout << add(5, 3) << '\n';
}
8

이 입력에서는 프로그램이 합계를 반환합니다. 위의 인라인 예제는 진단이 발생하는 원인을 보여 주고, 실행 가능한 버전은 모든 입력에 값을 반환해 문제를 해결합니다.

모든 경로에서 값 반환하기

조건을 따라가며 각 경우가 어떤 의미여야 하는지 판단하세요. 덧셈 함수라면 피연산자의 순서와 관계없이 두 수의 합을 반환해야 합니다. 다른 작업 때문에 조건문이 필요하다면 마지막 return으로 나머지 경로를 처리할 수 있습니다. 두 분기에서 같은 작업을 한다면 조건문을 없애도 됩니다.

#include <iostream>

int add(int a, int b) {
    if (a > b) {
        return a + b;
    }
    return a + b;
}

int main() {
    std::cout << add(3, 5) << '\n';
}

각 경우의 의미가 서로 다를 때는 모든 분기를 명시하는 구조가 유용합니다. 다음 예제는 두 경로를 모두 처리하면서 더 큰 피연산자를 반환합니다.

#include <iostream>

int larger(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

int main() {
    std::cout << larger(5, 3) << '\n';
}

중첩된 조건문과 switch 문도 확인하세요. 비 void 함수의 각 실행 경로는 값을 반환하거나, 다른 방식으로 닫는 중괄호에 도달하지 않아야 합니다.

기본 반환값 사용하기

마지막 return은 앞선 조건에 일치하지 않는 경우를 처리하는 대체 반환값이 될 수 있습니다. 단, 그 값이 함수의 계약에 부합할 때만 사용하세요. 예를 들어 이 조회 함수는 이름이 일치하면 해당 점수를 반환하고, 이름이 없으면 0을 반환합니다. 애플리케이션에서 점수가 없는 경우를 0점으로 정의한 경우에만 이 대체값이 적절합니다.

#include <iostream>
#include <string>

int scoreFor(const std::string& name) {
    if (name == "Ada") {
        return 10;
    }
    return 0;
}

int main() {
    std::cout << scoreFor("Ada") << '\n';
    std::cout << scoreFor("Lin") << '\n';
}

항목이 없다는 것이 별도의 결과라면 0을 반환할 때 그 차이가 감춰질 수 있습니다. 선택적 값이나 명시적인 상태처럼 실제 계약을 나타내는 반환 형식과 결과를 선택하세요. 진단 메시지를 없애려고 임의의 대체값을 추가하지 마세요.

컴파일러 경고로 디버깅하기

컴파일할 때 경고를 활성화하면 컴파일러가 확인할 경로를 찾는 데 도움을 줄 수 있습니다. 예를 들어 g++ -Wall -Wextra -Wreturn-type your_code.cpp -o your_program 명령은 관련 반환 경로 진단을 포함해 여러 경고를 GCC에 요청합니다. 경고의 제공 여부와 문구는 컴파일러, 버전, 옵션, 코드에 따라 다릅니다. 경고는 제어 흐름을 추적해 보라는 단서이지, 모든 컴파일러가 코드를 똑같이 처리한다는 증거는 아닙니다.

중첩된 if 문과 모든 switch 분기를 포함해 각각의 분기를 살펴보세요. 그런 다음 각 입력에서 함수가 어떤 동작을 해야 하는지 판단하고, 그 동작에 맞는 값을 반환하세요. 개발 중에는 경고를 활성화해 두면 비슷한 경로를 더 쉽게 발견할 수 있습니다.

결론

C++의 비 void 함수가 값을 반환하지 않은 채 닫는 중괄호에 도달할 수 있다면, 해당 경로를 추적해 함수 로직을 수정하세요. 각 경우에 함수의 목적에 맞는 값을 반환하도록 하세요. 대체값은 의미가 명확할 때만 사용하고, 컴파일러 경고는 검토할 경로를 찾는 데 활용하세요.

FAQ

“control reaches end of non-void function” 진단은 왜 발생하나요?

값을 반환하는 함수에서 실행 가능한 경로가 값을 반환하지 않은 채 닫는 중괄호에 도달하기 때문입니다. 컴파일러가 이 상태를 보고할 수 있지만, 진단 문구와 빌드 동작은 컴파일러와 옵션에 따라 다릅니다.

모든 if 분기에서 return을 사용해야 하나요?

아니요. 조건문 뒤의 return으로 나머지 경로를 처리할 수 있습니다. 중요한 점은 실행 가능한 경로 중 어느 것도 값 없이 함수 끝에 도달하지 않게 하는 것입니다.

경고를 없애려고 return 0;을 추가해야 하나요?

함수의 계약상 0이 유효한 결과일 때만 그렇게 하세요. 그렇지 않다면 누락된 경우를 정확히 나타내는 결과나 반환 형식을 선택해야 합니다.

컴파일러 경고로 값이 빠진 경로를 찾을 수 있나요?

끝까지 실행이 이어질 수 있는 함수를 경고가 가리켜 줄 수 있습니다. 그 경고를 참고해 제어 흐름을 살펴보세요. 경고 문구와 동작은 컴파일러와 설정에 따라 다릅니다.