พิมพ์ข้อความแทนค่าจาก C enum


89

ผลลัพธ์ที่คุณคาดหวังคือให้พิมพ์สตริง "Sunday" เป็นต้น?
GalacticCowboy

คำตอบ:


104

การแจงนับใน C คือตัวเลขที่มีชื่อที่สะดวกในรหัสของคุณ ไม่ใช่สตริงและชื่อที่กำหนดให้ในซอร์สโค้ดจะไม่ถูกคอมไพล์ลงในโปรแกรมของคุณดังนั้นจึงไม่สามารถเข้าถึงได้ในรันไทม์

วิธีเดียวที่จะได้รับสิ่งที่คุณต้องการคือการเขียนฟังก์ชันด้วยตัวคุณเองซึ่งจะแปลค่าการแจงนับเป็นสตริง เช่น (สมมติว่าที่นี่คุณย้ายการประกาศenum Daysภายนอกmain):

หรือคุณสามารถใช้อาร์เรย์เป็นแผนที่ได้เช่น

แต่ที่นี่คุณอาจต้องการกำหนดSunday = 0ในการแจงนับเพื่อความปลอดภัย ... ฉันไม่แน่ใจว่ามาตรฐาน C ต้องการให้คอมไพเลอร์เริ่มการแจงนับจาก 0 แม้ว่าส่วนใหญ่จะทำ (ฉันแน่ใจว่ามีคนแสดงความคิดเห็นเพื่อยืนยันหรือปฏิเสธสิ่งนี้ ).


3
อ๊ะคุณเอาชนะฉันด้วยโซลูชันอาร์เรย์ : P แต่ใช่ enums จะเริ่มต้นที่ 0 เสมอเว้นแต่คุณจะระบุค่าอื่น
casablanca

1
ถ้าฉันอาศัยการใช้การแจงนับเป็นดัชนีจริงๆแล้วฉันต้องการที่จะระบุตัวเลขแต่ละตัวอย่างชัดเจน ไม่จำเป็นตามมาตรฐาน แต่เนื่องจากคอมไพเลอร์กลุ่มไม่ได้ดีที่สุดในการปฏิบัติตามมาตรฐานในประสบการณ์ของฉัน
jdmichal

3
มาตรฐาน C กล่าวว่า "ถ้าตัวแจงนับ fi rst ไม่มี = ค่าของค่าคงที่การแจงนับคือ 0" แต่มันไม่เจ็บอะไรเลยที่จะมีความชัดเจน
Michael Burr

17
อย่าลืมว่ามี C99 const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };ที่คุณสามารถทำได้ คุณจะรู้ว่าในกรณีที่มีการจัดเรียงวันในสัปดาห์ใหม่หรือคุณตัดสินใจว่าวันจันทร์เป็นวันแรกของสัปดาห์
Tim Schaeffer

2
@ user3467349 สิ่งนั้น (สตริงตัวประมวลผลล่วงหน้า) เพียงแค่เปลี่ยนสัญลักษณ์ตาม # เป็นสตริง ใช่แล้ว # วันจันทร์จะกลายเป็น "วันจันทร์" แต่Days TheDay = Monday; printf("%s", #TheDay);จะพิมพ์ "TheDay"
Tyler McHenry

29

ฉันใช้สิ่งนี้:

ในไฟล์ "EnumToString.h":

จากนั้นในไฟล์ส่วนหัวใด ๆ ที่คุณทำการประกาศ enum วัน enum.h

จากนั้นในไฟล์ชื่อ EnumToString.c:

จากนั้นใน main.c:

สิ่งนี้จะสร้างสตริง "โดยอัตโนมัติ" สำหรับ enums ใด ๆ ที่ประกาศด้วยวิธีนี้และรวมอยู่ใน "EnumToString.c"


4
มันน่าเกลียดที่จะอ่าน แต่คุณไม่มีการทำสำเนาข้อมูล (ไม่เหมือนคนอื่น) ฉันรู้สึกว่าจะชอบสิ่งนี้ไหม
Kim Reece

1
+1 สำหรับโซลูชันที่สร้างสรรค์ที่ยอดเยี่ยมโดยไม่มีการทำสำเนาข้อมูลและอาจเป็นการบำรุงรักษา / ความยืดหยุ่นที่ดีที่สุด แต่ใช่! ฉันคิดว่าฉันยังคงแค่ไปเส้นทาง const char * []
รายการ

