etc./StackOverFlow

무엇인가요 ":-!!" C 코드에서?

청렴결백한 만능 재주꾼 2022. 1. 6. 11:11
반응형

질문자 :chmurli


/usr/include/linux/kernel.h 에서 이 이상한 매크로 코드를 만났습니다.

 /* Force a compilation error if condition is true, but also produce a result (of value 0 and type size_t), so the expression can be used eg in a structure initializer (or where-ever else comma expressions aren't permitted). */ #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))

무엇을 합니까 :-!! 하다?



이것은 실제로 표현식 e가 0으로 평가될 수 있는지 확인하고 그렇지 않으면 빌드에 실패하는 방법 입니다.

매크로의 이름이 다소 잘못되었습니다. ...ON_ZERO 보다는 BUILD_BUG_OR_ZERO 와 비슷해야 합니다. ( 이것이 혼란스러운 이름인지에 대한 토론 이 가끔 있었습니다.)

다음과 같은 표현을 읽어야 합니다.

 sizeof(struct { int: -!!(e); }))
  1. (e) : 계산식 e .

  2. !!(e) : 논리적으로 두 번 부정: 0 if e == 0 ; 그렇지 않으면 1 .

  3. -!!(e) : 2단계의 표현식을 수치적으로 부정합니다. 0 이면 0 ; 그렇지 않으면 -1 .

  4. struct{int: -!!(0);} --> struct{int: 0;} : 0이면 너비가 0인 익명의 정수 비트필드가 있는 구조체를 선언합니다. 모든 것이 정상이며 정상적으로 진행됩니다.

  5. struct{int: -!!(1);} --> struct{int: -1;} : 반면에 0이 아니면 음수가 됩니다. 음수 너비의 비트 필드를 선언하는 것은 컴파일 오류입니다.

그래서 우리는 구조체에서 너비가 0인 비트필드로 끝낼 것입니다. 괜찮습니다. 또는 음수 너비를 가진 비트필드는 컴파일 오류입니다. 그럼 우리가 가지고 sizeof 우리가 얻을 수 있도록, 해당 필드 size_t (여기서 경우 0이됩니다 적절한 폭 e 제로입니다).


일부 사람들은 다음과 같이 질문했습니다. assert 사용하지 않습니까?

keithmo의 대답 은 좋은 반응을 보입니다.

이러한 매크로는 컴파일 시간 테스트를 구현하는 반면 assert()는 런타임 테스트입니다.

정확히 맞아. 이전에 발견할 수 있었던 커널 문제를 런타임에 감지하고 싶지 않습니다! 운영 체제의 중요한 부분입니다. 컴파일 시간에 문제를 감지할 수 있는 범위에 관계없이 훨씬 좋습니다.


John Feminella

: 은 비트 필드입니다. 에 관해서는 !! , 이는 논리적 이중 부정 이므로 거짓 0 을, 참이면 1 그리고 - 는 빼기 기호, 즉 산술 부정입니다.

컴파일러가 잘못된 입력을 무시하도록 하는 것은 모두 속임수일 뿐입니다.

BUILD_BUG_ON_ZERO 고려하십시오. -!!(e) 가 음수 값으로 평가되면 컴파일 오류가 발생합니다. 그렇지 않으면 -!!(e) 는 0으로 평가되고 0 너비 비트 필드의 크기는 0입니다. 따라서 매크로는 값이 0인 size_t

입력이 0이 아닐 때 실제로 빌드가 실패하기 때문에 이름이 약합니다.

BUILD_BUG_ON_NULL int 대신 포인터를 생성합니다.


David Heffernan

어떤 사람들은 이러한 매크로를 assert() 와 혼동하는 것 같습니다.

이러한 매크로는 컴파일 시간 테스트를 구현하는 반면 assert() 는 런타임 테스트입니다.


keithmo

