静态修饰符如何在C中工作?



我试图了解"static"修饰语在C中的工作方式,我去寻找它的含义,我发现的一切似乎有点模糊。

这是一个修饰符,允许变量的值一直存在到程序执行结束。

我理解它的含义和目的,但除了这个定义之外,我想了解它是如何工作的,所以我生成了C代码的汇编

char    *thing(char *a)
{
char *b;
b = malloc(3);
b[0] = 'y';
b[1] = '';
return (b);
}
char    *some(int fd)
{
static char *a = "happened";
a = thing(a);
return (a);
}

我用非静态a变量创建了另一个代码,得到了这个

/* With static variable */
.file   "static_test.c"
.text
.globl  thing
.type   thing, @function
thing:
.LFB6:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movq    %rdi, -24(%rbp)
movl    $3, %edi
call    malloc@PLT
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
movb    $121, (%rax)
movq    -8(%rbp), %rax
addq    $1, %rax
movb    $0, (%rax)
movq    -8(%rbp), %rax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size   thing, .-thing
.globl  some
.type   some, @function
some:
.LFB7:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $16, %rsp
movl    %edi, -4(%rbp)
movq    a.0(%rip), %rax
movq    %rax, %rdi
call    thing
movq    %rax, a.0(%rip)
movq    a.0(%rip), %rax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE7:
.size   some, .-some
.section    .rodata
.LC0:
.string "happened"
.section    .data.rel.local,"aw"
.align 8
.type   a.0, @object
.size   a.0, 8
a.0:
.quad   .LC0
.ident  "GCC: (GNU) 12.1.0"
.section    .note.GNU-stack,"",@progbits
/* no static variable */
.file   "nostatic_test.c"
.text
.globl  thing
.type   thing, @function
thing:
.LFB6:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movq    %rdi, -24(%rbp)
movl    $3, %edi
call    malloc@PLT
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
movb    $121, (%rax)
movq    -8(%rbp), %rax
addq    $1, %rax
movb    $0, (%rax)
movq    -8(%rbp), %rax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size   thing, .-thing
.section    .rodata
.LC0:
.string "happened"
.text
.globl  some
.type   some, @function
some:
.LFB7:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
subq    $32, %rsp
movl    %edi, -20(%rbp)
leaq    .LC0(%rip), %rax
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
movq    %rax, %rdi
call    thing
movq    %rax, -8(%rbp)
movq    -8(%rbp), %rax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE7:
.size   some, .-some
.ident  "GCC: (GNU) 12.1.0"
.section    .note.GNU-stack,"",@progbits

问题是,这两个汇编代码之间发生了什么,有什么不同,在编译时和程序执行时是如何表现的。

在静态版本中,您在.data部分保留了一个指针大小的数据,该数据由程序文件/图像初始化以引用字符串文字(在.rodata部分中)。这是

a.0:
.quad   .LC0

在非静态版本中,变量是"automatic"-函数的局部变量,在函数入口时创建,在函数退出时销毁。因为变量在每次调用函数时都存在,所以每次都必须初始化它。在您显示的代码中(未优化),这个自动变量存在于堆栈中。(优化将改进代码。)

静态变量可以享受这种初始化的效率,而局部变量可以享受驻留在CPU寄存器中的性能可能性(快速访问&不占用内存),并且与静态量相比,可能是递归和线程安全的。

正如你所注意到的,静态变量在函数停止后仍然存在,因为它具有全局存储并且编译器知道如何访问它。相比之下,基于自动/堆栈的变量在从函数返回后丢失——更具体地说,每次调用函数时都会重新创建变量,因此旧的副本不再(实际上)可访问。您可以创建指向自动变量的指针,但在函数退出后使用/解引用该指针将是一个逻辑错误。

这可能是一个更好的例子。r具有局部作用域,但它不会位于堆栈的局部,而是位于程序的.bss或.data节中,并且只初始化一次为零。之后,每次调用Rnd32将更新r。程序在每次调用时以固定顺序返回一个伪随机32位无符号整数,因此它是一个可重复的序列,遍历所有2^32个可能的值。

uint32_t Rnd32()
{
static uint32_t r = 0;
r = r*1664525 + 1013904223;
return r;
}

相关内容

  • 没有找到相关文章

最新更新