为什么我可以通过指针强制转换来更改局部 const 变量,但不能在 C 中更改全局变量?

发布于 2024-07-23 06:38:00 字数 761 浏览 1 评论 0原文

我想通过使用指针来更改常量的值。

考虑以下代码

int main()
{
    const int const_val = 10;
    int *ptr_to_const = &const_val;

    printf("Value of constant is %d",const_val);
    *ptr_to_const = 20;
    printf("Value of constant is %d",const_val);
    return 0;
}

正如预期的那样,常量的值被修改。

但是当我使用全局常量尝试相同的代码时,我收到以下运行时错误。 Windows 崩溃报告器正在打开。 在打印此语句“*ptr_to_const = 20;”中的第一个 printf 语句后,可执行文件将停止

考虑以下代码

const int const_val = 10;
int main()
{
    int *ptr_to_const = &const_val;
    printf("Value of constant is %d",const_val);
    *ptr_to_const = 20;
    printf("Value of constant is %d",const_val);
    return 0;
}

该程序是在 mingw 环境中使用 codeblocks IDE 编译的。

谁能解释一下发生了什么事吗?

I wanted to change value of a constant by using pointers.

Consider the following code

int main()
{
    const int const_val = 10;
    int *ptr_to_const = &const_val;

    printf("Value of constant is %d",const_val);
    *ptr_to_const = 20;
    printf("Value of constant is %d",const_val);
    return 0;
}

As expected the value of constant is modified.

but when I tried the same code with a global constant, I am getting following run time error.
The Windows crash reporter is opening. The executable is halting after printing the first printf statement in this statement "*ptr_to_const = 20;"

Consider the following code

const int const_val = 10;
int main()
{
    int *ptr_to_const = &const_val;
    printf("Value of constant is %d",const_val);
    *ptr_to_const = 20;
    printf("Value of constant is %d",const_val);
    return 0;
}

This program is compiled in mingw environment with codeblocks IDE.

Can anyone explain what is going on?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

冷情妓 2024-07-30 06:38:00

它是一个常量,并且您正在使用一些技巧来更改它,因此会导致未定义的行为。 全局常量可能位于只读存储器中,因此无法修改。 当您尝试这样做时,您会收到运行时错误。

常量局部变量在堆栈上创建,可以修改。 所以在这种情况下你可以改变常量,但它仍然可能会导致奇怪的事情。 例如,编译器可以在各个地方使用常量的值而不是常量本身,因此“更改常量”不会在这些地方显示任何效果。

It's a constant and you are using some tricks to change it anyway, so undefined behavior results. The global constant is probably in read-only memory and therefore cannot be modified. When you try to do that you get a runtime error.

The constant local variable is created on the stack, which can be modified. So you get away with changing the constant in this case, but it might still lead to strange things. For example the compiler could have used the value of the constant in various places instead of the constant itself, so that "changing the constant" doesn't show any effect in these places.

Spring初心 2024-07-30 06:38:00

它在只读存储器中!

基本上,您的计算机使用两级页表系统将虚拟地址解析为物理地址。 伴随着这个宏大的数据结构而来的是一个特殊的位,表示页面是否可读。 这很有帮助,因为用户进程可能不应该过度编写自己的程序集(尽管自修改代码有点酷)。 当然,他们可能也不应该过度编写自己的常量变量。

您不能将“const”函数级变量放入只读内存中,因为它位于堆栈中,并且必须位于读写页面上。 但是,编译器/链接器会看到您的 const,并通过将其放入只读内存(它是常量)来帮您一个忙。 显然,覆盖它会给内核带来各种不愉快,内核会通过终止进程来消除对进程的愤怒。

It's in read only memory!

Basically, your computer resolves virtual to physical addresses using a two level page table system. Along with that grand data structure comes a special bit representing whether or not a page is readable. This is helpful, because user processes probably shouldn't be over writing their own assembly (although self-modifying code is kind of cool). Of course, they probably also shouldn't be over writing their own constant variables.

