ผลกระทบของคำสำคัญภายนอกในฟังก์ชั่น C


171

ใน C ฉันไม่ได้สังเกตเห็นผลกระทบใด ๆ ของexternคำหลักที่ใช้ก่อนการประกาศฟังก์ชัน ตอนแรกฉันคิดว่าเมื่อกำหนดextern int f();ไฟล์เดียวบังคับให้คุณใช้มันนอกขอบเขตของไฟล์ อย่างไรก็ตามฉันพบว่าทั้งสอง:

extern int f();
int f() {return 0;}

และ

extern int f() {return 0;}

รวบรวมได้ดีไม่มีคำเตือนจาก gcc ผมใช้gcc -Wall -ansi; มันจะไม่ยอมรับ//ความคิดเห็นด้วยซ้ำ

มีผลกระทบใด ๆ สำหรับการใช้extern ก่อนหน้านิยามฟังก์ชันหรือไม่? หรือเป็นเพียงแค่คีย์เวิร์ดเสริมที่ไม่มีผลข้างเคียงสำหรับฟังก์ชั่น

ในกรณีหลังนี้ฉันไม่เข้าใจว่าทำไมนักออกแบบมาตรฐานถึงเลือกที่จะทิ้งไวยากรณ์ด้วยคำหลักที่ไม่จำเป็น

แก้ไข:ชี้แจงฉันรู้ว่ามีการใช้งานสำหรับexternตัวแปร แต่ฉันก็แค่ถามเกี่ยวกับexternในฟังก์ชั่น


จากการวิจัยบางอย่างที่ฉันทำเมื่อพยายามที่จะใช้สิ่งนี้เพื่อจุดประสงค์ในการสร้างเทมเพลตที่บ้าคลั่ง extern ไม่ได้รับการสนับสนุนในรูปแบบที่โปรแกรมคอมไพเลอร์ส่วนใหญ่ตั้งใจและไม่ได้ทำอะไรเลย
Ed James

4
มันไม่ฟุ่มเฟือยเสมอไปดูคำตอบของฉัน เมื่อใดก็ตามที่คุณต้องการแบ่งปันบางสิ่งระหว่างโมดูลที่คุณไม่ต้องการในส่วนหัวสาธารณะจะมีประโยชน์มาก อย่างไรก็ตาม 'externing' ทุกฟังก์ชั่นเดียวในส่วนหัวสาธารณะ (พร้อมคอมไพเลอร์ทันสมัย) มีประโยชน์น้อยมากและไม่มีประโยชน์เพราะพวกเขาสามารถหาได้ด้วยตนเอง
Tim Post

@Ed .. ถ้า vol int ที่แฝงอยู่นั้นเป็นสากลใน foo.c และ bar.c ต้องการมัน bar.c ต้องประกาศว่าเป็น extern มันมีข้อดี นอกเหนือจากนั้นคุณอาจต้องแบ่งปันฟังก์ชั่นบางอย่างที่คุณไม่ต้องการให้เปิดเผยในส่วนหัวสาธารณะ
Tim Post

ดูเพิ่มเติมได้ที่: stackoverflow.com/questions/496448/…
Steve Melnikoff

2
@Barry หากทั้งหมดคำถามอื่น ๆ จะซ้ำกับคำถามนี้ 2009 vs 2012
Elazar Leibovich

คำตอบ:


138

เรามีสองไฟล์ foo.c และ bar.c

นี่คือ foo.c

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

