Como definir um tipo enumerado (enum) em C?

272

Não tenho certeza de qual é a sintaxe adequada para o uso de enumerações C. Eu tenho o seguinte código:

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;

Mas isso não é compilado, com o seguinte erro:

error: conflicting types for strategy
error: previous declaration of strategy was here

O que estou fazendo de errado?

lindelof
fonte
7
Pergunta de anos, provavelmente ninguém verá isso; mas por que isso está dando um erro? Deverá funcionar perfeitamente bem, pois está na questão, tanto quanto meu conhecimento vai.
Utkan Gezer
2
@ Solver Por que essa sintaxe está errada?
MCG
6
@ MCQ, necroing o necro necro'd: A sintaxe apresentada na pergunta não está errada em C. Declara strategycomo tendo um tipo enumerado anônimo e atribui a ele um dos valores declarados desse tipo. Além disso, se eu envolver o código apresentado em uma main()função trivial , ele será compilado para mim, mesmo sem aviso, com o gcc 4.4.7. Algumas das respostas sugerem o mesmo, embora não em tantas palavras.
John Bollinger
5
A maioria das respostas está ausente do fato de que as duas linhas de código na pergunta não são apenas um trecho. Eles são o arquivo de origem inteiro. Se essas duas linhas estiverem incluídas no corpo de uma função, não haverá erro. Se eles aparecerem no escopo do arquivo, fora de qualquer declaração de função, você obterá os erros sobre os quais o OP perguntou (além de outros quando tentei). O problema fundamental é que o compilador está tentando tratar strategy = IMMEDIATE;como uma declaração. Ele tem uma forma que seria legal no pré-ANSI C, mas no moderno C é ilegal. As atribuições não são permitidas no escopo do arquivo.
Keith Thompson
3
@Solver: enum strategy { ... };define um tipo enumerado chamado enum strategy, onde strategyestá a tag. enum { ... } strategy;define um tipo enumerado anônimo (sem tag) e um único objeto desse tipo chamado strategy. Ambos são perfeitamente legais; eles apenas significam coisas diferentes.
21417 Keith Thompson

Respostas:

377

A declaração de uma variável enum é feita assim:

enum strategy {RANDOM, IMMEDIATE, SEARCH};
enum strategy my_strategy = IMMEDIATE;

No entanto, você pode usar a typedefpara encurtar as declarações de variáveis, assim:

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy my_strategy = IMMEDIATE;

Ter uma convenção de nomenclatura para distinguir entre tipos e variáveis ​​é uma boa idéia:

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy_type;
strategy_type my_strategy = IMMEDIATE;
RichieHindle
fonte
1
Mas OP queria uma variável de um tipo de enumeração anônima
osvein
Eu não poderia simplesmente digitar enum MyEnum {} myVar;e, em seguida, usar a variável myVarda seguinte maneira:myVar = SOMEENUMCONSTANT;
Mushy
451

Vale ressaltar que você não precisa de um typedef. Você pode fazer isso da seguinte maneira

enum strategy { RANDOM, IMMEDIATE, SEARCH };
enum strategy my_strategy = IMMEDIATE;

É uma questão de estilo, se você prefere typedef. Sem ele, se você quiser se referir ao tipo de enumeração, precisará usar enum strategy. Com isso, você pode apenas dizer strategy.

Ambas as formas têm seus prós e contras. O primeiro é mais prolixo, mas mantém os identificadores de tipo no namespace da tag, onde eles não entram em conflito com os identificadores comuns (pense struct state a statfunção: eles também não entram em conflito) e você vê imediatamente que é um tipo. O outro é mais curto, mas traz identificadores de tipo para o espaço para nome comum.

