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;
}
89
Odpowiedzi:
Wyliczenia w C to liczby, które mają wygodne nazwy w kodzie. Nie są ciągami znaków, a nazwy przypisane do nich w kodzie źródłowym nie są wkompilowane w Twój program, więc nie są dostępne w czasie wykonywania.
Jedynym sposobem uzyskania tego, co chcesz, jest samodzielne napisanie funkcji, która tłumaczy wartość wyliczenia na ciąg. Np. (Zakładając tutaj, że przenosisz deklarację
enum Days
pozamain
):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));
Alternatywnie możesz użyć tablicy jako mapy, np
const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ }; /* ... */ printf("%s", dayNames[TheDay]);
Ale tutaj prawdopodobnie chciałbyś przypisać
Sunday = 0
w wyliczeniu, aby było bezpieczne ... Nie jestem pewien, czy standard C wymaga, aby kompilatory rozpoczynały wyliczenia od 0, chociaż większość tak robi (jestem pewien, że ktoś skomentuje, aby to potwierdzić lub zaprzeczyć ).źródło
const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };
. Wiesz, na wypadek, gdyby dni tygodnia zostały zmienione lub zdecydujesz, że poniedziałek jest pierwszym dniem tygodnia.Days TheDay = Monday; printf("%s", #TheDay);
wypisałby „TheDay”.Używam czegoś takiego:
w pliku „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
następnie w dowolnym pliku nagłówkowym tworzysz deklarację wyliczenia, dzień 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)
następnie w pliku o nazwie EnumToString.c:
#include "enum.h" #define GENERATE_ENUM_STRINGS // Start string generation #include "enum.h" #undef GENERATE_ENUM_STRINGS // Stop string generation
następnie w 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; }
spowoduje to automatyczne wygenerowanie ciągów dla wszystkich wyliczeń zadeklarowanych w ten sposób i uwzględnionych w „EnumToString.c”
źródło
return _(#element)
i tym podobne (za pomocą czegoś takiego jak gettext) .Sposób, w jaki zwykle to robię, polega na przechowywaniu reprezentacji ciągów w oddzielnej tablicy w tej samej kolejności, a następnie indeksowaniu tablicy z wartością wyliczenia:
const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ }; printf("%s", DayNames[Sunday]); // prints "Sunday"
źródło
enum
w C nie działają tak, jak tego oczekujesz. Możesz myśleć o nich jak o gloryfikowanych stałych (z kilkoma dodatkowymi korzyściami związanymi z byciem zbiorem takich stałych), a tekst, który napisałeś na „niedzielę”, jest naprawdę przekształcany na liczbę podczas kompilacji, tekst jest ostatecznie odrzucone.W skrócie: aby zrobić to, czego naprawdę chcesz, musisz zachować tablicę ciągów lub utworzyć funkcję do odwzorowania wartości wyliczenia na tekst, który chcesz wydrukować.
źródło
Wyliczenia w C są w zasadzie cukrem syntaktycznym dla nazwanych list wartości całkowitych z automatyczną sekwencją. To znaczy, gdy masz ten kod:
int main() { enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday}; Days TheDay = Monday; }
Twój kompilator faktycznie wypluwa to:
int main() { int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0. }
Dlatego wyprowadzanie wyliczenia C jako ciągu nie jest operacją, która ma sens dla kompilatora. Jeśli chcesz mieć dla nich ciągi czytelne dla człowieka, musisz zdefiniować funkcje do konwersji z wyliczeń na ciągi.
źródło
Oto bardziej przejrzysty sposób na zrobienie tego za pomocą makr:
#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; }
Nie jestem pewien, czy są to całkowicie przenośne b / w preprocesory, ale działa z gcc.
To jest c99 btw, więc użyj go,
c99 strict
jeśli podłączysz go do (kompilatora online) ideone .źródło
Wiem, że spóźniłem się na imprezę, ale co powiesz na to?
const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ }; printf("%s", dayNames[Sunday]); // prints "Sunday"
W ten sposób, nie trzeba ręcznie zachować
enum
i nachar*
tablicy w synchronizacji. Jeśli jesteś podobny do mnie, istnieje szansa, że później zmienisz wartośćenum
, achar*
tablica wydrukuje nieprawidłowe ciągi. Może to nie być funkcja powszechnie obsługiwana. Ale afaik, większość współczesnych kompilatorów dnia C obsługuje ten wyznaczony początkowy styl.Możesz przeczytać więcej o wyznaczonych inicjatorach tutaj .
źródło
Pytanie brzmi, czy chcesz napisać nazwę tylko raz.
Mam taki ider:
#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"; }
używa struktury do wstawienia wyliczenia, tak aby drukarka do łańcucha mogła następować po każdej zdefiniowanej wartości wyliczenia.
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
Różnica w stosunku do wyliczenia polega na tym, że program budujący nie może zgłosić błędu, jeśli liczby się powtarzają. jeśli nie lubisz pisać numeru, możesz
__LINE__
go zastąpić:#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
źródło
Jestem nowy w tym, ale instrukcja przełącznika zdecydowanie zadziała
#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; }
źródło
Podoba mi się, że wyliczenie w nazwach dni. Aby ograniczyć pisanie, możemy wykonać następujące czynności:
#define EP(x) [x] = #x /* ENUM PRINT */ const char* dayNames[] = { EP(Sunday), EP(Monday)};
źródło
Jest inne rozwiązanie: utwórz własną dynamiczną klasę wyliczania. Oznacza, że masz funkcję
struct
i jakąś funkcję do tworzenia nowego wyliczenia, które przechowuje elementy w a,struct
a każdy element ma ciąg znaków dla nazwy. Potrzebujesz także typu do przechowywania poszczególnych elementów, funkcji do ich porównywania i tak dalej. Oto przykład:#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; }
Funkcje wyliczania powinny znajdować się we własnej jednostce tłumaczeniowej, ale połączyłem je tutaj, aby było to prostsze.
Zaletą jest to, że to rozwiązanie jest elastyczne, działa na zasadzie DRY, możesz przechowywać informacje wraz z każdym elementem, możesz tworzyć nowe wyliczenia w trakcie działania, a także dodawać nowe elementy w trakcie działania. Wadą jest to, że jest to złożone, wymaga dynamicznej alokacji pamięci, nie może być używane w
switch
-case
, wymaga więcej pamięci i jest wolniejsze. Pytanie brzmi, czy nie powinieneś używać języka wyższego poziomu w przypadkach, gdy tego potrzebujesz.źródło
TheDay mapuje z powrotem do pewnego typu całkowitego. Więc:
printf("%s", TheDay);
Próbuje przeanalizować TheDay jako ciąg i albo wydrukuje śmieci, albo awarię.
printf nie jest bezpieczny dla typów i ufa, że przekażesz mu odpowiednią wartość. Aby wydrukować nazwę wartości, musisz stworzyć metodę mapowania wartości wyliczenia na łańcuch - tabelę przeglądową, instrukcję gigantycznego przełącznika itp.
źródło