常量变量存储在 C 中的啥位置?

Posted

技术标签:

【中文标题】常量变量存储在 C 中的啥位置?【英文标题】:Where are constant variables stored in C?常量变量存储在 C 中的什么位置? 【发布时间】:2010-12-07 06:48:22 【问题描述】:

我想知道常量变量存储在哪里。它和全局变量在同一个内存区域吗?还是在堆栈上?

【问题讨论】:

不知道这是什么语法... 这个问题毫无意义。请改写。 尝试重新表述这个问题。我希望我正确理解了 OP 的意图。 【参考方案1】:

它们的存储方式是一个实现细节(取决于编译器)。

例如,在 GCC 编译器中,在大多数机器上,只读变量、常量和跳转表都放在文本部分中。

【讨论】:

这可能是一个正确的答案(尽管在指出这一点之后提供流行的实现方法会更有帮助。) 简单说明“实施细节”虽然正确,但可能不是这里的“最佳”答案。我猜想是在与一个好奇的初学者打交道,因此,这里的挑战是弄清楚并回答:“我认为您的意思是问 this,因此,我认为您想阅读那个”。 @Lee:如果它是“定义的实现”,那么任何依赖“流行的实现方法”都意味着您的代码可能会在下一次编译器更新时中断。可悲的是,这并不是所有人都理解的,所以很多理解的人并没有“提供”关于特定实现的信息,希望不理解的类型得到提示...... 有时它不是作为数据存储的,而是作为代码存储的。 IE。它不是在任何要读取的文本或数据段中创建一个 int,而是使用一条指令将常量加载到寄存器中。这比将地址加载到寄存器中然后从存储的地址中读取数据更小更快。然而,这不一定适用于数组,这可能取决于编译器开关。 同意@Adrian,其实对于PIC32微控制器,它存储在闪存/程序存储器中。【参考方案2】:

通常它们存储在只读数据部分(而全局变量部分具有写权限)。因此,尝试通过获取其地址来修改常量可能会导致访问冲突,即 segfault。

但这真的取决于你的硬件、操作系统和编译器。

【讨论】:

"因此,尝试通过获取其地址来修改常量可能会导致访问冲突,即 segfault。"哦,如果这在 OS X 上是真的就好了。我刚刚对其进行了测试,而且效果非常好。 在 Windows 上,例如它仅适用于某些编译器。例如VC++ 和 gcc 会生成这样的代码,但 borland 的不会(至少我上次使用它们时没有;事情可能已经改变了)。 当我尝试修改全局 const 变量时,出现访问冲突错误(因为变量存储在只读数据段中)。但是我可以修改本地 const 变量,因为变量存储在堆栈中。【参考方案3】:

这主要是有根据的猜测,但我想说常量通常存储在编译程序的实际 CPU 指令中,作为即时数据。所以换句话说,大多数指令都包含用于从中获取数据的地址的空间,但如果它是一个常量,则该空间可以保存值本身。

【讨论】:

【参考方案4】:

取决于您的编译器、系统功能、编译时的配置。

gcc puts .text 部分的只读常量,除非另有说明。

【讨论】:

【参考方案5】:

全局和常量是两个完全分开的关键字。你可以有一个或另一个,没有或两者兼有。

那么,您的变量在内存中的存储位置取决于配置。阅读heap 和stack 的一些内容,这将为您提供一些知识来提出更多(如果可以的话,更好、更具体)的问题。

【讨论】:

【参考方案6】:

考虑代码:

const int i = 0;
static const int k = 99;

int function(void)

    const int j = 37;
    totherfunc(&j);
    totherfunc(&i);
  //totherfunc(&k);
    return(j+3);

一般情况下,i 可以存储在文本段中(它是一个具有固定值的只读变量)。如果它不在文本段中,它将存储在全局变量旁边。鉴于它被初始化为零,它可能在“bss”部分(通常分配零变量的地方)或“数据”部分(通常分配初始化变量的地方)。

如果编译器确信k 未被使用(可能是因为它是单个文件的本地文件),它可能根本不会出现在目标代码中。如果对引用ktotherfunc() 的调用没有被注释掉,那么k 必须在某处分配一个地址——它可能与i 在同一段中。

常量(如果是常量,它仍然是变量吗?)j 很可能会出现在传统 C 实现的堆栈中。 (如果你在 comp.std.c 新闻组中提问,有人会提到标准并没有说自动变量出现在堆栈上;幸运的是,SO 不是 comp.std.c!)

请注意,我强制变量出现是因为我通过引用传递了它们 - 大概是传递给期望指向常量整数的指针的函数。如果地址从未被占用,那么jk 可以完全在代码之外进行优化。要删除i,编译器必须知道整个程序的所有源代码——它可以在其他翻译单元(源文件)中访问,因此不能轻易删除。如果程序沉迷于动态加载共享库,那就更不用说了——其中一个库可能依赖于该全局变量。

(在风格上 - 变量 ij 应该有更长、更有意义的名称;这只是一个示例!)

【讨论】:

