"คงที่" หมายถึงอะไรใน C


1135

ฉันเห็นคำที่staticใช้ในที่ต่างๆในรหัส C มันเป็นเหมือนฟังก์ชั่นแบบคงที่ / คลาสใน C # (ที่มีการใช้งานร่วมกันทั่ววัตถุ)?


6
ที่เกี่ยวข้อง: ส แตติก (คำหลัก) @ Wikipedia
Palec

15
อะไรคือเหตุผลที่ต้องลบ "ในโปรแกรม C" ออกจากท้ายชื่อ @Lundin? มันซ้ำซ้อนเล็กน้อยในที่ที่มีแท็กcแต่มันทำให้ฉันเห็นการจัดหมวดหมู่ได้เร็วขึ้นโดยไม่ตรวจสอบแท็ก ความซ้ำซ้อนนี้สะดวกสบายมากเมื่อฉันไปถึงคำถามจากทิศทางที่อาจมีคำถามเกี่ยวกับภาษาอื่นเช่นกันเช่นการค้นหาแบบสแตติกหรือ Google
Palec

5
@Palec มีนโยบาย SO ที่รายการที่มีอยู่ในรายการแท็กซ้ำซ้อนในชื่อ เว็บไซต์จะผนวก C เข้ากับเว็บไซต์จริงโดยอัตโนมัติ Google สำหรับ "C นิ่ง" ให้คำตอบนี้เป็นที่นิยมที่สุด เหตุผลที่เปลี่ยนไปนี้เป็นเพราะคำถามนี้เป็นส่วนหนึ่งของคำถามที่พบบ่อยเกี่ยวกับภาษา SO Cและการโพสต์ทั้งหมดที่เพิ่มเข้ามาได้รับการขัดเล็กน้อย
Lundin

1
@Lundin ฉันต้องการเก็บ "C" ไว้ในชื่อเพราะต่อท้ายเพียงหนึ่งแท็กกับชื่อ (ที่พบมากที่สุด?) เกิดอะไรขึ้นถ้าบางวัน "ไวยากรณ์" ถึงคำถามมากกว่า C (เนื่องจากเป็นสิ่งที่ข้ามภาษา)? ฉันอยากจะใช้พฤติกรรมที่ชัดเจน :-) แก้ไข: อ่า แต่มีคำถามเมตาพูดเป็นอย่างอื่น: meta.stackexchange.com/questions/19190/…
Ciro Santilli 冠状病毒审查审查六四法轮功

คำตอบ:


1519
  1. ตัวแปรคงที่ภายในฟังก์ชั่นจะเก็บค่าไว้ระหว่างการเรียกใช้
  2. ตัวแปรโกลบอลแบบคงที่หรือฟังก์ชั่น "เห็น" เฉพาะในไฟล์มันประกาศมา

(1) เป็นหัวข้อต่างประเทศมากขึ้นถ้าคุณเป็นมือใหม่ดังนั้นนี่คือตัวอย่าง:

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

ภาพพิมพ์นี้:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

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

(2) มีการใช้งานอย่างกว้างขวางว่าเป็นคุณสมบัติ "การควบคุมการเข้าถึง" หากคุณมีไฟล์. c ที่ใช้ฟังก์ชั่นการใช้งานบางอย่างมันมักจะแสดงฟังก์ชั่น "สาธารณะ" เพียงเล็กน้อยให้กับผู้ใช้ ควรทำฟังก์ชั่นที่เหลือstaticเพื่อให้ผู้ใช้ไม่สามารถเข้าถึงได้ นี่คือการห่อหุ้มการปฏิบัติที่ดี

การอ้างถึงWikipedia :

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

และเพื่อตอบคำถามที่สองของคุณมันไม่เหมือนกับใน C #

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


179
สันติภาพ, OP ไม่รู้เกี่ยวกับสแตติกดังนั้นคุณแนะนำให้เขาพุ่งเข้าไปในความแตกต่างระหว่างหน่วยรวบรวมและไฟล์? :-)
Eli Bendersky

