กลับไปหน้าบทความ

อ่าน 10 นาที

ความเข้ากันได้ของภาษา C และ C++

ซี++/C (ภาษาโปรแกรม)/การเปรียบเทียบภาษาโปรแกรมแต่ละภาษา/ใช้วันที่ dmy ตั้งแต่เดือนมกราคม 2558/ลิงก์ย้อนกลับเทมเพลต Webarchive

ภาษาโปรแกรมCและC++ มีความสัมพันธ์กันอย่างใกล้ชิด แต่ก็มีความแตกต่างที่สำคัญหลายประการ C++ เริ่มต้นจากการแยก สาขาของภาษา C ในยุค แรกๆ ก่อน ที่จะ...

ความเข้ากันได้ของภาษา C และ C++

ภาษาโปรแกรมCและC++ มีความสัมพันธ์กันอย่างใกล้ชิด แต่ก็มีความแตกต่างที่สำคัญหลายประการ C++ เริ่มต้นจากการแยก สาขาของภาษา C ในยุค แรกๆ ก่อน ที่จะ มีการกำหนดมาตรฐานและได้รับการออกแบบให้เข้ากันได้กับคอมไพเลอร์ C ในยุคนั้นโดยส่วนใหญ่[ 1 ] [ 2 ] ด้วยเหตุนี้ เครื่องมือพัฒนาสำหรับทั้งสองภาษา (เช่นIDEและคอมไพเลอร์ ) จึงมักถูกรวมเข้าไว้ในผลิตภัณฑ์เดียว โดยโปรแกรมเมอร์สามารถระบุ C หรือ C++ เป็นภาษาต้นฉบับได้

อย่างไรก็ตาม C ไม่ใช่ส่วนย่อยของ C++ [ 3 ]และโปรแกรม C ที่ไม่ธรรมดาจะไม่สามารถคอมไพล์เป็นโค้ด C++ ได้หากไม่มีการแก้ไข ในทำนองเดียวกัน C++ นำเสนอคุณสมบัติมากมายที่ไม่มีใน C และในทางปฏิบัติโค้ดเกือบทั้งหมดที่เขียนใน C++ ไม่ใช่โค้ด C ที่สอดคล้อง บทความนี้มุ่งเน้นไปที่ความแตกต่างที่ทำให้โค้ด C ที่สอดคล้องกลายเป็นโค้ด C++ ที่ไม่ถูกต้อง หรือสอดคล้อง/ถูกต้องในทั้งสองภาษาแต่มีพฤติกรรมที่แตกต่างกันใน C และ C++

Bjarne Stroustrupผู้สร้าง C++ ได้เสนอแนะ[ 4 ]ว่าความไม่เข้ากันระหว่าง C และ C++ ควรลดลงให้มากที่สุดเท่าที่จะเป็นไปได้ เพื่อเพิ่มความสามารถในการทำงานร่วมกันระหว่างสองภาษาให้สูงสุด คนอื่นๆ โต้แย้งว่า เนื่องจาก C และ C++ เป็นภาษาที่แตกต่างกันสองภาษา ความเข้ากันได้ระหว่างกันจึงมีประโยชน์แต่ไม่สำคัญ ตามความเห็นของกลุ่มนี้ ความพยายามในการลดความไม่เข้ากันไม่ควรขัดขวางความพยายามในการปรับปรุงแต่ละภาษาแยกกัน เหตุผลอย่างเป็นทางการสำหรับมาตรฐาน C ปี 1999 ( C99 ) "รับรองหลักการของการรักษาส่วนย่อยร่วมที่ใหญ่ที่สุด" ระหว่าง C และ C++ "ในขณะที่ยังคงรักษาความแตกต่างระหว่างกันและอนุญาตให้พัฒนาแยกจากกัน" และระบุว่าผู้เขียน "พอใจที่จะให้ C++ เป็นภาษาที่ใหญ่และทะเยอทะยาน" [ 5 ]

ส่วนเพิ่มเติมหลายอย่างของ C99 ไม่ได้รับการสนับสนุนในมาตรฐาน C++ ปัจจุบัน หรือขัดแย้งกับคุณสมบัติของ C++ เช่นอาร์เรย์ที่มีความยาวแปรผัน ประเภท จำนวนเชิงซ้อนดั้งเดิมและrestrictตัวกำหนดคุณสมบัติประเภทในทางกลับกัน C ได้ค่อยๆ ลดความไม่เข้ากันอื่นๆ บางประการโดยการรวมคุณสมบัติของ C++ เช่น ความคิดเห็นบรรทัดเดียว//และการประกาศและโค้ดแบบผสมใน C99 [ 6 ]และการเพิ่มแอตทริบิวต์[ 7 ]การอนุมานประเภทด้วยคำautoหลัก[ 8 ]และnullptrค่าคงที่ใน C23 [ 9 ]

