c语言const变量的生命周期和存储位置

时间:2022-12-27 15:23:29
只考虑c语言,不考虑c++。
看某书上说,程序运行期间,占用内存空间分为6段:
代码段,只读数据段(常量,字面值,const声明的变量),已初始化可读写数据段(已初始化静态和全局变量),未初始化可读写数据段(未初始化静态和全局变量),栈,堆。
看了之后对于const声明的变量有几个疑惑:

1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
2.声明周期:“只读数据段”,是和静态变量一样程序运行前就分配好内存,程序退出时才释放内存,生命周期是整个程序运行时间?
3.对于一个const类型的局部变量,如const int a=1;1这个值是在编译期间就确定的,还是到运行时才确定的?
4.问题1,3对于const类型的全局变量呢?
查了好多资料,都不太清楚,还有矛盾的地方,求详细解答。。。求高手。。。

22 个解决方案

#2


const数据放在常量数据区(constant segment),应用程序结束后,由操作系统释放。因此其生命周期大致和应用程序差不多(稍长一些)。

#3


引用 2 楼 pathuang68 的回复:
const数据放在常量数据区(constant segment),应用程序结束后,由操作系统释放。因此其生命周期大致和应用程序差不多(稍长一些)。

问题2呢?静态变量的值在编译阶段就确定了,那const类型的呢?也是吗?

#4


1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规定属于未定义行为,因此一个实现对于试图修改const的行为如何处理都没有违反标准,也就是说,无论把const变量放在只读段也好,放在可被修改的地方也好(例如你说的栈),都是允许的行为。




2.声明周期:“只读数据段”,是和静态变量一样程序运行前就分配好内存,程序退出时才释放内存,生命周期是整个程序运行时间?
-----------------------------------------------------------------------------------
NO,只读数据段与生命周期无关


3.对于一个const类型的局部变量,如const int a=1;1这个值是在编译期间就确定的,还是到运行时才确定的?
-----------------------------------------------------------------------------
这要分抽象语义和实现语义,对于抽象语义,a必定是运行时才确定的,抽象语义才是a的本质;而对于一个实现,如果确定代码的其它地方并没有使用a的运行期信息,例如没有使用a的地址等,编译器也可以选择将a的值隐式作为字面值处理,即是说,编译器在实现a的语义时可以灵活处理,只要能表现出a的抽象语义即可。


4.问题1,3对于const类型的全局变量呢?
------------------------------------------------------------
问题1的处理是一样的,但全局变量不可能在栈中。而对于问题3,全局变量的值在编译期是确定的。

#5


==========C专家编程 21页=================
关键字const并不能把变量变成常量!在一个符号前加上const限定符只是表示这个符号不能被复制。也就是他的值对于这个符号来说是只读的

#6


引用 4 楼 supermegaboy 的回复:
1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规……

总结出来都是和编译器实现有关,,伤不起。。

#7


2l和4l的答案让我不知道信谁,一个给了明确的说法,一个说和具体实现有关,标准只是规定未定义。。来点佐证的资料吧。。

#8


回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“只读数据段”的内存,运行完才释放。

3.const int a=1;其实类似于宏定义#define a 1,编译时就将局部函数内的a替换成了1。

4.对于全局的const变量,是在常量区分配内存的,是真正的常量。

#9


每个人的说法都不一样,信谁?

#10


引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“……

感觉你的回答应该是针对某些编译器吧?所有编译器实现都是这样的?

#11


每次看 supermegaboy 的回帖都能学到新名词 

#12


该回复于2011-10-24 09:28:58被版主删除

#13


引用 10 楼 um_java 的回复:
引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分……

应该是的,局部const会被编译时替换,不需要分配内存,类似于宏替换。而全局const是需要在常量区分配内存的。以下两个程序供测试:

程序一(局部const)
#include <iostream>
using namespace std;
int main()
{
const int a=10;
int *p = (int *) &a;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
*p = 20;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
cout << *p << " " << a << endl; //*p和a的地址相同,输出的值却不同,这怎么解释?

return 0;
}


