[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
留言分享你的想法!
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
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
就簡單介紹一下static
Thumbnail
就簡單介紹一下static
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元一次方程式
Thumbnail
中學數學基礎練習—一元二次方程式
Thumbnail
中學數學基礎練習—一元二次方程式
Thumbnail
高中數學主題練習—對數方程式
Thumbnail
高中數學主題練習—對數方程式
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
Thumbnail
高中數學主題練習—根式化簡
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News