簡體   English   中英

是否有正當的理由將變量聲明為C程序的main()函數內部的靜態變量?

[英]Are there valid reasons to declare variables static inside the main() function of a C program?

我知道C中static關鍵字的各種含義,但我的問題更具體:是否有充分的理由將嵌入式C語言程序的main()函數中的某些變量聲明為static?

由於我們正在談論的是在main()的花括號內聲明的變量,因此作用域已經在main()本地了。 關於持久性 ,主要功能在調用堆棧的頂部,並且只要程序正在運行就不能退出。 因此,從表面上看,似乎沒有正當理由在main()內部使用static進行聲明。

但是,我注意到使用靜態聲明的作用是避免將變量和數組放置在堆棧中。 在堆棧大小有限的情況下,這可能很重要。

另一種可能但不常見的情況是main()遞歸調用自身,在這種情況下,您可能需要一些變量才能在遞歸的不同級別持久化。

在main()函數體內使用變量的靜態聲明還有其他可能的正當理由嗎?

..在main()函數體內使用變量的靜態聲明的正當理由?

  1. 初始化

     int main(void) { static int a; // initialized to 0 int b; // uninitialized 
  2. 在C中,將調用main()調用。 ref因此,適用於static變量的常見問題。

     int main(void) { static int c; // Only one instance int d; // One instance per call ... main(); 
  3. 內存位置。 各種編譯器將組織main()的變量。 它不是由C指定的,因此是編譯器相關的問題。

變量除類型外還具有三個屬性:

  • 范圍(可見性)
  • 一生
  • 位置

最好根據代碼的語義來選擇這些屬性。 盡管main()static變量似乎與非static變量具有相同的作用域和生存期,因此僅在位置上有所不同,但它的語義並不相同。 如果-可能正在開發中-如果您決定將main()的代碼移動或重新組織到子例程中,則使用static將導致此類代碼的行為有所不同,並可能會導致錯誤的行為。

因此,我的建議是,與在其他函數中一樣,對main()static使用相同的確定,並且不要將它視為在語義上與非static相同的特殊情況 這種方法不會導致代碼可重用或可維護。

在任何情況下,您都必須將內存划分為堆棧,堆和靜態空間,如果您有一個堆棧變量具有整個過程的生命周期,則您會搶走Peter來支付Paul的費用,方法是使用更多的堆棧來交換較少的靜態空間,因此除了內存不足是構建時而不是運行時的問題外,這並不是一個真正的爭論,但是,如果這是一個嚴重的問題,那么您當然可以考慮使所有大多數變量保持靜態(當然,重入,從而實現遞歸和多線程)。 如果堆棧空間確實是一個問題,那么在任何情況下,遞歸都是一個壞主意-在大多數情況下,這已經是一個壞主意,當然, main()遞歸也是如此。

我看到在main內部聲明static變量的實際原因(大多數台式機或筆記本電腦操作系統上的大多數編譯器): main內部的局部變量占用了調用堆棧上的空間。 static變量會占用調用堆棧外部的空間(通常在.bss.data段中,至少在ELF可執行文件中)

如果該變量占用大量空間(想像一百萬個整數的數組),那將有很大的不同。

在當前(台式機,筆記本電腦,平板電腦)系統上,堆棧空間通常受到限制(限制為一個或幾個兆字節)。

在某些嵌入式處理器上,堆棧限制為小於一千字節。

我想你已經說過了。 它使它們成為靜態的,這對於main()來說不是很有趣,其次使它們成為我所謂的局部全局變量,它本質上將它們與全局變量一起放在.data中(而不是放在堆棧上),但限制了它們的訪問范圍,例如本地人。 因此,對於main(),我想原因是要節省一些堆棧。 如果您閱讀有關堆棧溢出的內容,盡管看起來有些編譯器仍然在main()上放置了一個較大的堆棧框架,那么大多數情況下這沒有任何意義,那么您真的節省了空間嗎? 同樣,如果它們是從main到堆棧的,除非您遞歸調用main,它們不會占用.data與堆棧中的空間。 如果將它們優化為寄存器,那么您仍然會在.data上燒掉您本來不會燒掉堆棧空間的位置,因此可能會花費一些空間。

嘗試間接訪問以自動存儲持續時間定義的對象(即局部變量)的結果是在實現中定義的(請參見:6.2.4對象的存儲持續時間,p5),該對象與該對象處於不同的線程。

如果希望代碼具有可移植性,則應僅與使用靜態,線程或分配的存儲持續時間定義的線程對象共享。

在這個例子中,main中定義的自動object應該已經用static定義了:

int main( void )
{
    CreateThreads();
    type object = { 0 };   //missing static storage-class specifier
    ShareWithThreads( &object );
}

如前所述,主要原因是可以節省堆棧空間,還可以使您更好地了解程序的實際堆棧大小。

聲明此類變量為static另一個原因是程序安全。 這里需要考慮各種嵌入式系統設計規則:

堆棧應該始終是內存映射的,以便它向無效的內存而不是.bss.data增長。 這意味着在堆棧溢出的情況下,程序有機會引發異常,而不是遍歷Toyota的所有靜態變量。

出於類似的原因,您應該避免在堆棧上聲明大塊數據,因為這會使代碼更容易出現堆棧溢出。 這尤其適用於受存儲器限制的小型微控制器系統。


另一個可能但不太常見的情況是main()遞歸調用自身的情況

那是胡說八道,沒有理智的人會寫這樣的代碼。 在嵌入式系統中使用遞歸是非常可疑的做法,通常會被編碼標准所禁止。 事實是,在很少的情況下, 無論是否嵌入,在任何程序中可以使用遞歸。

原因之一是可執行文件中靜態變量的偏移量是在鏈接時確定的,因此可以將其放在同一位置。

因此,主變量靜態變量的一個有用目的是將程序版本數據包括為可讀字符串或二進制可讀數據,這些數據以后可用於分析/組織可執行文件,而無需原始源代碼或任何特定於程序的實用程序,而不是十六進制轉儲程序。 我們在部署程序時就使用了這種技巧,因為這些程序無法依賴目標系統具有任何開發工具。

例如,

int main(void)
{
    // tag to search for revision number
    static char versionID[3] = {'V','E','R'};
    // statically embedded program version is 2.1
    static unsigned char major_revision = 2;
    static unsigned char minor_revision = 1;

    printf("\nHello World");
    return 0;
}

現在無需運行程序即可確定程序的版本:

$ od -c -x hello-world | grep“ VER”

0010040 001 002 VERGCC:(bun

實際上,有一個很大的原因!

如果main() (或任何函數...)中將變量聲明為static ,則說明此局部變量也是static

這意味着,如果此函數調用自身……(雖然肯定可以,但main()可能不會這樣做)……每個遞歸實例將看到相同的值,因為此變量是“ t被分配在堆棧上。 (因為,嗯,“這是static !”)

“堆棧大小” 不是使用static.的有效理由static. (實際上,它與它無關。)如果您擔心在“堆棧上”有空間來存儲某些內容,則正確的做法是使用指針 “將其存儲在堆中”變量。 (與任何變量一樣,它可以是靜態的,也可以不是。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM