수색…
소개
어설 션 은 어설 션이 소프트웨어에서 발생하는 순간에 제시된 조건이 참이어야한다는 조건 자입니다. 가장 일반적인 것은 간단한 assertion 이며 실행 시간에 유효성이 검사됩니다. 그러나 정적 어설 션 은 컴파일 할 때 검사됩니다.
통사론
- 어설트 (표현)
- static_assert (표현식, 메시지)
- _Static_assert (표현식, 메시지)
매개 변수
매개 변수 | 세부 |
---|---|
표현 | 스칼라 유형의 표현. |
메시지 | 진단 메시지에 포함될 문자열 리터럴. |
비고
assert
와 static_assert
는 모두 assert.h
정의 된 매크로입니다.
assert
의 정의는 표준 라이브러리에 의해 정의되지 않은 매크로 NDEBUG
에 달려있다. NDEBUG
가 정의되면, assert
는 no-op입니다 :
#ifdef NDEBUG # define assert(condition) ((void) 0) #else # define assert(condition) /* implementation defined */ #endif
NDEBUG
를 항상 프로덕션 편집에 사용해야하는지 여부에 대한 의견이 다릅니다.
- 프로 캠프는
assert
callsabort
와 assertion 메시지는 최종 사용자에게 도움이되지 않는다고 주장하며 결과는 사용자에게 도움이되지 않는다고 주장한다. 프로덕션 코드를 검사 할 치명적인 조건이있는 경우 일반if/else
조건을 사용하고exit
하거나quick_exit
을 사용하여 프로그램을 종료해야합니다.abort
와는 대조적으로, 이것들은 프로그램이 (atexit
또는at_quick_exit
등록 된 함수를 통해) 약간의 클린업을 할 수있게 해준다. - 양 캠프는
assert
요청이 프로덕션 코드에서 결코 발생해서는 안된다고assert
하지만, 만약 그렇다면 확인 된 조건은 극적으로 잘못된 것이 있다는 것을 의미하며, 실행이 계속된다면 프로그램은 잘못 행동 할 것입니다. 따라서 생산 코드에서 주장을 활성화하는 것이 더 좋습니다. 왜냐하면 화재가 발생하면 지옥은 이미 느슨해 졌기 때문입니다. - 또 다른 옵션은 항상 검사를 수행하지만 개발 (
abort
이 적절한 경우)과 생산 ( '예기치 않은 내부 오류 - 기술 지원부에 문의하십시오'가 더 적절할 수 있음) 사이의 오류를 다르게 처리하는 가정 양조 시스템을 사용하는 것입니다.
static_assert
는 키워드 인 _Static_assert
확장됩니다. 조건은 컴파일 타임에 검사되므로 condition
은 상수 표현식이어야합니다. 개발과 생산간에 다르게 처리 할 필요는 없습니다.
사전 조건 및 사후 조건
어설 션을위한 하나의 유즈 케이스는 전제 조건과 사후 조건이다. 이것은 계약에 의해 불변성 과 디자인 을 유지하는 데 매우 유용 할 수 있습니다. 예를 들어 길이는 항상 0 또는 양수이므로이 함수는 0 또는 양수 값을 반환해야합니다.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int length2 (int *a, int count)
{
int i, result = 0;
/* Precondition: */
/* NULL is an invalid vector */
assert (a != NULL);
/* Number of dimensions can not be negative.*/
assert (count >= 0);
/* Calculation */
for (i = 0; i < count; ++i)
{
result = result + (a[i] * a[i]);
}
/* Postcondition: */
/* Resulting length can not be negative. */
assert (result >= 0);
return result;
}
#define COUNT 3
int main (void)
{
int a[COUNT] = {1, 2, 3};
int *b = NULL;
int r;
r = length2 (a, COUNT);
printf ("r = %i\n", r);
r = length2 (b, COUNT);
printf ("r = %i\n", r);
return 0;
}
단순한 주장
어설 션은 코드 라인에 도달했을 때 사실이 맞아야한다는 것을 주장하는 데 사용되는 명령문입니다. 어설 션은 예상 조건을 충족시키는 데 유용합니다. 어설 션에 전달 된 조건이 참일 때 아무런 조치가 없습니다. 잘못된 조건에 대한 동작은 컴파일러 플래그에 따라 다릅니다. 어설 션이 활성화되면 잘못된 입력은 즉각적인 프로그램 중지를 유발합니다. 장애인은 아무런 조치도 취하지 않습니다. 일반적으로 내부 및 디버그 빌드에서 어설 션을 활성화하고 릴리스 빌드에서 어설 션을 비활성화하는 것이 일반적입니다. 단, 릴리스에서 어설 션이 활성화되는 경우가 많습니다. (종료가 에러보다 좋든 나쁘 든간에 프로그램에 달려있다.) 어설 션은 내부 프로그래밍 오류를 잡는 데에만 사용해야한다. 내부 프로그래밍 오류는 일반적으로 나쁜 매개 변수를 전달한다는 의미이다.
#include <stdio.h>
/* Uncomment to disable `assert()` */
/* #define NDEBUG */
#include <assert.h>
int main(void)
{
int x = -1;
assert(x >= 0);
printf("x = %d\n", x);
return 0;
}
NDEBUG
가능한 출력은 다음과 같습니다.
a.out: main.c:9: main: Assertion `x >= 0' failed.
정의 된 NDEBUG
가능한 출력 :
x = -1
모든 어설 션을 사용하여 코드를 쉽게 컴파일하거나 컴파일 할 수 있도록 NDEBUG
전역 적으로 정의하는 것이 좋습니다. 이를 수행하는 쉬운 방법은 NDEBUG
를 컴파일러의 옵션으로 정의하거나 공유 구성 헤더 (예 : config.h
)에 정의하는 것입니다.
정적 주장
정적 어설 션은 코드 컴파일시 조건이 true인지 확인하는 데 사용됩니다. 그렇지 않은 경우, 컴파일러는 오류 메시지를 _ 행하고 컴파일 프로세스를 중지해야합니다.
정적 어서션은 컴파일 타임에 확인되며 런타임에는 검사되지 않습니다. 조건은 상수 표현식이어야하며, 거짓이면 컴파일러 오류가 발생합니다. 첫 번째 인수 인 확인 된 조건은 상수식이 고 두 번째 인수는 문자열 리터럴이어야합니다.
assert와 달리 _Static_assert
는 키워드입니다. 편의 매크로 static_assert
는 <assert.h>
정의되어 있습니다.
#include <assert.h>
enum {N = 5};
_Static_assert(N == 5, "N does not equal 5");
static_assert(N > 10, "N is not greater than 10"); /* compiler error */
C11 이전에는 정적 어설 션을 직접 지원하지 않았습니다. 그러나 C99에서는 컴파일 타임 조건이 false 인 경우 컴파일 실패를 트리거하는 매크로를 사용하여 정적 어설 션을 에뮬레이션 할 수 있습니다. _Static_assert
와 달리 두 번째 매개 변수는 적절한 토큰 이름이되어야 변수 이름을 만들 수 있습니다. 어설 션이 실패하면 구문 적으로 잘못된 배열 선언에서 해당 변수가 사용 되었기 때문에 변수 이름이 컴파일러 오류에 표시됩니다.
#define STATIC_MSG(msg, l) STATIC_MSG2(msg, l)
#define STATIC_MSG2(msg,l) on_line_##l##__##msg
#define STATIC_ASSERT(x, msg) extern char STATIC_MSG(msg, __LINE__) [(x)?1:-1]
enum { N = 5 };
STATIC_ASSERT(N == 5, N_must_equal_5);
STATIC_ASSERT(N > 5, N_must_be_greater_than_5); /* compile error */
C99 이전에는 블록의 임의 위치에서 변수를 선언 할 수 없었으므로이 매크로를 사용하는 것에 대해 매우 신중해야하므로 변수 선언이 유효한 위치에만 표시되도록해야합니다.
연결할 수없는 코드의 어설 션
개발 중에 특정 코드 경로가 제어 흐름의 범위를 assert(0)
하는 경우 assert(0)
을 사용하여 그러한 조건이 잘못되었음을 나타낼 수 있습니다.
switch (color) {
case COLOR_RED:
case COLOR_GREEN:
case COLOR_BLUE:
break;
default:
assert(0);
}
assert()
매크로의 인수가 false로 평가 될 때마다 매크로는 진단 정보를 표준 오류 스트림에 기록한 다음 프로그램을 중단합니다. 이 정보에는 assert()
문의 파일 및 행 번호가 포함되며 디버깅에 매우 유용 할 수 있습니다. 어설 션은 NDEBUG
매크로를 정의하여 비활성화 할 수 있습니다.
오류가 발생할 때 프로그램을 종료하는 또 다른 방법은 표준 라이브러리 함수 exit
, quick_exit
또는 abort
입니다. exit
및 quick_exit
는 사용자 환경으로 다시 전달 될 수있는 인수를 취합니다. abort()
(따라서 assert
)는 프로그램을 심각하게 종료시킬 수 있으며, 실행이 끝나면 수행되는 특정 정리는 수행되지 않을 수 있습니다.
assert()
의 가장 큰 장점은 자동으로 디버깅 정보를 출력한다는 것입니다. abort()
호출하면 어설 션처럼 비활성화 할 수 없다는 이점이 있지만 디버깅 정보가 표시되지 않을 수도 있습니다. 경우에 따라 두 구문을 함께 사용하면 효과적 일 수 있습니다.
if (color == COLOR_RED || color == COLOR_GREEN) {
...
} else if (color == COLOR_BLUE) {
...
} else {
assert(0), abort();
}
어설 션이 활성화 되면 assert()
호출은 디버그 정보를 출력하고 프로그램을 종료합니다. 실행은 abort()
호출에 도달하지 않습니다. 어설 션이 비활성화 되면 assert()
호출은 아무 것도하지 않고 abort()
가 호출됩니다. 이렇게하면이 오류 조건에 대해 프로그램이 항상 종료됩니다. 어설 션의 활성화 및 비활성화는 디버그 출력이 인쇄되는지 여부에 관계없이 영향을줍니다.
디버그 정보는 최종 사용자에게 도움이되지 않으며 abort
은 일반적으로 exit
또는 quick_exit
실행을 위해 설치된 정리 처리기를 금지하는 너무 심한 종료이기 때문에 프로덕션 코드에서 그러한 assert
을 남겨서는 안됩니다.
오류 메시지 어설 션
어설 션과 함께 오류 메시지를 표시 할 수있는 트릭이 있습니다. 일반적으로 다음과 같은 코드를 작성합니다.
void f(void *p)
{
assert(p != NULL);
/* more code */
}
어설 션이 실패하면 오류 메시지가 닮아 있습니다
어설 션 실패 : p! = NULL, 파일 main.c, 5 행
그러나 논리 AND ( &&
)를 사용하여 오류 메시지를 표시 할 수도 있습니다
void f(void *p)
{
assert(p != NULL && "function f: p cannot be NULL");
/* more code */
}
이제 어설 션이 실패하면 오류 메시지가 다음과 같이 표시됩니다.
어설 션 실패 : p! = NULL && "함수 f : p는 NULL 일 수 없습니다"파일 main.c, 5 행
이유는 이것이 작동하는 이유는 문자열 리터럴이 항상 0이 아닌 (true) 것으로 평가되기 때문입니다. 부울 표현식에 && 1
을 추가해도 아무 효과가 없습니다. 따라서 && "error message"
추가해도 컴파일러가 실패한 전체 표현식을 표시한다는 점만 다릅니다.