extern inline ทำอะไร?


93

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

ฉันคิดว่าstatic inlineมันเหมือนกัน (อาจหรืออาจไม่อินไลน์ก็ได้) แต่จะไม่สร้างโค้ดออบเจ็กต์ที่เชื่อมโยงได้เมื่ออินไลน์ (เนื่องจากไม่มีโมดูลอื่นที่สามารถเชื่อมโยงไปได้)

สถานที่ที่ไม่extern inlineเหมาะสมลงในภาพหรือไม่

สมมติว่าฉันต้องการแทนที่มาโครตัวประมวลผลล่วงหน้าด้วยฟังก์ชันอินไลน์และต้องการให้ฟังก์ชันนี้อินไลน์ (เช่นเนื่องจากใช้มาโคร__FILE__และ__LINE__มาโครที่ควรแก้ไขสำหรับผู้เรียก แต่ไม่ใช่ฟังก์ชันที่เรียกว่านี้) นั่นคือฉันต้องการเห็นข้อผิดพลาดของคอมไพเลอร์หรือตัวเชื่อมโยงในกรณีที่ฟังก์ชันไม่ได้รับการอินไลน์ ไม่extern inlineทำเช่นนี้? (ฉันคิดว่าถ้าไม่เป็นเช่นนั้นจะไม่มีวิธีใดที่จะบรรลุพฤติกรรมนี้นอกจากการยึดติดกับมาโคร)

มีความแตกต่างระหว่าง C ++ และ C หรือไม่?

มีความแตกต่างระหว่างผู้จำหน่ายคอมไพเลอร์และเวอร์ชันต่างๆหรือไม่?

คำตอบ:


129

ใน K&R C หรือ C89 อินไลน์ไม่ได้เป็นส่วนหนึ่งของภาษา คอมไพเลอร์หลายตัวใช้มันเป็นส่วนเสริม แต่ไม่มีความหมายที่กำหนดไว้เกี่ยวกับวิธีการทำงาน GCC เป็นคนแรกที่จะใช้ inlining และแนะนำinline, static inlineและextern inlineโครงสร้าง; คอมไพเลอร์ pre-C99 ส่วนใหญ่มักทำตามผู้นำ