程序二(全局const常量),运行到*p = 20;时会出错。
#include <iostream>
using namespace std;

const int a = 10;

int main()
{
int *p = (int *) &a;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
*p = 20;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
cout << *p << " " << a << endl;

return 0;
}

#14


引用 4 楼 supermegaboy 的回复:
1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规……

我比较赞同4楼的解释,我个人觉得,const是一个变量的属性的修饰,其不能决定一个变量的存储位置和生命周期。决定一个变量的存储位置和生命周期是由该变量是局部变量还是全局变量来决定。我觉得const是属于编译器的范畴,一个const变量可以存放在可读写区域,但是当你再程序中修改时,编译阶段就会报错,const变量可能通过其他途径来修改。而内存中的常量区是再操作系统的范畴,如果一个变量被放在常量区,你是无论如何也修改不了的。如
char *ptr="hello";
ptr[0] = 'a';

这段代码在编译阶段没有任何问题,而运行时就出错了,因为操作系统发现你试图写该系统的常量区。

#15


VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。

不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

#16


MSDN98中的例子walker又名pwalk。完整列出指定进程的内存使用情况,显示进程地址空间内容,装载哪些DLL,代码、数据、堆栈段分配在何处,可以用来检测内存泄漏,监测内存使用。
http://download.csdn.net/detail/zhao4zhong1/3667896

#17


呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:

int ctest(const int a, int b)
{
int *p = &a;
        // a = 2; // 这条语句肯定不能通过编译,C语言保证且只保证了这一条。
*p = a + b;
return *p;
}

void main(void) {
  /* put your own code here */
  
  int t3;
  int t1 = 1, t2 = 2;
  
  t3 = ctest(t1,t2);
  for(;;);

}


在有些编译器上不仅是能通过编译,而且连运行时错误都不会出现。(你可以找个嵌入式领域的编译器,比如keil C, CodeWarrior测试这段代码)

嗯,结论就是,C语言只要求编译器保证const对象不能出现在左边,其它什么都没要求。

#18


const 纠结啊。
路过,学习的。
如果是文件系统中的只读文件,一切都好办了。

#19


不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

#20


引用 17 楼  的回复:
呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:
C/C++ c……


你这段代码是不会改变const 变量的值的。

不行你可以用打印函数打印你修改过的变量的是来来看看到底变了没有。我在vs2005 上面测试你这样是不能改变的

#21


引用 17 楼 finewind 的回复:
呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:

int ctest(const int a, int b)
{
int *p = &a;
        // a = 2; // 这条语句肯定不能通过编译,C语言保证且只保证了这一条。
*p = a + b;
return *p;
}

void main(void) {
  /* put your own code here */
  
  int t3;
  int t1 = 1, t2 = 2;
  
  t3 = ctest(t1,t2);
  for(;;);

}


在有些编译器上不仅是能通过编译,而且连运行时错误都不会出现。(你可以找个嵌入式领域的编译器,比如keil C, CodeWarrior测试这段代码)

嗯,结论就是,C语言只要求编译器保证const对象不能出现在左边,其它什么都没要求。


我觉得你这个有问题,你想想你传递的t1只不过是个形参而已,根本没有修改实际的t1这个值啊!那这个意思也就是t1没有没修改,const也起作用了

#22


引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“只读数据段”的内存,运行完才释放。

3.const int a=1;其实类似于宏定义#define a 1,编译时就将局部函数内的a替换成了1。

4.对于全局的const变量,是在常量区分配内存的,是真正的常量。
"内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了"只有动态区的才是,静态数据区是编译器分配,程序结束后os释放

#1


#2


const数据放在常量数据区(constant segment),应用程序结束后,由操作系统释放。因此其生命周期大致和应用程序差不多(稍长一些)。

#3


引用 2 楼 pathuang68 的回复:
const数据放在常量数据区(constant segment),应用程序结束后,由操作系统释放。因此其生命周期大致和应用程序差不多(稍长一些)。

