ฟังก์ชั่นคงที่ใน C


172

อะไรคือสิ่งที่ทำให้ฟังก์ชั่นคงที่ใน C?


7
@nightcracker: ไม่มีสิ่งต่าง ๆ เช่น "วิธีการ" ใน C ++ ฉันคิดว่าคุณสับสนกับ Objective-C
โบเพอร์สัน

1
ไม่ฉันสับสนกับ Python ฟังก์ชั่นภายในชั้นเรียนเรียกว่าวิธีการในหลาม
orlp

6
เป็นไปได้ที่ซ้ำกันของฟังก์ชัน "คงที่" คืออะไร? (ใน C)
atoMerz

คำตอบ:


212

ทำให้ฟังก์ชั่นstaticหนังได้จากหน่วยแปลอื่น ๆ ซึ่งจะช่วยให้การห่อหุ้ม

helper_file.c

int f1(int);        /* prototype */
static int f2(int); /* prototype */

int f1(int foo) {
    return f2(foo); /* ok, f2 is in the same translation unit */
                    /* (basically same .c file) as f1         */
}

int f2(int foo) {
    return 42 + foo;
}

main.c :

int f1(int); /* prototype */
int f2(int); /* prototype */

int main(void) {
    f1(10); /* ok, f1 is visible to the linker */
    f2(12); /* nope, f2 is not visible to the linker */
    return 0;
}

8
หน่วยการแปลเป็นคำศัพท์ที่ถูกต้องที่จะใช้ที่นี่หรือไม่ วัตถุไฟล์จะไม่แม่นยำมากขึ้น? จากสิ่งที่ฉันเข้าใจฟังก์ชั่นคงที่ถูกซ่อนจาก linker และ linker ไม่ทำงานในหน่วยการแปล
Steven Eckhoff

2
ฉันควรจะพูดด้วยว่าฉันชอบคิดว่ามันถูกซ่อนไว้จากลิงเกอร์ ดูเหมือนชัดเจนมากขึ้น
Steven Eckhoff

1
ดังนั้นฟังก์ชั่นภายใน (ที่เราแน่ใจว่าจะไม่เรียกมันนอกไฟล์ c ของมัน) เราควรจะใส่มันเป็นฟังก์ชั่นคงที่ใช่ไหม? ดังนั้นเราจึงมั่นใจได้ว่ามันจะไม่สามารถโทรไปที่อื่นได้ ขอบคุณ :)
hqt

1
คุณจะรวบรวมสิ่งนี้ได้อย่างไร คุณใช้#include <helper_file.c>ไหม ฉันคิดว่านั่นจะทำให้เป็นหน่วยการแปลเดียวในตอนนั้น ...
Atcold

2
@Atcold: วิธีการที่ผมเขียนรหัสคุณก็รวมถึงไฟล์ที่มา 2 gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.cในบรรทัดคำสั่งเช่นนี้ ต้นแบบสำหรับฟังก์ชั่นมีอยู่ในไฟล์ต้นฉบับทั้งสอง (ไม่จำเป็นต้องใช้ไฟล์ส่วนหัว) ลิงเกอร์จะแก้ปัญหาฟังก์ชั่น
pmg

80

