C에서 모든 작은 오류를 확인해야합니까?


60

훌륭한 프로그래머는 프로그램의 모든 단일 결과를 처리 할 강력한 코드를 작성해야합니다. 그러나 오류가 발생하면 C 라이브러리의 거의 모든 함수가 0 또는 -1 또는 NULL을 반환합니다.

예를 들어 파일을 열려고 할 때와 같이 오류 검사가 필요한 경우가 있습니다. 그러나 나는 종종 필요하다고 느끼지 않기 때문에 printf또는 오류로 인해 오류 검사를 무시 malloc합니다.

if(fprintf(stderr, "%s", errMsg) < 0){
    perror("An error occurred while displaying the previous error.");
    exit(1);
}

특정 오류를 무시하는 것이 좋습니까, 아니면 모든 오류를 처리하는 더 좋은 방법이 있습니까?


13
작업중인 프로젝트에 필요한 견고성 수준에 따라 다릅니다. 신뢰할 수없는 당사자 (예 : 공용 서버)로부터 입력을 받거나 완전히 신뢰할 수없는 환경에서 작동 할 가능성이있는 시스템은 코드가 시한 폭탄 (또는 가장 약한 링크가 해킹되는 것)이되지 않도록 매우 신중하게 코딩해야합니다. ). 분명히, 취미 및 학습 프로젝트에는 그러한 견고성이 필요하지 않습니다.
rwong

1
일부 언어는 예외를 제공합니다. 예외를 잡지 않으면 스레드가 종료되므로 상태가 나쁜 상태로 계속 유지하는 것보다 낫습니다. 예외를 포착하기로 선택한 경우 호출 된 함수 및 메소드를 포함하여 수많은 try명령문 에서 많은 오류를 하나의 명령문으로 처리 할 수 ​​있으므로 모든 단일 호출 또는 조작을 점검 할 필요는 없습니다. (또한 일부 언어는 널 역 참조 또는 배열 색인이 범위를 벗어난 것과 같은 간단한 오류를 감지 할 때 다른 언어보다 낫다는 점에 유의하십시오.)
Erik Eidt

1
문제는 대부분 방법론적인 것입니다. 일반적으로 구현할 대상을 파악할 때 오류 검사를 작성하지 않고 "행복한 경로"를 원하기 때문에 지금 오류 검사를 추가 하고 싶지 않습니다. 먼저. 그러나 당신은 여전히 malloc 과 co 를 확인해야합니다 . 오류 검사 단계를 건너 뛰는 대신 코딩 활동의 우선 순위를 정하고 TODO 목록에서 오류 검사를 암시 적 영구 리팩토링 단계로 두어야합니다. grepable "/ * CHECK * /"주석을 추가하면 도움이 될 수 있습니다.
coredump

7
나는 아무도 언급되지 않았다는 것을 믿을 수 없다 errno! 이 것이 사실이지만 경우에 당신은 익숙하지 않은 "거의 C 라이브러리의 모든 기능을 반환합니다 0 또는 -1 또는 NULL오류가있는 때,"그들은 또한 글로벌 설정 errno변수를 사용하여 액세스 할 수있는, #include <errno.h>그리고 단순히 읽기 의 값 errno. 경우에 따라서, 예를 들어, open(2) 반환 -1, 당신이 있는지 여부를 확인 할 수 있습니다 errno == EACCES사용 권한 오류를 표시하거나 것이다, ENOENT요청 된 파일이 존재하지 않는 것을 나타냅니다 것이다.
wchargin

1
@ErikEidt C는 try/을 지원하지 않지만 catch점프로 시뮬레이션 할 수는 있습니다.
마스트

답변:


29

일반적으로 코드는 적절한 곳에서 예외적 인 조건을 처리해야합니다. 예, 이것은 모호한 진술입니다.