4
ใช่การบำรุงรักษานั้นยอดเยี่ยมมาก! ง่ายมากที่จะอัปเดตเมื่อวันในสัปดาห์เปลี่ยนไป! </sarcasm> อย่างไรก็ตามสิ่งนี้ไม่ได้มีประโยชน์สำหรับวัตถุประสงค์ในการแปลเป็นภาษาท้องถิ่นเนื่องจากการแมประหว่างสตริงภาษาอังกฤษและชื่อในโปรแกรมได้รับการเข้ารหัสโดยคุณพยายามหลีกเลี่ยงการทำซ้ำ อย่างน้อยด้วยวิธีการอื่น ๆ คุณสามารถแปลสตริงได้โดยไม่ต้องเปลี่ยนทุกครั้งที่เกิดขึ้นในไฟล์ต้นฉบับ
R .. GitHub STOP HELPING ICE

1
คุณสามารถทำให้เป็นสากลได้โดย (มีบางอย่างเช่น gettext) เปลี่ยนคำสั่ง return เป็นreturn _(#element)และสิ่งที่คล้ายกัน
Vargas

เมื่อตัวประมวลผลก่อนหน้า C มีประโยชน์ แต่สิ่งนี้น่าเกลียดฉันมักจะแทนที่ด้วยตัวสร้างโค้ดอย่างง่ายหรือตัวประมวลผลล่วงหน้าที่กำหนดเองในภาษาสคริปต์ และอันที่จริงฉันมีสคริปต์ Python ที่ใช้เพื่อจุดประสงค์นี้ในหลายโครงการ แต่ฉันไม่ได้ใช้บ่อยขนาดนั้นในปัจจุบัน - สำหรับกรณีการใช้งานจำนวนมากคุณสามารถใช้งานได้โดยใช้สตริงและไม่รบกวน enums (และอื่น ๆ อีกมากมายใน C ++ หรือ ObjC)
ยกเลิก

7

วิธีที่ฉันมักจะทำคือการจัดเก็บการแสดงสตริงในอาร์เรย์แยกต่างหากในลำดับเดียวกันจากนั้นสร้างดัชนีอาร์เรย์ด้วยค่า enum:


4

enumใน C ไม่ได้ผลจริงอย่างที่คุณคาดหวังไว้ คุณสามารถคิดว่าค่าคงที่ที่ได้รับการยกย่อง (มีประโยชน์เพิ่มเติมบางประการเกี่ยวกับการรวบรวมค่าคงที่ดังกล่าว) และข้อความที่คุณเขียนสำหรับ "วันอาทิตย์" ได้รับการแก้ไขเป็นตัวเลขในระหว่างการรวบรวมข้อความคือ ทิ้งไปในที่สุด

กล่าวโดยย่อ: ในการทำสิ่งที่คุณต้องการจริงๆคุณจะต้องเก็บอาร์เรย์ของสตริงหรือสร้างฟังก์ชันเพื่อแมปจากค่า enum กับข้อความที่คุณต้องการพิมพ์


4

การแจงนับใน C นั้นโดยพื้นฐานแล้วน้ำตาลสังเคราะห์สำหรับรายการที่มีชื่อของค่าจำนวนเต็มที่เรียงลำดับโดยอัตโนมัติ นั่นคือเมื่อคุณมีรหัสนี้:

คอมไพเลอร์ของคุณคายสิ่งนี้ออกมา:

ดังนั้นการส่งออกการแจงนับ C เป็นสตริงจึงไม่ใช่การดำเนินการที่เหมาะสมกับคอมไพเลอร์ หากคุณต้องการมีสตริงที่มนุษย์อ่านได้สำหรับสิ่งเหล่านี้คุณจะต้องกำหนดฟังก์ชันเพื่อแปลงจากการแจงนับเป็นสตริง


4

นี่เป็นวิธีที่สะอาดกว่าในการใช้มาโคร:

ฉันไม่แน่ใจว่านี่เป็นพรีโปรเซสเซอร์ b / w แบบพกพาทั้งหมด แต่ใช้ได้กับ gcc

นี่คือ c99 ครับดังนั้นการใช้งานc99 strictถ้าคุณเสียบเข้า(คอมไพเลอร์ออนไลน์) ideone


ต้องรักว่ามาโครที่ "สะอาด" เป็นอย่างไร :-)
mk12