pmgเป็นจุดที่เกี่ยวกับการห่อหุ้ม; นอกเหนือจากการซ่อนฟังก์ชันจากหน่วยการแปลอื่น (หรือมากกว่านั้นเพราะการทำให้ฟังก์ชั่นstaticยังสามารถมอบผลประโยชน์ด้านประสิทธิภาพเมื่อมีการคอมไพเลอร์ปรับให้เหมาะสม

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

ซึ่งหมายความว่าสามารถใช้ ABI ที่ไม่ได้มาตรฐานแบบอินไลน์ได้ทั้งหมดหรือดำเนินการเพิ่มประสิทธิภาพอื่น ๆ ที่อาจไม่สามารถใช้ได้กับฟังก์ชันที่มีการเชื่อมโยงภายนอก


9
... เว้นแต่จะใช้ที่อยู่ของฟังก์ชั่น
caf

1
@caf คุณหมายถึงที่อยู่ของฟังก์ชั่นอะไร? สำหรับฉันความคิดของฟังก์ชั่น / ตัวแปรที่มีที่อยู่หรือถูกที่อยู่ที่ได้รับมอบหมายในเวลารวบรวมเป็นความสับสนเล็กน้อย คุณช่วยอธิบายรายละเอียดได้ไหม?
SayeedHussain

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

5
@crypticcoder: ฉันหมายถึงการแสดงออกประเมินตัวชี้ไปยังฟังก์ชั่นและทำอะไรกับมันนอกเหนือจากการเรียกใช้ฟังก์ชันได้ทันที หากตัวชี้ไปยังstaticฟังก์ชันหนีหน่วยการแปลปัจจุบันฟังก์ชันนั้นอาจถูกเรียกจากหน่วยการแปลอื่นโดยตรง
caf

@caf หากมีการใช้ที่อยู่ของฟังก์ชั่นคอมไพเลอร์จะตรวจสอบและปิดการทำงานของฟังก์ชั่นคงที่ที่กล่าวถึงในคำตอบนี้ (เช่นการใช้ ABI ที่ไม่ได้มาตรฐาน) หรือไม่ ฉันคิดว่ามันจะต้องมี
sevko

28

staticคำหลักใน C จะใช้ในการรวบรวมไฟล์ (.c เมื่อเทียบกับ .h) เพื่อให้การทำงานมีอยู่เฉพาะในแฟ้มที่

โดยปกติเมื่อคุณสร้างฟังก์ชันคอมไพเลอร์จะสร้าง cruft ที่ linker สามารถใช้เชื่อมโยงการเรียกฟังก์ชันไปยังฟังก์ชันนั้นได้ หากคุณใช้คำสำคัญคงที่ฟังก์ชั่นอื่น ๆ ในไฟล์เดียวกันสามารถเรียกใช้ฟังก์ชั่นนี้ (เพราะมันสามารถทำได้โดยไม่ต้องหันไปหาลิงเกอร์) ในขณะที่ลิงเกอร์ไม่มีข้อมูลที่ให้ไฟล์อื่นเข้าถึงฟังก์ชั่น


1
3Doub: การใช้คำว่า "cruft" นั้นแม่นยำกว่าที่คุณให้เครดิต ในบริบทของคำถาม "cruft" เป็นคำที่เหมาะสมที่จะใช้ที่นี่
Erik Aronesty

@ 3Doubloons ฉันเห็นด้วยว่ามันง่าย แต่ฉันคิดว่ามันทำให้เข้าใจได้ง่ายขึ้นสำหรับผู้เริ่มต้น
Ingo Bürk

11

มองที่โพสต์ด้านบนฉันต้องการจะชี้รายละเอียด

สมมติว่าไฟล์หลักของเรา ("main.c") มีลักษณะดังนี้:

#include "header.h"

int main(void) {
    FunctionInHeader();
}

พิจารณาสามกรณี:

  • กรณีที่ 1: ไฟล์ส่วนหัวของเรา ("header.h") มีลักษณะดังนี้:

    #include <stdio.h>
    
    static void FunctionInHeader();
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    จากนั้นคำสั่งต่อไปนี้บน linux:

    gcc main.c header.h -o main

    จะประสบความสำเร็จ ! ติดตามว่าถ้าใครวิ่ง

    ./main

    ผลลัพธ์จะเป็น

    ฟังก์ชั่นการโทรภายในส่วนหัว

    ฟังก์ชั่นคงที่นั้นควรพิมพ์อะไร

  • กรณีที่ 2: ไฟล์ส่วนหัวของเรา ("header.h") มีลักษณะดังนี้:

    static void FunctionInHeader();     

    และเรายังมีไฟล์ "header.c" อีกหนึ่งไฟล์ซึ่งมีลักษณะดังนี้:

    #include <stdio.h>
    
    #include "header.h"
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }

    จากนั้นคำสั่งดังต่อไปนี้

    gcc main.c header.h header.c -o main

    จะให้ข้อผิดพลาด

  • กรณีที่ 3:

    คล้ายกับกรณีที่ 2 ยกเว้นว่าตอนนี้ไฟล์ส่วนหัวของเรา ("header.h") คือ:

    void FunctionInHeader(); // keyword static removed

    จากนั้นคำสั่งเดียวกับในกรณีที่ 2 จะประสบความสำเร็จและการดำเนินการต่อ /. หลักจะให้ผลลัพธ์ที่คาดหวัง

