Drucken Sie Text anstelle von Wert aus C enum


86
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;
}

Ihre erwartete Ausgabe ist, dass die Zeichenfolge "Sonntag" usw. gedruckt wird?
GalacticCowboy

Antworten:


99

Aufzählungen in C sind Zahlen, deren Code bequeme Namen enthält. Sie sind keine Zeichenfolgen, und die ihnen im Quellcode zugewiesenen Namen werden nicht in Ihr Programm kompiliert, sodass Sie zur Laufzeit nicht darauf zugreifen können.

Der einzige Weg, um das zu bekommen, was Sie wollen, besteht darin, selbst eine Funktion zu schreiben, die den Aufzählungswert in eine Zeichenfolge übersetzt. ZB (vorausgesetzt, Sie verschieben hier die Deklaration von enum Daysaußerhalb von 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));

Alternativ können Sie ein Array als Karte verwenden, z

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

/* ... */

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

Aber hier möchten Sie wahrscheinlich aus Sunday = 0Sicherheitsgründen die Aufzählung zuweisen ... Ich bin mir nicht sicher, ob der C-Standard verlangt, dass Compiler Aufzählungen von 0 beginnen, obwohl die meisten dies tun (ich bin sicher, dass jemand einen Kommentar abgeben wird, um dies zu bestätigen oder abzulehnen ).


3
Aw, du hast mich mit der Array-Lösung geschlagen. : P Aber ja, Aufzählungen beginnen immer bei 0, es sei denn, Sie geben einen anderen Wert an.
Casablanca

1
Wenn ich mich darauf verlassen würde, die Aufzählungen als Indizes zu verwenden, würde ich es tatsächlich vorziehen, jede explizit zu nummerieren. Nach Standards unnötig, aber als Gruppe waren Compiler meiner Erfahrung nach nicht gerade die besten, um Standards zu befolgen.
jdmichal

3
Der C-Standard sagt: "Wenn der erste Enumerator no = hat, ist der Wert seiner Aufzählungskonstante 0". Aber es tut nichts weh, es explizit zu haben.
Michael Burr

16
Vergessen Sie nicht, dass Sie dies mit C99 tun können const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Sie wissen, falls die Wochentage neu geordnet werden, oder Sie entscheiden, dass Montag der erste Tag der Woche ist.
Tim Schaeffer

2
@ user3467349 Das (Präprozessor-Stringifizierung) verwandelt nur das Symbol nach dem # in einen String. Also, ja, #Montag würde sich in "Montag" verwandeln, aber Days TheDay = Monday; printf("%s", #TheDay);"TheDay" drucken.
Tyler McHenry

28

Ich benutze so etwas:

in einer Datei "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

Dann geben Sie in jeder Header-Datei die Enum-Deklaration day enum.h ab

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

dann in einer Datei namens EnumToString.c:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

dann in 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;
}

Dadurch werden "automatisch" die Zeichenfolgen für alle auf diese Weise deklarierten und in "EnumToString.c" enthaltenen Aufzählungen generiert.


3
Es ist hässlich zu lesen, aber Sie haben keine Datenverdoppelung. (Im Gegensatz zu allen anderen.) Ich bin hin und her gerissen, ob mir das gefallen soll.
Kim Reece

1
+1 für die unglaublich kreative Lösung ohne Datenverdoppelung und wahrscheinlich die beste Wartbarkeit / Flexibilität, aber yech! Ich denke, ich würde immer noch lieber den Weg von const char * [] gehen.
Manifest

3
Ja, die Wartbarkeit ist fantastisch! Es ist wirklich einfach zu aktualisieren, wenn sich die Wochentage ändern! </ sarcasm> Dies ist übrigens nicht einmal für Lokalisierungszwecke nützlich, da die Zuordnung zwischen den englischen Zeichenfolgen und den Namen im Programm jetzt durch Ihren Versuch, Doppelarbeit zu vermeiden, fest codiert ist. Zumindest mit den anderen Ansätzen ist es möglich, die Zeichenfolgen zu übersetzen, ohne jedes Vorkommen in den Quelldateien zu ändern.
R .. GitHub STOP HELPING ICE

