web-dev-qa-db-ja.com

Cでの列挙型のサイズの指定

すでに この関連する質問 を読んでいますが、もう少し具体的なものを探していました。

  • コンパイラに列挙型の幅を具体的に指示する方法はありますか?
  • もしそうなら、どのようにそれをしますか? C#で指定する方法を知っています。 Cでも同様に行われますか?
  • やりがいがありますか?列挙値が関数に渡されると、それはint- sized値として渡されますか?
28
Will

コンパイラに列挙型の幅を具体的に指示する方法はありますか?

一般的にはありません。標準Cではありません。

やりがいがありますか?

コンテキストに依存します。関数にパラメーターを渡すことについて話している場合、いいえ、実行する価値はありません(以下を参照)。列挙型から集計を構築するときにメモリを節約する場合は、実行する価値があります。ただし、Cでは、集約で列挙型の代わりに適切なサイズの整数型を使用できます。 Cでは(C++とは対照的に)列挙型と整数型はほとんど常に交換可能です。

列挙値が関数に渡されると、それはintサイズの値として渡されますか?

最近の多くの(ほとんどの)コンパイラは、すべてのパラメーターを特定のハードウェアプラットフォームの自然なWordサイズの値として渡します。たとえば、64ビットプラットフォームでは、多くのコンパイラが、そのプラットフォームでint型に32ビットが含まれている場合でも、実際のサイズに関係なく、すべてのパラメーターを64ビット値として渡します。通常、このようなプラットフォームでは「int-sized」値として渡されます)。このため、パラメーターを渡すために列挙サイズを最適化しようとすることは意味がありません。

18
AnT

GCCを使用している場合はフラグがあると思います。

-fshort-enums

17
Nyx0uf

適切な値を定義することで、少なくとも特定のサイズにすることができます。たとえば、すべての値がintに収まる場合でも、enumをcharと同じサイズで保存する場合は、次のようにできます。

typedef enum {
    firstValue = 1,
    secondValue = 2,

    Internal_ForceMyEnumIntSize = MAX_INT
} MyEnum;

ただし、動作は実装に依存する可能性があることに注意してください。

お気付きのように、そのような値を関数に渡すと、とにかくintに展開されますが、配列または構造体で型を使用している場合は、サイズが重要になります。要素のサイズを本当に気にする場合は、int8_tint32_tなど.

8

Enumが構造体の一部である場合、別の方法もあります。

struct something {
     :0;
   enum whatever field:CHAR_BIT;
     :0;
};

:0; enumフィールドが通常のフィールドで囲まれている場合は省略できます。前に別のビットフィールドがある場合、:0を指定すると、後続のフィールドの次のバイトにバイト調整が強制されます。

7

状況によっては、これが役立つ場合があります。

typedef uint8_t command_t;
enum command_enum
{
    CMD_IDENT                = 0x00,     //!< Identify command
    CMD_SCENE_0              = 0x10,     //!< Recall Scene 0 command
    CMD_SCENE_1              = 0x11,     //!< Recall Scene 1 command
    CMD_SCENE_2              = 0x12,     //!< Recall Scene 2 command
};

/* cmdVariable is of size 8 */
command_t cmdVariable = CMD_IDENT; 

一方、タイプcommand_tのサイズは8であり、変数および関数パラメータータイプに使用できます。一方、デフォルトではint型の割り当てに列挙値を使用できますが、command_t型変数に割り当てられるとすぐにコンパイラがそれらをキャストします。

また、CMD_16bit = 0xFFFF,を定義して使用するなどの安全でない操作を行うと、コンパイラは次のメッセージを警告します。

警告:暗黙的に符号なし型に切り捨てられた大きな整数[-Woverflow]

0
Julien

列挙型に割り当てられた値に依存します。

例:2 ^ 32-1より大きい値が保存されている場合、enum全体に割り当てられたサイズは次のサイズに変更されます。

0xFFFFFFFFFFFF値を列挙型変数に格納します。32ビット環境でコンパイルしようとすると警告が表示されます(ラウンドオフ警告)。64ビットコンパイルの場合と同様に成功し、割り当てられたサイズは8バイトになります。

0
user5057906

厳密なCコードを記述している場合でも、結果はコンパイラに依存します。このスレッドからの戦略を使用して、私はいくつかの興味深い結果を得ました...

enum_size.c

_#include <stdio.h>

enum __attribute__((__packed__)) PackedFlags {
    PACKED = 0b00000001,
};

enum UnpackedFlags {
    UNPACKED = 0b00000001,
};

int main (int argc, char * argv[]) {
  printf("packed:\t\t%lu\n", sizeof(PACKED));
  printf("unpacked:\t%lu\n", sizeof(UNPACKED));
  return 0;
}
_
_$ gcc enum_size.c
$ ./a.out
packed:         4
unpacked:       4
_
_$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         4
unpacked:       4
_
_$ g++ enum_size.c
$ ./a.out
packed:         1
unpacked:       4
_
_$ g++ enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1
_

上記の例では、C++コンパイラーの使用を開始するまで、__attribute__((__packed__))修飾子の利点を認識していませんでした。

編集:

@technosaurusの疑いは正しかった。

sizeof(enum PackedFlags)の代わりにsizeof(PACKED)のサイズを確認することで、期待した結果が得られました...

_  printf("packed:\t\t%lu\n", sizeof(enum PackedFlags));
  printf("unpacked:\t%lu\n", sizeof(enum UnpackedFlags));
_

gccから期待される結果が表示されます。

_$ gcc enum_size.c
$ ./a.out
packed:         1
unpacked:       4
_
_$ gcc enum_size.c -fshort_enums
$ ./a.out
packed:         1
unpacked:       1
_
0
Zak

@ Nyx0uf says のように、GCCには設定可能なフラグがあります。

-fshort-enums

可能な値の宣言された範囲に必要なバイト数だけを列挙型に割り当てます。具体的には、列挙型は十分なスペースがある最小の整数型と同等です。

警告:-fshort-enumsスイッチにより、GCCはそのスイッチなしで生成されたコードとバイナリ互換性のないコードを生成します。これを使用して、デフォルト以外のアプリケーションバイナリインターフェイスに準拠します。

ソース: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

一般的な洞察のための追加の素晴らしい読書:https://www.embedded.fm/blog/2016/6/28/how-big -is-an-enum
興味深い...下の黄色で強調表示した行に注目してください!

enter image description here

0
Gabriel Staples