プリプロセッサマクロで「sizeof」を使用するにはどうすればよいですか?


95

sizeofプリプロセッサマクロでを使用する方法はありますか?

たとえば、私が長年にわたって次のようなことをしたいと思った状況はたくさんあります。

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

私がここでチェックしている正確なことは完全に構成されています-重要なのは、これらのタイプ(サイズまたは配置)のコンパイル時チェックを入れて、誰かがデータ構造を誤って調整したり再調整したりしないようにするためです。それらを壊すサイズのもの。

言うまでもなく-私はsizeof上記の方法でaを使用することができないようです。


これが、ビルドシステムが存在する正確な理由です。
サイモン・トス

3
これが、#errorディレクティブを常に二重引用符で囲む必要がある正確な理由です(「しない」ために終了しない文字定数)。
イェンス

1
@Bradこんにちは。受け入れられた回答をネバーマインドの回答に変更することを検討してください。それまでの間、現在受け入れられている回答は少し時代遅れになっています。
Bodo Thiesen、

@BodoThiesen完了しました。
ブラッド

回答:


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 ++コンパイラでは、C_ASSERTマクロ(が必要#include <windows.h>)を使用できます。これは、セクション2で説明されているのと同様のトリックを使用します。

使用法:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);

4
...それは正気ではない なぜこれが受け入れられない回答である@Brad(OP)なのですか?
エンジニア

BUILD_BUG_ONへの適切な参照。
PetrVepřek16年

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 2017年

静的アサートを2011年よりずっと長く使用してから1年になります。
Dan

1
@Engineerの見た目、狂気は止まった;)
Bodo

70

とにかくsizeofプリプロセッサマクロで" " を使用することはありますか?

いいえ。条件ディレクティブは、制限された一連の条件式を取ります。sizeof許可されていないことの1つです。

ソースが解析される前に(少なくとも概念的には)前処理ディレクティブが評価されるため、サイズを取得するための型や変数はまだありません。

ただし、Cでコンパイル時のアサーションを取得する手法があります(たとえば、このページを参照)。


素晴らしい記事-賢い解決策!あなたは管理する必要がありますが-彼らは本当にこれを機能させるためにCの構文を限界まで押し上げました!:-O
ブラッド

1
結局-記事で述べているように-私は現在Linuxカーネルコードをビルドしています-カーネルにはすでに定義があります-BUILD_BUG_ON-カーネルはそれを次のように使用します:BUILD_BUG_ON(sizeof(char)!= 8)
ブラッド

2
@Brad BUILD_BUG_ONおよびその他のコードは、コンパイルに失敗する確かに正しくないコードを生成します(そして、処理中の明らかでないエラーメッセージを表示します)。実際には#ifステートメントではないため、たとえばこれに基づいてコードのブロックを除外することはできません。
ケルター2013年

10

私はそれが遅い答えであることを知っていますが、マイクのバージョンに追加するために、メモリを割り当てない私たちが使用するバージョンがあります。元のサイズチェックを思い付きませんでした。数年前にインターネットで見つけたので、残念ながら著者を参照できません。他の2つは、同じアイデアの単なる拡張です。

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プロジェクトで非常にうまく機能します。
アシュリーダンカン

1
割り当てがゼロのため、これが正しい答えになるはずです。さらに明確に定義:#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];
Renaud Cerrato 2018

p__LINE__は一意の名前を生成しません。変数としてp__LINE__を生成します。preprocマクロが必要で、sys / cdefs.hから__CONCATを使用します。
Coroos

9

このスレッドは本当に古いのですが...

私の解決策:

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

その式がゼロに等しい限り、問題なくコンパイルされます。それ以外のものはそれがすぐに爆発します。変数はexternされているため、スペースをとらず、誰も参照しない限り(参照しません)、リンクエラーは発生しません。

assertマクロほど柔軟ではありませんが、私のバージョンのGCCでコンパイルすることはできませんでした。これで...どこでもコンパイルできると思います。


6
2つのアンダースコアで始まる独自のマクロを作成しないでください。この道は狂気(別名未定義の動作)にあります。
イェンス

このページには、たくさんの例がリストされています。pixelbeat.org
programming / gcc /

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-モニカを2014年

1

この議論の参考として、コンパイラによっては、sizeof()がプリプロセッサ時間を取得することを報告します。

JamesMcNellisの答えは正しいですが、一部のコンパイラはこの制限を通過します(これはおそらく厳密なansi cに違反しています)。

この例として、私はIAR Cコンパイラ(おそらく、プロのマイクロコントローラ/組み込みプログラミングの主要なコンパイラ)を参照します。


よろしいですか?IAR 、コンパイラーがISO C90およびC99標準に準拠していると主張しておりsizeof、前処理時の評価は許可されていません。sizeof単なる識別子として扱う必要があります。
キーストンプソン

6
1998年に、comp.std.cニュースグループの誰かが書いた:「#if (sizeof(int) == 8)(一部のコンパイラーで)実際に機能するような時代には、よかったです。」回答:「私の時間より前でなければなりませんでした。」はデニス・リッチーからのものでした。
キーストンプソン

返信が遅くなってすみません...はい、確かに、8/16/32ビットマイクロコントローラー、ルネサスコンパイラー(R8とRXの両方)用にコンパイルされたコードの実例があります。
グラツィアーノガバナトーリ2013

実際には、「厳密な」ISO Cを要求するためのいくつかのオプションがあるはずです
grazianoガバナトリ

規格がそれを禁止しない限り、それは規格の違反ではありません。それから私はそれをまれで非標準の機能と呼びます-したがって、コンパイラの独立性とプラットフォームの移植性を維持するために通常の場合にはそれを避けます。
Alexander Stohr

1

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) うまくいくかもしれません


これは興味深い解決策ですが、型ではなく、定義された変数でのみ機能します。タイプではなく、変数で動作します別の解決策は、次のようになります#define SIZEOF_TYPE(x) (((x*)0) + 1)
greydet

7
#if条件内で結果を使用できないため、機能しません。それ以上のメリットはありませんsizeof(x)
interjay 2013年

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として解釈されず、プログラムがすべてのアドレス可能なメモリを要求することを確信していますか?
アントンサムソノフ2015年

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 wilは「配列のサイズが大きすぎる」エラーを報告します。VC ++はエラーC2148( '配列の合計サイズは0x7fffffffバイトを超えてはなりません')またはC4266 '定数サイズ0の配列を割り当てることができません'を発行します。

gccではサイズがゼロの配列をこのように定義できるため(sizeof x-n)、2つの定義が必要です。


-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年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.