ตอนนี้ที่นี่คือ bar.c

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   while (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

อย่างที่คุณเห็นเราไม่มีส่วนหัวที่ใช้ร่วมกันระหว่าง foo.c และ bar.c อย่างไรก็ตาม bar.c ต้องการบางสิ่งที่ประกาศเป็น foo.c เมื่อเชื่อมโยงและ foo.c ต้องการฟังก์ชันจาก bar.c เมื่อเชื่อมโยง

ด้วยการใช้ 'extern' คุณกำลังบอกคอมไพเลอร์ว่าสิ่งใดก็ตามที่ตามมามันจะพบได้ (ไม่คงที่) ในเวลาลิงก์ อย่าจองอะไรไว้ในบัตรผ่านปัจจุบันเนื่องจากจะพบในภายหลัง ฟังก์ชั่นและตัวแปรได้รับการปฏิบัติอย่างเท่าเทียมกันในเรื่องนี้

มันมีประโยชน์มากถ้าคุณต้องการแชร์ระดับโลกระหว่างโมดูลและไม่ต้องการใส่ / เริ่มต้นในส่วนหัว

ในทางเทคนิคแล้วทุกฟังก์ชั่นในส่วนหัวของ public library คือ 'extern' แต่การทำ label มันให้มีประโยชน์น้อยมากและไม่มีประโยชน์ขึ้นอยู่กับคอมไพเลอร์ คอมไพเลอร์ส่วนใหญ่สามารถหาได้ด้วยตัวเอง อย่างที่คุณเห็นฟังก์ชั่นเหล่านั้นถูกนิยามไว้ที่อื่น

ในตัวอย่างด้านบน main () จะพิมพ์ hello world เพียงครั้งเดียว แต่ยังคงป้อน bar_function () โปรดทราบว่า bar_function () จะไม่กลับมาในตัวอย่างนี้ (เนื่องจากเป็นเพียงตัวอย่างง่ายๆ) เพียงแค่จินตนาการว่า stop_now กำลังถูกแก้ไขเมื่อมีการให้บริการสัญญาณ (ด้วยเหตุนี้ระเหยได้) หากนี่ดูเหมือนจะไม่เพียงพอ

Externs นั้นมีประโยชน์มากสำหรับสิ่งต่างๆเช่นตัวจัดการสัญญาณ, mutex ที่คุณไม่ต้องการใส่ไว้ในส่วนหัวหรือโครงสร้างเป็นต้นคอมไพเลอร์ส่วนใหญ่จะปรับให้เหมาะสมเพื่อให้แน่ใจว่าพวกเขาจะไม่จองหน่วยความจำใด ๆ สำหรับวัตถุภายนอก จะทำการจองในโมดูลที่มีการกำหนดวัตถุ อย่างไรก็ตามมีจุดเล็ก ๆ น้อย ๆ ในการระบุคอมไพเลอร์สมัยใหม่เมื่อสร้างต้นแบบฟังก์ชั่นสาธารณะ

หวังว่าจะช่วย :)


56
รหัสของคุณจะรวบรวมได้โดยไม่ต้อง extern ก่อน bar_function
Elazar Leibovich

2
@Tim: ถ้าอย่างนั้นคุณก็ไม่มีสิทธิ์ที่จะทำงานกับรหัสที่ฉันใช้ มันสามารถเกิดขึ้นได้ บางครั้งส่วนหัวยังมีนิยามฟังก์ชันแบบสแตติก มันน่าเกลียดและไม่จำเป็น 99.99% ของเวลา (ฉันสามารถปิดโดยการสั่งซื้อหรือสองหรือขนาดขนาดใหญ่เกินความจำเป็นบ่อย) มันมักจะเกิดขึ้นเมื่อคนเข้าใจผิดว่าส่วนหัวมีความจำเป็นเฉพาะเมื่อไฟล์ต้นฉบับอื่นจะใช้ข้อมูลนั้น ส่วนหัวคือ (ab) ใช้ในการเก็บข้อมูลการประกาศสำหรับไฟล์ต้นฉบับหนึ่งไฟล์และคาดว่าจะไม่มีไฟล์อื่นรวมอยู่ด้วย บางครั้งมันเกิดขึ้นด้วยเหตุผลที่ขัดแย้งกันมากขึ้น
Jonathan Leffler

2
@ Jonathan Leffler - น่าสงสัยจริงๆ! ฉันได้รับรหัสร่างค่อนข้างบางมาก่อน แต่ฉันสามารถพูดได้อย่างสุจริตว่าฉันไม่เคยเห็นใครบางคนใส่การประกาศแบบคงที่ไว้ในส่วนหัว ดูเหมือนว่าคุณจะมีงานที่ค่อนข้างสนุกและน่าสนใจ :)
Tim Post

