有意思的const问题

时间:2022-12-25 19:34:53
   我在VC6.0下运行了下面的代码
         const int b = 0;
int *p = (int *)&b;
*p = 5;
printf("b = %d, p = %d\n",b,*p);
printf("address b = %#x, p = %#x",&b,p);

   输出结果是:
         b = 0, p = 5 
         address b = 0x12ff7c , p = 0x12ff7c
  
         为什么地址相同,值却不相同?
         希望高人指点一下, 谢谢了!

18 个解决方案

#1


编译器做了手脚了,因为b声明为const变量了。
所以在引用b值的地方,是编译为0,而不是编译为 取b。

#2


搜索“常量折叠”

#3


要分清楚指针的地址和指针变量的地址,
这两个是不一样的额

#4


引用 1 楼 healer_kx 的回复:
编译器做了手脚了,因为b声明为const变量了。
 所以在引用b值的地方,是编译为0,而不是编译为 取b。

学习了,还有这么一个名词啊。。。

#5


学习了 

#6


呵呵,搜索了一下“常量折叠”,应该说“常量替换”更好,就是编译器预先已经将常量都替换了,你后面的修改没用了。
哪位大佬介绍一下能否关掉这个编译优化项?

#7


引用 6 楼 v2002750 的回复:
呵呵,搜索了一下“常量折叠”,应该说“常量替换”更好,就是编译器预先已经将常量都替换了,你后面的修改没用了。
 哪位大佬介绍一下能否关掉这个编译优化项?

我以为这个不是优化的问题了!我觉得就应该是这个样子的。

#8


常量重叠
C区以前已经讨论过多次了。

#9


很多人说是常量折叠。。。编译器优化的结果。

#include<iostream>    
using   namespace   std;   
    
int main(int   argc,   char*   argv[])   
{   
  const   int   a   =   1;   
  int   &b   =   const_cast<int&>(a);   
  std::cout   <<   &a   <<   std::endl;   
  std::cout   <<   &b   <<   std::endl;   
  b   =   2;   
  std::cout   <<   a   <<   std::endl;   
  std::cout   <<   b   <<   std::endl;   
  return   0;   
}   


最后特意尝试了下。。。在命令行下,通过CL指定禁止优化选项/Od。。编译了之后。发现好像没起作用。如下。。
F:\CT8\test>cl /Od test.cpp

/out:test.exe
test.obj

F:\CT8\test>test.exe
0012FF74
0012FF74
1
2


这个程序和楼主的类似吧。。。最后在ISO/IEC 14882:2003(E)标准上找到这么一段。说是未定义行为。
楼主完全可以把你这段代码放到C语言编译器下编译编译。。如TC,可以看看结果。。下面这段楼主主要看4条和7条吧。


5.2.11 Const cast

1 The result of the expression const_cast<T>(v) is of type T. If T is a reference type, the result is an
lvalue; otherwise, the result is an rvalue and, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and
function-to-pointer (4.3) standard conversions are performed on the expression v.  Types shall not be
defined in a const_cast.  Conversions that can be performed explicitly using const_cast are listed
below.  No other conversion shall be performed explicitly using const_cast.


2 [Note: Subject to the restrictions in this section, an expression may be cast to its own type using a
const_cast operator. ]

3 For two pointer types T1 and T2 where
T1 is cv1 , 0 pointer to cv1 , 1 pointer to . . .
cv1 ,n   1 pointer to cv1 ,n T
and
T2 is cv2 , 0 pointer to cv2 , 1 pointer to . . . cv2 ,n   1 pointer to cv2 ,n T
where T is any object type or the void type and where cv1 ,k and cv2 ,k may be different cv-qualifications,
an rvalue of type T1 may be explicitly converted to the type T2 using a const_cast.  The result of a
pointer const_cast refers to the original object.

An lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the cast
const_cast<T2&> (where T1 and T2 are object types) if a pointer to T1 can be explicitly converted to
the type pointer to T2 using a const_cast.  The result of a reference const_cast refers to the original object.

5 For a const_cast involving pointers to data members, multi-level pointers to data members and multi-
level mixed pointers and pointers to data members (4.4), the rules for const_cast are the same as those
used for pointers; the “member” aspect of a pointer to member is ignored when determining where the cv-
qualifiers are added or removed by the const_cast.  The result of a pointer to data member
const_cast refers to the same member as the original (uncast) pointer to data member.


6 A null pointer value (4.10) is converted to the null pointer value of the destination type.  The null member
pointer value (4.11) is converted to the null member pointer value of the destination type.

[Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data
member resulting from a const_cast that casts away a const-qualifier
68)
may produce undefined behavior (7.1.5.1).  ]

#10


4 An lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the
 cast const_cast<T2&> (where T1 and T2 are object types) if a pointer to T1 can be
 explicitly converted to the type pointer to T2 using a const_cast.  The result of a 
