Imprimir texto em vez de valor de C enum


87
int main()
{

  enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

  Days TheDay;

  int j = 0;

  printf("Please enter the day of the week (0 to 6)\n");

  scanf("%d",&j);

  TheDay = Days(j);

  //how to PRINT THE VALUES stored in TheDay

  printf("%s",TheDay);  //   isnt working

  return 0;
}

Sua saída esperada é imprimir a string "Domingo", etc.?
GalacticCowboy

Respostas:


104

Enumerações em C são números que possuem nomes convenientes dentro de seu código. Eles não são strings e os nomes atribuídos a eles no código-fonte não são compilados em seu programa e, portanto, não são acessíveis em tempo de execução.

A única maneira de obter o que deseja é escrever você mesmo uma função que traduza o valor de enumeração em uma string. Por exemplo (supondo que você mova a declaração de enum Daysfora de main):

const char* getDayName(enum Days day) 
{
   switch (day) 
   {
      case Sunday: return "Sunday";
      case Monday: return "Monday";
      /* etc... */
   }
}

/* Then, later in main: */
printf("%s", getDayName(TheDay));

Alternativamente, você pode usar uma matriz como um mapa, por exemplo

const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ };

/* ... */

printf("%s", dayNames[TheDay]);

Mas aqui você provavelmente desejaria atribuir Sunday = 0na enumeração para ser seguro ... Não tenho certeza se o padrão C exige que os compiladores comecem as enumerações a partir de 0, embora a maioria faça (tenho certeza que alguém comentará para confirmar ou negar isso )


3
Ah, você chegou antes de mim na solução de array. : P Mas sim, enums sempre começam em 0, a menos que você especifique um valor diferente.
casablanca

1
Se eu estivesse contando com o uso das enumerações como índices, preferiria numerar cada uma explicitamente. Desnecessário de acordo com os padrões, mas como grupo, os compiladores não são exatamente os melhores em seguir os padrões em minha experiência.
jdmichal

3
O padrão C diz: "Se o primeiro enumerador não tiver =, o valor de sua constante de enumeração é 0". Mas não custa nada ter isso explícito.
Michael Burr

17
Não se esqueça que com o C99 você pode fazer const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Você sabe, caso os dias da semana sejam reordenados, ou você decida que segunda-feira é o primeiro dia da semana.
Tim Schaeffer

2
@ user3467349 Isso (string do pré-processador) apenas transforma o símbolo após o # em uma string. Então, sim, #Segunda-feira se transformaria em "Segunda-feira", mas Days TheDay = Monday; printf("%s", #TheDay);imprimiria "O Dia".
Tyler McHenry

29

Eu uso algo assim:

em um arquivo "EnumToString.h":