1
Sie können es wahrscheinlich internationalisieren, indem Sie (mit so etwas wie gettext) die return-Anweisungen in return _(#element)und dergleichen ändern .
Vargas

Wenn der C-Präprozessor so nützlich, aber so hässlich ist, ersetze ich ihn normalerweise durch einen einfachen Codegenerator oder einen benutzerdefinierten Präprozessor in einer Skriptsprache. Tatsächlich habe ich ein Python-Skript, das ich genau für diesen Zweck in mehreren Projekten verwendet habe. Aber ich benutze es heutzutage nicht mehr so ​​oft - für viele Anwendungsfälle kann man einfach nur Strings verwenden und sich nicht mit den Aufzählungen beschäftigen (und noch mehr in C ++ oder ObjC).
abarnert

5

Normalerweise speichere ich die Zeichenfolgendarstellungen in einem separaten Array in derselben Reihenfolge und indiziere das Array dann mit dem Aufzählungswert:

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

4

enums in C funktionieren nicht so, wie Sie es erwarten. Sie können sich diese wie verherrlichte Konstanten vorstellen (mit einigen zusätzlichen Vorteilen, die sich aus der Sammlung ergeben solcher Konstanten), und der Text, den Sie für "Sonntag" geschrieben haben, wird während der Kompilierung wirklich in eine Zahl aufgelöst letztendlich verworfen.

Kurz gesagt: Um das zu tun, was Sie wirklich wollen, müssen Sie ein Array der Zeichenfolgen beibehalten oder eine Funktion erstellen, um den Wert der Aufzählung dem Text zuzuordnen, den Sie drucken möchten.


4

Aufzählungen in C sind im Grunde genommen syntaktischer Zucker für benannte Listen automatisch sequenzierter ganzzahliger Werte. Das heißt, wenn Sie diesen Code haben:

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

    Days TheDay = Monday;
}

Ihr Compiler spuckt tatsächlich Folgendes aus:

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

Daher ist die Ausgabe einer C-Aufzählung als Zeichenfolge für den Compiler keine sinnvolle Operation. Wenn Sie für diese lesbare Zeichenfolgen haben möchten, müssen Sie Funktionen definieren, die von Aufzählungen in Zeichenfolgen konvertiert werden sollen.


4

Hier ist eine sauberere Möglichkeit, dies mit Makros zu tun:

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

Ich bin nicht sicher, ob dies vollständig tragbare s / w-Präprozessoren sind, aber es funktioniert mit gcc.

Dies ist übrigens c99, also verwenden c99 strictSie es, wenn Sie es an (den Online-Compiler) ideone anschließen .


Ich muss lieben, wie "sauber" Makros sind :-).
mk12

2

Ich weiß, dass ich zu spät zur Party komme, aber wie wäre es damit?

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

Auf diese Weise müssen Sie das enumund das char*Array nicht manuell synchron halten. Wenn Sie wie ich sind, besteht die Möglichkeit, dass Sie das später ändern enumund das char*Array ungültige Zeichenfolgen druckt. Dies ist möglicherweise keine allgemein unterstützte Funktion. Aber afaik, die meisten modernen C-Compiler unterstützen diesen designierten Initialisierungsstil.

Weitere Informationen zu bestimmten Initialisierern finden Sie hier .


1

Die Frage ist, Sie möchten den Namen nur einmal schreiben.
Ich habe einen Ider wie diesen:

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

Es verwendet eine Struktur, um eine Aufzählung einzufügen, sodass ein Drucker, der eine Zeichenfolge enthält, jedem definierten Aufzählungswert folgen kann.

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

Der Unterschied zu enum ist, dass der Builder keinen Fehler melden kann, wenn die Zahlen wiederholt werden. Wenn Sie keine Schreibnummer mögen, können Sie diese __LINE__ersetzen:

#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

Ich bin neu in diesem Bereich, aber eine switch-Anweisung wird auf jeden Fall funktionieren

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

Die richtige Formatierung (
sprich

0

Ich mag es, wenn die Tagesnamen eine Aufzählung enthalten. Um die Eingabe zu reduzieren, können wir Folgendes tun:

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

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

-2

TheDay wird einem ganzzahligen Typ zugeordnet. So:

printf("%s", TheDay);

Versucht, TheDay als Zeichenfolge zu analysieren, und druckt entweder Müll aus oder stürzt ab.

printf ist nicht typsicher und vertraut darauf, dass Sie den richtigen Wert an übergeben. Um den Namen des Werts auszudrucken, müssen Sie eine Methode zum Zuordnen des Aufzählungswerts zu einer Zeichenfolge erstellen - entweder eine Nachschlagetabelle, eine riesige switch-Anweisung usw.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.