1
ข้อเสียของ 'ฟังก์ชั่นต้นแบบที่ไม่ได้อยู่ในส่วนหัว' คือคุณไม่ได้รับการตรวจสอบความเป็นอิสระโดยอัตโนมัติของความสอดคล้องระหว่างนิยามฟังก์ชันในbar.cและการประกาศfoo.cระบบ หากฟังก์ชั่นถูกประกาศในfoo.h และทั้งสองไฟล์รวมfoo.hแล้วส่วนหัวบังคับใช้ความสอดคล้องกันระหว่างไฟล์ต้นฉบับสองไฟล์ ก็ไม่มีถ้าคำนิยามของbar_functionในbar.cการเปลี่ยนแปลง แต่การประกาศในfoo.cจะไม่เปลี่ยนแปลงแล้วสิ่งที่ผิดพลาดในเวลาทำงาน; คอมไพเลอร์ไม่สามารถระบุปัญหาได้ เมื่อมีการใช้ส่วนหัวอย่างถูกต้องคอมไพเลอร์มองเห็นปัญหา
Jonathan Leffler

1
ภายนอกในการประกาศฟังก์ชั่นนั้นไม่จำเป็นเหมือน 'int' ใน 'int ที่ไม่ได้ลงนาม' เป็นวิธีปฏิบัติที่ดีที่จะใช้ 'extern' เมื่อต้นแบบไม่ใช่การประกาศไปข้างหน้า ... แต่ควรมีชีวิตอยู่ในส่วนหัวโดยไม่มี 'extern' เว้นแต่ว่างานจะเป็นแบบขอบ stackoverflow.com/questions/10137037/…

82

เท่าที่ฉันจำได้มาตรฐานการประกาศฟังก์ชั่นทั้งหมดถือเป็น "extern" โดยค่าเริ่มต้นดังนั้นจึงไม่จำเป็นต้องระบุอย่างชัดเจน

ไม่ได้ทำให้คำหลักนี้ไร้ประโยชน์เพราะมันสามารถใช้กับตัวแปรได้ (และในกรณีนี้ - มันเป็นทางออกเดียวในการแก้ปัญหาการเชื่อมโยง) แต่ด้วยฟังก์ชั่น - ใช่มันเป็นตัวเลือก


21
จากนั้นในฐานะผู้ออกแบบมาตรฐานฉันจะไม่อนุญาตให้ใช้ฟังก์ชั่น extern ภายนอกเพราะมันเป็นการเพิ่มสัญญาณรบกวนให้กับไวยากรณ์
Elazar Leibovich

3
ความเข้ากันได้ย้อนหลังอาจเป็นอาการปวด
MathuSum Mut

1
@ElazarLeibovich จริงๆแล้วในกรณีนี้ไม่อนุญาตให้มันเป็นสิ่งที่จะเพิ่มเสียงให้กับไวยากรณ์
การแข่งขัน Lightness ใน Orbit

1
การ จำกัด คำหลักเพิ่มเสียงนั้นเกินกว่าฉัน แต่ฉันคิดว่ามันเป็นเรื่องของรสนิยม
Elazar Leibovich

มันมีประโยชน์ที่จะอนุญาตให้ใช้ "extern" สำหรับฟังก์ชั่นได้ตามที่ระบุต่อโปรแกรมเมอร์อื่น ๆ ที่ฟังก์ชั่นนั้นถูกกำหนดในไฟล์อื่นไม่ใช่ในไฟล์ปัจจุบันและไม่ได้ประกาศไว้ในส่วนหัวที่รวมไว้
DimP

23

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

ถ้าฉันเขียน

extern int i;

ในขอบเขตไฟล์ (นอกบล็อกฟังก์ชั่น) ในไฟล์ C จากนั้นคุณกำลังพูดว่า "ตัวแปรอาจถูกกำหนดที่อื่น"

