vocus logo

方格子 vocus

[C]static修飾

更新 發佈閱讀 8 分鐘


拜託,注意一下變數的作用域,都出來工作了,別像國中生寫程式一樣


1.修飾局部變數


先看如果沒有使用static修飾時的情況

給定一程式

#include <stdio.h>
#include <stdlib.h>

void func(void)
{
int x = 0 ;

x = x + 1 ;

printf("%p -> %d \\n", &x , x );
}
int main()
{
func();
func();
func();
exit(0);
}

執行結果:

0x7ffc766476c4 -> 1
0x7ffc766476c4 -> 1
0x7ffc766476c4 -> 1

可以看到重複執行func()輸出結果都是1

因為每次的func( )中的int x = 0 皆會重複將x定義為0

之後再+1

另外雖然印出來的地址相同且變數相同

x變數實際上每次都會在函式結束時銷毀,下次執行時再被定義

所以實際上三個是不同的變數

若用古老一點的gcc版本,每次印出來就是三組不同的記憶體位址了

若將func()中的i使用static修飾:

void func(void)
{
static int x = 0 ;

x = x + 1 ;

printf("%p -> %d \\n", &x , x );
}

執行結果:

0x55ed3fc40014 -> 1
0x55ed3fc40014 -> 2
0x55ed3fc40014 -> 3

可以發現static int x = 0 ; 僅僅被定義了一次

後續重複執行func時則會根據x的初始值進行累加

此時無論你使用何種gcc的版本,記憶體位址印出來的絕對會是同一塊地址

這裡可以簡單理解為:static修飾的變量具繼承性,且在文件中只被定義一次



2.修飾全局變數


在一些稍具規模的專案中最常使用的情況

給定三個檔案文件,主文件main.c 以及定義函式的func.c , func.h

func.c :

#include <stdio.h>
#include <stdlib.h>

#include "func.h"

static int i = 100;

void func (void)
{

printf("[%s] : i = %d\\n" , __FUNCTION__ , i);

exit(0);
}
/* 實現func() 作用為印出i值 */

func.h :

#ifndef FUNC_H___
#define FUNC_H___

void func(void);/* 定義func() */

#endif

main.c :

#include <stdio.h>
#include <stdlib.h>

#include "func.h"

static int i = 10 ;

int main()
{
printf("[%s] : i = %d\\n" , __FUNCTION__ , i);/* 印出i值 */

func();/* 呼叫func() */

exit(0);
}

其中__FUNCTION__ 為gcc預定義的巨集,可以輸出當前函式名稱

輸出結果:

[main] : i = 10
[func] : i = 100

可以看到透過static,使兩個文件的變數i分別獨立,生命週期僅涵蓋當前文件

main函數中的printf印出的當前文件的i=10

而之後使用的func()則是使用func.c中定義的i=100




3.全局變數-沒有static的話?


接著剛剛的程式碼

假設將func.c以及main.c中的i的前方static拿掉

在進行編譯時,會顯示下列錯誤訊息:

/usr/bin/ld: /tmp/ccjtT5QF.o:(.data+0x0): multiple definition of `i'; 
/tmp/cc2KBh7e.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

看到了訊息中的multiple definition of `i',表示我們對i的定義重複且發生衝突了

使用static修飾即是防止對象向外擴展,而造成這種編譯錯誤

因此在大型專案的整合上,變數的作用域要小心且嚴謹的檢查


「欸欸欸我跑自己的程式就編譯成功,怎麼跟你們的合一起就不行?你們寫錯了吧?」 by 三周後被資遣雷包工程師-(真人真事🙃)


4.用static修飾函數與簡單封裝


static 亦可以用於修飾函數

不更改main.c

func.c以及func.h中將func()static修飾-

func.c :

#include <stdio.h>
#include <stdlib.h>

#include "func.h"

static int i = 100;

static void func (void)
{

printf("[%s] : i = %d\\n" , __FUNCTION__ , i);

exit(0);
}
/* 實現func() 作用為印出i值 */

func.h :

#ifndef FUNC_H___
#define FUNC_H___

static void func(void);/* 定義func() */

#endif

此時func()已經被staic修飾而無法被main.c使用

因此編譯時gcc會跳出以下訊息:

