为什么指向常量的指针所指向的位置可以被其他变量赋值?

3
我已经写了这段代码。
#include <stdio.h>
int main() {
     int b = 15;
     const int *foo = &b;
     //b = 20;
     *foo = 20;
     printf("%d", *foo);
     return 0;
}

这意味着foo指向的位置不能改变。这意味着b不能改变。但是当我取消注释b = 20这一行时,它不会显示任何错误,并且我得到输出20。而在这段代码中,我会得到这个错误。

main.c: In function 'main':
main.c:15:10: error: assignment of read-only location '*foo'
      *foo = 20;

如果*foo是只读位置,为什么可以更改其值b = 20

@haccks 常量变量存储在堆栈段的写保护区域中。这是因为它在main()函数中声明了堆栈! - Pradeep Kumar
1
@PradeepKumar 有没有 写保护的栈段区域?如果有的话,它需要是典型栈帧的许多分散区域(在某些实现中)。我的理解是这是对编译器的指令。 - Weather Vane
https://dev59.com/p3I-5IYBdhLWcg3w-96b - Pradeep Kumar
我不是很清楚,但这个区域必须存在直到函数执行的时间,然后与其他局部变量一起清除! - Pradeep Kumar
C语言不强制要求任何物理硬件,甚至不需要栈。 - Weather Vane
显示剩余7条评论
5个回答

8

What does

 int b=15;
 const int* foo=&b;   

mean?

这意味着,

  • b 是可修改的 int 对象。
  • &b 的类型是 int *。对于 foo 的声明将 &b 转换为 const int * 类型。这是一种有效的限定符转换。
  • 不能使用 foo 修改 foo 所指向的 int 对象。

上述限定符转换不会使得 b 的声明无效,即它仍然是可修改的。b 仍然可以用于更新其值,但不能使用 *foo

const 视为一种承诺。你承诺编译器不会更改由 foo 指向的位置上的值,但你仍然可以通过其他方式更改该位置上的值来打破这个承诺。


2
你不能使用 foo 来改变它所指向的位置。但是,没有什么可以阻止你使用其他合法机制来改变被指向的位置。使用 b 是一种合法机制,可以修改 foo 所指向的位置。 - Jonathan Leffler
2
@PradeepKumar:我们可以看出你还没有理解。困难在于弄清楚为什么你还没有理解。foo是一个指向常量值的指针,这意味着你不能使用foo来改变它所指向的内容。但是b不是常量。它是可以被更改的。只是不能使用foo来更改它。 - Jonathan Leffler
@PradeepKumar;我修改了答案并进一步解释了它。请再次阅读。 - haccks
在 Xcode 上它显示为“只读变量不可分配”。 - Pradeep Kumar
@PradeepKumar;是的,这很具有误导性。 - haccks
显示剩余5条评论

7
当chqrlie表示错误信息“assignment of read-only location '*foo'”是误导性的时,我认为他是正确的。实际上,const int *foo(即指向const int的指针)并不意味着由指针寻址的内存本身是或变得不可变;它只是意味着通过该指针不能更改指针寻址的内存;但是,可以通过其他方式更改该内存。例如,您可以定义第二个指针int *foo2 = &b,通过它可以更改由foo2寻址的值。
因此,您可以将指针视为对特定内存地址处值的单独视图,并且可以声明视图是否为只读。然而,这并不影响特定内存地址处的值本身是否是不可变的:
int main()
{
    int b=15;  // b is mutable
    const int* foo=&b; // *foo is immutable; b remains mutable
    int *foo2=&b; // *foo2 is mutable
    b=20;  // allowed, since b is mutable
    *foo=20; // not allowed, since "*foo" is immutable, even if the value to which it points (i.e. b) would be mutable
    *foo2=20; // allowed, since "*foo2" is mutable

    return 0;
}

虽然没有被要求,但反过来说,您实际上可以将一个定义为不可变的,这个值就不能以任何方式被更改;如果您通过写入其内存地址来操纵该值,行为将是未定义的:

const int c = 15; // c is immutable
int* bar = &c; // warning: discards const qualifier
printf("%d\n", c);
*bar = 20;  // undefined behaviour from here on; modifying a const value is not allowed.
printf("%d\n", c); // undefined behaviour; good chance that it still prints 15, because a compiler might have relied on the fact that c is 15 and immutable
printf("%d\n", *bar); // undefined behaviour; probably printing 20?

希望能对你有所帮助。

谢谢您的回答,但我们如何拥有主内存的两个独立视图! - Pradeep Kumar
如果你喜欢的话,可以有任意多个指针,都指向主存储器的相同地址。 - Stephan Lechner
@PradeepKumar 指针只是一个变量,它以地址的形式保存了其他东西的值。您可以拥有许多指针,所有这些指针都保存着相同的地址(指向同一件其他东西)。"views" 可能不是最好的词,但它准确地描述了您通过多个指针查看内存中相同位的能力,如果它们都指向那些位的话。 - David C. Rankin

3
不,const int* foo = &b;并不意味着你不能改变b的值,它意味着你不能解引用该指针并像这样更改解引用指针的值。
*foo = 20;

使用 const 关键字并不能保证某块内存对于所有人都是不可修改的。你只能通过这个关键字来声明一个变量不会修改这块内存,所以如果 b 没有 const 修饰符,即使你有一个指向 b 的 const 指针,对其进行操作仍然是合法的。

 b=20;

@Filip Kočica 我认为这是一个“T”字问题。 - 0___________
1
@PradeepKumar:错误信息显示 *foo 是只读位置,因为 foo 被定义为指向不应通过此指针更改的位置。错误信息有些令人困惑。 - chqrlie
@chqrlie:说得好,错误信息误导性强,很可能是导致OP对代码含义误解的根本原因。 - Stephan Lechner
指针声明和对象声明之间有区别。指针不能用于更改所引用的对象。对象本身可以被更改,但不能通过const指针进行更改。 - 0___________
这里的重点是指向非const的指针可以无缝地转换为相同类型的const指针。这被解释为不打算通过此指针修改值。当然,原始的lvalue不受添加新指针的影响,仍然可写。 - ddbug

0
如果你想要一个指向非常量对象的常量指针,请这样声明它:
 T * const ptr = &t

其中T是对象的类型,t是对象本身。


1
这不是我要问的! - Pradeep Kumar
请Kumar别大声喊叫 :D - kocica
所以要学会如何提问。常量指针意味着常量指针,而不是指向常量对象的指针。责备自己,并感激有人浪费时间回答你的问题。 - 0___________
我承认我的错误。但是你应该读完整个问题并建议修改。无论如何,谢谢! - Pradeep Kumar

0

这个问题的答案是在声明指针之后

const int *foo = &b;

你不能将 *foo 用作 L-value,因为这段代码也会产生相同的错误

#include <stdio.h>
int main() {
     int a=10,b = 15;
     const int *foo = &b;
     foo=&a; //assigning foo the address of a
     *foo=30; // Again here you cant use *foo
     printf("%d", *foo);
     return 0;
}

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