extern int f() {return 0;}

เป็นการประกาศฟังก์ชัน f และนิยามของฟังก์ชัน f คำจำกัดความในกรณีนี้มากเกินไปที่จะขับ extern

extern int f();
int f() {return 0;}

เป็นการประกาศครั้งแรกตามด้วยคำจำกัดความ

การใช้externผิดถ้าคุณต้องการประกาศและกำหนดตัวแปรขอบเขตไฟล์พร้อมกัน ตัวอย่างเช่น,

extern int i = 4;

จะให้ข้อผิดพลาดหรือคำเตือนขึ้นอยู่กับคอมไพเลอร์

การใช้งานexternจะมีประโยชน์หากคุณต้องการหลีกเลี่ยงการนิยามตัวแปรอย่างชัดเจน

ให้ฉันอธิบาย:

สมมติว่าไฟล์ ac มี:

#include "a.h"

int i = 2;

int f() { i++; return i;}

ไฟล์ ah รวมถึง:

extern int i;
int f(void);

และไฟล์ bc มี:

#include <stdio.h>
#include "a.h"

int main(void){
    printf("%d\n", f());
    return 0;
}

extern ในส่วนหัวมีประโยชน์เพราะมันบอกคอมไพเลอร์ในระหว่างขั้นตอนการเชื่อมโยง "นี่เป็นการประกาศและไม่ใช่คำจำกัดความ" หากฉันลบบรรทัดใน ac ซึ่งกำหนด i ให้จัดสรรพื้นที่สำหรับมันและกำหนดค่าให้กับมันโปรแกรมควรจะคอมไพล์ด้วยการอ้างอิงที่ไม่ได้กำหนด สิ่งนี้บอกผู้พัฒนาว่าเขาได้อ้างถึงตัวแปร แต่ยังไม่ได้กำหนด หากตรงกันข้ามฉันละเว้นคำหลัก "extern" และลบint i = 2บรรทัดโปรแกรมยังคงคอมไพล์ - ฉันจะถูกกำหนดด้วยค่าเริ่มต้นเป็น 0

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

สำหรับฟังก์ชั่นในการประกาศฟังก์ชั่นคำหลักซ้ำซ้อนแน่นอน ประกาศฟังก์ชั่นไม่ได้มีความหมายโดยนัย


คุณหมายถึงการลบint i = 2บรรทัดในย่อหน้าที่ 3 หรือไม่? และมันถูกต้องในการมองเห็นint i;คอมไพเลอร์จะจัดสรรหน่วยความจำสำหรับตัวแปรนั้น แต่การเห็นextern int i;คอมไพเลอร์จะไม่จัดสรรหน่วยความจำ แต่ค้นหาตัวแปรที่อื่น?
Frozen Flame

ที่จริงถ้าคุณไม่ใช้คีย์เวิร์ด "extern" โปรแกรมจะไม่คอมไพล์เนื่องจากการกำหนดค่าใหม่ของ i ใน ac และ bc (เนื่องจาก ah)
Nixt

15

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

static int g();
extern int g(); /* g has internal linkage */

extern int j(); /* j has tentative external linkage */

extern int h();
static int h(); /* error */

นี่คือย่อหน้าที่เกี่ยวข้องจาก C99 ฉบับร่าง (n1256):

6.2.2 การเชื่อมโยงของตัวระบุ

[ ... ]

4สำหรับตัวระบุที่ประกาศพร้อมตัวระบุคลาสการเก็บข้อมูลภายนอกในขอบเขตที่การประกาศก่อนหน้าของตัวระบุนั้นปรากฏให้เห็น 23) หากการประกาศก่อนหน้าระบุการเชื่อมโยงภายในหรือภายนอกการเชื่อมโยงของตัวระบุที่การประกาศในภายหลังจะเหมือนกัน ตามที่ระบุไว้ในประกาศก่อนหน้า หากไม่เห็นการประกาศก่อนหน้านี้หรือหากการประกาศก่อนหน้าระบุว่าไม่มีการเชื่อมโยงตัวระบุนั้นจะมีการเชื่อมโยงภายนอก

