C方法中定义静态static字段是什么意思?

最近在看 C 语言,发现在方法中居然还可以定义 static 变量,有点意思,代码如下:

int test() {

 int num1 = 10;
 static int num2 = 6;

 printf("num1=%d, num2=%d\n", num1++, num2++);
}

如上面的 static int num2 = 6, 哈哈,在 C# 中还真不允许这么写, 那这种写法和普通的 局部变量 有什么不同呢? 我们可以不断累加它,看看是啥情况。.

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

#define _CRT_SECURE_NO_WARNINGS

int test();

int main()
{
 for (int i = 0; i < 5; i++) {
  test();
 }

 getchar();
}

int test() {

 int num1 = 10;
 static int num2 = 6;

 printf("num1=%d, num2=%d\n", num1++, num2++);
}

运行结果如下:

C方法中定义静态static字段是什么意思?

 

可以看到,被 static 修饰的 num2 在每次迭代中会被记住,貌似不和 线程栈 发生关系,那到底是不是这样呢?要寻找答案,还得从 汇编代码 上挖, 在 int num1 = 10; 下一个断点查看该方法的 反汇编, 简化后的代码如下:

 

0:000> uf ConsoleApplication1!test
ConsoleApplication1!test [D:\net5\ConsoleApp4\ConsoleApplication1\ConsoleApplication1.c @ 21]:
   23 004719a5 c745f80a000000  mov     dword ptr [ebp-8],0Ah
   26 004719ac a100a04700      mov     eax,dword ptr [ConsoleApplication1!num2 (0047a000)]
   26 004719b1 898530ffffff    mov     dword ptr [ebp-0D0h],eax
   26 004719b7 8b0d00a04700    mov     ecx,dword ptr [ConsoleApplication1!num2 (0047a000)]

从输出中可以看到:num1 存到了线程栈上 ebp-8 的位置,拥有方法级作用域, 而 num2 是直接取了内存地址 0047a000 上的值,果然和线程栈不发生任何关系,接下来看看这个 address 到底存了什么,毕竟我代码还没执行到这一句。

0:000> dp 0047a000 L1
0047a000  00000006

果然初始化6就在里面,接下来看看 地址 0047a000 到底在内存中属于什么类别:

0:000> !address 0047a000

Usage:                  Image
Base Address:           0047a000
End Address:            0047b000
Region Size:            00001000 (   4.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   01000000          MEM_IMAGE
Allocation Base:        00460000
Allocation Protect:     00000080          PAGE_EXECUTE_WRITECOPY
Image Path:             ConsoleApplication1.exe
Module Name:            ConsoleApplication1
Loaded Image Name:      D:\net5\ConsoleApp4\Debug\ConsoleApplication1.exe

从 Usage:Image 中可知,这个地址直接嵌入到了 ConsoleApplication1.exe 中,也就是说它随进程启动就被初始化好了,对了,看num1只需用 !address ebp-8 命令验证是否属于 stack 区。

0:000> !address ebp-8

Usage:                  Stack
Base Address:           0019d000
End Address:            001a0000
Region Size:            00003000 (  12.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        000a0000
Allocation Protect:     00000004          PAGE_READWRITE
More info:              ~0k


Content source: 1 (target), length: 204

接下来将 num2 提取出来做全局变量,看看有没有什么区别。

C方法中定义静态static字段是什么意思?

 

可以看到,同样也属于 Image 区块,所以在底层上没什么区别,可能在编译器层面会做一些限制。