138
หน่วยการรวบรวมเป็นไฟล์เดียวที่คอมไพเลอร์เห็น ไฟล์. c ของคุณอาจรวมถึงไฟล์. c อื่น ๆ แต่หลังจากที่ผู้ประมวลผลล่วงหน้าคัดแยกการรวมในที่สุดคอมไพเลอร์ก็เห็นเพียงแค่ "หน่วยการรวบรวม" เดียว
Eli Bendersky

81
@robUK: คอมไพเลอร์ไม่ทราบเกี่ยวกับไฟล์. h - สิ่งเหล่านี้รวมเข้ากับไฟล์. c ในโปรเซสเซอร์ล่วงหน้า ดังนั้นใช่คุณสามารถพูดได้ว่าไฟล์. c ที่มีส่วนหัวทั้งหมดรวมอยู่ในนั้นเป็นหน่วยรวบรวมเดียว
Eli Bendersky

6
@TonyD อาจจะสับสน แต่มันเป็นวิธีการรวบรวมการทำงาน โดยทั่วไปอาจเป็น.cไฟล์ส่วนหัวหนึ่งไฟล์ แต่ปีศาจมักจะอยู่ในสิ่งที่ไม่ธรรมดา
เตอร์

7
@TonyD คอมไพเลอร์ทำการรวบรวม ตัวประมวลผลล่วงหน้าทำการประมวลผลล่วงหน้า การเรียก toolchain 'คอมไพเลอร์' จะไม่เปลี่ยนแปลงว่ามันคืออะไรหรือทำอะไร
Miles Rout

231

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

int someFunction(char arg[static 10])
{
    ...
}

ในบริบทนี้สิ่งนี้ระบุว่าอาร์กิวเมนต์ที่ส่งผ่านไปยังฟังก์ชันนี้ต้องเป็นอาร์เรย์ที่charมีองค์ประกอบอย่างน้อย 10 องค์ประกอบ สำหรับข้อมูลเพิ่มเติมโปรดดูที่คำถามของฉันที่นี่


3
ฉันไม่คิดว่า C มีข้อโต้แย้งอาเรย์ Linus Torvalds โกรธมากเกี่ยวกับคนที่ทำสิ่งนี้
suprjami

13
@jamieb: C ไม่ได้มีข้อโต้แย้งอาร์เรย์ แต่เฉพาะนี้หมายถึงไวยากรณ์ที่คาดว่าฟังก์ชั่นarg[0]ผ่านไปarg[9]จะมีค่า (ซึ่งหมายความว่าฟังก์ชั่นไม่ยอมรับตัวชี้โมฆะ) คอมไพเลอร์สามารถใช้ข้อมูลนี้เพื่อการปรับให้เหมาะสมและตัววิเคราะห์แบบสแตติกสามารถใช้ข้อมูลนี้เพื่อให้แน่ใจว่าฟังก์ชั่นไม่เคยได้รับตัวชี้โมฆะ (หรือถ้ามันสามารถบอกได้อาร์เรย์ที่มีองค์ประกอบน้อยกว่าที่ระบุ)
dreamlax

19
@Qix - นี่เป็นความหมายที่โอเวอร์โหลดใหม่staticใน C99 เป็นเวลากว่าทศวรรษครึ่งแล้วที่นักเขียนคอมไพเลอร์ทุกคนไม่ได้ใช้คุณสมบัติ C99 ทั้งหมดดังนั้น C99 จึงยังไม่เป็นที่ทราบแน่ชัด
Happy Green Kid Naps

@suprjami ฉันไม่แน่ใจ 100% ว่าคุณหมายถึงอะไรโดย"อาร์กิวเมนต์อาเรย์"แต่ถ้าคุณหมายถึงint arr[n];นั่นคือVLA (อาเรย์ที่มีความยาวผันแปร)ซึ่งถูกเพิ่มใน C99 นั่นคือสิ่งที่คุณหมายถึงอะไร
RastaJedi

170

คำตอบสั้น ๆ ... มันขึ้นอยู่กับ

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

  2. ตัวแปรโกลบอลแบบสแตติกไม่สามารถมองเห็นได้นอกไฟล์ C ที่ถูกกำหนดไว้

  3. ฟังก์ชั่นคงที่ไม่สามารถมองเห็นได้นอกไฟล์ C ที่มีการกำหนดไว้


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