GNU89:

  • inline: ฟังก์ชันอาจอยู่ในบรรทัด (เป็นเพียงคำใบ้เท่านั้น) เวอร์ชันนอกบรรทัดจะถูกปล่อยออกมาและมองเห็นได้จากภายนอกเสมอ ดังนั้นคุณสามารถกำหนดอินไลน์ดังกล่าวได้ในหน่วยคอมไพล์หนึ่งหน่วยเท่านั้นและอีกหน่วยหนึ่งจำเป็นต้องเห็นว่าเป็นฟังก์ชันนอกบรรทัด (หรือคุณจะได้รับสัญลักษณ์ซ้ำในเวลาลิงก์)
  • extern inline จะไม่สร้างเวอร์ชันนอกบรรทัด แต่อาจเรียกใช้เวอร์ชันหนึ่ง (ซึ่งคุณต้องกำหนดในหน่วยการรวบรวมอื่น ๆ อย่างไรก็ตามกฎคำจำกัดความเดียวจะใช้แม้ว่าเวอร์ชันนอกบรรทัดจะต้องมีรหัสเดียวกันกับ อินไลน์เสนอที่นี่ในกรณีที่คอมไพเลอร์เรียกสิ่งนั้นแทน
  • static inlineจะไม่สร้างเวอร์ชันนอกบรรทัดที่มองเห็นได้จากภายนอกแม้ว่าอาจสร้างไฟล์แบบคงที่ กฎคำจำกัดความเดียวใช้ไม่ได้เนื่องจากไม่มีสัญลักษณ์ภายนอกที่เปล่งออกมาหรือไม่มีการเรียกใช้

C99 (หรือ GNU99):

  • inline: เช่น GNU89 "extern inline"; ไม่มีการปล่อยฟังก์ชันที่มองเห็นได้จากภายนอก แต่อาจมีการเรียกใช้ฟังก์ชันหนึ่งจึงต้องมีอยู่
  • extern inline: เช่น GNU89 "inline": โค้ดที่มองเห็นได้จากภายนอกจะถูกปล่อยออกมาดังนั้นหน่วยการแปลสูงสุดหนึ่งหน่วยที่สามารถใช้สิ่งนี้ได้
  • static inline: เช่น GNU89 "static inline" นี่เป็นเครื่องเดียวแบบพกพาระหว่าง gnu89 และ c99

C ++:

ฟังก์ชันที่อยู่ในบรรทัดที่ใดก็ได้จะต้องอยู่ในบรรทัดเดียวทุกที่โดยมีคำจำกัดความเดียวกัน คอมไพเลอร์ / ตัวเชื่อมโยงจะจัดเรียงสัญลักษณ์หลายอินสแตนซ์ ไม่มีคำจำกัดความของstatic inlineหรือextern inlineแม้ว่าคอมไพเลอร์จำนวนมากจะมี (โดยทั่วไปจะเป็นไปตามโมเดล gnu89)


2
ในคลาสสิกคลาสสิก C "อินไลน์" ไม่ใช่คีย์เวิร์ด สามารถใช้เป็นชื่อตัวแปรได้ สิ่งนี้จะใช้กับ C89 และมาตรฐานล่วงหน้า (K&R) C.
Jonathan Leffler

คุณถูกต้องดูเหมือนว่า แก้ไขแล้ว. ฉันคิดว่ามันถูกสงวนไว้เป็นคีย์เวิร์ดใน C89 (แม้ว่าจะไม่ใช่ใน K&R) แต่ดูเหมือนว่าฉันจะจำผิด
puetzk

ฉันต้องการเพิ่มสำหรับ Visual C ++ ของ Microsoft มีคีย์เวิร์ด __forceinline ที่จะบังคับให้ฟังก์ชันของคุณอินไลน์ เห็นได้ชัดว่านี่เป็นส่วนขยายเฉพาะของคอมไพเลอร์สำหรับ VC ++ เท่านั้น
untitled8468927

มีความแตกต่างระหว่าง C99 "extern inline" และไม่มี specifiers เลยหรือไม่?
Jo So

ไม่มีความหมาย; เช่นเดียวกับฟังก์ชันที่ไม่ใช่แบบอินไลน์extern inlineจะอยู่ภายใต้กฎคำนิยามเดียวและนี่คือคำจำกัดความ แต่ถ้าฮิวริสติกการเพิ่มประสิทธิภาพที่กำหนดโดยการนำไปใช้ทำตามคำแนะนำให้ใช้inlineคีย์เวิร์ดเป็นคำแนะนำที่ว่า "เรียกใช้ฟังก์ชันให้เร็วที่สุด" (ISO 9899: 1999 §6.7.4 (5) ให้extern inlineนับ
puetzk

31

ฉันเชื่อว่าคุณเข้าใจผิด __FILE__ และ __LINE__ ตามข้อความนี้:

เนื่องจากใช้แมโคร __FILE__ และ __LINE__ ซึ่งควรแก้ไขสำหรับผู้โทร แต่ไม่ใช่ฟังก์ชันที่เรียกว่านี้

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


14

ดูเหมือนว่าคุณกำลังพยายามเขียนสิ่งนี้:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}

{
...
  printLocation();
...
  printLocation();
...
  printLocation();

และหวังว่าคุณจะได้รับค่าที่แตกต่างกันออกไปในแต่ละครั้ง ดังที่ Don กล่าวไว้คุณจะไม่ทำเพราะ __FILE__ และ __LINE__ ถูกนำไปใช้งานโดยตัวประมวลผลล่วงหน้า แต่คอมไพเลอร์จะใช้อินไลน์ ดังนั้นไม่ว่าคุณจะเรียก printLocation จากที่ใดคุณจะได้รับผลลัพธ์เดียวกัน

เพียงวิธีการที่คุณจะได้รับนี้ในการทำงานคือการทำให้ printLocation แมโคร (ใช่ฉันรู้...)

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}

...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

18
เคล็ดลับทั่วไปคือสำหรับมาโคร PRINT_LOCATION เพื่อเรียกใช้ฟังก์ชัน printLocation โดยส่งผ่านFILEและLINEเป็นพารามิเตอร์ ซึ่งอาจส่งผลให้พฤติกรรมดีบักเกอร์ / ตัวแก้ไข / ฯลฯ ดีขึ้นเมื่อเนื้อความของฟังก์ชันไม่สำคัญ
Steve Jessop

@Roddy ดูโซลูชันของฉัน - ส่วนขยายของคุณ แต่ครอบคลุมและขยายได้มากขึ้น
กระตือรือร้น

@SteveJessop มีบางอย่างที่ฉันระบุไว้ในโซลูชันด้านล่าง
กระตือรือร้น

3

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


2

มาโครเป็นตัวเลือกของคุณที่นี่แทนที่จะเป็นฟังก์ชันแบบอินไลน์ โอกาสที่เกิดขึ้นได้ยากที่มาโครจะควบคุมฟังก์ชันแบบอินไลน์ ลองทำดังต่อไปนี้: ฉันเขียนโค้ด "MACRO MAGIC" นี้แล้วและมันควรจะใช้งานได้! ทดสอบบน gcc / g ++ Ubuntu 10.04

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)

