C语言 静态数组和静态常量数组在内存使用方面有区别吗?

xxhby3vn  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(217)

声明一个static数组(基元类型)和一个static const数组(基元类型)在内存使用方面有区别吗?

static const int array[4] = {1,2,3,4};
// vs
static int array[4] = {1,2,3,4};

具体地说,static const数组是否“简单地”是加载到内存中的可执行文件的数据,而在只有static的情况下是该数组的副本?后者则意味着数据在内存中存在两次,需要在启动期间复制。
我特别考虑的是一个对单元全局的数组(在cpp文件中声明)。在内存方面,在函数内部声明有什么区别吗?(如果复制了)是在函数被第一次调用时复制的吗?那么,如果该函数从未被调用呢?
PS.我目前使用C++ Builder 11.3使用Clang

33qvvth1

33qvvth11#

首先,请注意,这取决于很多事情-系统类型和使用的编译器,启用的优化级别,“C运行时”如何为给定系统工作等等。
与常见的误解相反,数据不能存储在“稀薄的空气”中-它总是要去某个地方。编译器可以做的是优化数组分配,并将数组内容放在机器码中。数据部分缩小,但可执行部分增加-分配的内存总量应该大致相同。
这种优化的原因通常是速度优化-如果您不必将值从RAM加载到寄存器中,则可以减少指令的总量。
例如

#include <stdio.h>
static const int array[4] = {1,2,3,4};

void func (void)
{
    for(int i=0; i<4; i++)
      printf("%d%",array[i]);
}

在x86_64的gcc -O3上,这归结为一堆movl $1, %esimovl $2, %esi等指令。数字仍然存在,但可执行代码中的movl指令的一部分。
一些注意事项:

  • 只要数组是static,无论我们是否使用const,都会发生上述优化。
  • 如果我们删除static但保留const,那么程序的其他部分可能会访问数组但不会更改它,因此同样的优化仍然是可能的。
  • 如果我们同时删除staticconst,那么编译器就不能再假设这个数组没有从其他地方修改过。因此,编译器被迫将其分配为数据部分中的数组,并在打印期间从该内存位置读取它。

具体地说,静态常量数组是否“仅仅”是加载到内存中的可执行文件的数据,而仅仅是静态数组的副本?
假设数组实际上是在数据段中分配的,并且没有优化为机器码,那么:

  • 在“托管系统”(例如Linux x86)上,可执行文件中的常量数据在可执行文件加载到RAM时加载到数组的内存位置。
  • 在“独立系统”(例如,具有闪存的微控制器)上,则:
  • static + const的情况下,阵列存储在真正的只读闪存中,任何地方都没有副本。
  • 如果static和/或声明在文件作用域但没有const,则数组存储在RAM中,其初始化器值必须在只读闪存中分配。在程序启动期间,闪存数据被复制到RAM中的阵列中。

在某种程度上,各种托管系统编译器的“C运行时”(CRT)可能会做类似于上述独立场景的事情-保留初始化器值的副本,这些初始化器值在启动期间上传到数组中。例如,在C++类的情况下,可能有默认的构造函数,它们执行比复制值更复杂的操作。
在内存方面,在函数内部声明有什么区别吗?
这完全是另一个故事。static的局部作用域变量在分配/初始化方面与任何其他static变量的工作方式相同。但是普通的局部作用域变量“自动存储”存储在寄存器或堆栈中,每次进入声明它们的作用域时,它们都会重新设置。因此,这些将需要从其他地方的只读存储器中复制默认的初始化值。
在这里,局部变量是否是const并不重要,它仍然会在堆栈上的寄存器/中分配,并从只读内存中初始化。

相关问题