5หากการประกาศของตัวระบุสำหรับฟังก์ชั่นไม่มีตัวระบุระดับการจัดเก็บการเชื่อมโยงของมันจะถูกกำหนดว่าราวกับว่ามันถูกประกาศด้วยตัวระบุระดับชั้นเก็บข้อมูลภายนอก หากการประกาศของตัวระบุสำหรับวัตถุที่มีขอบเขตไฟล์และไม่มีตัวระบุระดับการจัดเก็บการเชื่อมโยงของมันคือภายนอก


มันเป็นมาตรฐานหรือคุณแค่บอกพฤติกรรมของคอมไพเลอร์ทั่วไป ในกรณีของมาตรฐานฉันจะดีใจสำหรับลิงก์ไปยังมาตรฐาน แต่ขอบคุณ!
Elazar Leibovich

นี่คือพฤติกรรมมาตรฐาน มีร่าง C99 อยู่ที่นี่: < open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf > มาตรฐานจริงไม่ฟรีแม้ว่า (ร่างดีพอสำหรับวัตถุประสงค์ส่วนใหญ่)
dirkgently

1
ฉันเพิ่งทดสอบใน gcc และเป็นทั้ง "extern int h (); คง int h () {return 0;}" และ "int h (); คงที่ int h () {return 0;}" เป็นที่ยอมรับเหมือนกัน คำเตือน. เป็น C99 เท่านั้นและไม่ใช่ ANSI หรือไม่ คุณช่วยแนะนำฉันไปยังส่วนที่ถูกต้องในร่างได้หรือไม่เนื่องจากดูเหมือนจะไม่เป็นจริงสำหรับ gcc
Elazar Leibovich

ตรวจสอบอีกครั้ง. ฉันพยายามแบบเดียวกันกับ gcc 4.0.1 และฉันได้รับข้อผิดพลาดในที่ที่ควรจะเป็น ลองใช้คอมไพเลอร์ออนไลน์หรือ codepad.org เช่นกันหากคุณไม่มีสิทธิ์เข้าถึงคอมไพเลอร์ตัวอื่น อ่านมาตรฐาน
dirkgently

2
@dirkgently, คำถามที่แท้จริงของฉันคือจะมีผลกระทบใด ๆ สำหรับการใช้ exetrn กับการประกาศฟังก์ชั่นและถ้าไม่มีเหตุผลที่เป็นไปได้ที่จะเพิ่ม extern ให้กับการประกาศฟังก์ชั่น และคำตอบคือไม่ไม่มีผลและมีครั้งเดียวที่มีผลกระทบกับคอมไพเลอร์ที่ไม่ได้มาตรฐาน
Elazar Leibovich

11

ฟังก์ชันอินไลน์มีกฎพิเศษเกี่ยวกับความexternหมาย (โปรดทราบว่าฟังก์ชั่นแบบอินไลน์เป็นส่วนขยาย C99 หรือ GNU ซึ่งไม่ได้อยู่ในต้นฉบับ C

สำหรับฟังก์ชั่นที่ไม่ใช่แบบอินไลน์externไม่จำเป็นต้องใช้เนื่องจากเป็นค่าเริ่มต้น

โปรดทราบว่ากฎสำหรับ C ++ นั้นแตกต่างกัน ยกตัวอย่างเช่นextern "C"เป็นสิ่งจำเป็นในการประกาศ C ++ ของฟังก์ชั่นซีที่คุณจะโทรจาก C ++ inlineและมีกฎระเบียบที่แตกต่างกันเกี่ยวกับ


นี่เป็นคำตอบเดียวที่นี่ที่ทั้งถูกต้องและตอบคำถามจริง
robinjam

4

IOW ภายนอกคือซ้ำซ้อนและไม่ทำอะไรเลย

นั่นคือเหตุผล 10 ปีต่อมา:

ดูคอมมิท ad6dad0 , ยอมรับ b199d71 , ยอมรับ 5545442 (29 เม.ย. 2019) โดยDenton Liu ( Denton-L) )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 4aeeef3 , 13 พฤษภาคม 2019)