#ifdef __cplusplus

#include <cstdio>
#include <cstring>

#else

#include <stdio.h>
#include <string.h>

#endif

//=========== MACRO MAGIC BEGINS ============

//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )

#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))

#define LOG(x, s...) printf("(%s:%s:%s)"  x "\n" , __SFILE__, __func__, _LINE, ## s);

//=========== MACRO MAGIC ENDS ============

int main (int argc, char** argv) {

  LOG("Greetings StackOverflow! - from enthusiasticgeek\n");

  return 0;
}

สำหรับไฟล์หลายไฟล์กำหนดมาโครเหล่านี้ในไฟล์ส่วนหัวแยกกันรวมทั้งไฟล์ c / cc / cxx / cpp แต่ละไฟล์ โปรดเลือกฟังก์ชันแบบอินไลน์หรือตัวระบุ const (ตามความต้องการของกรณี) มากกว่ามาโครทุกที่ที่เป็นไปได้


2

แทนที่จะตอบว่า "มันทำอะไร" ฉันกลับตอบว่า "ฉันจะทำยังไงให้เป็นอย่างที่ฉันต้องการ" มีซับใน 5 ชนิดทั้งหมดมีให้ใน GNU C89 มาตรฐาน C99 และ C ++:

อินไลน์เสมอเว้นแต่จะใช้ที่อยู่

เพิ่ม__attribute__((always_inline))ในการประกาศใด ๆ จากนั้นใช้หนึ่งในกรณีด้านล่างนี้เพื่อจัดการกับความเป็นไปได้ของที่อยู่ที่ถูกยึด

คุณไม่ควรใช้สิ่งนี้เว้นแต่คุณจะต้องการความหมาย (เช่นมีผลต่อการประกอบในลักษณะใดวิธีหนึ่งหรือเพื่อใช้alloca) คอมไพเลอร์มักจะรู้ดีกว่าคุณว่าคุ้มไหม

ในบรรทัดและแสดงสัญลักษณ์ที่อ่อนแอ (เช่น C ++ หรือที่เรียกว่า "just make it work")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

โปรดทราบว่าสิ่งนี้จะทิ้งสำเนาของรหัสเดียวกันไว้หลายชุดและตัวเชื่อมโยงจะเลือกรหัสโดยพลการ

