拜託,注意一下變數的作用域,都出來工作了,別像國中生寫程式一樣
先看如果沒有使用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修飾的變量具繼承性,且在文件中只被定義一次
在一些稍具規模的專案中最常使用的情況
給定三個檔案文件,主文件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
接著剛剛的程式碼
假設將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 三周後被資遣雷包工程師-(真人真事🙃)
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.c
與func.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語言中對函式進行簡單的封裝
在此只整理c語言的用法
在c++的環境static更可以用來修飾class內的成員
而善用static至少有兩個明顯的好處: