전 처리기 매크로에서 "sizeof"를 어떻게 사용할 수 있습니까?


95

sizeof전 처리기 매크로에서 를 사용하는 방법이 있습니까?

예를 들어, 몇 년 동안 제가 다음과 같은 일을하고 싶었던 많은 상황이있었습니다.

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif

내가 여기서 확인하는 정확한 것은 완전히 구성되어 있습니다. 요점은 종종 이러한 유형의 (크기 또는 정렬) 컴파일 타임 검사를 삽입하여 잘못 정렬되거나 재정렬 될 수있는 데이터 구조를 수정하는 사람을 보호합니다. 그것들을 깨뜨릴 크기의 것들.

말할 필요도없이 sizeof위에서 설명한 방식으로 a를 사용할 수없는 것 같습니다 .


이것이 빌드 시스템이 존재하는 정확한 이유입니다.
Šimon Tóth

3
이것이 #error 지시문이 항상 큰 따옴표로 묶어야하는 정확한 이유입니다 ( "하지 않음"으로 인해 끝나지 않은 문자 상수).
Jens

1
안녕하세요 @Brad. 그동안 현재 허용되는 답변이 약간 구식이 되었기 때문에 허용 된 답변을 신경 쓰지 않음의 답변으로 변경하는 것이 좋습니다.
보도 Thiesen

@BodoThiesen 완료.
Brad

답변:


69

이를 수행하는 방법에는 여러 가지가 있습니다. 다음 스 니펫은 sizeof(someThing)같으면 코드를 생성하지 않습니다 PAGE_SIZE. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

1. C11 방법

C11부터 사용할 수 있습니다 static_assert(필요 #include <assert.h>).

용법:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");

2. 맞춤 매크로

sizeof(something)예상 한 것과 다를 때 컴파일 타임 오류가 발생하면 다음 매크로를 사용할 수 있습니다.

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

용법:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );

이 기사에서는 작동 이유를 자세히 설명합니다.

3. MS 특정

Microsoft C ++ 컴파일러 에서는 섹션 2에서 설명한 것과 유사한 트릭을 사용하는 C_ASSERT 매크로 (필요 #include <windows.h>)를 사용할 수 있습니다 .

용법:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);

4
... 미쳤어. 왜 이것이 허용되지 않는 대답 인 @Brad (OP)입니까?
엔지니어

BUILD_BUG_ON에 대한 좋은 참조입니다.
Petr Vepřek

