任何人都可以帮助我解决 C 中的这个指针问题



任何人都可以帮助我理解这段代码(在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作为指向非constint的指针。表达式&a的类型是const int *(指向const int的指针),但p的类型仅为int *(指向int的指针)。 在作业中

p = (int *) &a;

您正在抛弃aconst预选赛。 因此,当您将新值写入*p时,编译器不知道您正在尝试修改声明为const的内容,因此它不会将其标记为错误。

C 语言定义指出,尝试通过非const限定的lvalue 1更新const限定对象会导致未定义的行为- 编译器不需要以任何特定方式处理这种情况。 结果可以是以下任一选项:

  • 运行时错误;
  • a的价值保持不变;
  • 行为完全符合预期;

或者完全是别的东西。


  1. 左值是指定对象的任何表达式,以便读取或修改对象 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的内存内容。在我们的例子中,这将获得6a变量的内容。

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兼容-指针类型-丢弃-限定符]

这说明了为什么应始终尝试避免显式类型强制转换(除非绝对必要)。这段代码的作者搬起石头砸自己的脚。他们引入了未定义的行为,并且明显的问题被隐藏了,因为警告被丢弃或抑制了。

最新更新