40
นี่เป็นเรื่องเกี่ยวกับ C. ไม่มีเอกชน / สาธารณะใน C.
chris

19
@ user1599964 แม้ว่าจะไม่มีprivateใน C การเปรียบเทียบของคุณดี: สแตติกทำให้ทุกสิ่ง "ส่วนตัว" เป็นไฟล์ที่กำหนด และไฟล์ใน C มักจะจับคู่กับคลาสใน C ++
Ciro Santilli 法轮功病毒审查六四事件法轮功

67

ตัวอย่างขอบเขตตัวแปรหลายไฟล์

ที่นี่ฉันแสดงให้เห็นว่าสแตติกส่งผลกระทบต่อขอบเขตของนิยามฟังก์ชันในหลายไฟล์

ไฟฟ้ากระแสสลับ

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

main.c

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHub ต้นน้ำ

รวบรวมและเรียกใช้:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

เอาท์พุท:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

การตีความ

  • มีสองตัวแปรแยกกันสำหรับsiหนึ่งสำหรับแต่ละไฟล์
  • มีตัวแปรที่แชร์เดียวสำหรับ i

ตามปกติขอบเขตที่เล็กลงจะดีกว่าดังนั้นควรประกาศตัวแปรทุกครั้งstaticถ้าทำได้

ในการเขียนโปรแกรม C ไฟล์มักจะใช้เพื่อแสดง "คลาส" และstaticตัวแปรแสดงถึงสมาชิกสแตติกส่วนตัวของคลาส

มาตรฐานพูดถึงอะไร

C99 N1256 ฉบับร่าง 6.7.1 "ตัวระบุคลาสหน่วยเก็บข้อมูล" ระบุว่าstaticเป็น "ตัวระบุระดับชั้นเก็บข้อมูล"

6.2.2 / 3 "การเชื่อมโยงของตัวระบุ" กล่าวstaticถึงนัยinternal linkage:

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

และ 6.2.2 / 2 บอกว่ามันinternal linkageทำงานเหมือนในตัวอย่างของเรา:

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

โดยที่ "หน่วยการแปลเป็นไฟล์ต้นฉบับหลังจากประมวลผลล่วงหน้า

GCC ใช้สำหรับ ELF (Linux) ได้อย่างไร

ด้วยการSTB_LOCALผูกมัด

ถ้าเรารวบรวม:

int i = 0;
static int si = 0;

และแยกตารางสัญลักษณ์ด้วย:

readelf -s main.o

ผลลัพธ์ประกอบด้วย:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

ดังนั้นการผูกเป็นความแตกต่างที่สำคัญระหว่างพวกเขาเท่านั้น Valueเป็นเพียงส่วนชดเชยของพวกเขาใน.bssส่วนดังนั้นเราจึงคาดหวังว่ามันจะแตกต่างกัน

STB_LOCALเป็นเอกสารเกี่ยวกับข้อมูลจำเพาะของเอลฟ์ที่http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :

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

staticซึ่งจะทำให้มันเป็นทางเลือกที่สมบูรณ์แบบที่จะเป็นตัวแทน

ตัวแปรที่ไม่มีค่าคงที่คืออะไรSTB_GLOBALและสเป็คพูดว่า:

เมื่อตัวแก้ไขลิงก์รวมหลายอ็อบเจ็กต์ไฟล์ที่เปลี่ยนตำแหน่งได้มันไม่อนุญาตให้นิยามหลาย ๆ สัญลักษณ์ STB_GLOBAL ด้วยชื่อเดียวกัน

ซึ่งเชื่อมโยงกับข้อผิดพลาดการเชื่อมโยงในหลาย ๆ คำจำกัดความคงที่

ถ้าเราเหวี่ยงขึ้นการเพิ่มประสิทธิภาพด้วย-O3การsiสัญลักษณ์จะถูกลบออกทั้งหมดจากตารางสัญลักษณ์: มันไม่สามารถนำมาใช้จากนอกนะ ทำไมต้องเก็บตัวแปรสแตติกไว้ในตารางสัญลักษณ์เลยเมื่อไม่มีการปรับให้เหมาะสม พวกเขาสามารถใช้เพื่ออะไร อาจแก้จุดบกพร่อง