#undef DECL_ENUM_ELEMENT
#undef DECL_ENUM_ELEMENT_VAL
#undef DECL_ENUM_ELEMENT_STR
#undef DECL_ENUM_ELEMENT_VAL_STR
#undef BEGIN_ENUM
#undef END_ENUM

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element,
    #define DECL_ENUM_ELEMENT_VAL( element, value ) element = value,
    #define DECL_ENUM_ELEMENT_STR( element, descr ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_VAL( element, value )
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define BEGIN_ENUM( ENUM_NAME) const char * GetString##ENUM_NAME( enum tag##ENUM_NAME index ) {\
        switch( index ) { 
    #define DECL_ENUM_ELEMENT( element ) case element: return #element; break;
    #define DECL_ENUM_ELEMENT_VAL( element, value ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_STR( element, descr ) case element: return descr; break;
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_STR( element, descr )

    #define END_ENUM( ENUM_NAME ) default: return "Unknown value"; } } ;

#endif

então, em qualquer arquivo de cabeçalho, você faz a declaração enum, day enum.h

#include "EnumToString.h"

BEGIN_ENUM(Days)
{
    DECL_ENUM_ELEMENT(Sunday) //will render "Sunday"
    DECL_ENUM_ELEMENT(Monday) //will render "Monday"
    DECL_ENUM_ELEMENT_STR(Tuesday, "Tuesday string") //will render "Tuesday string"
    DECL_ENUM_ELEMENT(Wednesday) //will render "Wednesday"
    DECL_ENUM_ELEMENT_VAL_STR(Thursday, 500, "Thursday string") // will render "Thursday string" and the enum will have 500 as value
    /* ... and so on */
}
END_ENUM(MyEnum)

em seguida, em um arquivo chamado EnumToString.c:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

então em main.c:

int main(int argc, char* argv[])
{
    Days TheDay = Monday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "1 - Monday"

    TheDay = Thursday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "500 - Thursday string"

    return 0;
}

isto irá gerar "automaticamente" as strings para quaisquer enums declarados desta forma e incluídos em "EnumToString.c"


4
É feio de ler, mas você não tem duplicação de dados. (Ao contrário de todo mundo.) Estou indeciso sobre se gosto disso.
Kim Reece

1
1 para a solução incrivelmente criativa sem duplicação de dados e provavelmente a melhor manutenibilidade / flexibilidade, mas yech! Acho que ainda prefiro seguir a rota const char * [].
manifesto de

4
Sim, a manutenção é incrível! É muito fácil atualizar quando os dias da semana mudam! </sarcasm> A propósito, isso nem é útil para fins de localização, já que o mapeamento entre as strings em inglês e os nomes no programa agora está embutido em código por sua tentativa de evitar a duplicação. Pelo menos com as outras abordagens, é possível traduzir as strings sem alterar todas as ocorrências nos arquivos de origem.
R .. GitHub PARAR DE AJUDAR O ICE

1
Provavelmente, você pode internacionalizá-lo (com algo como gettext) alterando as instruções de retorno para return _(#element)e semelhantes.
Vargas

Quando o pré-processador C é tão útil, mas tão feio, geralmente o substituo por um gerador de código simples ou pré-processador personalizado em uma linguagem de script. E, na verdade, tenho um script Python que usei exatamente para esse propósito em vários projetos. Mas não o uso com frequência hoje em dia - para muitos casos de uso, você pode se safar apenas usando strings e não se preocupando com enums (e ainda mais em C ++ ou ObjC).
abarnert de

6

Normalmente, faço isso armazenando as representações de string em uma matriz separada na mesma ordem e, em seguida, indexando a matriz com o valor enum:

const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"

4

enums em C realmente não funcionam da maneira que você espera que funcionem. Você pode pensar nelas como constantes glorificadas (com alguns benefícios adicionais relacionados a ser uma coleção de tais constantes), e o texto que você escreveu para "Domingo" realmente é resolvido em um número durante a compilação, o texto é finalmente descartados.

Resumindo: para fazer o que você realmente deseja, você precisará manter um array de strings ou criar uma função para mapear do valor do enum para o texto que deseja imprimir.


4

Enumerações em C são basicamente açúcar sintático para listas nomeadas de valores inteiros sequenciados automaticamente. Ou seja, quando você tiver este código:

int main()
{
    enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

    Days TheDay = Monday;
}

Seu compilador realmente expõe isto:

int main()
{
    int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0.
}

Portanto, a saída de uma enumeração C como uma string não é uma operação que faz sentido para o compilador. Se você quiser ter strings legíveis por humanos para eles, precisará definir funções para converter de enumerações em strings.


4

Esta é uma maneira mais limpa de fazer isso com macros:

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

#define DOW(X, S)                                                         \
    X(Sunday) S X(Monday) S X(Tuesday) S X(Wednesday) S X(Thursday) S X(Friday) S X(Saturday)

#define COMMA ,

/* declare the enum */
#define DOW_ENUM(DOW) DOW
enum dow {
    DOW(DOW_ENUM, COMMA)
};

/* create an array of strings with the enum names... */
#define DOW_ARR(DOW ) [DOW] = #DOW
const char * const dow_str[] = {
    DOW(DOW_ARR, COMMA)
};

/* ...or create a switchy function. */
static const char * dowstr(int i)
{
#define DOW_CASE(D) case D: return #D

    switch(i) {
        DOW(DOW_CASE, ;);
    default: return NULL;
    }
}


int main(void)
{
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dow_str[i]);
    printf("\n");
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dowstr(i));
    return 0;
}

