在C语言中,常量变量存储在哪里?

61

我想知道常量变量存储在哪里。它是存储在与全局变量相同的内存区域吗?还是在栈上?


2
我想知道这是哪种语法... - DevSolar
1
这个问题没有意义,请重新表述。 - nall
3
试图重新表达这个问题。我希望我正确理解了原帖作者的意图。 - DevSolar
14个回答

50

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

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


3
可能这就是唯一正确的答案(指出后提供流行的实现方法会更有帮助)。 - Lee B
2
仅仅简单地陈述“实现细节”,虽然正确,但可能不是这里的“最佳”答案。我猜我们正在处理一个好奇的初学者,因此,这里的挑战是找出并回答:“我想你的意思是要问这个,因此,我认为你想读那个”。 - Mizipzor
1
@Lee:如果是“实现定义”,那么依赖于“流行的实现方法”意味着你的代码可能会在下一次编译器更新时出现问题。可悲的是,并非所有人都理解这一点,因此许多理解这一点的人不会“提供”有关特定实现的信息,希望那些不理解的人能够明白其中的含义... - DevSolar
5
有时候数据并不是以数据的形式存储,而是以代码的形式存储。也就是说,它不是在任何文本或数据段中创建一个int来读取,而是使用指令将常量加载到寄存器中。这比将地址加载到寄存器中,然后从存储的地址读取数据更加节省空间和更快速。但对于数组来说,并不一定总是采用这种方式,这取决于编译器开关的设置。 - Adrian
同意@Adrian的说法,事实上,对于PIC32微控制器,它存储在Flash/程序存储器中。 - pluto

37

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

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

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

现在,为什么要谈论数据分段,而不是告诉常量变量存储在哪里...其中有一个原因...

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

例如:

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

总之,“const”只是一个数据限定符,这意味着首先编译器必须决定变量应存储在哪个段中,然后如果该变量为const,则符合存储在该特定段的写保护区域的条件。


21
如果我有一个初始化为const var的全局变量,它会存储在BSS段,如果我有一个未初始化的const var,则会存储在数据段中... 我认为应该是未初始化-> BSS段,初始化-> 数据段。 - Subbu
5
如果我有一个const int作为本地变量,那么它会被存储在栈段的写保护区域。栈的哪个部分是受写保护的? 写保护的区域指的是栈中不允许被修改的部分,通常是存储局部变量和函数调用信息的区域。具体来说,在这个问题中,是存储const int类型的本地变量的那一部分。 - jimis
4
数据段有两部分:已初始化和未初始化。 BSS -> 未初始化的数据段。 - Mayank
4
这个答案中bss段和data段被交换了。bss段由起始地址和长度描述,用于存放未初始化或零初始化的变量,而数据段则用于存放非默认初始化的变量。 - Jonathan Leffler
2
正如@Jonathan Leffler所提到的,bss和data段被交换了。请纠正这个问题,因为它会导致混淆。 - Genís
显示剩余3条评论

14

考虑以下代码:

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”节(通常分配清零变量的地方)或“data”节(通常分配初始化变量的地方)中。

如果编译器确信 k 没有使用(因为它局部于单个文件),则它可能根本不会出现在目标代码中。 如果没有注释掉引用 ktotherfunc() 调用,则必须在某个位置分配 k 的地址 - 它很可能在与 i 相同的段中。

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

请注意,我强制让这些变量出现,因为我通过引用传递它们 - 大概是给一个期望指向常量整数的指针函数。 如果从未取得地址,那么 jk 可能会被优化出代码。 要删除 i,编译器必须知道整个程序的所有源代码 - 它在其他翻译单元(源文件)中是可访问的,因此不容易被删除。 特别是如果程序涉及动态加载共享库-其中一个库可能依赖于全局变量。

(在样例中,变量 ij 的命名应更具有意义和长度。)


“(如果它是一个常量,它还是一个变量吗?)”根据定义,不是。 - Chris Lutz
1
j 可以被视为一个变量。在 C 中,"variable" 这个词没有定义,但在 C++ 中,j 是一个变量。variable 只是一个命名的对象,无论它是否是 const。在 C 中,字面值和枚举被称为 constants - 我不认为 j 在 C 中被称为 "constant",因为它不能出现在常量表达式中。 - Johannes Schaub - litb

5
取决于您的编译器、系统能力和编译时的配置。 gcc 将只读常量放在 .text 段中,除非另有指示。puts

2
当然不是,因为在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) 数据段是已初始化变量,它有三种类型,

      (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个字节,大意味着大于8个字节和相等的值。

但我的疑问是,本地常量存储在哪里????


2
通常情况下,它们被存储在只读数据段中(而全局变量段具有写权限)。因此,尝试通过获取其地址来修改常量可能会导致访问冲突,也就是所谓的段错误。
但这实际上取决于您的硬件、操作系统和编译器。

2
所以,试图通过获取常量的地址来修改它可能会导致访问冲突,也就是segfault。噢,如果只有这在OS X上是真实存在的那该多好啊。我刚测试了一下,它居然可以工作,真是让人毛骨悚然。 - Chris Lutz
在Windows上,例如它只适用于某些编译器。例如VC++和gcc会生成这样的代码,但Borland的编译器不会(至少我上次使用它们时不会;可能已经改变了)。 - elder_george
当我尝试修改全局const变量时,我会收到访问冲突错误(因为变量存储在只读数据段中)。但是我可以修改本地const变量,因为变量存储在堆栈中。 - siva

1

这是编译器相关的,但请注意它可能甚至没有完全存储。因为编译器只需优化并将其值直接添加到使用它的表达式中。

我在程序中添加了此代码,并使用gcc编译为arm cortex m4,在内存使用方面进行了比较。

不使用const

int someConst[1000] = {0};

enter image description here

使用 const

const int someConst[1000] = {0};

enter image description here


1
这只是一个有根据的猜测,但我认为常量通常存储在编译程序的实际CPU指令中,作为立即数据。换句话说,大多数指令包括从中获取数据的地址空间,但如果它是一个常量,则该空间可以容纳值本身。

1

这个特定于Win32系统。

enter image description here


0

它可能根本不会被存储。

考虑以下代码:

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

这使得程序员能够了解正在发生的事情,但编译器可以优化掉其中的一些内容,大多数编译器通过在编译时评估常量表达式来实现这一点,这意味着PI的值可能根本不会出现在生成的程序中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接