소프트웨어 예외 처리 기능이있는 고급 언어에서는 종종 "실제로 무언가를 할 수있는 방법에서 예외를 잡아라"라고 말합니다. 파일 오류가 발생한 경우 실제로 사용자에게 "파일을 디스크에 저장하지 못했습니다"라고 말할 수있는 UI 코드로 스택을 버블 링 할 수 있습니다. 예외 메커니즘은 "모든 작은 오류"를 효과적으로 삼키고 적절한 위치에서 암시 적으로 처리합니다.

C에서는 그 사치가 없습니다. 오류를 처리하는 몇 가지 방법이 있으며, 그 중 일부는 언어 / 라이브러리 기능이며, 일부는 코딩 방법입니다.

특정 오류를 무시하는 것이 좋습니까, 아니면 모든 오류를 처리하는 더 좋은 방법이 있습니까?

특정 오류를 무시 하시겠습니까? 아마도. 예를 들어 표준 출력에 대한 쓰기가 실패하지 않는다고 가정하는 것이 합리적입니다. 그것이 실패하면 어쨌든 사용자에게 어떻게 말할 것입니까? 예, 특정 오류를 무시하거나 오류를 방지하기 위해 방어 적으로 코드를 작성하는 것이 좋습니다. 예를 들어, 나누기 전에 0을 확인하십시오.

모든 또는 적어도 대부분의 오류를 처리하는 방법이 있습니다.

  1. 오류 처리를 위해 gotos와 유사한 점프를 사용할 수 있습니다 . 소프트웨어 전문가들 사이에 논쟁의 여지가 있지만, 특히 임베디드 및 성능에 중요한 코드 (예 : Linux 커널)에서 소프트웨어를 올바르게 사용하고 있습니다.
  1. 계단식 ifs :

    if (!<something>) {
      printf("oh no 1!");
      return;
    }
    if (!<something else>) {
      printf("oh no 2!");
      return;
    }
    
  2. 첫 번째 조건 (예 : 파일 열기 또는 만들기)을 테스트 한 다음 후속 작업이 성공한다고 가정합니다.

강력한 코드가 좋으며 오류를 확인하고 처리해야합니다. 코드에 가장 적합한 방법은 코드의 기능, 장애의 심각도 등에 따라 달라지며 사용자 만이 진정으로 대답 할 수 있습니다. 그러나 이러한 방법은 실제 테스트를 통해 실제 코드에서 오류를 확인하는 방법을 확인할 수있는 다양한 오픈 소스 프로젝트에서 전투 테스트를 거쳤습니다.


21
죄송합니다, 여기에서 선택하는 작은 nit- "표준 출력에 쓰는 것은 실패하지 않습니다. 그것이 실패하면 어떻게하면 사용자에게 말하겠습니까?" -표준 오류에 기록하여? 한쪽에 쓰지 못한다는 것이 다른쪽에 쓰지 못한다는 보장은 없습니다.
Damien_The_Unbeliever

4
@Damien_The_Unbeliever-특히 stdout파일로 경로 재 지정 될 수 있거나 시스템에 따라 존재하지 않을 수도 있습니다. stdout"콘솔에 쓰기"스트림 은 아니지만 일반적으로 그렇지 않지만 반드시 그럴 필요는 없습니다.
Davor Ždralo

3
@JAB 당신에게 메시지를 보내기 stdout... 명백한 :-)
TripeHound

7
@JAB : 적절한 경우 EX_IOERR로 종료 할 수 있습니다.
John Marshall

6
당신이 무엇을 하든지 아무 일도 일어나지 않는 것처럼 달리기를 계속 하지 마십시오 ! dump_database > backups/db_dump.txt주어진 지점에서 명령 이 표준 출력에 쓰지 못하면 명령 이 성공적으로 수행되고 종료되는 것을 원하지 않습니다. (데이터베이스는 그런 식으로 백업되지 않지만 요점은 여전히 ​​유효합니다)
Ben S

15

그러나 오류가 발생하면 C 라이브러리의 거의 모든 함수가 0 또는 -1 또는 NULL을 반환합니다.

