关于C#:为什么没有分配原始数据类型的内存?

why memory for primitive data types is not allocated?

本问题已经有最佳答案,请猛点这里访问。

char、bool和int等基元数据类型有一些内存。假设char有1个字节的内存。在代码中使用char变量时,该变量必须需要1字节的内存。在这种情况下,我们为什么不分配内存呢?因为我们从来没有为它分配内存,所以它是如何使用内存的,也就是说,在这种情况下,CPU是为它分配内存的。另外,我在某个地方读到原始数据类型被放在堆栈上,并且在为它完成工作时被删除。什么样的堆栈?在这种情况下,堆栈是如何变成图片的?


在过于简单化的风险下,数据有三类内存:1)静态内存,2)堆栈内存,3)堆内存。

它们以不同的方式分配。

如果你有

1
 static char something ;

在函数中定义的或

1
char something ;

在函数外部,该数据由链接器使用编译器的指令定义,并由程序加载器分配。

几乎所有现有的处理器都使用一个堆栈来支持嵌套数据(例如,函数调用)。堆栈是存在于每个进程(以及每个处理器模式)的内存块。有一个称为堆栈指针的硬件寄存器,用于标识堆栈的当前位置。通常SP从堆栈的高端开始向下工作。为了在堆栈上分配内存,程序从堆栈指针中减去所需的字节数。要取消分配,它会添加到堆栈指针。分配和解除分配总是在同一端进行。

在堆栈上有两个操作。推意味着把一些东西放在堆栈上。Pop删除了它。大多数处理器都有推送和弹出指令

如果你有

1
 char something

在一个函数中定义,该内存由程序按照编译器的指示分配,通过这样的操作来调整堆栈指针(现在我只剩下一个帧指针)。

1
 SUB   BYTESNEEDED, SP

在进入函数并通过执行释放后

1
ADD BYTESNEEDED, SP

离开功能前。在函数执行期间,局部变量与堆栈指针的偏移量。

这通常通过使用第二个寄存器来完成,通常称为帧指针。函数通常在开始时执行类似的操作

1
2
3
PUSH  FP       ; Save the old Frame Point
MOV   SP  FP   ; Save the stack pointer
SUB   BYTESNEEDED, SP

最后,函数的作用是

mov fp,sp;释放函数分配的所有堆栈pop fp;还原旧堆栈指针

使用两个寄存器的原因是可以从堆栈动态分配数据。

有一个名为alloca的公共函数(尽管我认为它不是标准的C函数),它是从堆栈分配malloc的替代方法

1
2
3
4
void dosomething (int amount)
{
    char *data = alloca (amount) ;
}

使用alloca,当函数返回并重置堆栈时,数据会自动释放。

这是对你问题的冗长回答。是的,当声明一个字符时,必须为它分配一个分配。但是,这种分配是在幕后完成的,而您不需要付出任何努力。


当我们创建一个NSString *变量时,我们也不会为此分配内存。

我们只在直接由我们调用alloc或在我们调用的方法内部调用时分配内存。

在我们分配的内存中的堆上存在一个NSString对象,但在我们不分配的堆栈上的内存中存在NSString *变量(即指向NSString对象的指针)。

例如,给定这两个变量:

1
2
NSString *stringOne;
NSString *stringTwo;

到目前为止,还没有在堆上分配任何内存,尽管它们确实以与内存中存在charBOOLint完全相同的方式存在于内存中。

1
2
NSString *stringOne = [[NSString alloc] initWithString:@"Hello world"];
NSString *stringTwo = stringOne;

现在发生了什么?我们在堆上为一个NSString对象分配了一些内存。然后我们初始化这个内存来表示字符串"hello world",然后返回一个指向这个对象的指针,并将它分配给stringOne

接下来,我们简单地将指针复制到我们用于stringTwo的堆栈内存中。我们没有在堆上分配任何额外的内存。我们只是让两个字符串变量指向堆上分配的相同内存。

注释中链接的问答JSD对堆栈和堆内存有更多的解释,它们将回答您的一些问题。

值得注意的是,许多其他编程语言,如C++,允许在堆栈上创建对象,在这种情况下,我们不分配它们,就像我们使用堆对象一样。它们确实存在于内存中,更类似于原始数据类型。