You can't put a "const" function-level variable into read only memory, because it lives in the stack, where it MUST be on a read-write page. However, the compiler/linker sees your const, and does you a favor by putting it in read only memory (it's constant). Obviously, overwriting that will cause all kinds of unhappiness for the kernel who will take out that anger on the process by terminating it.

挽袖吟 2024-07-30 06:38:00

仅当您确定所指向的变量最初是非常量的(并且您恰好有一个指向它的常量指针)时,在 C 和 C++ 中放弃指针常量性才是安全的。 否则,它是未定义的,并且根据您的编译器、月相等,第一个示例也很可能失败。

Casting away pointer const-ness in C and C++ is only safe if you are certain that the pointed-to variable was originally non-const (and you just happen to have a const pointer to it). Otherwise, it is undefined, and depending on your compiler, the phase of the moon, etc, the first example could very well fail as well.

被翻牌 2024-07-30 06:38:00

您甚至不应该期望该值首先被修改。 根据标准,这是未定义的行为。 全局变量和首先都是错误的。 只是不要这样做:) 它可能会以另一种方式崩溃,或者本地和全局都崩溃。

You should not even expect the value to be modified at the first place. According to the standard, it is undefined behavior. It is wrong both with a global variable and in the first place. Just don't do it :) It could have crashed the other way, or with both local and global.

束缚m 2024-07-30 06:38:00

这里有两个错误。 第一个是:

int *ptr_to_const = &const_val;

根据 C11 6.5.4/3,这是一个约束违规(早期标准有类似的文本):

约束

涉及指针的转换,除非 6.5.16.1 的约束允许,否则应通过显式强制转换来指定

const int *的转换>int * 是 6.5.16.1 的约束不允许的(可以查看 此处)。

令人困惑的是,当某些编译器遇到约束违规时,它们会写“警告”(甚至什么也不写,具体取决于开关)并假装您在代码中写了其他内容,然后继续。 这通常会导致程序的行为不符合程序员的预期,或者实际上不以任何可预测的方式行为。 为什么编译器要这样做? 这让我很困惑,但这确实会引发无穷无尽的此类问题。


gcc,看起来就像您编写了 int *ptr_to_const = (int *)&const_val; 一样进行。

这段代码并不违反约束,因为使用了显式强制转换。 然而,这给我们带来了第二个问题。 然后,*ptr_to_const = 20; 行尝试写入 const 对象。 这会导致未定义的行为,标准中的相关文本位于6.7.3 /6:

如果尝试通过使用非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。

该规则是语义规则,而不是约束规则,这意味着标准不要求编译器发出任何类型的警告或错误消息。 该程序是错误的,并且可能会以无意义的方式运行,并出现任何奇怪的症状,包括但不限于您观察到的情况。

There are two errors here. The first one is:

int *ptr_to_const = &const_val;

which is a constraint violation according to C11 6.5.4/3 (earlier standards had similar text):

Constraints

Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast

The conversion from const int * to int * is not permitted by the constraints of 6.5.16.1 (which can be viewed here).

Confusingly, when some compilers encounter a constraint violation, they write "warning" (or even nothing at all, depending on switches) and pretend that you wrote something else in your code, and carry on. This often leads to programs that do not behave as the programmer expected, or in fact don't behave in any predictable way. Why do compilers do this? Beats me, but it certainly makes for an endless stream of questions like this.


gcc, appears to proceed as if you had written int *ptr_to_const = (int *)&const_val;.

This piece of code is not a constraint violation because an explicit cast is used. However this brings us to the second problem. The line *ptr_to_const = 20; then tries to write to a const object. This causes undefined behaviour, the relevant text from the Standard is in 6.7.3/6:

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.

This rule is a Semantic, not a Constraint, which means that the Standard does not require the compiler to emit any sort of warning or error message. The program is just wrong and may behave in nonsensical ways, with any sort of strange symptoms, including but not limited to what you observed.