예,하지만 어떤 기능 을 사용했는지 알고 있습니까?

실제로 오류 메시지에 넣을 수있는 많은 정보가 있습니다. 호출 된 함수, 호출 한 함수의 이름, 전달 된 매개 변수 및 함수의 리턴 값을 알고 있습니다. 그것은 매우 유익한 오류 메시지에 대한 많은 정보입니다.

모든 함수 호출에 대해이 작업을 수행 할 필요는 없습니다. 그러나 실제로 필요한 정보가 유용한 정보 인 경우 "이전 오류를 표시하는 동안 오류가 발생했습니다"라는 오류 메시지가 처음 표시 될 때 즉시 해당 오류 메시지가 표시됩니다. 문제 해결에 도움이되는 정보를 제공하는 오류 메시지.


5
이 답변은 사실이기 때문에 나를 웃게 만들었지 만 질문에 대답하지는 않습니다.
RubberDuck

질문에 어떻게 대답하지 않습니까?
Robert Harvey

2
C (및 다른 언어)가 부울 1과 0을 혼합했다고 생각하는 한 가지 이유는 오류 코드를 반환하는 괜찮은 함수가 오류없이 "0"을 반환하는 것과 같은 이유입니다. 그러나 당신은 말해야 if (!myfunc()) {/*proceed*/}합니다. 0은 오류가 없었으며 0이 아닌 것은 다소 오류였으며 각 오류는 0이 아닌 자체 코드를 가졌습니다. 내 친구가 말한 방식은 "진실은 하나 뿐이지 만 많은 거짓이 있습니다." if()테스트 에서 "0"은 "true"여야 하고 0이 아닌 것은 "false"여야합니다.
robert bristow-johnson

1
아니요, 귀하의 방식대로하면 가능한 부정적인 오류 코드가 하나뿐입니다. 그리고 가능한 많은 no_error 코드.
robert bristow-johnson 3