问题2呢?静态变量的值在编译阶段就确定了,那const类型的呢?也是吗?

#4


1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规定属于未定义行为,因此一个实现对于试图修改const的行为如何处理都没有违反标准,也就是说,无论把const变量放在只读段也好,放在可被修改的地方也好(例如你说的栈),都是允许的行为。




2.声明周期:“只读数据段”,是和静态变量一样程序运行前就分配好内存,程序退出时才释放内存,生命周期是整个程序运行时间?
-----------------------------------------------------------------------------------
NO,只读数据段与生命周期无关


3.对于一个const类型的局部变量,如const int a=1;1这个值是在编译期间就确定的,还是到运行时才确定的?
-----------------------------------------------------------------------------
这要分抽象语义和实现语义,对于抽象语义,a必定是运行时才确定的,抽象语义才是a的本质;而对于一个实现,如果确定代码的其它地方并没有使用a的运行期信息,例如没有使用a的地址等,编译器也可以选择将a的值隐式作为字面值处理,即是说,编译器在实现a的语义时可以灵活处理,只要能表现出a的抽象语义即可。


4.问题1,3对于const类型的全局变量呢?
------------------------------------------------------------
问题1的处理是一样的,但全局变量不可能在栈中。而对于问题3,全局变量的值在编译期是确定的。

#5


==========C专家编程 21页=================
关键字const并不能把变量变成常量!在一个符号前加上const限定符只是表示这个符号不能被复制。也就是他的值对于这个符号来说是只读的

#6


引用 4 楼 supermegaboy 的回复:
1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规……

总结出来都是和编译器实现有关,,伤不起。。

#7


2l和4l的答案让我不知道信谁,一个给了明确的说法,一个说和具体实现有关,标准只是规定未定义。。来点佐证的资料吧。。

#8


回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“只读数据段”的内存,运行完才释放。

3.const int a=1;其实类似于宏定义#define a 1,编译时就将局部函数内的a替换成了1。

4.对于全局的const变量,是在常量区分配内存的,是真正的常量。

#9


每个人的说法都不一样,信谁?

#10


引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“……

感觉你的回答应该是针对某些编译器吧?所有编译器实现都是这样的?

#11


每次看 supermegaboy 的回帖都能学到新名词 

#12


该回复于2011-10-24 09:28:58被版主删除

#13


引用 10 楼 um_java 的回复:
引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分……

应该是的,局部const会被编译时替换,不需要分配内存,类似于宏替换。而全局const是需要在常量区分配内存的。以下两个程序供测试:

程序一(局部const)
#include <iostream>
using namespace std;
int main()
{
const int a=10;
int *p = (int *) &a;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
*p = 20;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
cout << *p << " " << a << endl; //*p和a的地址相同,输出的值却不同,这怎么解释?

return 0;
}


程序二(全局const常量),运行到*p = 20;时会出错。
#include <iostream>
using namespace std;

const int a = 10;

int main()
{
int *p = (int *) &a;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
*p = 20;
cout << *p << " " << a << endl;
cout << p << " " <<&a<<" "<< &p << endl;
cout << *p << " " << a << endl;

return 0;
}

#14


引用 4 楼 supermegaboy 的回复:
1.存储位置:对于一个const类型的局部变量,真的是分配在“只读数据段”?还是和普通局部变量一样在栈上分配内存?
----------------------------------------------------------------------
只读数据段仅是实现的其中一种方式,目的是在运行时令试图修改const变量的行为产生错误。由于C标准并没有禁止对const变量的修改,而是规……

我比较赞同4楼的解释,我个人觉得,const是一个变量的属性的修饰,其不能决定一个变量的存储位置和生命周期。决定一个变量的存储位置和生命周期是由该变量是局部变量还是全局变量来决定。我觉得const是属于编译器的范畴,一个const变量可以存放在可读写区域,但是当你再程序中修改时,编译阶段就会报错,const变量可能通过其他途径来修改。而内存中的常量区是再操作系统的范畴,如果一个变量被放在常量区,你是无论如何也修改不了的。如
char *ptr="hello";
ptr[0] = 'a';

