当指针指向使用 malloc() 获得的内存位置时,编译器如何处理 CONST 限定符?
How do compilers treat CONST qualifier when the pointer points to a memory location obtained with malloc()?
我真的不知道怎么取一个更好的标题,请大家多多包涵。
我知道的。
当我做这样的事情时:
#include <stdio.h>
int main ( void ){
const char *arr = "Hello";
arr[0] = 'B';
printf( "Arr = %s\n", arr );
}
我不会得到 segfault
因为应用 const
限定符我向编译器承诺我不会触及 arr
指向的那个值。
至少在我的系统上(Linux mint 18.3 with GCC 7.2.0)我得到:
program.c:6:16: error: assignment of read-only location ‘*arr’
arr[0] = 'B';
^
下一个
当我这样做时:
const char *const arr = "Hello";
如以下程序:
#include <stdio.h>
int main ( void ){
const char *const arr = "Hello";
while ( *arr != '[=13=]' ){
arr++;
}
printf( "Arr = %s\n", arr );
}
编译器也知道我保证不会递增指针并且它看到了:
program.c:7:16: error: increment of read-only variable ‘arr’
arr++;
^~
我不明白。
编译器究竟如何处理这种情况:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr = calloc( 256 * sizeof( *arr ), sizeof( *arr ) );
strcpy ( (char*)arr , "Hello" );
printf( "Arr = %s\n", arr );
free ( (char*)arr );
}
在这里,当我调用 strcpy()
时我被迫施放 arr
并且 free()
也是如此。
但是为什么编译器看不到(忽略)这样一个事实,即即使我做了一个 "promise" 我不会尝试修改该变量,它也会忽略 const
限定符?
此外,当我执行以下操作时:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr;
while ( *arr != '[=16=]' ){
arr++;
}
free ( (char*)arr );
}
编译器发现我正在尝试递增指针:
program.c:9:16: error: increment of read-only variable ‘arr’
arr++;
^~
但是忽略了我承诺过我不会修改这个值
此处涉及 malloc
的事实是否对编译器有某种意义,即它需要忽略所有这些 const
限定符? ...
还是我在这里遗漏了一些重要的东西?
这一部分很简单:
strcpy ( (char*)arr , "Hello" );
看到 (char*)
了吗?当你这样做时,你收回了承诺。如果你写了
strcpy (arr, "Hello");
没有转换,编译器会反对。
另一部分是了解 const
C 中指针上的注释仅与指向的内存位置是否可写有微弱的联系。 malloc
和 calloc
返回的堆块总是在 是 可写的内存区域中。当您将 const
指针指向由 malloc
返回的堆块时,编译器将反对任何通过该指针进行写入的尝试,但是如果您丢弃 const
(取返回承诺),或者如果你只是忽略警告,写入将在运行时正常工作,因为内存区域仍然是可写的,即使指针是 const
.
相反,字符串文字通常位于不可写的内存区域。您仍然可以使用非 const
指针来引用它们,并且编译器不会反对您通过这些指针进行写入,但是写入 不会 在运行时工作,因为内存区域是只读的。并且用 const
声明的 数据对象 (例如 const int numbers[] = { 1, 2, 3 };
)也被放入只读内存区域,所以你不能再次写入它们——无论或者你不是通过一个非const
指针来做的。
通常可以改变一个内存区域是否可写,但是你必须使用像mprotect这样的操作系统函数。转换不会为您做这些——事实上,它们通常根本不会生成任何代码。
此处投给char *
strcpy ( (char*)arr , "Hello" );
让编译器忽略你的承诺只在这个地方。
如果 arr
指向的对象最初是作为非常量创建的,那么你没问题。如果不是,那么它是 未定义的行为,您可能会遇到段错误。
如果指针最初不是 const
,您可以从指针中删除 const
。
calloc
returns一个void *
。当您将其分配给 const char *
时,您会添加 const
。但是C的规则允许你临时加const
,以后再去掉。您可能想要这样做,例如,当某些例程创建数据时,然后将数据提供给其他一些只应读取数据的例程,但稍后,指针被传回以被释放或进一步更改,在这种情况下const
必须删除。
总之,const
是给想守规矩的软件一个便利。当您不小心尝试使用 const
指针进行写入时,它要求编译器通知您。它确实一定会阻止您通过不恰当地删除 const
来故意违反规则。
(开篇说得更准确一点:C对指针的转换非常灵活,可以在很大程度上转换不同类型的指针,只要满足对齐要求即可。它实际上是使用指针访问运行时的内存做错了会惹上麻烦。)
直接应用于变量定义的const
意味着该变量的值永远不会改变。
但是当一个指针指向一个 const
类型时,这并不意味着它指向的东西永远不会改变,只是它不能通过那个特定的指针改变。
例如,这个程序是完全有效的:
#include <stdio.h>
int main(void) {
char str[] = "abcd";
const char* p = str;
str[0] = 'x';
printf("%s\n", p); /* Prints "xbcd" */
return 0;
}
请注意,p
指向的数据已通过其原始对象进行了更改,即使 p
被声明为指向 const。然后p
继续指向有新值的对象str[0]
。
在您的calloc
示例中,没有const
变量定义,因此不能保证数据不变。您承诺不会仅通过 arr
更改它。但是随后显式转换会创建另一个没有 const
限制的指针,并且可以使用它更改最初的非常量数据。
我真的不知道怎么取一个更好的标题,请大家多多包涵。
我知道的。
当我做这样的事情时:
#include <stdio.h>
int main ( void ){
const char *arr = "Hello";
arr[0] = 'B';
printf( "Arr = %s\n", arr );
}
我不会得到 segfault
因为应用 const
限定符我向编译器承诺我不会触及 arr
指向的那个值。
至少在我的系统上(Linux mint 18.3 with GCC 7.2.0)我得到:
program.c:6:16: error: assignment of read-only location ‘*arr’
arr[0] = 'B';
^
下一个
当我这样做时:
const char *const arr = "Hello";
如以下程序:
#include <stdio.h>
int main ( void ){
const char *const arr = "Hello";
while ( *arr != '[=13=]' ){
arr++;
}
printf( "Arr = %s\n", arr );
}
编译器也知道我保证不会递增指针并且它看到了:
program.c:7:16: error: increment of read-only variable ‘arr’
arr++;
^~
我不明白。
编译器究竟如何处理这种情况:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr = calloc( 256 * sizeof( *arr ), sizeof( *arr ) );
strcpy ( (char*)arr , "Hello" );
printf( "Arr = %s\n", arr );
free ( (char*)arr );
}
在这里,当我调用 strcpy()
时我被迫施放 arr
并且 free()
也是如此。
但是为什么编译器看不到(忽略)这样一个事实,即即使我做了一个 "promise" 我不会尝试修改该变量,它也会忽略 const
限定符?
此外,当我执行以下操作时:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr;
while ( *arr != '[=16=]' ){
arr++;
}
free ( (char*)arr );
}
编译器发现我正在尝试递增指针:
program.c:9:16: error: increment of read-only variable ‘arr’
arr++;
^~
但是忽略了我承诺过我不会修改这个值
此处涉及 malloc
的事实是否对编译器有某种意义,即它需要忽略所有这些 const
限定符? ...
还是我在这里遗漏了一些重要的东西?
这一部分很简单:
strcpy ( (char*)arr , "Hello" );
看到 (char*)
了吗?当你这样做时,你收回了承诺。如果你写了
strcpy (arr, "Hello");
没有转换,编译器会反对。
另一部分是了解 const
C 中指针上的注释仅与指向的内存位置是否可写有微弱的联系。 malloc
和 calloc
返回的堆块总是在 是 可写的内存区域中。当您将 const
指针指向由 malloc
返回的堆块时,编译器将反对任何通过该指针进行写入的尝试,但是如果您丢弃 const
(取返回承诺),或者如果你只是忽略警告,写入将在运行时正常工作,因为内存区域仍然是可写的,即使指针是 const
.
相反,字符串文字通常位于不可写的内存区域。您仍然可以使用非 const
指针来引用它们,并且编译器不会反对您通过这些指针进行写入,但是写入 不会 在运行时工作,因为内存区域是只读的。并且用 const
声明的 数据对象 (例如 const int numbers[] = { 1, 2, 3 };
)也被放入只读内存区域,所以你不能再次写入它们——无论或者你不是通过一个非const
指针来做的。
通常可以改变一个内存区域是否可写,但是你必须使用像mprotect这样的操作系统函数。转换不会为您做这些——事实上,它们通常根本不会生成任何代码。
此处投给char *
strcpy ( (char*)arr , "Hello" );
让编译器忽略你的承诺只在这个地方。
如果 arr
指向的对象最初是作为非常量创建的,那么你没问题。如果不是,那么它是 未定义的行为,您可能会遇到段错误。
如果指针最初不是 const
,您可以从指针中删除 const
。
calloc
returns一个void *
。当您将其分配给 const char *
时,您会添加 const
。但是C的规则允许你临时加const
,以后再去掉。您可能想要这样做,例如,当某些例程创建数据时,然后将数据提供给其他一些只应读取数据的例程,但稍后,指针被传回以被释放或进一步更改,在这种情况下const
必须删除。
总之,const
是给想守规矩的软件一个便利。当您不小心尝试使用 const
指针进行写入时,它要求编译器通知您。它确实一定会阻止您通过不恰当地删除 const
来故意违反规则。
(开篇说得更准确一点:C对指针的转换非常灵活,可以在很大程度上转换不同类型的指针,只要满足对齐要求即可。它实际上是使用指针访问运行时的内存做错了会惹上麻烦。)
直接应用于变量定义的const
意味着该变量的值永远不会改变。
但是当一个指针指向一个 const
类型时,这并不意味着它指向的东西永远不会改变,只是它不能通过那个特定的指针改变。
例如,这个程序是完全有效的:
#include <stdio.h>
int main(void) {
char str[] = "abcd";
const char* p = str;
str[0] = 'x';
printf("%s\n", p); /* Prints "xbcd" */
return 0;
}
请注意,p
指向的数据已通过其原始对象进行了更改,即使 p
被声明为指向 const。然后p
继续指向有新值的对象str[0]
。
在您的calloc
示例中,没有const
变量定义,因此不能保证数据不变。您承诺不会仅通过 arr
更改它。但是随后显式转换会创建另一个没有 const
限制的指针,并且可以使用它更改最初的非常量数据。