Não tenho certeza se este é um pré-processador p / b totalmente portátil, mas funciona com gcc.

Este é c99 btw, então use c99 strictse você conectá-lo (o compilador online) ideone .


Tenho que amar como as macros “limpas” são :-).
mk12

3

Eu sei que estou atrasado para a festa, mas que tal isso?

const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ };
printf("%s", dayNames[Sunday]); // prints "Sunday"

Dessa forma, você não precisa manter manualmente o enume o char*array em sincronia. Se você for como eu, é provável que mais tarde altere o enume o char*array imprima strings inválidas. Este pode não ser um recurso com suporte universal. Mas afaik, a maioria dos compiladores C mais modernos suportam este estilo inicial designado.

Você pode ler mais sobre inicializadores designados aqui .


1

A questão é que você deseja escrever o nome apenas uma vez.
Eu tenho um ider assim:

#define __ENUM(situation,num) \
    int situation = num;        const char * __##situation##_name = #situation;

    const struct {
        __ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write, 
        __ENUM(get_negative_to_unsigned, -204);
        __ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
        int get_no_num = -201;      const char * __get_no_num_name = "get_no_num";
        int get_float_to_int = -202;        const char * get_float_to_int_name = "float_to_int_name";

    }eRevJson;
#undef __ENUM
    struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
    inline const char * enumRevJsonGetString(int num) {
        sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
        for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
            if (ptr[i].value == num) {
                return ptr[i].p_name;
            }
        }
        return "bad_enum_value";
    }

ele usa uma estrutura para inserir enum, de forma que uma impressora para string possa seguir cada definição de valor de enum.

int main(int argc, char *argv[]) {  
    int enum_test = eRevJson.get_other_string;
    printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);

>error is get_other_string, number is -203

A diferença para enum é que o construtor não pode relatar erros se os números forem repetidos. se você não gosta de escrever o número, __LINE__pode substituí-lo:

#define ____LINE__ __LINE__
#define __ENUM(situation) \
    int situation = (____LINE__ - __BASELINE -2);       const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
    __ENUM(Sunday);
    __ENUM(Monday);
    __ENUM(Tuesday);
    __ENUM(Wednesday);
    __ENUM(Thursday);
    __ENUM(Friday);
    __ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
    sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
    for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
        if (ptr[i].value == num) {
            return ptr[i].p_name;
        }
    }
    return "bad_enum_value";
}
int main(int argc, char *argv[]) {  
    int d = eDays.Wednesday;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
    d = 1;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
}

>day Wednesday, number is 3 >day Monday, number is 1


0

eu sou novo nisso, mas uma instrução switch definitivamente funcionará

#include <stdio.h>

enum mycolor;

int main(int argc, const char * argv[])

{
enum Days{Sunday=1,Monday=2,Tuesday=3,Wednesday=4,Thursday=5,Friday=6,Saturday=7};

enum Days TheDay;


printf("Please enter the day of the week (0 to 6)\n");

scanf("%d",&TheDay);

switch (TheDay)
 {

case Sunday:
        printf("the selected day is sunday");
        break;
    case Monday:
        printf("the selected day is monday");
        break;
    case Tuesday:
        printf("the selected day is Tuesday");
        break;
    case Wednesday:
        printf("the selected day is Wednesday");
        break;
    case Thursday:
        printf("the selected day is thursday");
        break;
    case Friday:
        printf("the selected day is friday");
        break;
    case Saturday:
        printf("the selected day is Saturaday");
        break;
    default:
        break;
}

return 0;
}

A formatação adequada (leia-se: recuo) deve ser uma obrigação para o código literal nas respostas ...
p4010

0

Gosto que tenha enum nos dayNames. Para reduzir a digitação, podemos fazer o seguinte:

#define EP(x) [x] = #x  /* ENUM PRINT */

const char* dayNames[] = { EP(Sunday), EP(Monday)};

0

