¿Cómo definir un tipo enumerado (enum) en C?

272

No estoy seguro de cuál es la sintaxis adecuada para usar las enumeraciones C. Tengo el siguiente código:

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

Pero esto no se compila, con el siguiente error:

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

¿Qué estoy haciendo mal?

lindelof
fuente
77
Pregunta de años, probablemente nadie verá esto; pero ¿por qué esto está dando un error? Debería funcionar perfectamente bien como está en la pregunta en lo que respecta a mi conocimiento.
Utkan Gezer
2
@Solver, ¿por qué esta sintaxis es incorrecta?
MCG
66
@MCQ, necroing necro'd necro: la sintaxis presentada en la pregunta no es incorrecta en C. Declara strategyque tiene un tipo enumerado anónimo y le asigna uno de los valores declarados de ese tipo. Además, si envuelvo el código presentado en una main()función de otra manera trivial , entonces me compila bien, sin siquiera una advertencia, con gcc 4.4.7. Algunas de las respuestas implican lo mismo, aunque no en muchas palabras.
John Bollinger
55
A la mayoría de las respuestas les falta el hecho de que las dos líneas de código en la pregunta no son solo un fragmento. Son el archivo fuente completo. Si esas dos líneas están incluidas en el cuerpo de una función, no hay error. Si aparecen en el alcance del archivo, fuera de cualquier declaración de función, obtendrá los errores sobre los que el OP preguntó (más algunos otros cuando lo probé). El problema fundamental es que el compilador está tratando de tratar strategy = IMMEDIATE;como una declaración. Tiene una forma que habría sido legal en C anterior a ANSI, pero en C moderna es ilegal. Las asignaciones no están permitidas en el alcance del archivo.
Keith Thompson
3
@Solver: enum strategy { ... };define un tipo enumerado llamado enum strategy, donde strategyestá la etiqueta. enum { ... } strategy;define un tipo anónimo enumerado (sin etiqueta) y un único objeto de ese tipo llamado strategy. Ambos son perfectamente legales; solo significan cosas diferentes.
Keith Thompson

Respuestas:

377

La declaración de una variable enum se hace así:

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

Sin embargo, puede usar a typedefpara acortar las declaraciones de variables, así:

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

Tener una convención de nomenclatura para distinguir entre tipos y variables es una buena idea:

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy_type;
strategy_type my_strategy = IMMEDIATE;
RichieHindle
fuente
1
Pero OP quería una variable de un tipo de enumeración anónimo
osvein
¿No podría simplemente escribir enum MyEnum {} myVar;y luego usar la variable de la myVarsiguiente manera:myVar = SOMEENUMCONSTANT;
Mushy
451

Vale la pena señalar que no necesita a typedef. Puedes hacerlo de la siguiente manera

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

Es una pregunta de estilo si lo prefieres typedef. Sin él, si desea hacer referencia al tipo de enumeración, debe usarlo enum strategy. Con eso, solo puedes decir strategy.

Ambas formas tienen sus ventajas y desventajas. El uno es más prolijo, pero mantiene los identificadores de tipo en el espacio de nombre de etiqueta donde no entrarán en conflicto con los identificadores ordinarios (piense struct staty la statfunción: tampoco entran en conflicto), y donde inmediatamente ve que es un tipo. El otro es más corto, pero trae identificadores de tipo al espacio de nombres ordinario.