글쎄, 나는 이 구문에 대한 대안이 언급되지 않았다는 사실에 매우 놀랐습니다. 또 다른 일반적인(그러나 오래된) 메커니즘은 정의되지 않은 함수를 호출하고 어설션이 올바른 경우 최적화 프로그램에 의존하여 함수 호출을 컴파일하는 것입니다.

 #define MY_COMPILETIME_ASSERT(test) \ do { \ extern void you_did_something_bad(void); \ if (!(test)) \ you_did_something_bad(void); \ } while (0)

이 메커니즘이 작동하는 동안(최적화가 활성화되어 있는 한) 연결할 때까지 오류를 보고하지 않고 you_did_something_bad() 함수에 대한 정의를 찾지 못한다는 단점이 있습니다. 이것이 커널 개발자가 음수 크기의 비트 필드 너비 및 음수 크기 배열과 같은 트릭을 사용하기 시작한 이유입니다(나중에 GCC 4.4에서 빌드 중단 중단).

컴파일 타임 어설션의 필요성에 공감하기 위해 GCC 4.3 error 함수 속성 을 도입했지만 선택한 메시지와 함께 컴파일 타임 오류를 생성합니다. 더 이상 비밀스러운 "음수 크기 배열" " 오류 메시지!

 #define MAKE_SURE_THIS_IS_FIVE(number) \ do { \ extern void this_isnt_five(void) __attribute__((error( \ "I asked for five and you gave me " #number))); \ if ((number) != 5) \ this_isnt_five(); \ } while (0)

사실, Linux 3.9부터는 이 기능을 사용하는 compiletime_assert bug.h 있는 대부분의 매크로가 그에 따라 업데이트되었습니다. 그래도 이 매크로는 이니셜라이저로 사용할 수 없습니다. 그러나 by 문 표현식 (또 다른 GCC C 확장)을 사용하면 할 수 있습니다!

 #define ANY_NUMBER_BUT_FIVE(number) \ ({ \ typeof(number) n = (number); \ extern void this_number_is_five(void) __attribute__(( \ error("I told you not to give me a five!"))); \ if (n == 5) \ this_number_is_five(); \ n; \ })

이 매크로는 매개변수를 정확히 한 번 평가하고(부작용이 있는 경우) "5점을 주지 말라고 했습니다!"라는 컴파일 시간 오류를 생성합니다. 표현식이 5로 평가되거나 컴파일 타임 상수가 아닌 경우.

그렇다면 음수 크기의 비트 필드 대신 이것을 사용하지 않는 이유는 무엇입니까? 아아, 현재 명령문 표현식 자체가 완전히 일정하더라도(즉, 완전히 평가될 수 있음) 상수 이니셜라이저(열거형 상수, 비트 필드 너비 등의 경우)로 사용하는 것을 포함하여 명령문 표현식 사용에 많은 제한이 있습니다. 컴파일 타임에 그렇지 않으면 __builtin_constant_p() 테스트를 통과합니다). 또한 함수 본문 외부에서 사용할 수 없습니다.

바라건대, GCC는 이러한 단점을 곧 수정하고 상수 문 표현식을 상수 이니셜라이저로 사용할 수 있게 해줄 것입니다. 여기서 문제는 법적 상수 표현이 무엇인지 정의하는 언어 사양입니다. C++11은 이 유형이나 사물에 대해서만 constexpr 키워드를 추가했지만 C11에는 해당 키워드가 없습니다. C11은 이 문제의 일부를 해결할 정적 주장을 얻었지만 이러한 모든 단점을 해결하지는 못합니다. 그래서 나는 gcc가 constexpr 기능을 -std=gnuc99 & -std=gnuc11 또는 그와 같은 것을 통해 확장으로 사용 가능하게 만들고 명령문 표현식 et에 사용할 수 있기를 바랍니다. 알.


Daniel Santos

조건이 거짓이면 크기 0 비트필드를 생성하지만 조건이 참/0이 아닌 경우 -1 ( -!!1 전자의 경우 오류가 없으며 구조체는 int 멤버로 초기화됩니다. 후자의 경우 컴파일 오류가 발생합니다( -1 비트 필드와 같은 것은 생성되지 않음).


Matt Phillips

출처 : http:www.stackoverflow.com/questions/9229601/what-is-in-c-code

반응형