โครงสร้างที่ใช้ได้ในภาษา C แต่ใช้ไม่ได้ในภาษา C++

C++ บังคับใช้กฎการกำหนดประเภทที่เข้มงวดกว่า (ไม่มีการละเมิดระบบประเภทคงที่โดยปริยาย[ 1 ] ) และข้อกำหนดการเริ่มต้น (การบังคับใช้ในเวลาคอมไพล์ว่าตัวแปรในขอบเขตจะไม่ถูกบิดเบือนการเริ่มต้น) [ 10 ]มากกว่า C ดังนั้นโค้ด C ที่ถูกต้องบางส่วนจึงไม่ถูกต้องใน C++ เหตุผลสำหรับสิ่งเหล่านี้มีให้ในภาคผนวก C.1 ของมาตรฐาน ISO C++ [ 11 ]

  • ความแตกต่างที่พบได้ทั่วไปอย่างหนึ่งคือ C มีการกำหนดประเภทแบบอ่อน กว่า ในส่วนของพอยเตอร์ โดยเฉพาะอย่างยิ่ง C อนุญาตvoid *ให้กำหนดพอยเตอร์ให้กับประเภทพอยเตอร์ใดก็ได้โดยไม่ต้องแปลงประเภท ในขณะที่ C++ ไม่ได้สำนวน นี้ มักปรากฏในโค้ด C ที่ใช้mallocการจัดสรรหน่วยความจำ[ 12 ]หรือในการส่งผ่านพอยเตอร์บริบทไปยัง API pthreads ของ POSIX และเฟรมเวิร์กอื่นๆ ที่เกี่ยวข้องกับการเรียกกลับตัวอย่างเช่น สิ่งต่อไปนี้ถูกต้องใน C แต่ไม่ถูกต้องใน C++:
    void * ptr ; // การแปลงโดยปริยายจาก void * เป็น int * int * i = ptr ;

    หรือในทำนองเดียวกัน:

    int * j = malloc ( 5 * sizeof * j ); // การแปลงโดยปริยายจาก void * เป็น int *

    เพื่อให้โค้ดคอมไพล์ได้ทั้งในภาษา C และ C++ ต้องใช้การแปลงแบบชัดเจนดังต่อไปนี้ (โดยมีข้อควรระวังบางประการในทั้งสองภาษา): [ 13 ]

    void * ptr ; int * i = ( int * ) ptr ; int * j = ( int * ) malloc ( 5 * sizeof * j );
  • ภาษา C++ มีกฎที่ซับซ้อนกว่าเกี่ยวกับการกำหนดค่าตัวชี้ โดยมีการเพิ่มคุณสมบัติพิเศษ เนื่องจากอนุญาตให้กำหนดค่าให้กับตัวแปรint ** ได้const int *const *แต่ไม่อนุญาตให้กำหนดค่าที่ไม่ปลอดภัยให้กับตัวแปรconst int **ในขณะที่ภาษา C ไม่อนุญาตให้ทำทั้งสองอย่าง (ถึงแม้ว่าโดยปกติแล้วคอมไพเลอร์จะแสดงเพียงคำเตือนก็ตาม)
  • C++ เปลี่ยนแปลง ฟังก์ชัน ไลบรารีมาตรฐานของ C บาง ฟังก์ชันเพื่อเพิ่มฟังก์ชันโอเวอร์โหลดเพิ่มเติมพร้อมconstตัวกำหนดประเภทเช่นstrchrการส่งคืนค่าchar *ใน C ในขณะที่ C++ ทำงานราวกับว่ามีฟังก์ชันโอเวอร์โหลดสองฟังก์ชัน (สำหรับchar *และconst char *) ในC23มีการใช้การเลือกทั่วไปเพื่อให้พฤติกรรมของ C คล้ายกับ C++ มากขึ้น[ 14 ]
  • C++ ยังมีความเข้มงวดมากกว่าในการแปลงค่าเป็น enum: int ไม่สามารถแปลงเป็น enum โดยปริยายได้เหมือนใน C นอกจากนี้ ก่อนC23 ค่าคงที่ของการแจงนับ ( enumenumerator) จะมีชนิดเป็น int เสมอintใน C ในขณะที่ใน C++ (และ C23 หรือเวอร์ชันที่ใหม่กว่า) ค่าคงที่ของการแจงนับจะมีชนิดเป็น int ที่แตกต่างกัน และอาจมีขนาดแตกต่างจาก int intทั่วไป
  • ในภาษา C++ constตัวแปรจะต้องได้รับการกำหนดค่าเริ่มต้น แต่ในภาษา C ไม่จำเป็นต้องทำเช่นนั้น
  • คอมไพเลอร์ C++ ห้ามไม่ให้คำสั่ง goto หรือ switch ข้ามการกำหนดค่าเริ่มต้น ดังเช่นในโค้ด C99 ต่อไปนี้:
    void fn ( void ) { goto flack ; int i = 1 ; flack : printf ( "ถึง flack แล้ว" ); }
  • แม้ว่าไวยากรณ์จะถูกต้อง แต่ ผลลัพธ์จะทำให้เกิดพฤติกรรมที่ไม่แน่นอนใน C++ หาก เฟรมสแต็กlongjmp()ที่กระโดดข้ามไปนั้นมีวัตถุที่มีตัวทำลายที่ไม่ธรรมดา[ 15 ]การใช้งาน C++ มีอิสระที่จะกำหนดพฤติกรรมเพื่อให้มีการเรียกตัวทำลาย อย่างไรก็ตาม สิ่งนี้จะขัดขวางการใช้งานบางอย่างที่อาจถูกต้อง เช่น การใช้งานเธรดหรือโครูทีนที่สลับระหว่างสแต็กการเรียกที่แยกจากกัน— เมื่อกระโดดจากสแต็กการเรียกด้านล่างไปยังสแต็กการเรียกด้านบนในพื้นที่ที่อยู่ส่วนกลาง ตัวทำลายจะถูกเรียกสำหรับทุกวัตถุในสแต็กการเรียกด้านล่าง ไม่มีปัญหาดังกล่าวใน Clongjmp()longjmp()
  • ภาษา C อนุญาตให้มีการกำหนดค่าชั่วคราวหลายค่าสำหรับตัวแปรส่วนกลางตัวเดียวในหน่วยการแปล เดียว ซึ่งถือว่าไม่ถูกต้องเนื่องจากเป็นการ ละเมิด ODRในภาษา C++
    int n ; int n = 10 ;
  • ในภาษา C การประกาศชนิดข้อมูลใหม่ที่มีชื่อเดียวกันกับชนิดข้อมูลที่มีอยู่แล้ว เช่นหรือstructนั้นถูกต้อง แต่ไม่ถูกต้องในภาษา C++ เพราะในภาษา C ชนิดข้อมูลเช่น หรือ จะต้องระบุให้ชัดเจนทุกครั้งที่มีการอ้างอิงถึงชนิดข้อมูลนั้น ในขณะที่ในภาษา C++ การประกาศชนิดข้อมูลดังกล่าวทั้งหมดจะระบุtypedef ไว้ โดยปริยาย unionenumstructunionenum
    enum Bool { FALSE , TRUE };typedef int Bool ; // ในภาษา C, Bool และ enum Bool นั้นแตกต่างกัน
  • การประกาศฟังก์ชันแบบไม่ใช้ต้นแบบ ("สไตล์ K&R") ถือว่าไม่ถูกต้องใน C++ แต่ยังคงถูกต้องใน C จนถึง C23 [ 16 ] [ 17 ]แม้ว่าจะถือว่าล้าสมัยไปแล้วตั้งแต่การกำหนดมาตรฐานดั้งเดิมของ C ในปี 1990 (คำว่า "ล้าสมัย" เป็นคำที่กำหนดไว้ในมาตรฐาน ISO C ซึ่งหมายถึงคุณลักษณะที่ "อาจพิจารณาถอนออกในการแก้ไขมาตรฐานในอนาคต") ในทำนองเดียวกัน การประกาศฟังก์ชันโดยปริยาย (การใช้ฟังก์ชันที่ไม่ได้ประกาศ) ไม่ได้รับอนุญาตใน C++ และถือว่าไม่ถูกต้องใน C ตั้งแต่ปี 1999
  • ในภาษา C จนถึง C23 [ 18 ]การประกาศฟังก์ชันที่ไม่มีพารามิเตอร์ เช่นint foo();หมายความว่าไม่ได้ระบุพารามิเตอร์ ดังนั้นจึงสามารถเรียกใช้ฟังก์ชันดังกล่าวโดยมีอาร์กิวเมนต์ หนึ่งตัวหรือมากกว่า เช่น ได้foo(42, "hello world")ในทางตรงกันข้าม ในภาษา C++ ต้นแบบฟังก์ชันที่ไม่มีอาร์กิวเมนต์หมายความว่าฟังก์ชันนั้นไม่รับอาร์กิวเมนต์ และการเรียกใช้ฟังก์ชันดังกล่าวโดยมีอาร์กิวเมนต์นั้นไม่ถูกต้อง ในภาษา C วิธีที่ถูกต้องในการประกาศฟังก์ชันที่ไม่รับอาร์กิวเมนต์คือการใช้ 'void' เช่นint foo(void);ซึ่งก็ถูกต้องในภาษา C++ เช่นกัน ต้นแบบฟังก์ชันว่างเปล่าเป็นคุณสมบัติที่เลิกใช้แล้วใน C99 (เช่นเดียวกับใน C89)
  • ทั้งในภาษา C และ C++ สามารถกำหนดstructประเภทซ้อนกันได้ แต่ขอบเขตจะถูกตีความแตกต่างกัน: ใน C++ ประเภทซ้อนกันstructจะถูกกำหนดเฉพาะภายในขอบเขต/เนมสเปซของประเภทภายนอกเท่านั้นstructในขณะที่ในภาษา C โครงสร้างภายในจะถูกกำหนดอยู่นอกโครงสร้างภายนอกด้วย
  • ภาษา C อนุญาตให้ประกาศชนิดข้อมูลstruct, union, และ ในต้นแบบฟังก์ชันได้ ในขณะที่ภาษา C++ ไม่ได้enum

