任何人都可以帮助我理解这段代码(在C中)。
#include <stdio.h>
void main()
{
const int a =5;int b;
int *p;
p= (int *) &a;
b=a;
*p= *p +1;
printf(" value of p is = %dn", *p);
printf(" value of b is = %dn", b);
printf(" value of a is = %dn",a);
}
结果是
:p 的值为 = 6
:b 的值 = 5
: a 的值 is = 6
根据指令p = (int *)&a;
你p
指向a
.
作为结果表达式*p
引用变量a
,因此*p = *p + 1;
等效于a = a + 1;
– 变量a
被分配了它以前的值(即 5)递增 1.
所以它最终是 6。
然而,这是一个未定义的行为,正如@interjay在此评论中指出的那样——a
变量被声明为const
,这意味着它不能被修改。因此,编译器可能会选择将其分配给内存的只读区域。在您的情况下,它没有,并且赋值成功,但在其他情况下,变量的修改可能会静默失败(a
值仍为 5)或产生内存访问异常(并终止程序)或其他什么。
此代码的目的是通过指针p
更新a
的值。 让我们剥离一些东西,从基础开始:
int a = 5;
int *p = &a;
我们有一个名为a
的对象,它存储整数值5
. 我们有另一个名为p
的对象,用于存储a
的地址. 在上述两个声明之后,满足以下条件:
p == &a == some address value
*p == a == 5
表达式*p
等效于表达式a
- 为*p
分配一个新值与将新值分配给a
相同,因此
*p = *p + 1
和写作一样
a = a + 1
但是,在您发布的代码中a
已被声明为constint
。 这意味着您告诉编译器,a
的值不应在其生命周期内更改。 编译器将标记任何语句,例如
a = a + 1
或
a++;
作为错误。 编译器还可以将a
存储在只读内存中;如果你从不获取a
的地址(也就是说,如果它从来不是一元&
的操作数),编译器可能根本不会为它保留任何存储,而只是用值替换它的任何实例(IOW,任何您希望在机器代码中看到对a
的引用的地方,你只会看到文字5
), 这意味着根本没有什么可写的。
但是这段代码作弊- 它声明p
作为指向非const
int
的指针。表达式&a
的类型是const int *
(指向const int
的指针),但p
的类型仅为int *
(指向int
的指针)。 在作业中
p = (int *) &a;
您正在抛弃a
的const
预选赛。 因此,当您将新值写入*p
时,编译器不知道您正在尝试修改声明为const
的内容,因此它不会将其标记为错误。
C 语言定义指出,尝试通过非const
限定的lvalue 1更新const
限定对象会导致未定义的行为- 编译器不需要以任何特定方式处理这种情况。 结果可以是以下任一选项:
- 运行时错误;
a
的价值保持不变;- 行为完全符合预期;
或者完全是别的东西。
- 左值是指定对象的任何表达式,以便读取或修改对象 my。
a
和*p
都是引用包含值的整数对象的 lvalue5
在解释之前,让我告诉你,这是部分有点问题,并且可能导致未定义的行为:
const int a =5;
p= (int *) &a;
您应该删除const
,因为在这种情况下,您希望通过指针修改a
的内容。某些编译器(例如 clang,正如有人在评论中提到的)可能会执行优化,例如将 const 变量的使用位置替换为其值,以减少内存访问操作的数量。
假设每个变量都是一个小盒子,您可以在其中输入数字。所以你有 3 个盒子:
+---+ +---+ +---+
| 5 | | | | |
+---+ +---+ +---+
a b p
现在让我们回顾一下每个语句,看看发生了什么。
int *p;
p= (int *) &a;
p
被定义为指针,指针是一种可以在内存中保存某些内容的地址的变量。在这种情况下,它被分配了变量a
的地址。所以我们的盒子现在看起来像这样:
+---------------+
v |
+---+ +---+ +----+
| 5 | | | | &a |
+---+ +---+ +----+
a b p
p
只包含a
的内存地址。你可以用printf("%u", a)
打印它,你会看到一些数字。那是a
的地址。
b=a;
这里我们将a
的值复制到b
中,因此我们的框变为:
+---------------+
v |
+---+ +---+ +----+
| 5 | | 5 | | &a |
+---+ +---+ +----+
a b p
<小时 />*p= *p +1;
使用*p
语法,我们可以取消引用指针,这意味着我们可以访问p
指向的内存(跟随箭头)。在这种情况下,*p
将允许我们获取或设置a
变量的内容。我们的盒子现在变成这样:
+---------------+
v |
+---+ +---+ +----+
| 6 | | 5 | | &a |
+---+ +---+ +----+
a b p
<小时 />printf(" value of p is = %dn", *p);
在这里,您再次取消引用p,这意味着我们正在获取地址p
的内存内容。在我们的例子中,这将获得6
的a
变量的内容。
printf(" value of b is = %dn", b);
查看b
框,我们可以看到它包含5
.
printf(" value of a is = %dn",a);
我们使用指针修改了a
。查看a
框,我们可以看到它包含值6
.
问题完全出在以下行:
p= (int *) &a;
如果没有类型转换,该行将显示为:
p = &a;
a
的类型是const int
.p
的类型是int *
.
很明显,编译器应该警告丢弃const
限定符:
警告:从"const int *"分配给"int *"会丢弃限定符 [-Win兼容-指针类型-丢弃-限定符]
这说明了为什么应始终尝试避免显式类型强制转换(除非绝对必要)。这段代码的作者搬起石头砸自己的脚。他们引入了未定义的行为,并且明显的问题被隐藏了,因为警告被丢弃或抑制了。