*.[ch]: ลบออกexternจากการประกาศฟังก์ชั่นโดยใช้spatch

มีการผลักที่จะลบ externจากการประกาศฟังก์ชัน

ลบอินสแตนซ์บางส่วนของ " extern" สำหรับการประกาศฟังก์ชันที่ Coccinelle จับ
โปรดทราบว่า Coccinelle มีปัญหาในการประมวลผลฟังก์ชั่นด้วย__attribute__หรือ varargs ดังนั้นบางอย่างexternการประกาศจะถูกทิ้งไว้ข้างหลังเพื่อจัดการกับแพทช์ในอนาคต

นี่คือแพทช์ Coccinelle ที่ใช้:

  @@
    type T;
    identifier f;
    @@
    - extern
    T f(...);

และมันก็ทำงานด้วย:

  $ git ls-files \*.{c,h} |
    grep -v ^compat/ |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place

สิ่งนี้ไม่ได้ตรงไปตรงมาเสมอแม้ว่า:

ดูกระทำ 7027f50 (4 กันยายน 2019) โดยDenton หลิว (Denton-L )
(ผสานโดยDenton Liu - Denton-L- in 7027f50 , 05 Sep 2019)

compat/*.[ch]: ลบออกexternจากการประกาศฟังก์ชั่นโดยใช้ spatch

ใน 5545442 ( *.[ch]: ลบexternจากการประกาศฟังก์ชั่นโดยใช้ spatch, 2019-04-29, Git v2.22.0-rc0) เราลบ externs ออกจากการประกาศฟังก์ชั่นการใช้spatchแต่เราตั้งใจแยกไฟล์ภายใต้compat/เนื่องจากบางส่วนถูกคัดลอกโดยตรงจากต้นน้ำและเราควรหลีกเลี่ยง การปั่นเพื่อให้การรวมการอัพเดทในอนาคตด้วยตนเองจะง่ายขึ้น

ในการคอมมิชชันล่าสุดเรากำหนดไฟล์ที่นำมาจากอัปสตรีมเพื่อให้เราสามารถแยกไฟล์และรัน spatchบนส่วนที่เหลือ

นี่คือแพทช์ Coccinelle ที่ใช้:

@@
type T;
identifier f;
@@
- extern
  T f(...);

และมันก็ทำงานด้วย:

$ git ls-files compat/\*\*.{c,h} |
    xargs spatch --sp-file contrib/coccinelle/noextern.cocci --in-place
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

Coccinelle มีปัญหาในการจัดการ__attribute__และ varargs ดังนั้นเราจึงดำเนินการต่อไปนี้เพื่อให้แน่ใจว่าไม่มีการเปลี่ยนแปลงที่เหลืออยู่:

$ git ls-files compat/\*\*.{c,h} |
    xargs sed -i'' -e 's/^\(\s*\)extern \([^(]*([^*]\)/\1\2/'
$ git checkout -- \
    compat/regex/ \
    compat/inet_ntop.c \
    compat/inet_pton.c \
    compat/nedmalloc/ \
    compat/obstack.{c,h} \
    compat/poll/

โปรดทราบว่าด้วย Git 2.24 (ไตรมาสที่ 4 ปี 2562) จะมีการปลอมแปลงใด ๆ externจะลดลง

ดูกระทำ 65904b8 (30 กันยายน 2019) โดยเอมิลี่ Shaffer (nasamuffin )
ช่วยโดย: เจฟฟ์คิง (peff )
ดูกระทำ 8464f94 (21 กันยายน 2019) โดยDenton หลิว (Denton-L )
ช่วยโดย: เจฟฟ์คิง (peff )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 59b19bc , 7 ตุลาคม 2019)

promisor-remote.h: ลดลงexternจากการประกาศฟังก์ชั่น

ในระหว่างการสร้างไฟล์นี้, externทุกครั้งที่มีการประกาศฟังก์ชั่นใหม่ที่ได้รับการแนะนำให้รู้จักก็รวมถึง
อย่างไรก็ตามเริ่มจาก5545442 ( *.[ch]: ลบexternจากการประกาศฟังก์ชั่นการใช้spatch2019-04-29, Git v2.22.0-rc0) เราได้พยายามป้องกันไม่ให้มีการใช้งานภายนอกในการประกาศฟังก์ชั่นเพราะมันไม่จำเป็น

ลบexterns ปลอมเหล่านี้


3

externแจ้งคำหลักคอมไพเลอร์ที่ว่าฟังก์ชั่นหรือตัวแปรที่มีการเชื่อมโยงภายนอก - ในคำอื่น ๆ ว่ามันมองเห็นได้จากไฟล์อื่น ๆ มากกว่าหนึ่งในการที่จะมีการกำหนด ในแง่นี้มันมีความหมายตรงกันข้ามกับstaticคำหลัก มันค่อนข้างแปลกที่จะใส่externในช่วงเวลาของการกำหนดเนื่องจากไม่มีไฟล์อื่น ๆ ที่จะมีความสามารถในการมองเห็นของความหมาย (หรือมันจะส่งผลในหลายคำนิยาม) โดยปกติคุณใส่externคำประกาศในบางจุดด้วยการมองเห็นภายนอก (เช่นไฟล์ส่วนหัว) และใส่คำจำกัดความที่อื่น


2

ประกาศฟังก์ชั่นภายนอกหมายความว่าคำจำกัดความของมันจะได้รับการแก้ไขในเวลาของการเชื่อมโยงไม่ได้ในระหว่างการรวบรวม

ซึ่งแตกต่างจากฟังก์ชั่นทั่วไปซึ่งไม่ได้ประกาศภายนอกมันสามารถกำหนดได้ในไฟล์ต้นฉบับใด ๆ (แต่ไม่ได้อยู่ในไฟล์ต้นฉบับหลายไฟล์มิฉะนั้นคุณจะได้รับข้อผิดพลาด linker ที่บอกว่าคุณได้กำหนดหลายฟังก์ชั่น) รวมถึง ซึ่งมันถูกประกาศภายนอกดังนั้นในกรณีที่ linker แก้ไขคำนิยามฟังก์ชั่นในไฟล์เดียวกัน

ฉันไม่คิดว่าการทำเช่นนี้จะมีประโยชน์มาก แต่การทำการทดลองแบบนี้จะช่วยให้เข้าใจได้ดีขึ้นว่าคอมไพเลอร์และตัวเชื่อมโยงของภาษาทำงานอย่างไร


2
IOW ภายนอกคือซ้ำซ้อนและไม่ทำอะไรเลย มันจะชัดเจนกว่านี้ถ้าคุณทำแบบนั้น
Elazar Leibovich

@ElazarLeibovich ฉันเพิ่งพบกรณีที่คล้ายกันใน codebase ของเราและมีข้อสรุปเดียวกัน คำตอบทั้งหมดเหล่านี้ที่นี่สามารถสรุปได้ในสายการบินเดียวของคุณ มันไม่มีผลในทางปฏิบัติ แต่อาจดีสำหรับการอ่าน ยินดีที่ได้พบคุณออนไลน์และไม่เพียง แต่ใน meetups :)
ลอาวีฟ

1

เหตุผลที่มันไม่มีผลกระทบเป็นเพราะในเวลาลิงค์ลิงเกอร์พยายามที่จะแก้ไขคำนิยามภายนอก (ในกรณีของคุณextern int f()) ไม่สำคัญว่าจะพบไฟล์เดียวกันหรือไฟล์อื่นตราบใดที่พบ

หวังว่านี่จะตอบคำถามของคุณ


1
ถ้าอย่างนั้นทำไมต้องเพิ่มexternฟังก์ชั่นใด ๆ เลย?
Elazar Leibovich

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