Existe outra solução: Crie sua própria classe de enumeração dinâmica. Significa que você tem uma structfunção e alguma para criar uma nova enumeração, que armazena os elementos em a structe cada elemento tem uma string para o nome. Você também precisa de algum tipo para armazenar elementos individuais, funções para compará-los e assim por diante. Aqui está um exemplo:

#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct Enumeration_element_T
{
  size_t index;
  struct Enumeration_T *parrent;
  char *name;
};

struct Enumeration_T
{
  size_t len;
  struct Enumeration_element_T elements[];
};
  


void enumeration_delete(struct Enumeration_T *self)
{
  if(self)
  {
    while(self->len--)
    {
      free(self->elements[self->len].name);
    }
    free(self);
  }
}

struct Enumeration_T *enumeration_create(size_t len,...)
{
  //We do not check for size_t overflows, but we should.
  struct Enumeration_T *self=malloc(sizeof(self)+sizeof(self->elements[0])*len);
  if(!self)
  {
    return NULL;
  }
  self->len=0;
  va_list l; 
  va_start(l,len);
  for(size_t i=0;i<len;i++)
  {
    const char *name=va_arg(l,const char *);
    self->elements[i].name=malloc(strlen(name)+1);
    if(!self->elements[i].name)
    {
      enumeration_delete(self);
      return NULL;
    }
    strcpy(self->elements[i].name,name);
    self->len++;
  }
  return self;
}


bool enumeration_isEqual(struct Enumeration_element_T *a,struct Enumeration_element_T *b)
{
  return a->parrent==b->parrent && a->index==b->index;
}

bool enumeration_isName(struct Enumeration_element_T *a, const char *name)
{
  return !strcmp(a->name,name);
}

const char *enumeration_getName(struct Enumeration_element_T *a)
{
  return a->name;
}

struct Enumeration_element_T *enumeration_getFromName(struct Enumeration_T *self, const char *name)
{
  for(size_t i=0;i<self->len;i++)
  {
    if(enumeration_isName(&self->elements[i],name))
    {
      return &self->elements[i];
    }
  }
  return NULL;
}
  
struct Enumeration_element_T *enumeration_get(struct Enumeration_T *self, size_t index)
{
  return &self->elements[index];
}

size_t enumeration_getCount(struct Enumeration_T *self)
{
  return self->len;
}

bool enumeration_isInRange(struct Enumeration_T *self, size_t index)
{
  return index<self->len;
}



int main(void)
{
  struct Enumeration_T *weekdays=enumeration_create(7,"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
  if(!weekdays)
  {
    return 1;
  }
    
  printf("Please enter the day of the week (0 to 6)\n");
  size_t j = 0;
  if(scanf("%zu",&j)!=1)
  {
    enumeration_delete(weekdays);
    return 1;
  }
  // j=j%enumeration_getCount(weekdays); //alternative way to make sure j is in range
  if(!enumeration_isInRange(weekdays,j))
  {
    enumeration_delete(weekdays);
    return 1;
  }

  struct Enumeration_element_T *day=enumeration_get(weekdays,j);
  

  printf("%s\n",enumeration_getName(day));
  
  enumeration_delete(weekdays);

  return 0;
}

As funções de enumeração devem estar em sua própria unidade de tradução, mas eu as combinei aqui para torná-la mais simples.

A vantagem é que esta solução é flexível, segue o princípio DRY, você pode armazenar informações junto com cada elemento, pode criar novas enumerações durante o tempo de execução e pode adicionar novos elementos durante o tempo de execução. A desvantagem é que isso é complexo, precisa de alocação dinâmica de memória, não pode ser usado em switch- case, precisa de mais memória e é mais lento. A questão é se você não deve usar uma linguagem de nível superior nos casos em que for necessário.


-3

TheDay mapeia de volta para algum tipo inteiro. Então:

printf("%s", TheDay);

Tenta analisar TheDay como uma string e irá imprimir lixo ou travar.

printf não é typesafe e confia em você para passar o valor correto para ele. Para imprimir o nome do valor, você precisa criar algum método para mapear o valor enum para uma string - uma tabela de pesquisa, uma instrução switch gigante etc.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.