3

ฉันรู้ว่าฉันไปงานปาร์ตี้สาย แต่แล้วเรื่องนี้ล่ะ?

ด้วยวิธีนี้คุณไม่จำเป็นต้องซิงค์อาร์เรย์enumและchar*อาร์เรย์ด้วยตนเอง ถ้าคุณเป็นเหมือนฉันโอกาสที่คุณจะเปลี่ยนในภายหลังenumและchar*อาร์เรย์จะพิมพ์สตริงที่ไม่ถูกต้อง นี่อาจไม่ใช่คุณสมบัติที่รองรับในระดับสากล แต่ afaik คอมไพเลอร์ C ยุคใหม่ส่วนใหญ่สนับสนุนรูปแบบเริ่มต้นที่กำหนดนี้

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ initializers กำหนดที่นี่


1

คำถามคือคุณต้องการเขียนชื่อเพียงครั้งเดียว
ฉันมี 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";
    }

มันใช้โครงสร้างเพื่อแทรก enum เพื่อให้เครื่องพิมพ์กับสตริงสามารถทำตามแต่ละค่า 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

ความแตกต่างของ enum คือ builder ไม่สามารถรายงานข้อผิดพลาดได้หากตัวเลขซ้ำกัน หากคุณไม่ชอบเขียนหมายเลข__LINE__สามารถแทนที่ได้:

#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

ฉันยังใหม่กับสิ่งนี้ แต่คำสั่ง switch จะใช้งานได้ดี


การจัดรูปแบบที่เหมาะสม (อ่าน: การเยื้อง) จะต้องมีสำหรับรหัสคำต่อคำในคำตอบ ...
p4010

0

ฉันชอบสิ่งนี้ที่จะมีใน the dayNames เพื่อลดการพิมพ์เราสามารถทำได้ดังต่อไปนี้:


0

มีวิธีแก้ปัญหาอื่น: สร้างคลาสการแจงนับแบบไดนามิกของคุณเอง หมายความว่าคุณมีstructและฟังก์ชันบางอย่างในการสร้างการแจงนับใหม่ซึ่งเก็บองค์ประกอบไว้ใน a structและแต่ละองค์ประกอบจะมีสตริงสำหรับชื่อ คุณต้องมีบางประเภทเพื่อจัดเก็บองค์ประกอบแต่ละรายการฟังก์ชันเพื่อเปรียบเทียบและอื่น ๆ นี่คือตัวอย่าง:

ฟังก์ชันของการแจงนับควรอยู่ในหน่วยการแปลของตัวเอง แต่ฉันรวมไว้ที่นี่เพื่อให้ง่ายขึ้น

ข้อดีคือโซลูชันนี้มีความยืดหยุ่นตามหลักการ DRY คุณสามารถจัดเก็บข้อมูลพร้อมกับแต่ละองค์ประกอบคุณสามารถสร้างการแจงนับใหม่ระหว่างรันไทม์และคุณสามารถเพิ่มองค์ประกอบใหม่ในระหว่างรันไทม์ได้ ข้อเสียคือมันซับซ้อนต้องการการจัดสรรหน่วยความจำแบบไดนามิกไม่สามารถใช้ในswitch- caseต้องการหน่วยความจำเพิ่มและช้ากว่า คำถามคือหากคุณไม่ควรใช้ภาษาระดับสูงในกรณีที่คุณต้องการสิ่งนี้


-3

TheDay แมปกลับเป็นจำนวนเต็มบางประเภท ดังนั้น:

พยายามแยกวิเคราะห์ TheDay เป็นสตริงและจะพิมพ์ขยะหรือข้อขัดข้อง

printf ไม่ใช่ typesafe และเชื่อมั่นว่าคุณจะส่งค่าที่ถูกต้องไปให้ ในการพิมพ์ชื่อของค่าคุณจะต้องสร้างวิธีการบางอย่างสำหรับการแมปค่า enum กับสตริง - ไม่ว่าจะเป็นตารางการค้นหาคำสั่งสวิตช์ขนาดใหญ่ ฯลฯ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.