如何在GCC中指定枚举大小?

Mik*_*wan 8 c enums gcc

我想为枚举指定64位的枚举大小.这怎么可能通过海湾合作委员会?代码不需要是"可移植的",因为我只对将代码用于x86-32和x86-64 Linux的GCC编译感兴趣.这意味着任何可以提供我想要的功能的黑客都可以,只要它适用于那些目标.

鉴于此代码:

#include <stdlib.h>
#include <stdio.h>

enum some_enum
{
    garbage1,
    garbage2
};

int main(void)
{
    enum some_enum some_val;
    printf("size: %lu\n", sizeof(some_val));

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

这当前打印出4,而我希望能够强制大小为8.尝试在枚举赋值中指定大于4个字节的值会导致警告.例如,

enum some_enum
{
    garbage1 = '12345',
    garbage2
};
Run Code Online (Sandbox Code Playgroud)

会产生:

warning: character constant too long for its type [enabled by default]
Run Code Online (Sandbox Code Playgroud)

这里对类似问题的回答似乎没有产生任何好结果.也就是说,由于以下原因产生了相同的警告:

enum some_enum
{
    garbage1 = 'adfs',
    garbage2 = 'asdfasdf'
};
Run Code Online (Sandbox Code Playgroud)

注意:可以通过编译来关闭多字符警告-Wno-multichar.


合理

由于人们对我为什么这样做感兴趣,我编写了一个反汇编引擎.我将指令的每个部分都作为字符串.所以我希望枚举看起来像这样:

enum mnemonic
{
    mov = 'mov',
    cmp = 'cmp',
    sysenter = 'sysenter'
};
Run Code Online (Sandbox Code Playgroud)

然后,我可以使用以下代码轻松存储语义信息:

enum mnemonic insn;

char *   example_insn = "mov";
uint64_t buf          = 0;

strncpy((char *)&buf, example_insn, sizeof(uint64_t));
Run Code Online (Sandbox Code Playgroud)

如果buf是,enum mnemonic那么我们不需要做任何其他事情.的strncpy是串到空字符结束后用于填充字节.如果我无法做到这一点,我将不得不做这样的事情:

if(strcmp(example_insn, "mov") == 0) {
    insn = mov;
} else if(strcmp(example_insn, "cmp") == 0) {
    insn = cmp;
} ...
Run Code Online (Sandbox Code Playgroud)

由于这个例程将被击中数百万次,这种优化将产生巨大的差异.我打算对寄存器等操作数做同样的事情.

Kei*_*son 8

正如Matteo Italia的回答所说,gcc允许您通过为其中一个成员指定64位值来定义64位枚举类型.例如:

enum some_enum {
    /* ... */
    max = 0x7fffffffffffffff
};
Run Code Online (Sandbox Code Playgroud)

至于你的使用'mov','cmp'等等,字符串文字"mov"的表示与多字符字符常量的表示之间没有必然的相关性'mov'.

后者是合法的(并由gcc支持),但该值是实现定义的.标准表示类型始终是int,并且gcc似乎没有允许您覆盖它的扩展名.因此,如果int是4个字节,那么'sysenter',如果它完全被接受,则不一定具有您正在寻找的值.gcc似乎忽略了这个常量的低位字节以外的所有字节.对于big-endian和little-endian系统,常量的值似乎是一致的 - 这意味着它不会始终匹配类似字符串文字的表示.

例如,这个程序:

#include <stdio.h>
int main(void) {
    const char *s1 = "abcd";
    const char *s2 = "abcdefgh";
    printf("'abcd'     = 0x%x\n", (unsigned)'abcd');
    printf("'abcdefgh' = 0x%x\n", (unsigned)'abcdefgh');
    printf("*(unsigned*)s1 = 0x%x\n", *(unsigned*)s1);
    printf("*(unsigned*)s2 = 0x%x\n", *(unsigned*)s2);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在little-endian系统(x86)上使用gcc编译时产生此输出:

'abcd'     = 0x61626364
'abcdefgh' = 0x65666768
*(unsigned*)s1 = 0x64636261
*(unsigned*)s2 = 0x64636261
Run Code Online (Sandbox Code Playgroud)

并在big-endian系统(SPARC)上输出:

'abcd'     = 0x61626364
'abcdefgh' = 0x65666768
*(unsigned*)s1 = 0x61626364
*(unsigned*)s2 = 0x61626364
Run Code Online (Sandbox Code Playgroud)

所以我担心你想要匹配像'mov'字符串这样的字符常量"mov"是不行的.(可以想象你可以将字符串表示规范化为big-endian,但我不会自己采用这种方法.)

您尝试解决的问题是快速将字符串映射"mov"到表示CPU指令的特定整数值.你说得对,长序列strcmp()通话将是低效的(你有没有实际测量了一下,发现速度是不可接受的?) -但还有更好的方法.某种哈希表可能是最好的.有一些工具可以生成完美的哈希函数,因此对字符串值的相对便宜的计算会给出一个唯一的整数值.

您将无法非常方便地编写枚举值的定义,但是一旦您拥有正确的哈希函数,您就可以编写一个程序来生成枚举类型的C源代码.

这是假设枚举是这里最好的方法; 它可能不是.如果我这样做,中央数据结构将是结构的集合,其中每个结构包含运算符的字符串名称以及与之关联的任何其他信息.哈希函数会将字符串映射"mov"到此集合中的索引.(我故意模糊地使用什么样的"集合";使用正确的散列函数,它可能是一个简单的数组.)有了这种解决方案,我认为不需要64位枚举类型.


oua*_*uah 6

你可以使用一种union类型:

union some {
    enum { garbage1, garbage2 } a;
    int64_t dummy;
};
Run Code Online (Sandbox Code Playgroud)


Mat*_*lia 6

尽管 C99 标准规定枚举不能基于int(§6.7.2.2 ¶2) 1 之外的任何东西,但似乎gcc遵循 C++ 的想法,如果 an 中的值enum大于 an int,它可以基于 a更大的整数类型。我对这段代码没有任何问题,在 x86 和 x64 上都没有问题:

enum myEnum
{
    a=1234567891234567890LL
};
 
int main()
{
    enum myEnum e;
    printf("%u %u", sizeof(void *), sizeof(e));
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在 x86 上我得到

4 8
Run Code Online (Sandbox Code Playgroud)

在 x64(在我的机器上)我得到

8 8
Run Code Online (Sandbox Code Playgroud)

虽然,要求对标准的迂腐尊重,我得到了,正如预期的那样:

matteo@teodeb:~/cpp$ gcc -ansi -pedantic testenum.c
testenum.c:5:7: warning: use of C99 long long integer constant
testenum.c:5: warning: ISO C restricts enumerator values to range of ‘int’
Run Code Online (Sandbox Code Playgroud)
  1. 实际上,它有点复杂;¶4 指定实现可以自由选择“与 兼容char,有符号整数类型或无符号整数类型”的任何特定类型作为“基本类型”,只要它可以表示enum.

    另一方面, ¶2 指定 的每个成员都enum必须表示为int,因此,即使实现可以自由地将您的enum偶数基于一个数以百万计的位整数,为其定义的常量也不能是任何无法表示的东西由int. 因此,这意味着,在实践中,编译器将无法立足enum于任何东西比大int,但它可以作为它的基础上的东西,如果你的价值观不要求全范围较小int

感谢@jons34yp 指出我最初的错误。


Per*_*son 5

您误解了警告,它的意思是字符文字始终是 type int,而不是 typelonglong long

你可以摆脱这样的事情:

enum foo {
    garbage1 = (long long)'1' << 32 | (long long)'2' << 24 | (long long)'3' << 16 | (long long)'4' << 8 | (long long)'5',
    garbage2
};
Run Code Online (Sandbox Code Playgroud)

但是如果你想使用 strncpy 解决方案,你仍然必须小心使用小端,确保使用正确的移位数字。