const 参数 vs 非常量参数

问题描述

让我们采用以下两个函数

#include<stdio.h>

void my_print1(char* str) {
    // str = "OK!";
    printf("%s\n",str);
}

void my_print2(char* const str) {
    // str = "OK!";
    printf("%s\n",str);
}

它们都产生same assembly

enter image description here

那么 const 是如何在这里强制执行的?例如,如果我取消注释 str = "OK!;,它当然会在第一个函数调用中起作用,但在第二个 (error: assignment of read-only parameter ‘str’) 中不起作用。

局部变量的 const-ness 是否只是编译器构造,它负责检查这一点,或者如果两个函数的程序集相同,它如何工作?注意:这只是 C,不是 C++(因为我认为他们对待 const 是不同的)。

解决方法

正确,在大多数实现中,它只是一个编译器构造。

在典型的主流操作系统实现中,有一种方法可以将具有 const 存储持续时间的 static 对象放置在实际上由 CPU 的内存管理单元 (MMU) 写保护的内存中,例如.text.rodata 部分。然后尝试写入它,如果在编译时没有阻止,将在运行时导致陷阱。但是硬件写保护适用于大内存块(例如整个页面)。对于 auto 对象,例如位于堆栈内存或寄存器中的局部变量或函数参数,没有好的方法可以做到这一点。在堆栈上,由于它们与非 const 变量混合在一起,因此硬件写保护的粒度不足以应用于它们,并且在任何情况下不断更改都会非常昂贵(它需要调用到操作系统)。大多数机器上的寄存器根本不能设为只读。

由于没有好的方法在运行时保护它们,编译器通常为 const 自动对象生成与非 const 完全相同的代码。

您可能会在某些情况下看到差异,因为 const 通知编译器该对象的值不应该改变,因此编译器可以假设它没有改变。例如,如果您将指向非const 对象的指针传递给另一个函数,则编译器必须假设该对象的值可能已更改,并会在每次函数调用后从内存中重新加载它。但是 const 对象可能会在整个函数调用期间将其值缓存在寄存器中,或者如果可能的话只是优化为立即数。

,

函数参数和其他局部变量上的 const 限定符通常对生成的代码没有影响。它只是告诉编译器阻止对变量赋值。

理论上,它可以生成防止通过其他方式修改变量的代码。例如。如果你有

void my_print2(char* const str) {
    *(char *)&str = "OK!";
    printf("%s\n",str);
}

赋值会导致未定义的行为,但不会导致错误(尽管编译器可能会警告丢弃常量)。但是编译器理论上可以将 str 存储在标记为只读的内存中;在这种情况下,分配将导致分段错误。这通常不会对函数参数进行,因为很难将其与使用堆栈用于自动数据相协调。 (Nate Eldredge 的回答更好地解释了这一点。)

,

编译器通过拒绝生成不合规的代码来强制执行 const。如果 my_print2 源尝试修改 str,编译器会发出错误消息。

关于您的代码:

void my_print2(char* const str)...

这有点毫无意义,因为它只能限制函数对指针本身的值可以做什么,而不能对它指向的内存做什么,所以:

void my_print2(char* const str)
{
    str++; // Not allowed,compile time error.
    *str = 'A'; // Okay.
}

不需要修改所指向内容的函数,应声明为 const <type> * 以让调用者确信您的函数(在本例中为打印)不会更改其数据:

void my_print3(const char* str)
{
    str++; // Okay
    *str = 'A'; // Not allowed,compile time error.
}

void my_print4(char const* str)
{
    str++; // Okay
    *str = 'A'; // Not allowed,compile time error.
}