Johannes Schaub - litb
fonte
6
Não deve ser a resposta aceita porque está errada. Você não pode usar a estratégia enum {...}; em C - você pode e deve fazê-lo em C ++.
claro
19
@ Clearer: Este código funciona perfeitamente. Aqui está um exemplo de trabalho: ideone.com/T0YV17 Observe que ele usa a enumpalavra-chave nas duas linhas.
RichieHindle
Ou "estratégia typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy_t;" e o desenvolvedor que usa o enum pode usar a convenção que quiser.
Andy Nugent
isso funciona excelente: enum strategy { RANDOM, IMMEDIATE, SEARCH }; então, quando você deseja uma instância dessa enum: `enum strategy myEnum;
precisa saber é o seguinte
2
@AndyNugent não faça isso! * tipos _T são reservados pela POSIX
osvein
58

Você está tentando declarar strategyduas vezes e é por isso que está recebendo o erro acima. O seguinte funciona sem nenhuma reclamação (compilada com gcc -ansi -pendantic -Wall):

#include <stdio.h>

enum { RANDOM, IMMEDIATE, SEARCH } strategy = IMMEDIATE;

int main(int argc, char** argv){
    printf("strategy: %d\n", strategy);

    return 0;
}

Se, em vez do acima, a segunda linha foi alterada para:

...
enum { RANDOM, IMMEDIATE, SEARCH } strategy;
strategy = IMMEDIATE;
...

A partir dos avisos, você pode ver facilmente seu erro:

enums.c:5:1: warning: data definition has no type or storage class [enabled by default]
enums.c:5:1: warning: type defaults to int in declaration of strategy [-Wimplicit-int]
enums.c:5:1: error: conflicting types for strategy
enums.c:4:36: note: previous declaration of strategy was here

Então o compilador levou strategy = IMMEDIATE solicitou uma declaração de uma variável chamada strategycom o tipo padrão int, mas já havia uma declaração anterior de uma variável com esse nome.

No entanto, se você colocasse a atribuição na main()função, seria um código válido:

#include <stdio.h>

enum { RANDOM, IMMEDIATE, SEARCH } strategy = IMMEDIATE;

int main(int argc, char** argv){
    strategy=SEARCH;
    printf("strategy: %d\n", strategy);

    return 0;
}
Tarc
fonte
48

Quando voce diz

enum {RANDOM, IMMEDIATE, SEARCH} strategy;

você cria uma variável de instância única, chamada 'estratégia' de uma enumer sem nome. Isso não é uma coisa muito útil a se fazer - você precisa de um typedef:

typedef enum {RANDOM, IMMEDIATE, SEARCH} StrategyType; 
StrategyType strategy = IMMEDIATE;

fonte
9
Por que isso não é útil? Se eu não me importo com o nome do tipo, por que devo dar um? A única coisa pretendida aqui era nomear a variável, para que seja possível atribuir novos valores a ela.
MSalters
3
Eu disse que não era MUITO útil, e não acredito que seja. Certamente, eu não uso esse padrão no meu próprio código. YMMV.
3
@HorseSMith Um enum sem nome não é muito útil porque você não pode ter nenhuma outra variável desse tipo, parâmetro de função ou valor de retorno. Se a única variável é tudo que você precisa, tudo bem.
Bob Stein
3
Alguém que não usa enumerações anônimas não prova que não tem utilidade. Você não precisa de typedef. Algumas diretrizes de código (kernel.org/doc/Documentation/CodingStyle) até o desencorajam.
martinkunev
2
Esta resposta também é enganosa. A resposta de Tarc é a única certa aqui.
nightpool 27/02
13

Como está escrito, não há nada de errado com o seu código. Tem certeza de que não fez algo como

int strategy;
...
enum {RANDOM, IMMEDIATE, SEARCH} strategy;

Para quais linhas as mensagens de erro apontam? Quando diz que "a declaração anterior de 'estratégia' estava aqui", o que está "aqui" e o que mostra?

John Bode
fonte
6
Ele provavelmente fez strategy = IMMEDIATE;no escopo do arquivo. Uma atribuição não pode acontecer no escopo do arquivo fora de todas as funções. Portanto, o compilador tentou tirar o melhor proveito do erro e assumiu o que ele queria dizer int strategy = IMMEDIATE;, quando o conflito ocorreu.
Johannes Schaub - litb
2
Esta é a melhor resposta, há tanta confusão nas outras respostas que é doloroso.
descontrair
12

@ThoAppelsin em seu comentário à pergunta postada está certo. O trecho de código postado na pergunta é válido e sem erros. O erro que você tem deve ser devido a outra sintaxe incorreta em qualquer outro local do seu arquivo de origem c. enum{a,b,c};define três constantes simbólicas ( a, be c) que são números inteiros com valores 0, 1e 2, respectivamente, mas quando usamos enumé porque nós normalmente não se preocupam com o valor inteiro específico, que se preocupam mais com o significado do nome constante simbólica. Isso significa que você pode ter isso:

#include <stdio.h>
enum {a,b,c};
int main(){
  printf("%d\n",b);
  return 0;
}

e isso será exibido 1.

Isso também será válido:

#include <stdio.h>
enum {a,b,c};
int bb=b;
int main(){
  printf("%d\n",bb);
  return 0;
}

e produzirá o mesmo de antes.

Se você fizer isto:

enum {a,b,c};
enum {a,b,c};

você terá um erro, mas se você fizer isso:

enum alfa{a,b,c};
enum alfa;

você não terá nenhum erro.

você consegue fazer isso:

enum {a,b,c};
int aa=a;

e aaserá uma variável inteira com valor 0. mas você também pode fazer isso:

enum {a,b,c} aa= a;

e terá o mesmo efeito (ou seja, aasendo um intcom 0valor).

você também pode fazer isso:

enum {a,b,c} aa= a;
aa= 7;

e aaserá intcom valor 7.

porque você não pode repetir a definição constante simbólica com o uso de enum, como já disse anteriormente, você deve usar tags se desejar declarar intvars com o uso de enum:

enum tag1 {a,b,c};
enum tag1 var1= a;
enum tag1 var2= b;

o uso typedefdisso é para evitar que você escreva todas as vezes enum tag1para definir variáveis. Com typedefvocê, basta digitar Tag1:

typedef enum {a,b,c} Tag1;
Tag1 var1= a;
Tag1 var2= b;

Você também pode ter:

typedef enum tag1{a,b,c}Tag1;
Tag1 var1= a;
enum tag1 var2= b;

A última coisa a dizer é que, como estamos falando de constantes simbólicas definidas, é melhor usar letras maiúsculas ao usar enum, ou seja, por exemplo:

enum {A,B,C};

ao invés de

enum {a,b,c};
roggc
fonte
10

Vale ressaltar que em C ++ você pode usar "enum" para definir um novo tipo sem precisar de uma instrução typedef.

enum Strategy {RANDOM, IMMEDIATE, SEARCH};
...
Strategy myStrategy = IMMEDIATE;

Acho essa abordagem muito mais amigável.

[editar - status C ++ esclarecido - eu tinha isso originalmente e depois o removi!]

Roddy
fonte
Sim, você nunca deve usar typedef com enumerações (ou estruturas, uniões etc.) em C ++.
17
Esta pergunta é para C, não para C ++. Em C, o código acima é inválido - você precisa usar typedefou especificar também enumna declaração de variável: enum Strategy {RANDOM, IMMEDIATE, SEARCH}; ... enum Strategy myStrategy = IMMEDIATE;
Pavel Minaev 9/07/2009
@ pavel - meu mal. Eu tinha "em C ++" originalmente, então fiz algumas pesquisas que pareciam contradizer isso.
Roddy
@ Pavel Eu acho que deveria ser uma resposta separada descrevendo os benefícios do uso enum Strategy. Eu fiz isso, veja abaixo.
Johannes Schaub - litb
8

Parece haver uma confusão sobre a declaração.

Quando strategyvier antes, {RANDOM, IMMEDIATE, SEARCH}como a seguir,

enum strategy {RANDOM, IMMEDIATE, SEARCH};

você está criando um novo tipo chamado enum strategy. No entanto, ao declarar a variável, você precisa usar- enum strategyse. Você não pode simplesmente usar strategy. Portanto, o seguinte é inválido.

enum strategy {RANDOM, IMMEDIATE, SEARCH};
strategy a;

Embora, o seguinte seja válido

enum strategy {RANDOM, IMMEDIATE, SEARCH};

enum strategy queen = RANDOM;
enum strategy king = SEARCH;
enum strategy pawn[100];

Quando strategyvem depois {RANDOM, IMMEDIATE, SEARCH}, você está criando uma enumeração anônima e declarando strategyser uma variável desse tipo.

Então agora você pode fazer algo como

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = RANDOM;

No entanto, você não pode declarar nenhuma outra variável do tipo enum {RANDOM, IMMEDIATE, SEARCH} porque nunca o nomeou. Portanto, o seguinte é inválido

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
enum strategy a = RANDOM;

Você pode combinar as duas definições também

enum strategy {RANDOM, IMMEDIATE, SEARCH} a, b;

a = RANDOM;
b = SEARCH;
enum strategy c = IMMEDIATE;

Typedef como observado anteriormente, é usado para criar uma declaração de variável mais curta.

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;

Agora você disse ao compilador que enum {RANDOM, IMMEDIATE, SEARCH}é sinônimo de strategy. Então agora você pode usar livremente strategycomo tipo de variável. Você não precisa enum strategymais digitar . O seguinte é válido agora

strategy x = RANDOM;

Você também pode combinar Typedef junto com o nome do enum para obter

typedef enum strategyName {RANDOM, IMMEDIATE, SEARCH} strategy;

Não há muita vantagem em usar esse método, além do fato de que agora você pode usá strategy-lo de forma enum strategyNameintercambiável.

typedef enum strategyName {RANDOM, IMMEDIATE, SEARCH} strategy;

enum strategyName a = RANDOM;
strategy b = SEARCH;
Confundir
fonte
1
Ótima resposta. Eu também me deparei com definições de enum escritas assim: typedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategyou typedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategy_type. Isso tem alguma vantagem typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy? Você pensaria em adicioná-los à sua resposta, para completar?
Tjalling
Sim. Eu modifiquei minha resposta. De acordo com o meu conhecimento, não há nenhuma grande vantagem no caso geral.
Confunda
2
Ótimo, sua resposta cobre tudo agora, obrigado. Uma pena que esteja tão longe na lista de respostas, nem um pouco porque aborda explicitamente a pergunta original, com uma explicação adequada.
Tjalling
2

Se você declarar o nome para enumeração, nenhum erro ocorrerá.

Se não declarado, você deve usar um typedef:

enum enum_name {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;

Não exibirá um erro ...

Peter Mortensen
fonte
2

Minha construção favorita e única usada sempre foi:

typedef enum MyBestEnum
{
    /* good enough */
    GOOD = 0,
    /* even better */
    BETTER,
    /* divine */
    BEST
};

Eu acredito que isso irá remover o seu problema que você tem. Usar novo tipo é do meu ponto de vista a opção correta.

Sany
fonte
1

A resposta de Tarc é a melhor.

Grande parte da discussão enum é um arenque vermelho.

Compare este trecho de código: -

int strategy;
strategy = 1;   
void some_function(void) 
{
}

que dá

error C2501: 'strategy' : missing storage-class or type specifiers
error C2086: 'strategy' : redefinition

com este que compila sem nenhum problema.

int strategy;
void some_function(void) 
{
    strategy = 1;   
}

A variável strategy precisa ser definida na declaração ou dentro de uma função etc. Você não pode escrever software arbitrário - atribuições em particular - no escopo global.

O fato de ele ter usado enum {RANDOM, IMEDIATE, PESQUISA} em vez de int é relevante apenas na medida em que confunde pessoas que não podem ver além dele. As mensagens de erro de redefinição na pergunta mostram que foi isso que o autor fez de errado.

Portanto, agora você deve ver por que o primeiro exemplo abaixo está errado e os outros três estão bem.

Exemplo 1. ERRADO!

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
strategy = IMMEDIATE;
void some_function(void) 
{
}

Exemplo 2. DIREITO.

enum {RANDOM, IMMEDIATE, SEARCH} strategy = IMMEDIATE;
void some_function(void) 
{
}

Exemplo 3. DIREITO.

enum {RANDOM, IMMEDIATE, SEARCH} strategy;
void some_function(void) 
{
    strategy = IMMEDIATE;
}

Exemplo 4. DIREITO.

void some_function(void) 
{
    enum {RANDOM, IMMEDIATE, SEARCH} strategy;
    strategy = IMMEDIATE;
}

Se você tiver um programa em funcionamento, poderá colar esses trechos no seu programa e ver que alguns são compilados e outros não.

Ivan
fonte
0

Eu tentei com o gcc e, por ser necessário, fui forçado a usar a última alternativa, a compilar sem erros.

typedef enum estado {a = 0, b = 1, c = 2} estado ;

typedef enum state {a = 0, b = 1, c = 2} state;

typedef enum state old; // New type, alias of the state type.
typedef enum state new; // New type, alias of the state type.

new now     = a;
old before  = b;

printf("State   now = %d \n", now);
printf("Sate before = %d \n\n", before);
gg cg
fonte
newé uma má escolha de identificadores na família C porque é um operador em C ++.
JWW
0

C

enum stuff q;
enum stuff {a, b=-4, c, d=-2, e, f=-3, g} s;

Declaração que atua como uma definição provisória de um número inteiro assinado scom tipo completo e declaração que atua como uma definição provisória de um número inteiro assinado qcom tipo incompleto no escopo (que resolve para o tipo completo no escopo porque a definição de tipo está presente em qualquer lugar do escopo) (como qualquer definição provisória, os identificadores qe spodem ser redeclarados com a versão incompleta ou completa do mesmo tipo intou enum stuffvárias vezes, mas definidos apenas uma vez no escopo, por exemplo, int q = 3; e só podem ser redefinidos em um sub-escopo, e apenas utilizável após a definição). Além disso, você só pode usar o tipo completo de enum stuffuma vez no escopo porque ele atua como uma definição de tipo.

Uma definição de tipo de enumeração do compilador enum stufftambém é apresentada no escopo do arquivo (utilizável antes e abaixo), bem como uma declaração de tipo encaminhamento (o tipo enum stuffpode ter várias declarações, mas apenas uma definição / conclusão no escopo e pode ser redefinido em um sub-escopo) . Ele também atua como uma diretiva de compilador para substituir acom rvalue 0, bpor -4, ccom 5, dcom -2, ecom -3, fcom -1e gcom -2no escopo atual. As constantes de enumeração agora se aplicam após a definição até a próxima redefinição em uma enumeração diferente que não pode estar no mesmo nível de escopo.

typedef enum bool {false, true} bool;

//this is the same as 
enum bool {false, true};
typedef enum bool bool;

//or
enum bool {false, true};
typedef unsigned int bool;

//remember though, bool is an alias for _Bool if you include stdbool.h. 
//and casting to a bool is the same as the !! operator 

O espaço de nome da tag compartilhado por enum, struct e union é separado e deve ser prefixado pelo tipo keyword (enum, struct ou union) em C, ou seja enum a {a} b, depois , enum a cdeve ser usado e não a c. Como o espaço para nome da tag é separado do espaço para nome do identificador, enum a {a} bé permitido, mas enum a {a, b} bnão porque as constantes estão no mesmo espaço para nome dos identificadores da variável, o espaço para nome do identificador.typedef enum a {a,b} btambém não é permitido porque typedef-names fazem parte do espaço para nome do identificador.

O tipo de enum boole as constantes seguem o seguinte padrão em C:

+--------------+-----+-----+-----+
|   enum bool  | a=1 |b='a'| c=3 |  
+--------------+-----+-----+-----+
| unsigned int | int | int | int |  
+--------------+-----+-----+-----+

+--------------+-----+-----+-----+
|   enum bool  | a=1 | b=-2| c=3 |  
+--------------+-----+-----+-----+
|      int     | int | int | int |  
+--------------+-----+-----+-----+

+--------------+-----+---------------+-----+
|   enum bool  | a=1 |b=(-)0x80000000| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int |  unsigned int | int |
+--------------+-----+---------------+-----+

+--------------+-----+---------------+-----+
|   enum bool  | a=1 |b=(-)2147483648| c=2 |
+--------------+-----+---------------+-----+
| unsigned int | int |  unsigned int | int |
+--------------+-----+---------------+-----+

+-----------+-----+---------------+------+
| enum bool | a=1 |b=(-)0x80000000| c=-2 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=2147483648  | c=-2 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=-2147483648 | c=-2 |
+-----------+-----+---------------+------+
|    int    | int |      int      |  int |
+-----------+-----+---------------+------+

+---------------+-----+---------------+-----+
|   enum bool   | a=1 | b=99999999999 | c=1 |
+---------------+-----+---------------+-----+
| unsigned long | int | unsigned long | int |
+---------------+-----+---------------+-----+

+-----------+-----+---------------+------+
| enum bool | a=1 | b=99999999999 | c=-1 |
+-----------+-----+---------------+------+
|    long   | int |      long     |  int |
+-----------+-----+---------------+------+

Isso compila bem em C:

#include <stdio.h>
enum c j;
enum c{f, m} p;
typedef int d;
typedef int c;
enum c j;
enum m {n} ;
int main() {
  enum c j;
  enum d{l};
  enum d q; 
  enum m y; 
  printf("%llu", j);
}

C ++

Em C ++, enumerações podem ter um tipo

enum Bool: bool {True, False} Bool;
enum Bool: bool {True, False, maybe} Bool; //error

Nessa situação, as constantes e o identificador têm o mesmo tipo, bool e um erro ocorrerá se um número não puder ser representado por esse tipo. Talvez = 2, o que não é um bool. Além disso, True, False e Bool não podem estar em minúsculas, caso contrário, entrarão em conflito com as palavras-chave do idioma. Um enum também não pode ter um tipo de ponteiro.

As regras para enumerações são diferentes em C ++.

#include <iostream>
c j; //not allowed, unknown type name c before enum c{f} p; line
enum c j; //not allowed, forward declaration of enum type not allowed and variable can have an incomplete type but not when it's still a forward declaration in C++ unlike C
enum c{f, m} p;
typedef int d;
typedef int c; // not allowed in C++ as it clashes with enum c, but if just int c were used then the below usages of c j; would have to be enum c j;
[enum] c j;
enum m {n} ;
int main() {
  [enum] c j;
  enum d{l}; //not allowed in same scope as typedef but allowed here 
  d q;
  m y; //simple type specifier not allowed, need elaborated type specifier enum m to refer to enum m here
  p v; // not allowed, need enum p to refer to enum p
  std::cout << j;
}

Variáveis ​​de enumerações em C ++ não são mais apenas números inteiros não assinados etc., elas também são do tipo enumeração e só podem ser atribuídas constantes na enumeração. No entanto, isso pode ser descartado.

#include <stdio.h>
enum a {l} c;
enum d {f} ;
int main() {
  c=0; // not allowed;
  c=l;
  c=(a)1;
  c=(enum a)4;
  printf("%llu", c); //4
}

Classes Enum

enum struct é idêntico a enum class

#include <stdio.h>
enum class a {b} c;
int main() {
  printf("%llu", a::b<1) ; //not allowed
  printf("%llu", (int)a::b<1) ;
  printf("%llu", a::b<(a)1) ;
  printf("%llu", a::b<(enum a)1);
  printf("%llu", a::b<(enum class a)1) ; //not allowed 
  printf("%llu", b<(enum a)1); //not allowed
}

O operador de resolução do escopo ainda pode ser usado para enumerações sem escopo.

#include <stdio.h>
enum a: bool {l, w} ;
int main() {
  enum a: bool {w, l} f;
  printf("%llu", ::a::w);
}

Mas como w não pode ser definido como outra coisa no escopo, não há diferença entre ::we::a::w

Lewis Kelsey
fonte