1
@ robertbristow-johnson : 그런 다음 "오류가 없습니다."라고 읽습니다. if(function()) { // do something }"오류없이 함수가 실행되면 무언가를 수행하십시오"로 읽습니다. 또는 "함수가 성공적으로 실행되면 무언가를 수행하십시오." 진지하게, 협약을 이해하는 사람들 은 이것으로 혼란스럽지 않습니다 . false오류가 발생했을 때 (또는 0)을 반환 하면 오류 코드를 사용할 수 없습니다. 0은 C에서 거짓을 의미합니다. 수학이 작동하는 방식이기 때문입니다.
Robert Harvey

11

TLDR; 거의 오류를 무시해서는 안됩니다.

C 언어에는 각 라이브러리 개발자가 자체 솔루션을 구현할 수있는 오류 처리 기능이 부족합니다. 보다 현대적인 언어에는 예외가 내장되어있어이 특정 문제를 훨씬 쉽게 처리 할 수 ​​있습니다.

그러나 C에 갇혀 있으면 그러한 특권이 없습니다. 불행히도 원격 실패 가능성이있는 함수를 호출 할 때마다 가격을 지불하면됩니다. 그렇지 않으면 실수로 메모리의 데이터를 덮어 쓰는 것과 같은 훨씬 더 나쁜 결과를 겪게됩니다. 따라서 일반적으로 항상 오류를 확인해야합니다.

귀국 여부를 확인하지 않으면 fprintf버그가 남아있을 가능성이 높습니다. 최상의 경우 사용자가 기대하는 것을 수행하지 못하고 최악의 경우 비행 중에 전체가 폭발합니다. 그런 식으로 자신을 훼손 할 변명이 없습니다.

그러나 C 개발자는 코드를 쉽게 유지 관리하는 것이 귀하의 임무입니다. 따라서 때로는 애플리케이션의 전체 동작에 위협이되지 않는 경우 명확성을 위해 오류를 무시 해도됩니다. .

이 작업을 수행하는 것과 같은 문제입니다.

try
{
    run();
} catch (Exception) {
    // comments expected here!!!
}

빈 캐치 블록 안에 좋은 주석이 없으면 이것이 문제입니다. 호출이 malloc()100 %의 시간 동안 성공적으로 실행될 것이라고 생각할 이유가 없습니다 .


유지 관리가 정말 중요한 측면이며 해당 단락이 실제로 내 질문에 답했습니다. 자세한 답변 주셔서 감사합니다.
Derek 朕 會 功夫

나는 데스크탑 프로그램이 단지 몇 MB의 메모리만을 사용하는 경우 malloc 호출을 확인하려고하지만 결코 충돌하지 않는 프로그램의 gazillions가 게으른 이유라고 생각합니다.
whatsisname

5
@whatsisname : 충돌하지 않는다고해서 데이터가 자동으로 손상되지는 않습니다. malloc()하나 개의 기능은 당신이 확실히 에 반환 값을 확인하시기 바랍니다!
TMN

1
@TMN : malloc이 실패하면 프로그램은 즉시 segfault하고 종료되며, 작업을 수행하지 않습니다. 그것은 "거의 결코"가 아니라 위험과 적절한 것에 관한 것입니다.
whatsisname

2
@Alex C는 오류 처리 능력이 부족하지 않습니다. 예외를 원하면 사용할 수 있습니다. 예외를 사용하지 않는 표준 라이브러리는 신중한 선택입니다 (예외로 인해 자동화 된 메모리 관리가 필요하며 종종 저수준 코드에서는 원하지 않습니다).
martinkunev

9

질문은 실제로 언어에 국한된 것이 아니라 사용자에 따라 다릅니다. 사용자 관점에서 질문에 대해 생각하십시오. 사용자는 명령 행에 프로그램 이름을 입력하고 Enter 키를 누르는 등의 작업을 수행합니다. 사용자는 무엇을 기대합니까? 문제가 발생했는지 어떻게 알 수 있습니까? 오류가 발생하면 중재 할 여유가 있습니까?

많은 유형의 코드에서 이러한 검사는 과도합니다. 그러나 원자로의 코드와 같은 신뢰성이 높은 안전 핵심 코드에서 병리학 적 오류 점검 및 계획된 복구 경로는 일상 업무의 일부입니다. "X가 실패하면 어떻게 되나요? 어떻게 안전한 상태로 돌아가나요?"라고 묻는 데 시간이 걸리는 것으로 여겨집니다. 비디오 게임용 코드와 같이 안정성이 떨어지는 코드에서는 훨씬 적은 오류 검사로 벗어날 수 있습니다.

또 다른 유사한 접근법은 오류를 포착하여 실제로 상태를 얼마나 개선 할 수 있습니까? 예외를 자랑스럽게 포착하는 C ++ 프로그램의 수를 셀 수는 없습니다. 실제로 처리해야 할 작업을 알지 못했기 때문에 다시 throw하기 위해 예외 처리를해야한다는 것을 알고있었습니다. 이러한 프로그램은 추가적인 노력으로 얻은 것이 없습니다. 단순히 오류 코드를 확인하지 않는 것보다 실제로 상황을 더 잘 처리 할 수 ​​있다고 생각되는 오류 확인 코드 만 추가하십시오. 즉, C ++에는 예외 처리 중에 발생하는 예외를보다 의미있는 방식으로 포착하기 위해 발생하는 예외를 처리하기위한 특정 규칙이 있습니다 (따라서, 여러분이 직접 만든 장례식 pyre가 점등되도록하기 위해 terminate ()를 호출하는 것을 의미합니다 적절한 영광으로)

병리학적인 방법은 무엇입니까? 필자는 1과 0의 균일 한 분포를 신중하게 선택한 true 및 false 값을 가진 "SafetyBool"을 정의한 프로그램에서 작업했으며 여러 하드웨어 오류 중 하나 (단일 비트 플립, 데이터 버스)를 선택했습니다. 흔적이 깨지는 등)로 인해 부울이 잘못 해석되지 않았습니다. 말할 것도없이, 나는 이것이 이전 프로그램에서 사용되는 범용 프로그래밍 관행이라고 주장하지는 않을 것이다.