ดูสิ่งนี้ด้วย

เนมสเปซที่ไม่ระบุชื่อ C ++

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


39

มันขึ้นอยู่กับ:

int foo()
{
   static int x;
   return ++x;
}

ฟังก์ชันจะคืนค่า 1, 2, 3 และอื่น ๆ --- ตัวแปรไม่ได้อยู่ในสแต็ก

AC:

static int foo()
{
}

หมายความว่าฟังก์ชันนี้มีขอบเขตในไฟล์นี้เท่านั้น ดังนั้น ac และ bc อาจมีfoo()s ที่แตกต่างกันและ foo จะไม่ถูกเปิดเผยกับวัตถุที่ใช้ร่วมกัน ดังนั้นหากคุณกำหนด foo ใน ac คุณจะไม่สามารถเข้าถึงได้จากb.cหรือจากที่อื่น ๆ

ในไลบรารี C ส่วนใหญ่ฟังก์ชัน "ส่วนตัว" ทั้งหมดเป็นแบบสแตติกและ "สาธารณะ" ส่วนใหญ่ไม่ใช่


18
+1 สำหรับการพูดถึง x ไม่ได้อยู่ในกองซ้อนหรือกอง มันอยู่ในพื้นที่หน่วยความจำแบบคงที่
Gob00st

1
@ Gob00st พื้นที่หน่วยความจำแบบคงที่? คุณหมายถึง "กลุ่มข้อมูล" ... หรือไม่
Yousha Aleayoub

24

ผู้คนต่างบอกว่า 'คงที่' ใน C มีความหมายสองประการ ฉันเสนอวิธีการอื่นในการดูซึ่งให้ความหมายเดียว:

  • การใช้ 'คงที่' กับรายการบังคับให้รายการนั้นมีคุณสมบัติสองอย่าง: (a) ไม่สามารถมองเห็นได้นอกขอบเขตปัจจุบัน (b) มันขัดขืน

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

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

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

หมายเหตุ: ความคิดเห็นเหล่านี้ใช้เฉพาะกับ C ใน C ++ การใช้ 'คงที่' กับวิธีการเรียนเป็นการให้ความหมายที่แตกต่างกันอย่างแท้จริง ในทำนองเดียวกันสำหรับการขยายอาเรย์อาร์กิวเมนต์ C99


(a) ของคุณซ้ำซ้อนที่สุด ไม่มีตัวแปรใด ๆ ที่สามารถมองเห็นได้นอกขอบเขต นั่นเป็นเพียงนิยามของขอบเขต สิ่งที่คุณหมายถึงเรียกว่าการเชื่อมโยงในมาตรฐาน C staticให้การเชื่อมโยงภายในกับตัวระบุ
Jens

16

จาก Wikipedia:

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


16

static หมายถึงสิ่งที่แตกต่างในบริบทที่แตกต่างกัน

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

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. การใช้งานสแตติกอีกอย่างหนึ่งคือเมื่อคุณใช้ฟังก์ชั่นหรือตัวแปรโกลบอลในไฟล์. c แต่ไม่ต้องการให้สัญลักษณ์ของมันปรากฏอยู่ด้านนอกของไฟล์ที่.objสร้างขึ้น เช่น

    static void foo() { ... }

8

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

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


8

ฉันเกลียดที่จะตอบคำถามเก่า แต่ฉันไม่คิดว่ามีใครพูดถึงวิธีที่ K&R อธิบายในหัวข้อ A4.1 ของ "ภาษาการเขียนโปรแกรม C"

กล่าวโดยย่อคำว่า static ใช้กับความหมายสองประการ:

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

Peter Van Der Linden ให้ความหมายทั้งสองนี้ใน "การเขียนโปรแกรมผู้เชี่ยวชาญ C":

  • ภายในฟังก์ชั่นยังคงค่าของมันระหว่างการโทร
  • ที่ระดับฟังก์ชั่นสามารถมองเห็นได้เฉพาะในไฟล์นี้