C99และC11ได้เพิ่มคุณสมบัติเพิ่มเติมหลายอย่างให้กับภาษา C ซึ่งยังไม่ได้ถูกรวมเข้าไว้ในมาตรฐาน C++ ในC++20เช่น จำนวนเชิงซ้อน อาร์เรย์ที่มีความยาวแปรผันได้ (จำนวนเชิงซ้อนและอาร์เรย์ที่มีความยาวแปรผันได้ถูกกำหนดให้เป็นส่วนขยายเสริมใน C11) สมาชิก อาร์เรย์ที่ยืดหยุ่น คำหลัก restrict ตัว กำหนดคุณสมบัติพารามิเตอร์อาร์เรย์ และตัวอักษรประกอบ

  • การคำนวณเลขคณิตเชิงซ้อนโดยใช้ ชนิดข้อมูลพื้นฐาน float complexและdouble complexชนิดข้อมูลดั้งเดิมถูกเพิ่มใน มาตรฐาน C99โดยใช้_Complexคำหลักและcomplexมาโครอำนวยความสะดวก ใน C++ การคำนวณเลขคณิตเชิงซ้อนสามารถทำได้โดยใช้คลาสจำนวนเชิงซ้อน แต่ทั้งสองวิธีไม่เข้ากันในโค้ด (อย่างไรก็ตาม มาตรฐานตั้งแต่C++11 เป็นต้นไป กำหนดให้ต้องเข้ากันได้แบบไบนารี) [ 19 ]
  • อาร์เรย์ที่มีความยาวแปรผัน คุณสมบัตินี้ทำให้ตัวดำเนินการsizeof อาจไม่ทำงานในเวลาคอมไพล์ [ 20 ]
    void foo ( size_t x , int a [ * ]); // การประกาศ VLAvoid foo ( size_t x , int a [ x ]) { printf ( "%zu \n " , sizeof a ); // เหมือนกับ sizeof(int *) char s [ x * 2 ]; printf ( "%zu \n " , sizeof s ); // จะพิมพ์ค่าของ x * 2 }
  • สมาชิกตัวสุดท้ายของโครงสร้างประเภท C99 ที่มีสมาชิกมากกว่าหนึ่งตัว อาจเป็นสมาชิกอาร์เรย์แบบยืดหยุ่นซึ่งมีรูปแบบทางไวยากรณ์เป็นอาร์เรย์ที่มีความยาวไม่ระบุ จุดประสงค์คล้ายกับอาร์เรย์ที่มีความยาวแปรผัน แต่ VLA ไม่สามารถปรากฏในคำจำกัดความของประเภทได้ และแตกต่างจาก VLA ตรงที่สมาชิกอาร์เรย์แบบยืดหยุ่นไม่มีขนาดที่กำหนดไว้ ISO C++ ไม่มีคุณสมบัติดังกล่าว ตัวอย่าง:
    struct X { int m ; long n ; char bytes []; };
  • ตัวrestrictกำหนดคุณสมบัติประเภทที่กำหนดไว้ใน C99 ไม่ได้รวมอยู่ในมาตรฐาน C++03 แต่คอมไพเลอร์หลักส่วนใหญ่ เช่นGNU Compiler Collection [ 21 ] Microsoft Visual C++และIntel C++ Compilerมีฟังก์ชันที่คล้ายกันเป็นส่วนขยาย
  • การกำหนดคุณสมบัติพารามิเตอร์แบบอาร์เรย์ในฟังก์ชันนั้นรองรับในภาษา C แต่ไม่รองรับในภาษา C++
    int foo ( int a [ const ]); // เทียบเท่ากับ int *const a int bar ( char s [ static 5 ]); // ระบุว่า s มีความยาวอย่างน้อย 5 ตัวอักษร
  • ฟังก์ชันการทำงานของตัวอักษรประกอบในภาษา C ได้รับการขยายให้ครอบคลุมทั้งประเภทข้อมูลพื้นฐานและประเภทข้อมูลที่ผู้ใช้กำหนดเองโดยไวยากรณ์การเริ่มต้นรายการใน C++11 แม้ว่าจะมีความแตกต่างทางไวยากรณ์และความหมายอยู่บ้างก็ตาม

    ใน C:

    struct X a = ( struct X ){ 4 , 6 }; // ใน C++ จะเทียบเท่ากับ X{4, 6} รูปแบบไวยากรณ์ C ที่ใช้ใน C99 ได้รับการสนับสนุนเป็นส่วนขยายในคอมไพเลอร์ GCC และ Clang C++ foo ( & ( struct X ){ 4 , 6 }); // อ็อบเจ็กต์จะถูกจัดสรรในสแต็กและสามารถส่งที่อยู่ไปยังฟังก์ชันได้ ซึ่งไม่ได้รับการสนับสนุนใน C++ถ้า( memcmp ( d , ( int []){ 8 , 6 , 7 , 5 , 3 , 0 , 9 }, n ) == 0 ) { // ... }

    ในภาษา C++:

    using Digits = int []; if ( std :: memcmp ( d , Digits { 8 , 6 , 7 , 5 , 3 , 0 , 9 }, n ) == 0 ) { // ... }
  • ตัวกำหนดค่าเริ่มต้นสำหรับอาร์เรย์นั้นใช้ได้เฉพาะในภาษาซีเท่านั้น:
    char s [ 20 ] = { [ 0 ] = 'a' , [ 8 ] = 'g' }; // อนุญาตใน C แต่ไม่อนุญาตใน C++
  • ฟังก์ชันที่ไม่ส่งคืนค่าสามารถระบุคำอธิบายประกอบได้โดยใช้แอตทริบิวต์ใน C++ ในขณะที่ C ใช้คีย์เวิร์ดที่แตกต่างกันใน C23 ไวยากรณ์แอตทริบิวต์ถูกนำมาใช้ในขณะที่ถูกยกเลิก[ 22 ][[noreturn]]_Noreturn_Noreturn
  • C2Y นำเสนอลูปที่มีชื่อและคำสั่ง break ที่มีชื่อ (คล้ายกับJava ) คุณสมบัตินี้ยังไม่ได้ถูกเพิ่มลงใน C++ ณC++ 26 [ 23 ]
  • ภาษา C ใช้_Genericการเลือก (selection) เพื่อให้สามารถตัดสินใจเกี่ยวกับนิพจน์ได้ในระหว่างการคอมไพล์ ซึ่งอาจมองได้ว่าเป็นการจำลองการโอเวอร์โหลดฟังก์ชัน ส่วนภาษา C++ ไม่จำเป็นต้องใช้วิธีนี้ เนื่องจากภาษา C++ รองรับการโอเวอร์โหลดฟังก์ชันและการกำหนดค่าเฉพาะของเทมเพลตอยู่แล้ว
  • C23 แนะนำtypeofตัวดำเนินการที่สร้างประเภทได้ทั้งแบบชนิดหรือแบบนิพจน์ ใน C++ คุณสมบัติที่เทียบเท่ากันคือdecltype.

C++ เพิ่มคีย์เวิร์ดเพิ่มเติมจำนวนมากเพื่อรองรับคุณสมบัติใหม่ ๆ ทำให้โค้ด C ที่ใช้คีย์เวิร์ดเหล่านั้นสำหรับตัวระบุไม่ถูกต้องใน C++ ตัวอย่างเช่น โค้ดต่อไปนี้เป็นโค้ด C ที่ถูกต้อง แต่ถูกปฏิเสธโดยคอมไพเลอร์ C++ เนื่องจากคีย์เวิร์ด `id`, `id` templateและnew` id` classเป็นคำสงวน

struct template { int new ; struct template * class ; };

โครงสร้างที่มีพฤติกรรมแตกต่างกันในภาษา C และ C++

มีโครงสร้างทางไวยากรณ์บางอย่างที่ใช้ได้ทั้งในภาษา C และ C++ แต่ให้ผลลัพธ์ที่แตกต่างกันในสองภาษานี้

  • ตัวอักษรลิเทอรัลเช่น'a'มีชนิดข้อมูลเป็นintในภาษา C และเป็นชนิดข้อมูลเป็นcharในภาษา C++ ซึ่งหมายความว่าsizeof 'a'โดยทั่วไปแล้วจะให้ผลลัพธ์ที่แตกต่างกันในสองภาษา: ใน C++ จะเป็น1ในขณะที่ใน C จะเป็นsizeof(int)ผลที่ตามมาอีกประการหนึ่งจากความแตกต่างของชนิดข้อมูลนี้คือ ใน C 'a'จะเป็นนิพจน์ที่มีเครื่องหมายเสมอ ไม่ว่าcharจะเป็นชนิดข้อมูลที่มีเครื่องหมายหรือไม่มีเครื่องหมายก็ตาม ในขณะที่ใน C++ นั้นขึ้นอยู่กับการใช้งานของคอมไพเลอร์
  • ภาษา C++ กำหนดการเชื่อมโยงภายในให้กับตัวแปรที่มีขอบเขตเฉพาะเนมสเปซconstเว้นแต่จะมีการประกาศไว้อย่างชัดเจนexternซึ่งต่างจากภาษา C ที่externค่าเริ่มต้นคือการเชื่อมโยงแบบไฟล์สำหรับเอนทิตีทั้งหมด ในทางปฏิบัติ สิ่งนี้ไม่ได้นำไปสู่การเปลี่ยนแปลงความหมายโดยไม่แจ้งให้ทราบระหว่างโค้ด C และ C++ ที่เหมือนกัน แต่จะนำไปสู่ข้อผิดพลาดในการคอมไพล์หรือการเชื่อมโยงแทน
  • ในภาษา C การใช้ฟังก์ชันแบบอินไลน์จำเป็นต้องเพิ่มการประกาศต้นแบบของฟังก์ชันด้วยตนเองโดยใช้คีย์เวิร์ด `extern` ในหน่วยการแปลเพียงหน่วยเดียวเพื่อให้แน่ใจว่ามีการเชื่อมโยงเวอร์ชันที่ไม่ใช่แบบอินไลน์เข้ามา ในขณะที่ภาษา C++ จัดการเรื่องนี้โดยอัตโนมัติ โดยละเอียดแล้ว ภาษา C แยกความแตกต่างระหว่างการนิยามinlineฟังก์ชัน สองประเภท ได้แก่ การนิยามภายนอกทั่วไป (ซึ่งexternใช้ `external` อย่างชัดเจน) และการนิยามแบบอินไลน์ ในทางกลับกัน ภาษา C++ ให้เฉพาะการนิยามแบบอินไลน์สำหรับฟังก์ชันแบบอินไลน์เท่านั้น ในภาษา C การนิยามแบบอินไลน์คล้ายกับการนิยามภายใน (เช่น แบบสแตติก) ตรงที่สามารถอยู่ร่วมกันในโปรแกรมเดียวกันกับการนิยามภายนอกหนึ่งรายการและการนิยามภายในและแบบอินไลน์จำนวนเท่าใดก็ได้ของฟังก์ชันเดียวกันในหน่วยการแปลอื่นๆ ซึ่งทั้งหมดอาจแตกต่างกันได้ นี่เป็นข้อพิจารณาที่แยกต่างหากจากการเชื่อมโยงของฟังก์ชัน แต่ไม่ใช่ข้อพิจารณาที่เป็นอิสระ คอมไพเลอร์ของภาษา C มีดุลยพินิจในการเลือกใช้ระหว่างการนิยามแบบอินไลน์และการนิยามภายนอกของฟังก์ชันเดียวกันเมื่อทั้งสองแบบปรากฏให้เห็น อย่างไรก็ตาม C++ กำหนดว่า หากมีการประกาศฟังก์ชันที่มีการเชื่อมโยงภายนอกinlineในหน่วยการแปลใดๆ ฟังก์ชันนั้นจะต้องถูกประกาศ (และกำหนดความหมาย) ในทุกหน่วยการแปลที่ใช้งาน และคำจำกัดความทั้งหมดของฟังก์ชันนั้นจะต้องเหมือนกันตามหลักการ ODR ฟังก์ชันอินไลน์แบบสแตติกทำงานเหมือนกันในภาษา C และ C++
  • ทั้งภาษา C (ตั้งแต่ C99) และ C++ ต่างก็มีชนิดข้อมูลบูลีนboolที่มีค่าคงที่trueแต่falseมีการกำหนดนิยามที่แตกต่างกัน
    • ในภาษา C++ boolเป็นชนิดข้อมูลพื้นฐานและเป็นคำสงวน
    • ใน C99 มีการแนะนำคีย์เวิร์ดใหม่_Bool`boolean` เป็นชนิดข้อมูลบูลีนในตัว ส่วนหัวของไฟล์stdbool.hมีมาโคร `boolean` bool, `boolean` trueและ `boolean` falseที่ถูกกำหนดเป็น ` boolean` _Bool, `boolean` 1และ0`boolean` ตามลำดับ ดังนั้น ` boolean` trueและ `boolean` จึง falseมีชนิดข้อมูลเป็นint` boolean`
    • อย่างไรก็ตามในC23bool (และค่าต่างๆ ของมันtrue) falseถือเป็นคำสำคัญ
  • C++ มีชนิดข้อมูลchar8_t, char16_tและchar32_tเพื่อเข้ารหัส หน่วยรหัส UTF เดียว C23 ก็มีชนิดข้อมูลเหล่านี้เช่นกัน แต่เป็นการกำหนดชนิดข้อมูลให้กับชนิดข้อมูลจำนวนเต็มอื่นๆ แทนที่จะเป็นชนิดข้อมูลในตัวที่แตกต่างกัน ดังนั้นชื่อของชนิดข้อมูลเหล่านี้จึงไม่ใช่คำสงวนใน<uchar.h>.
  • ในภาษา C นั้น การกำหนดว่าฟิลด์บิตของชนิดข้อมูล ใด intจะเป็นแบบมีเครื่องหมายหรือไม่มีเครื่องหมายขึ้นอยู่กับการใช้งาน ในขณะที่ในภาษา C++ นั้น ฟิลด์บิตจะเป็นแบบมีเครื่องหมายเสมอเพื่อให้ตรงกับชนิดข้อมูลพื้นฐาน

ความแตกต่างอื่นๆ จากหัวข้อก่อนหน้านี้หลายประการ สามารถนำมาใช้สร้างโค้ดที่คอมไพล์ได้ทั้งสองภาษา แต่มีพฤติกรรมที่แตกต่างกันได้ ตัวอย่างเช่น ฟังก์ชันต่อไปนี้จะส่งคืนค่าที่แตกต่างกันในภาษา C และ C++:

ภายนอกภายในT ;int size ( void ) { struct T { long i , j ; }; return sizeof ( T ); // C: return sizeof(int) // C++: return sizeof(struct T) }

นี่เป็นเพราะภาษา C กำหนดให้ต้องมีstructเครื่องหมาย `<br>` อยู่หน้าแท็กโครงสร้าง (และจึงsizeof(T)หมายถึงตัวแปร) แต่ภาษา C++ อนุญาตให้ละเว้นได้ (และจึงsizeof(T)หมายถึงค่าโดยปริยายtypedef) โปรดระวังว่าผลลัพธ์จะแตกต่างกันเมื่อexternประกาศตัวแปรไว้ภายในฟังก์ชัน: ในกรณีนั้น การมีตัวระบุที่มีชื่อเดียวกันในขอบเขตของฟังก์ชันจะยับยั้งไม่ให้ค่าโดยปริยายtypedefมีผลในภาษา C++ และผลลัพธ์สำหรับภาษา C และ C++ จะเหมือนกัน นอกจากนี้ โปรดสังเกตว่าความกำกวมในตัวอย่างข้างต้นเกิดจากการใช้วงเล็บกับsizeofตัวดำเนินการ `<br>` การใช้ `<br> sizeof T` จะคาดหวังว่าTจะเป็นนิพจน์ไม่ใช่ชนิดข้อมูล ดังนั้นตัวอย่างจึงไม่สามารถคอมไพล์ได้ในภาษา C++

การเชื่อมโยงโค้ด C และ C++

แม้ว่าภาษา C และ C++ จะมีความเข้ากันได้ของซอร์สโค้ดในระดับสูง แต่ไฟล์ออบเจ็กต์ที่คอมไพเลอร์ของแต่ละภาษาสร้างขึ้นอาจมีความแตกต่างที่สำคัญ ซึ่งจะปรากฏให้เห็นเมื่อมีการผสมผสานโค้ด C และ C++ เข้าด้วยกัน โดยเฉพาะอย่างยิ่ง:

ด้วยเหตุผลเหล่านี้ ในการที่โค้ด C++ จะเรียกใช้ฟังก์ชัน C foo()นั้น โค้ด C++ จะต้องประกาศต้นแบบfoo()ด้วย `prototype` extern "C"ในทำนองเดียวกัน ในการที่โค้ด C จะเรียกใช้ฟังก์ชัน C++ bar()นั้น โค้ด C++ สำหรับฟังก์ชันนั้นbar()จะต้องประกาศด้วยextern "C"` prototype`

แนวปฏิบัติทั่วไปสำหรับไฟล์ส่วนหัวเพื่อรักษาความเข้ากันได้ทั้ง C และ C++ คือการทำให้การประกาศเป็นไปextern "C"ในขอบเขตของส่วนหัว: [ 25 ]

ในfoo.h :

// ถ้าเป็นคอมไพเลอร์ C++ ให้ใช้การเชื่อมโยง C #ifdef __cplusplus extern "C" { #endif// สัญลักษณ์ใดๆ ที่ประกาศไว้ที่นี่จะได้รับการเชื่อมโยงแบบ C void foo (); int bar ( int a , int b );// ถ้าเป็นคอมไพเลอร์ C++ ให้จบการเชื่อมโยง C #ifdef __cplusplus } #endif

ความแตกต่างระหว่าง การเชื่อมโยง และการเรียกใช้ใน ภาษา C และ C++ อาจมีนัยสำคัญเล็กน้อยสำหรับโค้ดที่ใช้ตัวชี้ฟังก์ชันคอมไพเลอร์บางตัวจะสร้างโค้ดที่ไม่ทำงานหากตัวชี้ฟังก์ชันที่ประกาศไว้extern "C"ชี้ไปยังฟังก์ชัน C++ ที่ไม่ได้extern "C"ประกาศ[ 26 ]

ตัวอย่างเช่น โค้ดต่อไปนี้:

void qux ();extern "C" void foo ( void ( * fp )( void ));void bar () {foo ( qux );}

เมื่อใช้ คอมไพเลอร์ C++ ของ Sun Microsystemsอาจทำให้เกิดคำเตือนต่อไปนี้:

"test.cpp", บรรทัดที่ 5: คำเตือน (Anachronism): อาร์กิวเมนต์อย่างเป็นทางการ fp ประเภท extern "C" void(*)() ในการเรียก foo(extern "C" void(*)()) กำลังถูกส่งผ่านเป็นvoid(*)()

เนื่องจากตัวแปรqux()ไม่ได้ถูกประกาศโดยใช้การเชื่อมโยงและการเรียกใช้ตามหลักการของภาษา C แต่ถูกส่งผ่านไปยังฟังก์ชันของภาษา C foo()แทน

ดูเพิ่มเติม

  • การเปรียบเทียบโดยละเอียดทีละประโยค จากมุมมองของมาตรฐาน C89
  • ความไม่เข้ากันระหว่าง ISO C และ ISO C++โดย David R. Tribble (สิงหาคม 2544)
  • คู่มือการย้ายระบบ C++ ของ Oracle (Sun Microsystems) ส่วนที่ 3.11เอกสารประกอบคอมไพเลอร์ของ Oracle/Sun เกี่ยวกับขอบเขตการเชื่อมโยง (linkage scope)
  • Oracle: การผสมผสานโค้ด C และ C++ ในโปรแกรมเดียวกันภาพรวมโดย Steve Clamage (ประธานคณะกรรมการ ANSI C++)

ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Compatibility_of_C_and_C%2B%2B&oldid=1354163341 "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ ความเข้ากันได้ของภาษา C และ C++

ภาษาโปรแกรมCและC++ มีความสัมพันธ์กันอย่างใกล้ชิด แต่ก็มีความแตกต่างที่สำคัญหลายประการ C++ เริ่มต้นจากการแยก สาขาของภาษา C ในยุค แรกๆ ก่อน ที่จะ...

โครงสร้างที่ใช้ได้ในภาษา C แต่ใช้ไม่ได้ในภาษา C++

C++ บังคับใช้กฎการกำหนดประเภทที่เข้มงวดกว่า (ไม่มีการละเมิดระบบประเภทคงที่โดยปริยาย [ 1 ] ) และข้อกำหนดการเริ่มต้น (การบังคับใช้ในเวลาคอมไพล์ว่าตัวแปรในขอบเขตจะไม่ถูกบิดเบือนการเริ่มต้น) [ 10 ] มากกว่า C ดังนั้นโค้ด C ที่ถูกต้องบางส่วนจึงไม่ถูกต้องใน C++...

โครงสร้างที่มีพฤติกรรมแตกต่างกันในภาษา C และ C++

มีโครงสร้างทางไวยากรณ์บางอย่างที่ใช้ได้ทั้งในภาษา C และ C++ แต่ให้ผลลัพธ์ที่แตกต่างกันในสองภาษานี้

การเชื่อมโยงโค้ด C และ C++

แม้ว่าภาษา C และ C++ จะมีความเข้ากันได้ของซอร์สโค้ดในระดับสูง แต่ไฟล์ออบเจ็กต์ที่คอมไพเลอร์ของแต่ละภาษาสร้างขึ้นอาจมีความแตกต่างที่สำคัญ ซึ่งจะปรากฏให้เห็นเมื่อมีการผสมผสานโค้ด C และ C++ เข้าด้วยกัน โดยเฉพาะอย่างยิ่ง: