const 参数 vs 非常量参数
const param vs non-const param
让我们采用以下两个函数:
#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
:
const
-ness 是如何在这里强制执行的?例如,如果我取消注释 str = "OK!;
它当然会在第一个函数调用中起作用,但不会在第二个函数调用中起作用(error: assignment of read-only parameter ‘str’
)。
局部变量的 const
-ness 是否只是一个编译器构造,它负责检查它,或者如果两个函数的汇编相同,它如何工作?注意:这只是 C,不是 C++(因为我认为他们对待 const 的方式不同)。
编译器通过拒绝生成不兼容的代码来强制执行 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> *
以使调用者相信您的函数(在本例中为 print)不会更改其数据:
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.
}
正确,在大多数实现中它只是一个编译器构造。
在典型的主流 OS 实现中,有一种方法可以将具有 static
存储持续时间的 const
对象放置在实际上由 [=31= 写保护的内存中]的内存管理单元(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 的回答更好地解释了这一点。)
让我们采用以下两个函数:
#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
:
const
-ness 是如何在这里强制执行的?例如,如果我取消注释 str = "OK!;
它当然会在第一个函数调用中起作用,但不会在第二个函数调用中起作用(error: assignment of read-only parameter ‘str’
)。
局部变量的 const
-ness 是否只是一个编译器构造,它负责检查它,或者如果两个函数的汇编相同,它如何工作?注意:这只是 C,不是 C++(因为我认为他们对待 const 的方式不同)。
编译器通过拒绝生成不兼容的代码来强制执行 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> *
以使调用者相信您的函数(在本例中为 print)不会更改其数据:
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.
}
正确,在大多数实现中它只是一个编译器构造。
在典型的主流 OS 实现中,有一种方法可以将具有 static
存储持续时间的 const
对象放置在实际上由 [=31= 写保护的内存中]的内存管理单元(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 的回答更好地解释了这一点。)