มีระดับการจัดเก็บข้อมูลที่สามลงทะเบียน บางคนทำเคสสำหรับคลาสหน่วยเก็บข้อมูลที่สี่ซึ่งจัดสรรไว้สำหรับหน่วยเก็บที่ส่งคืนโดย malloc และเพื่อน
Jens

@Jens 'register' เป็นเพียงคำแนะนำแก่คอมไพเลอร์เท่านั้น ที่เก็บข้อมูลรีจิสเตอร์ไม่สามารถบังคับใช้จากภายในแหล่ง C ดังนั้นฉันจะไม่พิจารณาว่าเป็นคลาสหน่วยเก็บข้อมูล
GermanNerd

1
@GermanNerd ฉันกลัวว่ามาตรฐาน ISO C จะไม่เห็นด้วยกับมุมมองของคุณเพราะมันทำให้registerตัวระบุระดับการจัดเก็บ (C99 6.7.1 ตัวระบุระดับการจัดเก็บ) ชัดเจน และมันเป็นมากกว่าคำใบ้ตัวอย่างเช่นคุณไม่สามารถใช้ที่อยู่ของผู้ประกอบการ&บนวัตถุที่มีคลาสหน่วยเก็บข้อมูลregisterไม่ว่าคอมไพเลอร์จะจัดสรรการลงทะเบียนหรือไม่
Jens

@Jens ขอบคุณที่เตือนฉันเกี่ยวกับ & ฉันอาจทำ C ++ มากเกินไป ... อย่างไรก็ตามในขณะที่ 'register' เป็นตัวระบุคลาสหน่วยเก็บข้อมูลในความเป็นจริงคอมไพเลอร์จะสร้างรหัสเครื่องเดียวกันสำหรับตัวระบุอัตโนมัติ (ไร้ประโยชน์) สำหรับ 'ลงทะเบียน' 'ตัวระบุ ดังนั้นสิ่งเดียวที่เหลืออยู่คือการ จำกัด ระดับซอร์สโค้ดของการไม่สามารถรับที่อยู่ BTW การสนทนาเล็กน้อยนี้ทำให้ฉันพบข้อผิดพลาดใน Netbeans; นับตั้งแต่การอัปเดตครั้งล่าสุดของฉันมันจะมีค่าเริ่มต้นที่โซ่ g ++ ของโครงการ C ใหม่!
GermanNerd

6

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

ในขอบเขตอื่นใดมันจะประกาศวัตถุที่จะเก็บค่าไว้ระหว่างเวลาต่าง ๆ ที่มีการป้อนขอบเขตเฉพาะ ตัวอย่างเช่นถ้า int ถูก delcared ภายในขั้นตอน:

void procedure(void)
{
   static int i = 0;

   i++;
}

ค่าของ 'i' ถูกเตรียมใช้งานเป็นศูนย์ในการเรียกไปยังโพรซีเดอร์แรกและค่าจะถูกเก็บไว้ในแต่ละครั้งที่โพรซีเดอร์ถูกเรียกใช้ ถ้าพิมพ์ 'i' มันจะส่งออกลำดับ 0, 1, 2, 3, ...


5

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

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


5

หากคุณประกาศสิ่งนี้ในmytest.cไฟล์:

static int my_variable;

ดังนั้นตัวแปรนี้สามารถเห็นได้จากไฟล์นี้เท่านั้น ตัวแปรไม่สามารถส่งออกได้ทุกที่

หากคุณประกาศภายในฟังก์ชันค่าของตัวแปรจะเก็บค่าไว้ทุกครั้งที่เรียกใช้ฟังก์ชัน

ฟังก์ชั่นคงที่ไม่สามารถส่งออกจากนอกไฟล์ ดังนั้นใน*.cไฟล์คุณกำลังซ่อนฟังก์ชั่นและตัวแปรหากคุณประกาศพวกเขาแบบคงที่


4

ตัวแปรสแตติกใน C มีอายุการใช้งานของโปรแกรม

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

ตัวอย่างเช่น:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

ในโปรแกรมข้างต้นvarจะถูกเก็บไว้ในส่วนข้อมูล อายุการใช้งานคือโปรแกรม C ทั้งหมด

หลังจากการเรียกใช้ฟังก์ชัน 1 varกลายเป็น 2 หลังจากการเรียกใช้ฟังก์ชัน 2 varกลายเป็น 3

