如果一个 C 函数被调用两次,它会创建一个在函数中声明的变量两次吗?
If a C function is called twice will it create a variable declared in the function twice?
我有一个用 C 编写的函数,它由一个像这样的指针变量组成
#include<stdio.h>
void print()
{
char *hello="hello world";
fprintf(stdout,"%s",hello);
}
void main()
{
print();
print();
}
如果我调用 print() 函数两次,它会为 hello 变量分配两次内存吗?
if i call the print() function twice, will it allocate the memory for the hello variable twice?
不,它是一个字符串文字,只分配一次。
您可以通过检查地址来确认:
fprintf(stdout,"%p: %s\n", hello, hello);
示例输出:
0x563b972277c4: hello world
0x563b972277c4: hello world
if i call the print() function twice, will it allocate the memory for the hello variable twice?
是的。但是,hello
是一个 指针 并且在 64 位机器上占用 8 个字节的堆栈 space。成本实际上是无法衡量的。此外,编译器根本不需要分配变量 ,因为您永远不会尝试获取它的地址。编译器可以自由有效地将您的代码转换为:
void print()
{
fprintf(stdout, "%s", "hello world");
}
意味着 hello
的声明不会在运行时导致任何内存分配。并非出于所有意图和目的,将 hello
作为局部变量是 cost-free.
相比之下,zero-terminated 字符串文字“hello world”仅在应用程序的数据段中分配一次。编译器可以这样做是因为它知道 C 字符串文字是只读的,因此不允许修改它。此外,在运行时不会执行 dynamic 内存分配。字符串文字的内存是静态分配的,它的生命周期就是应用程序的生命周期。
因此,您的 print
函数本质上是尽可能廉价的 — 它根本不执行任何实际分配。
您可以将代码粘贴到 compiler explorer 以查看会发生什么。从您的代码中,这是生成的 assebler:
print(): # @print()
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
jmp fwrite # TAILCALL
main: # @main
push rax
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
call fwrite
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
call fwrite
xor eax, eax
pop rcx
ret
.L.str:
.asciz "hello world"
重要的部分在最后:
.L.str:
.asciz "hello world"
"hello world"
仅在此处声明为全局变量,每次调用函数时都会使用 print
。
就像这样声明:
#include<stdio.h>
const char* hello = "hello world";
void print()
{
fprintf(stdout,"%s",hello);
}
void main()
{
print();
print();
}
在这种情况下,编译器看到了优化并进行了优化。但是,我不能肯定地说情况总是如此,因为它不能依赖于编译器。
hello
变量
自动变量在函数退出时结束它们的生命。因此,当您在函数内部时,内存只会在堆栈上分配。当函数存在时它将被释放。
- 字符串文字具有程序生命周期并且是存储文字的地方(通常是 .rodata 段)。这个区域在程序构建期间被填充,它在内存中的实际表示方式取决于实现
我有一个用 C 编写的函数,它由一个像这样的指针变量组成
#include<stdio.h>
void print()
{
char *hello="hello world";
fprintf(stdout,"%s",hello);
}
void main()
{
print();
print();
}
如果我调用 print() 函数两次,它会为 hello 变量分配两次内存吗?
if i call the print() function twice, will it allocate the memory for the hello variable twice?
不,它是一个字符串文字,只分配一次。
您可以通过检查地址来确认:
fprintf(stdout,"%p: %s\n", hello, hello);
示例输出:
0x563b972277c4: hello world
0x563b972277c4: hello world
if i call the print() function twice, will it allocate the memory for the hello variable twice?
是的。但是,hello
是一个 指针 并且在 64 位机器上占用 8 个字节的堆栈 space。成本实际上是无法衡量的。此外,编译器根本不需要分配变量 ,因为您永远不会尝试获取它的地址。编译器可以自由有效地将您的代码转换为:
void print()
{
fprintf(stdout, "%s", "hello world");
}
意味着 hello
的声明不会在运行时导致任何内存分配。并非出于所有意图和目的,将 hello
作为局部变量是 cost-free.
相比之下,zero-terminated 字符串文字“hello world”仅在应用程序的数据段中分配一次。编译器可以这样做是因为它知道 C 字符串文字是只读的,因此不允许修改它。此外,在运行时不会执行 dynamic 内存分配。字符串文字的内存是静态分配的,它的生命周期就是应用程序的生命周期。
因此,您的 print
函数本质上是尽可能廉价的 — 它根本不执行任何实际分配。
您可以将代码粘贴到 compiler explorer 以查看会发生什么。从您的代码中,这是生成的 assebler:
print(): # @print()
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
jmp fwrite # TAILCALL
main: # @main
push rax
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
call fwrite
mov rcx, qword ptr [rip + stdout]
mov edi, offset .L.str
mov esi, 11
mov edx, 1
call fwrite
xor eax, eax
pop rcx
ret
.L.str:
.asciz "hello world"
重要的部分在最后:
.L.str:
.asciz "hello world"
"hello world"
仅在此处声明为全局变量,每次调用函数时都会使用 print
。
就像这样声明:
#include<stdio.h>
const char* hello = "hello world";
void print()
{
fprintf(stdout,"%s",hello);
}
void main()
{
print();
print();
}
在这种情况下,编译器看到了优化并进行了优化。但是,我不能肯定地说情况总是如此,因为它不能依赖于编译器。
hello
变量
自动变量在函数退出时结束它们的生命。因此,当您在函数内部时,内存只会在堆栈上分配。当函数存在时它将被释放。
- 字符串文字具有程序生命周期并且是存储文字的地方(通常是 .rodata 段)。这个区域在程序构建期间被填充,它在内存中的实际表示方式取决于实现