C의 정적 주장


답변:


91

C11 표준은 _Static_assert키워드를 추가합니다 .

이것은 gcc-4.6 이후구현되었습니다 .

첫 번째 슬롯은 정수 상수 표현식이어야합니다. 두 번째 슬롯은 긴 ( _Static_assert(0, L"assertion of doom!")) 일 수있는 상수 문자열 리터럴입니다 .

이것은 최신 버전의 clang에서도 구현된다는 점에 유의해야합니다.


4
[... gcc, clang에 의해 구현되는 것 같습니다 ...] ;-) 가 C11 표준의 일부이며 C11을 지원하는 모든 컴파일러가이를 가질 것이라는 점을 더 단언 할 수 있습니다 _Static_assert.
PP

1
파일 범위에서 사용할 수 있습니까 (기능 외부)? 내가 error: expected declaration specifiers or '...' before 'sizeof'줄을 얻기 때문에 static_assert( sizeof(int) == sizeof(long int), "Error!); (나는 C ++이 아닌 C를 사용하고 있습니다)
user10607

@ user10607 이것이 작동하지 않는다는 것에 놀랐습니다. 잠깐, 오류 문자열 끝에 따옴표가 없습니다. 그것을 넣고 돌아 오십시오. 이것은 gcc-4.9에서 나를 위해 작동합니다. _Static_assert( sizeof(int) == sizeof(long int), "Error!");내 macine에서 오류가 발생합니다.
emsr 2014

Ubuntu에 gcc 4.8.2가 있습니다. 누락 된 따옴표는 주석 오타였습니다 (코드에 포함). 이것은 두 개의 헤더가 포함 된 후 파일의 첫 번째 줄입니다. 컴파일러는 나에게 정확히 동일한 두 가지 오류를 제공합니다. error: expected declaration specifiers or '...' before 'sizeof'AND error: expected declaration specifiers or '...' before string constant(그는 "Error!"문자열을 참조하고 있습니다 ) (또한 : -std = c11로 컴파일하고 있습니다. 함수 내부에 선언을 넣으면 모두 잘 작동합니다 (예상대로 실패 및 성공))
user10607

2
@ user10607 또한 명령 줄에 -std = gnu11을 지정해야했습니다. 4.8과 4.8 사이에 차이가 있다는 것에 정말 놀랐습니다. 한 줄만있는 소스가 있습니다. 또한 _Static_assertC ++ ish가 아닌 C 표준을 사용했습니다 static_assert. static_assert 매크로를 얻으려면`#include <assert.h>가 필요합니다.
emsr 2014

93

이것은 함수 및 비 함수 범위에서 작동합니다 (구조체, 조합 내부에서는 작동하지 않음).

  1. 컴파일 시간 어설 션이 일치하지 않으면 GCC에서 거의 이해할 수있는 메시지가 생성됩니다. sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. 매크로는 typedef에 대한 고유 한 이름을 생성하기 위해 변경 될 수 있거나 변경되어야합니다 (예 : 이름 __LINE__끝에 연결 static_assert_...).

  3. 삼항 대신에, 이것은 #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]녹슨 오래된 cc65 (6502 cpu 용) 컴파일러에서도 작동하는 경우 에도 사용될 수 있습니다 .

업데이트 : 완전성을 위해 여기에 버전이 있습니다.__LINE__

UPDATE2 : GCC 특정 코드

GCC 4.3 (내 생각 엔)은 "오류"및 "경고"기능 속성을 도입했습니다. 해당 속성이있는 함수에 대한 호출이 데드 코드 제거 (또는 기타 조치)를 통해 제거 될 수없는 경우 오류 또는 경고가 생성됩니다. 이것은 사용자 정의 실패 설명으로 컴파일 시간 어설 션을 만드는 데 사용할 수 있습니다. 더미 함수에 의존하지 않고 네임 스페이스 범위에서 사용할 수있는 방법을 결정해야합니다.

그리고 이것이 어떻게 생겼는지 :


1
Visual Studio에서 그냥 "음의 첨자", 변수 이름을 언급하지 ... 말한다
SZX

Nordic Mainframe-답변의 옵션 3은 clang에서 작동하지 않습니다.
Elazar

1
마지막 (GCC 4.3+ 관련) 솔루션에 관하여 : 이것은 최적화 프로그램이 알아낼 수있는 모든 것을 확인할 수 있기 때문에 매우 강력하지만 최적화가 활성화되지 않으면 실패합니다. 최소한의 최적화 수준 ( -Og)은 이것이 작동하기에 충분할 수 있지만 디버깅을 방해해서는 안됩니다. __OPTIMIZE__(and __GNUC__)가 정의되지 않은 경우 정적 assert를 no-op 또는 런타임 assert로 만드는 것을 고려할 수 있습니다 .
Søren Løvborg 2014 년

LINE 버전이 포함 된 코드 스 니펫 (UPDATE : 완전성을 위해 여기에`LINE이 포함 된 버전)이 있습니다. 컴파일 할 때 (STATIC_ASSERT (X, static_assertion_at_line _ ## L)) 줄에서 오류가 발생하며 하나 더 추가하여 수정할 수 있습니다. 아래와 같은 수준 : #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, ""Assertion at : ## L "");
sundar

나는 __LINE__gcc 4.1.1 의 버전 과 비슷한 것을 사용합니다 ... 두 개의 다른 헤더가 같은 번호가 매겨진 줄에 하나가있을 때 가끔 짜증이납니다!
MM

10

cl

질문에 gcc가 명시 적으로 언급되어 있다는 것을 알고 있지만 여기에서 완전성을 위해 Microsoft 컴파일러에 대한 조정이 있습니다.

음수 크기의 배열 typedef를 사용하면 cl 이 괜찮은 오류를 내도록 설득하지 못합니다 . 그것은 단지 말한다 error C2118: negative subscript. 이 점에서 너비가 0 인 비트 필드가 더 좋습니다. 여기에는 구조체 형식화가 포함되므로 고유 한 형식 이름을 사용해야합니다. __LINE__겨자를 자르지 않습니다 COMPILE_TIME_ASSERT(). 헤더와 소스 파일에서 같은 줄에있는 것이 가능하며 컴파일이 중단됩니다. __COUNTER__구조에 온다 (그리고 4.3 이후 gcc에 있었다).

지금

아래 cl제공 :

오류 C2149 : 'static_assertion_failed_use_another_compiler_luke': 명명 된 비트 필드의 너비는 0 일 수 없습니다.

Gcc는 또한 이해하기 쉬운 메시지를 제공합니다.

오류 : 비트 필드 'static_assertion_failed_use_another_compiler_luke'의 너비가 0입니다.


4

에서 위키 백과 :


15
진정한 소스에 연결했다면 더 좋을 것입니다 : jaggersoft.com/pubs/CVu11_3.html
Matt Joiner

gcc 4.6에서는 작동하지 않습니다. "케이스 레이블이 정수 상수로 축소되지 않습니다"라고 말합니다. 요점이 있습니다.
Liosan 2013

당신은 아마 지금까지 waaay로 움직였을 것입니다. 그러나 나는 결국 제 자신의 글을 작성했습니다. 내 답변 ). 나는 나를 돕기 위해 당신의 연결 @MattJoiner를 사용
Hashbrown

성가 시다면 @Liosan이 효과가 있는지 알려주세요. 방금 C ++에 대한 탐구를 시작했기 때문에 파티에 늦었습니다.
Hashbrown

Visual C ++의 경우 버전 2010부터 static_assert가 기본 제공되며 C ++ 및 C 모드에서 모두 작동합니다. 그러나 c99 _Static_assert가 내장되어 있지 않습니다.
ddbug 2010 년

3

다음을 사용하여 솔루션을 사용 하지 않는 것이 좋습니다 typedef.

typedef키워드가 있는 배열 선언 은 컴파일 타임에 평가된다는 보장이 없습니다. 예를 들어 블록 범위의 다음 코드가 컴파일됩니다.

대신 이것을 권장합니다 (C99에서) :

때문에 static키워드 컴파일 타임에 배열이 정의됩니다. 이 assert는 COND컴파일 타임에 평가되는 경우 에만 작동 합니다. 변수에 할당 된 값과 같이 메모리의 값을 기반으로하는 조건에서는 작동하지 않습니다 (즉, 컴파일이 실패합니다).


4
이것이 작동하지만 메모리 요구 사항도 증가합니다.
sherrellbc

1
오류 : 'static_assertion_INVALID_CHAR_SIZE'가 정의되었지만 사용되지 않음 [-Werror = unused-variable]
Alex

2

와 함께 STATIC_ASSERT () 매크로를 사용하는 경우 __LINE__.c 파일의 항목과 헤더 파일의 다른 항목 사이에 __INCLUDE_LEVEL__.

예 :


1

고전적인 방법은 배열을 사용하는 것입니다.

어설 션이 true이면 배열의 크기가 1이고 유효하기 때문에 작동하지만 false이면 크기 -1은 컴파일 오류를 제공합니다.

대부분의 컴파일러는 변수의 이름을 표시하고 어설 션에 대한 최종 주석을 남길 수있는 코드의 오른쪽 부분을 가리 킵니다.


이것을 제네릭 #define STATIC_ASSERT()타입 매크로 로 래핑 하고 제네릭 예제에서 더 많은 일반 예제와 샘플 컴파일러 출력을 STATIC_ASSERT()제공하면 훨씬 더 많은 업 보트를 얻을 수 있고이 기술이 더 합리적이라고 생각합니다.
가브리엘 스테이 플스

동의하지 않습니다. 컴파일러는 생각 매크로를보고 더 혼란스러운 메시지를 제공합니다.
Paolo.Bolzoni

1

Perl에서 특히 perl.h3455 행 ( <assert.h>사전에 포함됨) :

static_assert가능한 경우 (에서<assert.h> ) 사용됩니다. 그렇지 않고 조건이 거짓이면 음수 크기의 비트 필드가 선언되어 컴파일이 실패합니다.

STMT_START/ STMT_END는 각각 do/로 확장되는 매크로 while (0)입니다.


1

때문에:

  1. _Static_assert() 이제 모든 버전의 C에 대해 gcc에서 정의됩니다.
  2. static_assert() C ++ 11 이상에서 정의 됨

따라서 다음과 같은 간단한 매크로가 STATIC_ASSERT()작동합니다.

  1. C ++ :
    1. C ++ 11 ( g++ -std=c++11) 이상
  2. 씨:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (지정된 표준 없음)

STATIC_ASSERT다음과 같이 정의하십시오 .

이제 사용하십시오.

예 :

gcc 4.8.4를 사용하여 Ubuntu에서 테스트되었습니다.

예 1 : 좋은 gcc출력 (예 : STATIC_ASSERT()코드는 작동하지만 조건이 거짓이어서 컴파일 타임 어설 션이 발생 함) :

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c : In function ' main'static_assert.c
: 78 : 38 : error : static assertion failed : "(1> 2) failed"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "("#test_for_true ") 실패")
^
static_assert.c : 88 : 5 : 참고 : 매크로 'STATIC_ASSERT'확장시
STATIC_ASSERT (1> 2);
^

예제 2 : 좋은 g++ -std=c++11출력 (예 : STATIC_ASSERT()코드는 작동하지만 조건이 거짓이어서 컴파일 타임 어설 션이 발생 함) :

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c : In function 'int main ()'
static_assert.c : 74 : 32 : error : static assertion failed : (1> 2) failed
#define _Static_assert static_assert / * static_assertis part of C ++ 11 or later * /
^
static_assert.c : 78 : 38 : note : in expansion of macro '_Static_assert'#
define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "("#test_for_true ") 실패")
^
static_assert.c : 88 : 5 : 참고 : 매크로 'STATIC_ASSERT'확장시
STATIC_ASSERT (1> 2);
^

예제 3 : C ++ 출력 실패 (예 : C ++ 11 이전 의 C ++ 버전을 사용하고 있기 때문에 assert 코드가 제대로 작동하지 않음 ) :

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c : 88 : 5 : 경고 : 식별자 'static_assert'는 C ++ 11의 키워드입니다. [-Wc ++ 0x-compat]
STATIC_ASSERT (1> 2 );
^
static_assert.c : 'int main ()'함수에서
static_assert.c : 78 : 99 : 오류 : 'static_assert'가이 범위에서 선언되지 않았습니다.
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "("#test_for_true " ) 실패 ")
^
static_assert.c : 88 : 5 : 참고 : 매크로 'STATIC_ASSERT'확장시
STATIC_ASSERT (1> 2);
^

여기에 전체 테스트 결과 :

관련 :

  1. static_assert를 사용하여 매크로에 전달 된 유형 확인 [내 답변]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. static_assert를 사용하여 매크로에 전달 된 유형 확인
  3. C에서 정적 어설 션을 사용하여 매크로에 전달 된 매개 변수 유형을 확인하는 방법

1
static_assert매크로 가 있는데 왜 그렇게 복잡 assert.h합니까?
Goodbye SE

@KamiKaze, 실제로 내 대답을 읽지 않은 것 같아서 질문에 놀랐습니다. 내 대답의 두 번째 줄은 "static_assert ()는 C ++ 11 이상에서 정의됩니다"라고 말합니다. 따라서 static_assert()C에서는 전혀 사용할 수 없습니다. 여기도 참조하십시오 : en.cppreference.com/w/cpp/language/static_assert-static_assert "(C ++ 11 이후)"존재를 보여줍니다 . 내 대답의 아름다움은 gcc의 C90 이상뿐만 아니라 C ++ 11 이상에서와 같이 C ++ 11 이상에서 작동하지 않는다는 것 static_assert()입니다. 또한 내 대답에 대해 무엇이 복잡합니까? 그것은 단지 몇 #defines입니다.
Gabriel Staples 19 년

static_assertC11 이후 C에서 정의됩니다. 로 확장되는 매크로입니다 _Static_assert. en.cppreference.com/w/c/error/static_assert . 또한 귀하의 답변과는 대조적으로 _Static_assertgcc의 c99 및 c90에서 사용할 수 없습니다 (gnu99 및 gnu90에서만). 이것은 표준을 준수합니다. 기본적으로 많은 추가 작업을 수행합니다. 이는 gnu90 및 gnu99로 컴파일 된 경우에만 이점을 제공하고 실제 사용 사례를 크게 작게 만듭니다.
Goodbye SE

> "_Static_assert는 gcc의 c99 및 c90에서 사용할 수 없습니다 (gnu99 및 gnu90에서만)". 무슨 말인지 알겠습니다. gcc 확장이므로 정확합니다. > "기본적으로 추가 작업을 많이합니다." 동의하지 않습니다. 2 매우 단순한 정의는 결코 "많은"추가 작업이 아닙니다. 즉, 지금 당신이 의미하는 바를 알 수 있습니다. 나는 여전히 내가 한 일이 유용하고 여기에 제시된 지식과 답변에 가치를 더한다고 생각하기 때문에 그것이 반대 투표의 가치가 있다고 생각하지 않습니다. 또한 "gcc C90 이상"또는 "g90 이상"대신 "C90 이상"이라고 말하는 실수는 위의 의견에만 있었으며 제 답변에는 없었습니다.
Gabriel Staples 19 년

사실이 잘못 되었기 때문에 반대표가 정당화되었습니다. 잘못된 진술을 수정하면 답을 다시 확인하고 내 반대 투표를 철회 할 수 있습니다. 필요하지 않은 경우에도 이러한 코드를 추가하는 것은 (따라서 gnu90 및 gnu99로 작업하지 않는 경우) 명확성을 위해 유익하지 않고 더 많은 혼란을 추가합니다. 유스 케이스가 있다면 그만한 가치가 있습니다. 그러나 gnu99 / 90 및 C ++ 11 호환성이 필요한 사용 사례가 드문 지 궁금합니다.
Goodbye SE

0

정말 기본적이고 이식 가능한 것을 원하지만 C ++ 11 기능에 액세스 할 수없는 분들을 위해 제가 작성했습니다. 정상적으로
사용 STATIC_ASSERT하고 (원하는 경우 동일한 함수에 두 번 쓸 수 있음) GLOBAL_STATIC_ASSERT첫 번째 매개 변수로 고유 한 구문이있는 함수 외부에서 사용 하십시오.


설명 :
먼저 사용 가능한 경우 확실히 사용하고 싶을 실제 주장이 있는지 확인합니다.
그렇지 않은 경우 predicate 를 가져 와서 자체적으로 나누어 주장 합니다. 이것은 두 가지 일을합니다.
그것이 0, id est, 어설 션이 실패하면 0으로 나누기 오류가 발생합니다 (배열을 선언하려고하기 때문에 산술이 강제 실행 됨).
0이 아니면 배열 크기를로 정규화합니다 1. 따라서 어설 션이 통과되면 술어가 -1(유효하지 않음) 또는 232442(최적화되면 IDK로 막대한 공간 낭비 ) 평가 되었기 때문에 어쨌든 실패하는 것을 원하지 않을 것 입니다.
For 는 여러 번 쓸 수 있음을 의미합니다. 그것은 또한 그것을 캐스팅합니다STATIC_ASSERT 이 괄호로 싸여이 그것을 변수 스코프 블록을 만든다assert
void, 이는 unused variable경고를 제거하는 알려진 방법 입니다.
의 경우 GLOBAL_STATIC_ASSERT코드 블록에있는 대신 네임 스페이스를 생성합니다. 함수 외부에서 네임 스페이스가 허용됩니다. unique식별자는 당신이 한 번 이상이 하나를 사용하는 경우 충돌하는 정의를 중지해야합니다.


GCC 및 VS'12 C ++에서 나를 위해 일했습니다.


2
C에는 네임 스페이스가 없습니다.
martinkunev

아, 이런, 질문을 잘못 읽었습니다. 나는처럼 보이는이 같은 나는 경우 다른 사람에 여기를 떠날거야, 그래서 (내 대답의 마지막 줄을보고) 어쨌든 ++ C에 대한 답을 찾고 어떻게 여기 온
Hashbrown

0

이것은 "사용하지 않는 제거"옵션 세트와 함께 작동합니다. 전역 매개 변수를 확인하기 위해 하나의 전역 함수를 사용할 수 있습니다.


1
전혀 작동한다면 실행 파일의 소스에서만 작동합니다.
Coder

0

이것은 오래된 gcc에서 작동했습니다. 어떤 버전인지 잊어 버려 죄송합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.