ดังนั้นจากการทดสอบเหล่านี้ (ดำเนินการบนเครื่อง Acer x86, Ubuntu OS) ฉันตั้งสมมติฐานว่า

คำหลักคงที่ป้องกันฟังก์ชั่นที่จะเรียกในไฟล์ * .c อื่นกว่าที่มันถูกกำหนดไว้

ถูกต้องฉันถ้าฉันผิด


5

โปรแกรมเมอร์ C ใช้แอตทริบิวต์แบบคงที่เพื่อซ่อนการประกาศตัวแปรและฟังก์ชั่นภายในโมดูลมากเท่าที่คุณจะใช้การประกาศสาธารณะและส่วนตัวใน Java และ C ++ ไฟล์ต้นฉบับ C มีบทบาทเป็นโมดูล ตัวแปรหรือฟังก์ชันโกลบอลใด ๆ ที่ประกาศพร้อมกับสแตติกแอ็ตทริบิวต์เป็นไพรเวตกับโมดูลนั้น ในทำนองเดียวกันตัวแปรทั่วโลกหรือฟังก์ชั่นที่ประกาศโดยไม่มีแอตทริบิวต์คงที่เป็นสาธารณะและสามารถเข้าถึงได้โดยโมดูลอื่น ๆ มันเป็นวิธีการเขียนโปรแกรมที่ดีในการปกป้องตัวแปรและฟังก์ชั่นของคุณด้วยคุณลักษณะคงที่ทุกที่ที่เป็นไปได้


4

คำตอบของ pmg น่าเชื่อถือมาก หากคุณต้องการทราบว่าการประกาศคงที่ทำงานในระดับวัตถุข้อมูลด้านล่างนี้อาจเป็นที่น่าสนใจสำหรับคุณ ฉันใช้โปรแกรมเดิมที่เขียนโดย pmg และคอมไพเลอร์มันเป็นไฟล์. so (shared object)

เนื้อหาต่อไปนี้หลังจากดัมพ์ไฟล์. so ไปเป็นสิ่งที่มนุษย์สามารถอ่านได้

0000000000000675 f1 : ที่อยู่ของฟังก์ชัน f1

000000000000068c f2 : ที่อยู่ของฟังก์ชัน f2 (staticc)

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

ลิงเกอร์ใช้สิ่งที่เรียกว่า PLT (ตารางการเชื่อมโยงขั้นตอน) และ GOT (ตารางออฟเซ็ตทั่วโลก) เพื่อทำความเข้าใจสัญลักษณ์ที่พวกเขาสามารถเข้าถึงลิงก์ได้

สำหรับตอนนี้คิดว่า GOT และ PLT ผูกที่อยู่ทั้งหมดอย่างน่าอัศจรรย์และส่วนแบบไดนามิกเก็บข้อมูลของฟังก์ชั่นเหล่านี้ทั้งหมดที่ linker สามารถมองเห็นได้

หลังจากทิ้งส่วนแบบไดนามิกของไฟล์. so เราจะได้รับรายการจำนวนมาก แต่สนใจฟังก์ชั่นf1และf2เท่านั้น

ส่วนแบบไดนามิกถือรายการสำหรับฟังก์ชั่นf1ตามที่อยู่0000000000000675และไม่ใช่สำหรับf2 !

Num: ชื่อขนาดค่าชนิดผูกเข้ากับชื่อ Ndx

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

และนั่นมัน! จากนี้ชัดเจนว่าตัวเชื่อมโยงจะไม่ประสบความสำเร็จในการค้นหาฟังก์ชันf2เนื่องจากไม่อยู่ในส่วนแบบไดนามิกของไฟล์. so


0

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

            /* file ab.c */ 
static void function1(void) 
{ 
  puts("function1 called"); 
} 
And store the following code in another file ab1.c

/* file ab1.c  */ 
int main(void) 
{ 
 function1();  
  getchar(); 
  return 0;   
} 
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */

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