这段代码在编译阶段没有任何问题,而运行时就出错了,因为操作系统发现你试图写该系统的常量区。

#15


VC调试(TC或BC用TD调试)时按Alt+8、Alt+6和Alt+5,打开汇编窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应内存和寄存器变化,这样过一遍不就啥都明白了吗。
(Linux或Unix下可以在用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。)

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。

不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

#16


MSDN98中的例子walker又名pwalk。完整列出指定进程的内存使用情况,显示进程地址空间内容,装载哪些DLL,代码、数据、堆栈段分配在何处,可以用来检测内存泄漏,监测内存使用。
http://download.csdn.net/detail/zhao4zhong1/3667896

#17


呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:

int ctest(const int a, int b)
{
int *p = &a;
        // a = 2; // 这条语句肯定不能通过编译,C语言保证且只保证了这一条。
*p = a + b;
return *p;
}

void main(void) {
  /* put your own code here */
  
  int t3;
  int t1 = 1, t2 = 2;
  
  t3 = ctest(t1,t2);
  for(;;);

}


在有些编译器上不仅是能通过编译,而且连运行时错误都不会出现。(你可以找个嵌入式领域的编译器,比如keil C, CodeWarrior测试这段代码)

嗯,结论就是,C语言只要求编译器保证const对象不能出现在左边,其它什么都没要求。

#18


const 纠结啊。
路过,学习的。
如果是文件系统中的只读文件,一切都好办了。

#19


不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

#20


引用 17 楼  的回复:
呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:
C/C++ c……


你这段代码是不会改变const 变量的值的。

不行你可以用打印函数打印你修改过的变量的是来来看看到底变了没有。我在vs2005 上面测试你这样是不能改变的

#21


引用 17 楼 finewind 的回复:
呃,你不清楚是对的,因为存储位置、生存周期以及是在何时确定其值都不是const管理的范畴。
你把事情搞得太复杂了。事实上,const是类型限定符,使用const的意图是表示值不会变化的对象,并且C编译器保证程序员不能修改这个对象的值。它的作用有且只有这一个,你不应该指望更多的作用。
另外还需要知道的是,const限定符并不是安全的,程序员可以绕过或者重写它们.下面这段代码:

int ctest(const int a, int b)
{
int *p = &a;
        // a = 2; // 这条语句肯定不能通过编译,C语言保证且只保证了这一条。
*p = a + b;
return *p;
}

void main(void) {
  /* put your own code here */
  
  int t3;
  int t1 = 1, t2 = 2;
  
  t3 = ctest(t1,t2);
  for(;;);

}


在有些编译器上不仅是能通过编译,而且连运行时错误都不会出现。(你可以找个嵌入式领域的编译器,比如keil C, CodeWarrior测试这段代码)

嗯,结论就是,C语言只要求编译器保证const对象不能出现在左边,其它什么都没要求。


我觉得你这个有问题,你想想你传递的t1只不过是个形参而已,根本没有修改实际的t1这个值啊!那这个意思也就是t1没有没修改,const也起作用了

#22


引用 8 楼 zollty 的回复:
回答你问题:
1.局部const,并不是在常量区分配的,编译器会将const优化,类似于宏定义,出现const变量的地方在编译时就已经替换其值了。所以其实根本没有分配丝毫空间。(只有当你取变量的地址时,才会在栈上分配空间,但是这个分配的空间其实没有什么意义的,因为所有取const变量值的地方,编译时就已经替换成初值了)

2.内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了。“只读数据段”的内存,运行完才释放。

3.const int a=1;其实类似于宏定义#define a 1,编译时就将局部函数内的a替换成了1。

4.对于全局的const变量,是在常量区分配内存的,是真正的常量。
"内存是运行时(运行初期)分配的,但分配内存的大小编译时就决定了"只有动态区的才是,静态数据区是编译器分配,程序结束后os释放