养猫人 2024-07-30 06:38:00

由于此行为未在规范中定义,因此它是特定于实现的,因此不可移植,因此不是一个好主意。

为什么要改变常量的值?

Since this behavior is not defined in the specification, it is implementation-specific, so not portable, so not a good idea.

Why would you want to change the value of a constant?

¢好甜 2024-07-30 06:38:00

注意:这是对 的回答我们可以通过指针更改使用 const 定义的对象的值吗? 链接到此问题作为重复项。

该标准没有要求编译器必须对构造指向 a 的指针的代码执行什么操作 。 const 对象并尝试写入它。 某些实现(尤其是嵌入式实现)可能具有有用的行为(例如,使用非易失性 RAM 的实现可以合法地将 const 变量放置在可写的内存区域中,但其内容将保留即使该单元断电并重新启动),并且该标准对编译器如何处理创建指向 const 内存的非 const 指针的代码没有提出任何要求不影响此类代码在明确允许的实现上的合法性。 然而,即使在这样的实现上,替换如下内容可能也是一个好主意:

volatile const uint32_t action_count;

BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to
BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA
BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number
*((uint32_t*)&action_count)++;
BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage

如果

void protected_ram_store_u32(uint32_t volatile const *dest, uint32_t dat)
{
    BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to
    BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA
    BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number
    *((volatile uint32_t*)dest)=dat;
    BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage
}
void protected_ram_finish(void) {}
...
protected_ram_store(&action_count, action_count+1);
protected_ram_finish();

编译器很容易对写入 const 存储的代码应用不需要的“优化”,则将“protected_ram_store”移动到单独编译的模块可以防止此类优化。 它也可能很有帮助,例如代码需要移动到使用其他协议写入内存的硬件。 例如,某些硬件可能会使用更复杂的写入协议来最大限度地减少错误写入的可能性。 拥有一个明确目的是写入“通常常量”内存的例程将使这种意图变得清晰。

Note: this is intended as an answer to Can we change the value of an object defined with const through pointers? which links to this question as a duplicate.

The Standard imposes no requirements on what a compiler must do with code that constructs a pointer to a const object and attempts to write to it. Some implementations--especially embedded ones--might possibly have useful behaviors (e.g. an implementation which uses non-volatile RAM could legitimately place const variables in an area of memory which is writable, but whose contents will remain even if the unit is powered down and back up), and the fact that the Standard imposes no requirements about how compilers handle code that creates non-const pointers to const memory does not affect the legitimacy of such code on implementations which expressly allow it. Even on such implementations, however, it's probably a good idea to replace something like:

volatile const uint32_t action_count;

BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to
BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA
BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number
*((uint32_t*)&action_count)++;
BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage

with

void protected_ram_store_u32(uint32_t volatile const *dest, uint32_t dat)
{
    BYPASS_WRITE_PROTECT = 0x55; // Hardware latch which enables writing to
    BYPASS_WRITE_PROTECT = 0xAA; // const memory if written with 0x55/0xAA
    BYPASS_WRITE_PROTECT = 0x04; // consecutively followed by the bank number
    *((volatile uint32_t*)dest)=dat;
    BYPASS_WRITE_PROTECT = 0x00; // Re-enable write-protection of const storage
}
void protected_ram_finish(void) {}
...
protected_ram_store(&action_count, action_count+1);
protected_ram_finish();

If a compiler would be prone to apply unwanted "optimizations" to code which writes to const storage, moving "protected_ram_store" into a separately-compiled module could serve to prevent such optimizations. It could also be helpful e.g. the code needs to move to hardware which uses some other protocol to write to memory. Some hardware, for example, might use a more complicated write protocols to minimize the probability of erroneous writes. Having a routine whose express purpose is to write to "normally-const" memory will make such intentions clear.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文