Johannes Schaub - litb
fuente
66
No debería ser la respuesta aceptada porque está mal. No puede usar la estrategia enum {...}; en C: puede y debe hacerlo en C ++.
Más claro
19
@Clearer: este código funciona perfectamente. Aquí hay un ejemplo de trabajo: ideone.com/T0YV17 Tenga en cuenta que usa la enumpalabra clave en ambas líneas.
RichieHindle
O "typedef enum strategy {RANDOM, INMEDIATE, SEARCH} strategy_t;" y el desarrollador que usa la enumeración puede usar la convención que desee.
Andy Nugent
esto funciona excelente: enum strategy { RANDOM, IMMEDIATE, SEARCH }; entonces cuando quieres una instancia de esa enumeración: `enum strategy myEnum;
user3629249
2
@AndyNugent no hagas eso! * _t tipos están reservados por POSIX
osvein
58

Estás tratando de declarar strategydos veces, y es por eso que obtienes el error anterior. Lo siguiente funciona sin ninguna queja (compilado con 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;
}

Si en lugar de lo anterior, la segunda línea se cambió a:

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

De las advertencias, puede ver fácilmente su error:

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

Entonces el compilador tomó strategy = IMMEDIATEuna declaración de una variable llamada strategycon el tipo predeterminado int, pero ya había una declaración previa de una variable con este nombre.

Sin embargo, si coloca la asignación en la main()función, sería un 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
fuente
48

Cuando tu dices

enum {RANDOM, IMMEDIATE, SEARCH} strategy;

crea una variable de instancia única, llamada 'estrategia' de una enumeración sin nombre. Esto no es algo muy útil, necesita un typedef:

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

fuente
9
¿Por qué esto no es útil? Si no me importa el nombre del tipo, ¿por qué debería darle uno? Lo único que se pretendía era nombrar la variable, por lo que es posible asignarle nuevos valores.
MSalters
3
Dije que no era MUY útil, y no creo que lo sea. Ciertamente, no uso este patrón en mi propio código. YMMV.
3
@HorseSMith Una enumeración sin nombre no es muy útil porque no puede tener ninguna otra variable de ese tipo, parámetro de función o valor de retorno. Si la única variable es todo lo que necesita, entonces está bien.
Bob Stein
3
Alguien que no usa enumeraciones anónimas no prueba que no tengan ningún uso. No necesitas typedef. Algunas pautas de código (kernel.org/doc/Documentation/CodingStyle) incluso lo desalientan.
martinkunev
2
Esta respuesta también es engañosa. La respuesta de Tarc es la única correcta aquí.
nightpool
13

Como está escrito, no hay nada malo con su código. ¿Estás seguro de que no has hecho algo como

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

¿A qué líneas apuntan los mensajes de error? Cuando dice "la declaración previa de 'estrategia' estaba aquí", ¿qué es "aquí" y qué muestra?

John Bode
fuente
66
Probablemente lo hizo strategy = IMMEDIATE;en el ámbito del archivo. Una asignación no puede suceder en el ámbito del archivo fuera de todas las funciones. Entonces el compilador trató de sacar el máximo provecho del error y asumió que se refería a él int strategy = IMMEDIATE;, momento en el cual ocurrió el conflicto.
Johannes Schaub - litb
2
Esta es la mejor respuesta, hay tanta confusión en las otras respuestas que es dolorosa.
relajarse
12

@ThoAppelsin en su comentario a la pregunta publicada es correcto. El fragmento de código publicado en la pregunta es válido y no contiene errores. El error que tiene debe deberse a otra sintaxis incorrecta en cualquier otro lugar de su archivo fuente c. enum{a,b,c};define tres constantes simbólicas ( a, by c) que son números enteros con valores 0, 1y 2respectivamente, pero cuando lo usamos enumes porque generalmente no nos importa el valor entero específico, nos importa más el significado del nombre simbólico constante. Esto significa que puedes tener esto:

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

y esto dará salida 1.

Esto también será válido:

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

y producirá lo mismo que antes.

Si haces esto:

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

tendrá un error, pero si hace esto:

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

No tendrás ningún error.

Puedes hacerlo:

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

y aaserá una variable entera con valor 0. pero también puedes hacer esto:

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

y tendrá el mismo efecto (es decir, aaser una intcon 0valor).

También puedes hacer esto:

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

y aaserá intcon valor 7.

porque no puede repetir la definición simbólica constante con el uso de enum, como he dicho anteriormente, debe usar etiquetas si desea declarar intvars con el uso de enum:

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

su uso typedefes para evitar que escriba cada vez enum tag1que defina variables. Con typedefusted solo puede escribir Tag1:

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

También puedes tener:

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

Lo último que hay que decir es que, dado que estamos hablando de constantes simbólicas definidas, es mejor usar letras mayúsculas cuando se usan enum, es decir, por ejemplo:

enum {A,B,C};

en vez de

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

Vale la pena mencionar que en C ++ puede usar "enum" para definir un nuevo tipo sin necesidad de una declaración typedef.

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

Este enfoque me parece mucho más amigable.

[editar - aclarado el estado de C ++ - ¡Tenía esto originalmente, luego lo eliminé!]

Roddy
fuente
Sí, nunca debe usar typedef con enumeraciones (o estructuras, uniones, etc.) en C ++.
17
Esta pregunta es para C, no para C ++. En C, el código anterior no es válido: debe usar typedefo especificar también enumen la declaración de variables: enum Strategy {RANDOM, INMEDIATE, SEARCH}; ... enumeración Estrategia myStrategy = INMEDIATO;
Pavel Minaev
@pavel - mi mal. Tenía "en C ++" originalmente, luego hice una investigación que parecía contradecir eso.
Roddy
@Pavel Creo que debería ser una respuesta separada que describa los beneficios del uso enum Strategy. Lo hice, ver más abajo.
Johannes Schaub - litb
8

Parece haber una confusión sobre la declaración.

Cuando strategyviene antes {RANDOM, IMMEDIATE, SEARCH}como en el siguiente,

enum strategy {RANDOM, IMMEDIATE, SEARCH};

Estás creando un nuevo tipo llamado enum strategy. Sin embargo, al declarar la variable, debe usarla enum strategy. No puedes simplemente usar strategy. Entonces lo siguiente no es válido.

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

Mientras, lo siguiente es válido

enum strategy {RANDOM, IMMEDIATE, SEARCH};

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

Cuando strategyviene después {RANDOM, IMMEDIATE, SEARCH}, está creando una enumeración anónima y luego declara strategyser una variable de ese tipo.

Ahora puedes hacer algo como

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

Sin embargo, no puede declarar ninguna otra variable de tipo enum {RANDOM, IMMEDIATE, SEARCH}porque nunca la ha nombrado. Entonces lo siguiente no es válido

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

Puedes combinar ambas definiciones también

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

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

Typedef como se señaló anteriormente, se utiliza para crear una declaración de variable más corta.

typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy;

Ahora le ha dicho al compilador que enum {RANDOM, IMMEDIATE, SEARCH}es sinónimo de strategy. Así que ahora puedes usarlo libremente strategycomo tipo variable. No necesitas escribir enum strategymás. Lo siguiente es válido ahora

strategy x = RANDOM;

También puede combinar Typedef junto con el nombre de enumeración para obtener

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

No hay mucha ventaja de utilizar este método, aparte del hecho de que ahora se puede utilizar strategyy enum strategyNamede manera intercambiable.

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

enum strategyName a = RANDOM;
strategy b = SEARCH;
Confundir
fuente
1
Gran respuesta. También me he encontrado con definiciones de enumeración escritas así: typedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategyo typedef enum strategy {RANDOM, IMMEDIATE, SEARCH} strategy_type. ¿Tiene eso alguna ventaja typedef enum {RANDOM, IMMEDIATE, SEARCH} strategy? ¿Consideraría agregar estos a su respuesta, para completar?
tjalling
Si. Modifiqué mi respuesta. Según mi conocimiento, no hay ninguna ventaja importante en el caso general.
Confundir
2
Genial, tu respuesta lo cubre todo ahora, gracias. Es una pena que esté tan abajo en la lista de respuestas, sobre todo porque aborda explícitamente la pregunta original, con la explicación adecuada.
tjalling
2

Si declara el nombre para la enumeración, no se producirá ningún error.

Si no se declara, debe usar un typedef:

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

No mostrará un error ...

Peter Mortensen
fuente
2

Mi construcción favorita y solo usada siempre fue:

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

Creo que esto eliminará el problema que tienes. Usar un nuevo tipo es, desde mi punto de vista, la opción correcta.

Sany
fuente
1

La respuesta de Tarc es la mejor.

Gran parte de la discusión sobre la enumeración es un arenque rojo.

Compare este fragmento de código: -

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

lo que da

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

con este que se compila sin problemas.

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

La variable strategy debe establecerse en la declaración o dentro de una función, etc. No puede escribir software arbitrario, asignaciones en particular, en el ámbito global.

El hecho de que haya usado enum {RANDOM, INMEDIATE, SEARCH} en lugar de int solo es relevante en la medida en que ha confundido a las personas que no pueden ver más allá. Los mensajes de error de redefinición en la pregunta muestran que esto es lo que el autor ha hecho mal.

Entonces, ahora debería poder ver por qué el primero del ejemplo a continuación está mal y los otros tres están bien.

Ejemplo 1. ¡Incorrecto!

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

Ejemplo 2. DERECHO.

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

Ejemplo 3. DERECHO.

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

Ejemplo 4. DERECHO.

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

Si tiene un programa en funcionamiento, debería poder pegar estos fragmentos en su programa y ver que algunos compilan y otros no.

Ivan
fuente
0

Intenté con gcc y pensé en mi necesidad. Me vi obligado a usar la última alternativa, para compilar sin errores.

typedef enum state {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
fuente
newes una mala elección de identificadores en la familia C porque es un operador en C ++.
jww
0

C

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

Declaración que actúa como una definición tentativa de un número entero scon signo con tipo completo y declaración que actúa como una definición tentativa de número entero qcon signo con tipo incompleto en el ámbito (que resuelve el tipo completo en el ámbito porque la definición de tipo está presente en cualquier lugar del alcance) (como cualquier definición tentativa, los identificadores qy se spueden volver a declarar con la versión incompleta o completa del mismo tipo into enum stuffvarias veces, pero solo se definen una vez en el alcance, es decir, int q = 3; y solo se pueden redefinir en un subscopio, y solo utilizable después de la definición). Además, solo puede usar el tipo completo deenum stuff una vez en el ámbito porque actúa como una definición de tipo.

Una definición de tipo de enumeración del compilador enum stufftambién se hace presente en el alcance del archivo (utilizable antes y a continuación), así como una declaración de tipo de reenvío (el tipo enum stuffpuede tener múltiples declaraciones pero solo una definición / finalización en el alcance y puede redefinirse en un subscopio) . También actúa como directiva compiladora para sustituir acon rvalue 0, bwith -4, cwith 5, dwith -2, ewith -3, fwith -1y gwith -2en el ámbito actual. Las constantes de enumeración ahora se aplican después de la definición hasta la próxima redefinición en una enumeración diferente que no puede estar en el mismo nivel de alcance.

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 

El espacio de nombres de etiqueta compartido por enum, struct y union está separado y debe tener el prefijo de la palabra clave type (enum, struct o union) en C, es decir enum a {a} b, después enum a cdebe usarse y no a c. Debido a que el espacio de nombres de la etiqueta está separado del espacio de nombres del identificador, enum a {a} bestá permitido pero enum a {a, b} bno porque las constantes estén en el mismo espacio de nombres que los identificadores variables, el espacio de nombres del identificador. typedef enum a {a,b} btampoco está permitido porque typedef-names son parte del espacio de nombres del identificador.

El tipo de enum booly las constantes siguen el siguiente patrón en 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 |
+-----------+-----+---------------+------+

Esto compila bien en 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 ++

En C ++, las enumeraciones pueden tener un tipo

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

En esta situación, las constantes y el identificador tienen el mismo tipo, bool, y se producirá un error si un número no puede ser representado por ese tipo. Quizás = 2, que no es un bool. Además, Verdadero, Falso y Bool no pueden ser minúsculas, de lo contrario chocarán con las palabras clave del idioma. Una enumeración tampoco puede tener un tipo de puntero.

Las reglas para las enumeraciones son diferentes en 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;
}

Las variables de enumeración en C ++ ya no son solo enteros sin signo, etc., también son de tipo enumeración y solo se les pueden asignar constantes en la enumeración. Sin embargo, esto puede descartarse.

#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
}

Clases de enumeración

enum struct es 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
}

El operador de resolución de alcance todavía se puede usar para enumeraciones sin ámbito.

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

Pero debido a que w no puede definirse como algo más en el alcance, no hay diferencia entre ::wy::a::w

Lewis Kelsey
fuente