อินไลน์ แต่ไม่เคยแสดงสัญลักษณ์ใด ๆ (ออกจากการอ้างอิงภายนอก)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

ปล่อยเสมอ (สำหรับหนึ่ง TU เพื่อแก้ไขก่อนหน้านี้)

เวอร์ชันที่มีคำใบ้แสดงสัญลักษณ์ที่อ่อนแอใน C ++ แต่สัญลักษณ์ที่ชัดเจนในภาษา C อย่างใดอย่างหนึ่ง:

void foo(void);
inline void foo(void) { ... }

หรือคุณสามารถทำได้โดยไม่มีคำใบ้ซึ่งแสดงสัญลักษณ์ที่ชัดเจนในทั้งสองภาษา:

void foo(void) { ... }

โดยทั่วไปคุณจะรู้ว่า TU ของคุณเป็นภาษาอะไรเมื่อคุณให้คำจำกัดความและอาจไม่จำเป็นต้องมีการฝังใน

อินไลน์และส่งเสียงในทุกม ธ

static inline void foo(void) { ... }

สำหรับสิ่งเหล่านี้ทั้งหมดยกเว้นรายการstaticเดียวคุณสามารถเพิ่มการvoid foo(void)ประกาศด้านบนได้ ซึ่งจะช่วยใน "แนวทางปฏิบัติที่ดีที่สุด" ในการเขียนส่วนหัวที่สะอาดจากนั้น#includeส่งไฟล์แยกต่างหากพร้อมด้วยคำจำกัดความแบบอินไลน์ จากนั้นหากใช้อินไลน์สไตล์ C #defineมาโครบางตัวจะแตกต่างกันไปใน TU เฉพาะอันเดียวเพื่อให้คำจำกัดความนอกบรรทัด

อย่าลืมextern "C"ว่าอาจใช้ส่วนหัวจากทั้ง C และ C ++ หรือไม่!


หายไป: แล้ว MSVC ล่ะ? มีนามสกุลภาษา C89 อยู่บ้าง แต่ฉันไม่เคยใช้ MSVC และไม่รู้ว่าจะเรียกใช้งานnmเทียบเท่าได้อย่างไร
o11c

ชื่อกรณี # 4 void foo(void); inline void foo(void) { ... }คือ "emit always (สำหรับ TU อันเดียวเพื่อแก้ปัญหาก่อนหน้านี้)" อีก 4 รายการขึ้นต้นด้วย "inline .... " ในกรณีที่ 4: อินไลน์ด้วยหรือไม่?
chux - คืนสถานะ Monica

@ chux-ReinstateMonica Inlining เป็นมากกว่าพฤติกรรมสัญลักษณ์มากกว่าประสิทธิภาพ คอมไพเลอร์ได้รับอนุญาตให้อินไลน์เพื่อประสิทธิภาพไม่ว่าจะมีคีย์เวิร์ดอยู่หรือไม่ก็ตาม (ขึ้นอยู่กับกฎการแทรกสอด) ส่วนสำคัญของ case # 4 คือความเกี่ยวข้องกับสัญลักษณ์ที่ยังไม่ได้แก้ไขของ case # 3 มักจะไม่มี (m) ผู้โทรใด ๆ ใน TU นั้นอยู่แล้ว
o11c

ความคิดเห็นของฉันเป็นข้อมูลเพิ่มเติมว่าทำไมคำตอบนี้ในกรณีที่ 1,2,3,5 มีอินไลน์ในคำอธิบายชื่อเรื่อง แต่ 4 ไม่ตอบ คุณกำลังแนะนำว่าในกรณีที่รหัส 4 ไม่เคยอินไลน์อินไลน์เสมออินไลน์บางครั้ง? ความเป็นไปได้แบบอินไลน์ของ case # 4 แตกต่างจาก 4 อื่น ๆ หรือไม่?
chux - คืนสถานะ Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.