2
매크로는 GNU gcc(버전 4.8.4에서 테스트 됨) (Linux)에서 작동하지 않습니다 . 상기 ((void)sizeof(...와 그것을 오류 expected identifier or '(' before 'void'expected ')' before 'sizeof'. 그러나 원칙적으로 size_t x = (sizeof(...대신 의도 한대로 작동합니다. 어떻게 든 결과를 "사용"해야합니다. 이것이 함수 내에서 또는 전역 범위에서 여러 번 호출 될 수 있도록하기 위해 다음과 같은 것을 extern char _BUILD_BUG_ON_ [ (sizeof(...) ];반복적으로 사용할 수 있습니다 (부작용없이 실제로 _BUILD_BUG_ON_어디에서나 참조하지 마십시오 ).
JonBrave

2011 년보다 훨씬 오랫동안 정적 어설 션을 사용해 왔습니다.
Dan

1
@Engineer 모습은 광기가 중지되었습니다)
보도 Thiesen을

70

어쨌든 sizeof전 처리기 매크로에서 " " 를 사용할 수 있습니까?

아니요. 조건부 지시문은 제한된 조건식 집합을 사용합니다. sizeof허용되지 않는 것 중 하나입니다.

전처리 지시문은 소스가 (적어도 개념적으로) 구문 분석되기 전에 평가되므로 아직 크기를 가져 오지 못한 유형이나 변수가 없습니다.

그러나 C에서 컴파일 타임 어설 션을 얻는 기술이 있습니다 (예 : 이 페이지 참조 ).


훌륭한 기사-영리한 솔루션! 관리자가 필요하지만-그들은 C 구문을 실제로 작동하도록 제한했습니다! : -O
Brad

1
기사에서 말했듯이-나는 지금 리눅스 커널 코드를 만들고 있습니다-그리고 커널에 이미 정의가 있습니다-BUILD_BUG_ON-커널이 다음과 같은 용도로 사용합니다. BUILD_BUG_ON (sizeof (char)! = 8)
Brad

2
@Brad BUILD_BUG_ON 및 컴파일에 실패 할 확실하게 잘못된 코드를 생성하는 다른 사용자 (및 프로세스에서 명확하지 않은 오류 메시지 제공). 실제로 #if 문이 아니므로이를 기반으로 코드 블록을 제외 할 수 없습니다.
keltar

10

나는 그것이 늦은 대답이라는 것을 알고 있지만 Mike의 버전에 추가하기 위해 여기에 메모리를 할당하지 않는 버전이 있습니다. 원래 크기 확인을하지 않았고, 몇 년 전에 인터넷에서 찾았고, 안타깝게도 저자를 참조 할 수 없습니다. 다른 두 가지는 동일한 아이디어의 확장입니다.

typedef이기 때문에 아무것도 할당되지 않습니다. 이름에 __LINE__이 있으면 항상 다른 이름이므로 필요한 곳에 복사하여 붙여 넣을 수 있습니다. 이것은 MS Visual Studio C 컴파일러 및 GCC Arm 컴파일러에서 작동합니다. CodeWarrior에서는 작동하지 않으며 CW는 __LINE__ 전 처리기 구조를 사용하지 않고 재정의에 대해 불평합니다.

//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];

//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];

//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];

이것은 실제로 표준 C 프로젝트에서 정말 잘 작동합니다.
Ashley Duncan

1
이것은 할당이 제로이기 때문에 정답이어야합니다. : 심지어 더 나은 정의에#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
르노 CERRATO

p__LINE__은 고유 한 이름을 생성하지 않습니다. p__LINE__을 변수로 생성합니다. preproc 매크로가 필요하고 sys / cdefs.h의 __CONCAT을 사용합니다.
Coroos

9

이 스레드는 정말 오래되었지만 ...

내 솔루션 :

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];

해당 표현식이 0과 같으면 제대로 컴파일됩니다. 다른 것은 바로 거기에서 폭발합니다. 변수가 extern'd이기 때문에 공간을 차지하지 않으며 아무도 참조하지 않는 한 링크 오류가 발생하지 않습니다.

assert 매크로만큼 유연하지는 않지만 내 버전의 GCC에서 컴파일 할 수 없었고 이것은 거의 모든 곳에서 컴파일 될 것이라고 생각합니다.


6
두 개의 밑줄로 시작하는 자체 매크로를 만들지 마십시오 . 이 경로는 광기 (일명 정의되지 않은 행동 )에 있습니다.
Jens

이 페이지에는 많은 예제가 나열되어 있습니다. pixelbeat.org/programming/gcc/static_assert.html
portforwardpodcast

arm gcc 컴파일러로 컴파일 할 때 작동하지 않습니다. 예상 오류 "오류 : 파일 범위에서 가변적으로 수정 된 ' CHECK '를 제공합니다."
thunderbird

@Jens 맞습니다. 그러나 이것은 말 그대로 매크로가 아니라 변수 선언입니다. 물론 매크로를 방해 할 수 있습니다.
Melebius

4

기존 답변은 유형의 크기에 따라 "컴파일 타임 어설 션"의 효과를 얻는 방법을 보여줍니다. 이 특별한 경우 OP의 요구를 충족시킬 수 있지만 유형의 크기에 따라 조건부 전처리 기가 실제로 필요한 다른 경우가 있습니다. 방법은 다음과 같습니다.

다음과 같은 작은 C 프로그램을 작성하십시오.

/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }

컴파일하십시오. 위의 C 프로그램을 실행하고 출력을 캡처하는 좋아하는 스크립팅 언어로 스크립트를 작성하십시오. 해당 출력을 사용하여 C 헤더 파일을 생성하십시오. 예를 들어 Ruby를 사용 중이라면 다음과 같이 보일 수 있습니다.

sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER

그런 다음 Makefile 또는 기타 빌드 스크립트에 규칙을 추가하면 위의 스크립트를 실행하여 sizes.h.

sizes.h크기에 따라 전 처리기 조건을 사용해야하는 모든 곳에 포함 합니다.

끝난!

( ./configure && make프로그램을 빌드하기 위해 타이핑 한 적이 있습니까? configure기본적으로 위와 같은 스크립트는 무엇입니까? )


"autoconf"와 같은 도구를 사용할 때도 마찬가지입니다.
Alexander Stohr

4

다음 매크로는 어떻습니까?

/* 
 * Simple compile time assertion.
 * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
 */
#define CT_ASSERT(exp, message_identifier) \
    struct compile_time_assertion { \
        char message_identifier : 8 + !(exp); \
    }

예를 들어 MSVC는 다음과 같이 말합니다.

test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits

1
이것은 #if전 처리기 지시문 에서 사용할 수 없기 때문에 질문에 대한 대답이 아닙니다 .
cmaster - 분석 재개 모니카

1

이 토론에 대한 참조로 일부 컴파일러는 sizeof () ar 전 처리기 시간을 얻습니다.

JamesMcNellis 대답은 정확하지만 일부 컴파일러는이 제한을 통과합니다 (이것은 아마도 엄격한 ansi c를 위반할 것입니다).

이 경우 IAR C- 컴파일러 (아마 전문적인 마이크로 컨트롤러 / 임베디드 프로그래밍의 선두 주자)를 언급합니다.


확실합니까? IAR 컴파일러가 ISO C90 및 C99 표준을 준수 한다고 주장 합니다.이 표준 sizeof은 전처리 시점에 평가를 허용하지 않습니다 . sizeof식별자로 취급해야합니다.
Keith Thompson

6
1998 년 comp.std.c 뉴스 그룹의 누군가가 이렇게 썼습니다. " #if (sizeof(int) == 8)(일부 컴파일러에서) 실제로 작동 했던 시절에는 좋았습니다 ." 응답 : "내 시대 이전에 있었을 것입니다.", Dennis Ritchie가 보낸 것입니다.
Keith Thompson

늦게 답장을 드려 죄송합니다 ... 예, 8/16/32 비트 마이크로 컨트롤러, Renesas 컴파일러 (R8 및 RX 모두) 용으로 컴파일 된 코드의 작업 예제가 있습니다.
graziano governatori

사실, "엄격한"ISO C를 요구하는 몇 가지 옵션이 있어야합니다
graziano 거버 네이터 2013 년

표준이 금지하지 않는 한 표준 위반이 아닙니다. 그런 다음 드물고 비표준 기능이라고 부를 것입니다. 따라서 컴파일러 독립성과 플랫폼 이식성을 유지하기 위해 일반적인 경우에는 피할 것입니다.
Alexander Stohr

1

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) 작동 할 수있다


이것은 흥미로운 솔루션이지만 유형이 아닌 정의 된 변수에서만 작동합니다. 유형으로 작동하지만 변수로는 작동하지 않는 또 다른 솔루션은 다음과 같습니다.#define SIZEOF_TYPE(x) (((x*)0) + 1)
greydet

7
#if조건 내에서 결과를 사용할 수 없기 때문에 작동하지 않습니다 . 이는 sizeof(x).
interjay

1

C11에서 _Static_assert키워드가 추가되었습니다. 다음과 같이 사용할 수 있습니다.

_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")

0

내 휴대용 C ++ 코드 ( http://www.starmessagesoftware.com/cpcclibrary/ )에서 일부 구조체 또는 클래스의 크기에 대한 안전 가드를 두려고했습니다.

전처리 기가 오류를 발생시키는 방법을 찾는 대신 (여기에 명시된대로 sizeof ()와 함께 작동 할 수 없음) 컴파일러가 오류를 발생시키는 해결책을 찾았습니다. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99

내 컴파일러 (xcode)에서 오류가 발생하도록 해당 코드를 수정해야했습니다.

static union
{
    char   int8_t_incorrect[sizeof(  int8_t) == 1 ? 1: -1];
    char  uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
    char  int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
    char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
    char  int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
    char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
};

2
"-1"이 0xFFFF… FF로 해석되지 않아 프로그램이 주소 지정 가능한 모든 메모리를 요청하게되는 것이 확실합니까?
Anton Samsonov

0

언급 된 매크로를 시도한 후이 조각은 원하는 결과 ( t.h) 를 생성하는 것 같습니다 .

#include <sys/cdefs.h>
#define STATIC_ASSERT(condition) typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(int) == 42);

실행 중 cc -E t.h:

# 1 "t.h"
...
# 2 "t.h" 2

typedef char _static_assert_3[ (sizeof(int) == 4) ? 1 : -1];
typedef char _static_assert_4[ (sizeof(int) == 42) ? 1 : -1];

실행 중 cc -o t.o t.h:

% cc -o t.o t.h
t.h:4:1: error: '_static_assert_4' declared as an array with a negative size
STATIC_ASSERT(sizeof(int) == 42);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.h:2:84: note: expanded from macro 'STATIC_ASSERT'
  ...typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
                                                       ^~~~~~~~~~~~~~~~~~~~
1 error generated.

42는 결국 모든 것에 대한 답이 아닙니다 ...


0

컴파일 타임에 제약 조건에 대한 데이터 구조의 크기를 확인하기 위해이 트릭을 사용했습니다.

#if defined(__GNUC__)
{ char c1[sizeof(x)-MAX_SIZEOF_X-1]; } // brakets limit c1's scope
#else
{ char c1[sizeof(x)-MAX_SIZEOF_X]; }   
#endif

x의 크기가 제한 MAX_SIZEOF_X보다 크거나 같으면 gcc는 '배열 크기가 너무 큽니다'오류로 불평합니다. VC ++는 오류 C2148 ( '배열의 총 크기가 0x7fffffff 바이트를 초과 할 수 없음') 또는 C4266 '일정 크기 0의 배열을 할당 할 수 없습니다'를 발생시킵니다.

gcc는 크기가 0 인 배열을 이런 방식으로 정의 할 수 있기 때문에 두 가지 정의가 필요합니다 (sizeof x-n).


-10

sizeof운영자는 프리 프로세서를 사용할 수 없습니다,하지만 당신은 전송할 수 있습니다 sizeof컴파일러와 런타임 상태를 확인 :

#define elem_t double

#define compiler_size(x) sizeof(x)

elem_t n;
if (compiler_size(elem_t) == sizeof(int)) {
    printf("%d",(int)n);
} else {
    printf("%lf",(double)n);
}

13
이미 받아 들여진 답변에서 어떻게 향상됩니까? 정의의 목적은 무엇입니까 compiler_size? 당신의 모범은 무엇을 보여 주려고합니까?
ugoren 2013-06-30
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.