func.h:4:13: warning: ‘func’ used but never defined
4 | static void func(void);
| ^~~~
/usr/bin/ld: /tmp/ccqYGJSq.o:(.data+0x0): multiple definition of `i'; /tmp/cchlq9eW.o:(.data+0x0): first defined here
/usr/bin/ld: /tmp/cchlq9eW.o: in function `main':
main.c:(.text+0x2f): undefined reference to `func'
collect2: error: ld returned 1 exit status

其中顯示了main.c無法引用到func()

func()static修飾後僅在func.cfunc.h 定義

因此static 的修飾防止了func()的定義外擴

如何使main.c使用func()的功能呢?

由於此時我們使用static 修飾func()來防止外擴

因此我們另外創建一個call_func()來提供main.c使用:

func.c :

#include <stdio.h>
#include <stdlib.h>

#include "func.h"

static int i = 100;

static void func (void)
{

printf("[%s] : i = %d\\n" , __FUNCTION__ , i);

exit(0);
}
/* 實現func() 作用為印出i值 */

void call_func(void)
{
func();
}
/* 實現call_func() 作用為執行func() */

func.h :

#ifndef FUNC_H___
#define FUNC_H___

static void func(void);/* 定義func() */
void call_func(void);/* 定義call_func() */
#endif

main.c :

#include <stdio.h>
#include <stdlib.h>

#include "func.h"

static int i = 10 ;

int main()
{
printf("[%s] : i = %d\\n" , __FUNCTION__ , i);/* 印出i值 */

call_func();/* 執行call_func() */

exit(0);
}

執行結果:

[main] : i = 10
[func] : i = 100

如此可以保證函式定義不外擴的同時

使外部的main.c使用func()的功能

透過修飾可以在C語言中對函式進行簡單的封裝




5.後記


在此只整理c語言的用法

在c++的環境static更可以用來修飾class內的成員

而善用static至少有兩個明顯的好處:

  • 避免全域變數在其他文件中可見,減少衝突跟不必要的依賴
  • 減少了不必要的初始化


留言
avatar-img
typedef struct 隨筆記{
1會員
6內容數
如果是失業的時候寫的 -那就是隨筆 如果是工作的時候寫的 -那就是筆記 但我狀態切換的很頻繁 那目前這坨東西就定義成「隨筆記」好了
2024/09/22
探討指向函數的指標 這種用法在稍具規模的專案中常看到 但自己實作時很少用到 但偏偏有時面試會問Orz
Thumbnail
2024/09/22
探討指向函數的指標 這種用法在稍具規模的專案中常看到 但自己實作時很少用到 但偏偏有時面試會問Orz
Thumbnail
2024/09/12
簡單紀錄二維陣列的配置方式及取址差異
Thumbnail
2024/09/12
簡單紀錄二維陣列的配置方式及取址差異
Thumbnail
2024/09/10
找工作面試常常問這個 就簡單紀錄一下
Thumbnail
2024/09/10
找工作面試常常問這個 就簡單紀錄一下
Thumbnail
看更多
你可能也想看
Thumbnail
債券投資,不只是高資產族群的遊戲 在傳統的投資觀念中,海外債券(Overseas Bonds)常被貼上「高資產族群專屬」的標籤。過去動輒 1 萬甚至 10 萬美元的最低申購門檻,讓許多想尋求穩定配息的小資族望而卻步。 然而,在股市波動劇烈的環境下,尋求穩定的美元現金流與被動收入成為許多投資人
Thumbnail
債券投資,不只是高資產族群的遊戲 在傳統的投資觀念中,海外債券(Overseas Bonds)常被貼上「高資產族群專屬」的標籤。過去動輒 1 萬甚至 10 萬美元的最低申購門檻,讓許多想尋求穩定配息的小資族望而卻步。 然而,在股市波動劇烈的環境下,尋求穩定的美元現金流與被動收入成為許多投資人
Thumbnail
透過川普的近期債券交易揭露,探討債券作為資產配置中「穩定磐石」的重要性。文章分析降息對債券的潛在影響,以及股神巴菲特的操作策略。並介紹玉山證券「小額債」平臺,如何讓小資族也能低門檻參與海外債券市場,實現「低門檻、低波動、固定收益」的務實投資方式。
Thumbnail
透過川普的近期債券交易揭露,探討債券作為資產配置中「穩定磐石」的重要性。文章分析降息對債券的潛在影響,以及股神巴菲特的操作策略。並介紹玉山證券「小額債」平臺,如何讓小資族也能低門檻參與海外債券市場,實現「低門檻、低波動、固定收益」的務實投資方式。
Thumbnail
解析「債券」如何成為資產配置中的穩定錨,提供低風險高回報的投資選項。 藉由玉山證券的低門檻債券服務,投資者可輕鬆入手,平衡風險並穩定財務。
Thumbnail
解析「債券」如何成為資產配置中的穩定錨,提供低風險高回報的投資選項。 藉由玉山證券的低門檻債券服務,投資者可輕鬆入手,平衡風險並穩定財務。
Thumbnail
相較於波動較大的股票,債券能提供固定現金流,而玉山證券推出的小額債,更以1000 美元的低門檻,讓學生與新手也能參與全球優質企業債投資。玉山E-Trader平台即時報價、條件式篩選與清楚的交易流程等特色,大幅降低投資難度,對於希望分散風險、建立穩定現金流的人來說,玉山小額債是一個值得嘗試的理財起點。
Thumbnail
相較於波動較大的股票,債券能提供固定現金流,而玉山證券推出的小額債,更以1000 美元的低門檻,讓學生與新手也能參與全球優質企業債投資。玉山E-Trader平台即時報價、條件式篩選與清楚的交易流程等特色,大幅降低投資難度,對於希望分散風險、建立穩定現金流的人來說,玉山小額債是一個值得嘗試的理財起點。
Thumbnail
就簡單介紹一下static
Thumbnail
就簡單介紹一下static
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元二次方程式
Thumbnail
中學數學基礎練習—一元二次方程式
Thumbnail
高中數學主題練習—對數方程式
Thumbnail
高中數學主題練習—對數方程式
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—標準化計算
Thumbnail
高中數學主題練習—標準化計算
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News