"(如果是常量,那还是变量吗?)"按定义,不是。 j 当然可以看作是一个变量。 C 中没有定义“变量”一词,但在 C++ 中 j 是一个变量。 variable 只是一个命名对象,不管它是不是 const。在 C 中,文字和枚举器被称为 constants - 我不认为 j 在 C 中被称为“常量”,也是因为它不能出现在常量表达式中。【参考方案7】:

有些常量甚至没有被存储。

考虑以下代码:

int x = foo();
x *= 2;

编译器可能会将乘法转换为x = x+x;,因为这样可以减少从内存中加载数字 2 的需要。

【讨论】:

...或常数乘法,其中 2 将存储在实际的 CPU 指令中 "Some constants arfen't even..." 应该是 "aren't" (会编辑它,但由于某种原因我不能做出少于 6 个字符的更改:/)跨度> 【参考方案8】:

它可能根本不存储。

考虑如下代码:

#import<math.h>//import PI
double toRadian(int degree)
  return degree*PI*2/360.0;

这使程序员能够收集正在发生的事情的想法,但是编译器可以通过在编译时评估常量表达式来优化其中的一些,大多数编译器都会这样做,这意味着值 PI 可能不在最终的程序。

【讨论】:

【参考方案9】:

根据特定处理器遵循的数据分段,我们有五个分段:

    代码段 - 仅存储代码,ROM BSS(或由符号开始的块) 数据段 - 存储初始化的全局和静态变量 堆栈段 - 存储所有局部变量和其他有关函数返回地址等信息 堆段 - 所有动态分配都发生在这里 数据 BSS(或由符号开始的块)段 - 存储未初始化的全局和静态变量

请注意,数据段和 BSS 段之间的区别在于,前者存储已初始化的全局变量和静态变量,而后者存储未初始化的变量。

现在,我为什么要谈论数据分段,而我必须只是告诉常量变量存储在哪里...这是有原因的...

每个段都有一个写保护区域,其中存储了所有常量。

例如:

如果我有一个局部变量 const int,那么它将存储在堆栈段的写保护区域中。 如果我有一个初始化为 const var 的全局变量,那么它会存储在数据段中。 如果我有一个未初始化的 const var,那么它将存储在 BSS 段中...

总而言之,“const”只是一个数据限定符,这意味着首先编译器必须决定变量必须存储在哪个段,然后如果变量是 const,那么它有资格存储在 write该特定段的受保护区域。

【讨论】:

"如果我有一个已初始化 const var 的全局变量,那么它会存储在 BSS 中,如果我有一个未初始化的 const var,那么它将存储在数据段中..." ...我认为它应该像 unitialized -> bss , initialized -> data segment "如果我有一个 const int 是局部变量,那么它将存储在堆栈段的写保护区域中"。堆栈的哪一部分是写保护的? 数据段有两部分:初始化和初始化。 BSS -> 未初始化的数据段。 此答案已交换 bss 和数据段。 bss 由一个起始地址和一个脚描述,用于零初始化或未初始化的变量,而数据段用于非默认初始化变量。 正如@Jonathan Leffler 提到的,bss 和数据段是交换的。请更正此问题,因为它会导致混淆【参考方案10】:

当然不是,因为

1) bss 段存储了非初始化变量,显然是另一种类型。

       (I) large static and global and non constants and non initilaized variables it stored .BSS section.

       (II) second thing small static and global variables and non constants and non initilaized variables stored in .SBSS section this included in .BSS segment.

2) 数据段是初始化变量,它有 3 种类型,

      (I) large static and global and initlaized and non constants variables its stord in .DATA section.
      (II) small static and global and non constant and initilaized variables its stord in .SDATA1 sectiion.
     (III) small static and global and  constant and initilaized OR non initilaized variables its stord in .SDATA2 sectiion.

我上面提到的小和大意味着取决于编译器,例如小意味着 8 字节和相等的值。

但我怀疑局部常数会在哪里出现??????

【讨论】:

【参考方案11】:

就像一个附加组件,你知道它在链接过程中决定了最终可执行文件的内存布局。还有一个称为 COMMON 的部分,其中放置了来自不同输入文件的公共符号。这个常见的部分实际上属于 .bss 部分。

【讨论】:

【参考方案12】:

我检查了 x86_64 GNU/Linux 系统。通过使用指向“const”变量的指针,可以更改值。我使用了objdump。在文本段中未找到“const”变量。 'const' 变量存储在堆栈中。 'const' 是“C”中的编译器指令。当遇到更改“const”变量的语句时,编译器会抛出错误。

【讨论】:

【参考方案13】:

这是特定于 Win32 系统的。

【讨论】:

【参考方案14】:

它依赖于编译器,但请注意它甚至可能没有完全存储。由于编译器只需要对其进行优化并将其值直接添加到使用它的表达式中。

我将这段代码添加到程序中,用gcc编译arm cortex m4,检查内存使用的差异。

没有const

int someConst[1000] = 0;

使用 const

const int someConst[1000] = 0;

【讨论】:

以上是关于常量变量存储在 C 中的啥位置?的主要内容,如果未能解决你的问题,请参考以下文章

java中 关于常量池 栈内存 堆内存

Azure 存储帐户位置变量/常量在哪里

C语言中字符串常量到底存在哪了?

java 静态变量和静态常量

05.C语言数据常量和变量

对象的实例变量存储在 JVM 中的啥位置?