reference const_cast refers to the original object.


7 [Note: Depending on the type of the object, a write operation through the pointer,
 lvalue or pointer to data member resulting from a const_cast that casts away a 
const-qualifier

#11


。。。没复制完全。。这是第7条的最后一句。
may produce undefined behavior (7.1.5.1).  ]

#12


up

#13


学习了,up!

#14


NND。。。CSDN这啥限制吗。。。一个用户只能连续回复3次。无语了。。。

补充:楼主可以把你自己那段程序放到TC下运行下。。会发现输出与你想的一样。

#15


转自 http://hi.baidu.com/lightningyaoyao/blog/item/c95edc583ef460dc9d820475.html
--常量折叠,在此对它作些解释:

首先来看一个例子:

int main(int argc, char* argv[])
{
const int i=0;
int *j = (int *) &i;
*j=1;
cout<<&i<<endlcout<<j<<endl;
cout<<i<<endl;
cout<<*j<<endl;
return 0;
}

结果是

0012ff7c
0012ff7c

0

1


因为i和j都指向相同的内存地址,所以输出的前两个结果是相同的,但为什么相同的内存里的结果不相同么?--这就是常量折叠.

这个"常量折叠"是     就是在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。可以算作一种编译优化。   
我只是改了这个地址内容,但是i还是0,

因为编译器在优化的过程中,会把碰见的const全部以内容替换掉(跟宏似的: #define PI 3.1415,用到PI时就用3.1415代替),这个出现在预编译阶段;但是在运行阶段,它的内存里存的东西确实改变了!!!

#16


拿分走人~~~~~~~~~~~~~~

#17


   谢谢各位的帮助,小弟懂了,学到个新名词“常量折叠”,呵呵。
   
   再次感谢!

#18


看个例子:
  char *p="china";
C语言编译器会让指针指向变量区的一个无名串,它是常串的替身。因为不可以对常量取址,所以不能用指针指向常串。“china”仅仅是初始化的依据。而c++呢 ?它是让指针指向一个长串,这一电与C语言不同。而这样的语法给人以假象:好像可以通过指针来修改常串,这都知道是改不了的,这也是C++不严密之处。
      const int b = 0; 
     int *p = (int *)&b; 
   强转的作用使pa 指的是a的一个替身,是个无名变量(不是常量),可以通过pa修改变量的值,但
改的不是a的值。奇怪的是这个变量和常量a的地址竟然是同一个!?
这个机制就是对char *p = “China”; 的解释

#1


编译器做了手脚了,因为b声明为const变量了。
所以在引用b值的地方,是编译为0,而不是编译为 取b。

#2


搜索“常量折叠”

#3


要分清楚指针的地址和指针变量的地址,
这两个是不一样的额

#4


引用 1 楼 healer_kx 的回复:
编译器做了手脚了,因为b声明为const变量了。
 所以在引用b值的地方,是编译为0,而不是编译为 取b。

学习了,还有这么一个名词啊。。。

#5


学习了 

#6


呵呵,搜索了一下“常量折叠”,应该说“常量替换”更好,就是编译器预先已经将常量都替换了,你后面的修改没用了。
哪位大佬介绍一下能否关掉这个编译优化项?

#7


引用 6 楼 v2002750 的回复:
呵呵,搜索了一下“常量折叠”,应该说“常量替换”更好,就是编译器预先已经将常量都替换了,你后面的修改没用了。
 哪位大佬介绍一下能否关掉这个编译优化项?

我以为这个不是优化的问题了!我觉得就应该是这个样子的。

#8


常量重叠
C区以前已经讨论过多次了。

#9


很多人说是常量折叠。。。编译器优化的结果。

#include<iostream>    
using   namespace   std;   
    
int main(int   argc,   char*   argv[])   
{   
  const   int   a   =   1;   
  int   &b   =   const_cast<int&>(a);   
  std::cout   <<   &a   <<   std::endl;   
  std::cout   <<   &b   <<   std::endl;   
  b   =   2;   
  std::cout   <<   a   <<   std::endl;   
  std::cout   <<   b   <<   std::endl;   
  return   0;   
}   


最后特意尝试了下。。。在命令行下,通过CL指定禁止优化选项/Od。。编译了之后。发现好像没起作用。如下。。
F:\CT8\test>cl /Od test.cpp

/out:test.exe
test.obj

F:\CT8\test>test.exe
0012FF74
0012FF74
1
2


这个程序和楼主的类似吧。。。最后在ISO/IEC 14882:2003(E)标准上找到这么一段。说是未定义行为。
楼主完全可以把你这段代码放到C语言编译器下编译编译。。如TC,可以看看结果。。下面这段楼主主要看4条和7条吧。


5.2.11 Const cast

1 The result of the expression const_cast<T>(v) is of type T. If T is a reference type, the result is an
lvalue; otherwise, the result is an rvalue and, the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and
function-to-pointer (4.3) standard conversions are performed on the expression v.  Types shall not be
defined in a const_cast.  Conversions that can be performed explicitly using const_cast are listed
below.  No other conversion shall be performed explicitly using const_cast.


2 [Note: Subject to the restrictions in this section, an expression may be cast to its own type using a
const_cast operator. ]

3 For two pointer types T1 and T2 where
T1 is cv1 , 0 pointer to cv1 , 1 pointer to . . .
cv1 ,n   1 pointer to cv1 ,n T
and
T2 is cv2 , 0 pointer to cv2 , 1 pointer to . . . cv2 ,n   1 pointer to cv2 ,n T
where T is any object type or the void type and where cv1 ,k and cv2 ,k may be different cv-qualifications,
an rvalue of type T1 may be explicitly converted to the type T2 using a const_cast.  The result of a
pointer const_cast refers to the original object.

An lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the cast
const_cast<T2&> (where T1 and T2 are object types) if a pointer to T1 can be explicitly converted to
the type pointer to T2 using a const_cast.  The result of a reference const_cast refers to the original object.

5 For a const_cast involving pointers to data members, multi-level pointers to data members and multi-
level mixed pointers and pointers to data members (4.4), the rules for const_cast are the same as those
used for pointers; the “member” aspect of a pointer to member is ignored when determining where the cv-
qualifiers are added or removed by the const_cast.  The result of a pointer to data member
const_cast refers to the same member as the original (uncast) pointer to data member.


6 A null pointer value (4.10) is converted to the null pointer value of the destination type.  The null member
pointer value (4.11) is converted to the null member pointer value of the destination type.

[Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data
member resulting from a const_cast that casts away a const-qualifier
68)
may produce undefined behavior (7.1.5.1).  ]