6
  • 다른 안전 요구 사항에는 다른 수준의 정확성이 필요합니다. 항공 또는 자동차 제어 소프트웨어에서 모든 반환 값을 확인합니다 (cf. MISRA.FUNC.UNUSEDRET . 기계를 떠나지 않는 빠른 개념 증명으로, 아마도 아닙니다.
  • 코딩에는 시간이 걸립니다. 안전하지 않은 중요 소프트웨어의 관련없는 검사가 너무 많으면 다른 곳에서 더 잘 노력할 수 있습니다. 그러나 품질과 비용의 장점은 어디에 있습니까? 디버깅 도구와 소프트웨어 복잡성에 따라 다릅니다.
  • 오류 처리는 제어 흐름을 방해하고 새로운 오류를 유발할 수 있습니다. 나는 최소한 오류를보고하는 Richard "network"Stevens의 래퍼 함수를 ​​아주 좋아한다.
  • 오류 처리가 성능 문제가되는 경우는 거의 없습니다. 그러나 대부분의 C 라이브러리 호출은 시간이 오래 걸리므로 반환 값을 확인하는 데 드는 비용이 엄청나게 작습니다.

3

질문에 대한 추상적 인 설명이 조금 있습니다. 그리고 반드시 C 언어를위한 것은 아닙니다.

더 큰 프로그램의 경우 추상화 계층이 있습니다. 엔진, 라이브러리 또는 프레임 워크의 일부일 수 있습니다. 이 레이어는 유효한 데이터를 얻는 날씨에 신경 쓰지 않거나 출력은 기본값 0-1, null등이됩니다.

그런 다음 추상 계층에 대한 인터페이스가 될 계층이 있으며, 많은 오류 처리 및 종속성 주입, 이벤트 수신 등과 같은 다른 작업을 수행합니다.

나중에 규칙을 설정하고 출력을 처리하는 구체적인 구현 계층을 갖게됩니다.

따라서이 부분은 단순히 코드 작업에서 오류 처리를 완전히 제외하는 것이 더 좋습니다. 해당 부분은 단순히 해당 작업을 수행하지 않기 때문입니다. 그런 다음 출력을 평가하고 오류를 가리키는 프로세서가 있습니다.

이것은 대부분 코드 가독성과 확장 성을 향상시키는 책임을 분리하기 위해 수행됩니다.


0

일반적으로 해당 오류 상태를 확인 하지 않을 만한 충분한 이유가 없는 경우 가 있습니다. 내가 오류 상태를 확인하지 않기 위해 생각할 수있는 유일한 이유는 실패하면 의미있는 일을 할 수 없을 때입니다. 항상 실행 가능한 옵션이 하나 있기 때문에 이것은 매우 어려운 바입니다.


오류는 설명하지 않은 상황이므로 일반적으로 동의하지 않습니다. 어떤 조건에서 오류가 발생했는지 모르는 경우 오류가 어떻게 나타날 수 있는지 알기가 어렵습니다. 따라서 실제 데이터 처리 전에 오류 처리.
Creative Magic

내가 말했듯이 오류가 발생하거나 오류가 발생하면 오류를 수정하고 계속하는 방법을 모르더라도 항상 정상적으로 실패하고 오류를 기록하며 사용자에게 예상대로 작동하지 않는다고 알릴 수 있습니다. 요점은 오류가 눈에 띄지 않거나 기록되지 않거나 "자동으로 실패"하거나 응용 프로그램이 충돌하지 않아야한다는 것입니다. 그것이 "정상적으로 실패"또는 "정상적으로 종료"라는 의미입니다.
Clever Neologism
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.