ค่าของvarไม่ถูกทำลายระหว่างการเรียกฟังก์ชัน

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

ตัวแปรสแตติกเริ่มต้นถูกเก็บไว้ในส่วนข้อมูลของโปรแกรม C ในขณะที่ตัวแปรเริ่มต้นจะถูกเก็บไว้ในเซ็กเมนต์ BSS

ข้อมูลอื่นเกี่ยวกับสแตติก: หากตัวแปรเป็นโกลบอลและสแตติกมันมีช่วงเวลาชีวิตของโปรแกรม C แต่มีขอบเขตไฟล์ มันสามารถมองเห็นได้เฉพาะในไฟล์นั้น

ลองทำสิ่งนี้:

file1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

file2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

ตอนนี้ลองเชื่อมโยงพวกเขาโดยใช้:

gcc -o output file1.o file2.o

มันจะให้ข้อผิดพลาด linker เป็น x มีขอบเขตไฟล์ของ file1.c และ linker จะไม่สามารถแก้ไขการอ้างอิงถึงตัวแปร x ที่ใช้ใน file2.c

อ้างอิง:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack

ผมเข้าใจว่าข้อมูลเป็นแบบถาวรซึ่งหมายความว่ามันจะไม่ได้หายไปหลังจากการโทรแต่ละฟังก์ชั่น แต่ทำไมไม่static int var = 1;เปลี่ยนกลับค่าเป็นหนึ่งในแต่ละครั้ง
เมส

3

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

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    ++count;
}

void main(){
    while(true){
        func();
    }
}

ผลลัพธ์:

0, 1, 2, 3, 4, 5, ...


คุณสามารถแทนที่printf("%d, ", count); count++;ด้วย `printf ("% d, ", นับ ++) (ไม่ใช่ว่าสำคัญ: P)
RastaJedi

2

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


2

มี 2 ​​กรณี:

(1) ตัวแปรโลคัลที่ประกาศstatic: จัดสรรในเซ็กเมนต์ข้อมูลแทนสแต็ก ค่าจะยังคงอยู่เมื่อคุณเรียกใช้ฟังก์ชันอีกครั้ง

(2) ตัวแปรหรือฟังก์ชั่นทั่วโลกประกาศstatic: หน่วยการรวบรวมภายนอกที่มองไม่เห็น (เช่นสัญลักษณ์ท้องถิ่นในตารางสัญลักษณ์ระหว่างการเชื่อมโยง)


1

ตัวแปรสแตติกมีคุณสมบัติในการรักษาค่าของพวกเขาแม้ว่าจะอยู่นอกขอบเขตดังนั้นตัวแปรสแตติกจะรักษาค่าก่อนหน้าในขอบเขตก่อนหน้านี้และจะไม่เริ่มต้นอีกครั้งในขอบเขตใหม่

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

#include<stdio.h> 
int fun() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

สิ่งนี้จะออก: 1 2

ในฐานะที่ 1 ยังคงอยู่ในหน่วยความจำตามที่ประกาศคงที่

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

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

สิ่งนี้จะออก: 0 [some_garbage_value]

นี่คือสิ่งสำคัญที่ฉันพบว่าไม่ได้อธิบายไว้ข้างต้นสำหรับมือใหม่!


-1

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

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

โปรแกรมด้านบนจะให้ผลลัพธ์นี้กับเรา:

First Counter Output = 1 
Second Counter Output = 1 

count = 0เพราะทันทีที่เราเรียกฟังก์ชั่นมันจะเริ่มต้น และในขณะที่เราดำเนินการcounterFunctionมันจะทำลายตัวแปรนับ


2
> โปรแกรมด้านบนจะให้ผลลัพธ์นี้กับเรา: ตัวนับผลลัพธ์ตัวแรก = 1 ตัวนับผลลัพธ์ที่สอง = 1 <ไม่เป็นความจริง ตัวแปรสแตติกรับการเริ่มต้นเพียงครั้งเดียว เอาท์พุทจะเป็น 1 จากนั้น 2 และต่อไปเรื่อย ๆ
GermanNerd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.