#10


4 An lvalue of type T1 can be explicitly converted to an lvalue of type T2 using the
 cast const_cast<T2&> (where T1 and T2 are object types) if a pointer to T1 can be
 explicitly converted to the type pointer to T2 using a const_cast.  The result of a 
reference const_cast refers to the original object.


7 [Note: Depending on the type of the object, a write operation through the pointer,
 lvalue or pointer to data member resulting from a const_cast that casts away a 
const-qualifier

#11


。。。没复制完全。。这是第7条的最后一句。
may produce undefined behavior (7.1.5.1).  ]

#12


up

#13


学习了,up!

#14


NND。。。CSDN这啥限制吗。。。一个用户只能连续回复3次。无语了。。。

补充:楼主可以把你自己那段程序放到TC下运行下。。会发现输出与你想的一样。

#15


转自 http://hi.baidu.com/lightningyaoyao/blog/item/c95edc583ef460dc9d820475.html
--常量折叠,在此对它作些解释:

首先来看一个例子:

int main(int argc, char* argv[])
{
const int i=0;
int *j = (int *) &i;
*j=1;
cout<<&i<<endlcout<<j<<endl;
cout<<i<<endl;
cout<<*j<<endl;
return 0;
}

结果是

0012ff7c
0012ff7c

0

1


因为i和j都指向相同的内存地址,所以输出的前两个结果是相同的,但为什么相同的内存里的结果不相同么?--这就是常量折叠.

这个"常量折叠"是     就是在编译器进行语法分析的时候,将常量表达式计算求值,并用求得的值来替换表达式,放入常量表。可以算作一种编译优化。   
我只是改了这个地址内容,但是i还是0,

因为编译器在优化的过程中,会把碰见的const全部以内容替换掉(跟宏似的: #define PI 3.1415,用到PI时就用3.1415代替),这个出现在预编译阶段;但是在运行阶段,它的内存里存的东西确实改变了!!!

#16


拿分走人~~~~~~~~~~~~~~

#17


   谢谢各位的帮助,小弟懂了,学到个新名词“常量折叠”,呵呵。
   
   再次感谢!

#18


看个例子:
  char *p="china";
C语言编译器会让指针指向变量区的一个无名串,它是常串的替身。因为不可以对常量取址,所以不能用指针指向常串。“china”仅仅是初始化的依据。而c++呢 ?它是让指针指向一个长串,这一电与C语言不同。而这样的语法给人以假象:好像可以通过指针来修改常串,这都知道是改不了的,这也是C++不严密之处。
      const int b = 0; 
     int *p = (int *)&b; 
   强转的作用使pa 指的是a的一个替身,是个无名变量(不是常量),可以通过pa修改变量的值,但
改的不是a的值。奇怪的是这个变量和常量a的地址竟然是同一个!?
这个机制就是对char *p = “China”; 的解释