考虑以下代码:
#include<stdio.h>
int main()
{
const char* c = malloc(1);
*c = 'a';
printf("%c\n",*c);
}
此代码显然会引发以下编译错误:
file1.c:7:3: error: assignment of read-only location ‘*c’
*c = 'a';
^
如何在堆内存中初始化const变量?
答案 0 :(得分:5)
首先,要了解没有“只读堆中的位置”之类的东西。堆是100%可写的。 malloc()
被定义为返回void *
。允许您写入malloc
返回的指针所指向的数据。
但是接下来要了解的第二件事是,const
也不一定意味着“只读”。
有 之类的东西,例如只读内存,它通常由const
指针指向,并且如果(尽管有任何const
指针)设法写入只读存储器,通常会遇到某种异常。
但是您可以拥有非const
指针所指向的只读内存,并且可以拥有指向可写内存的const
指针。
所以const
的真正含义是“我保证不写该存储器”或“我声明不写该存储器的意图”,并增加了规定“我希望编译器给出如果我不小心尝试写入此内存,则会出现编译时错误。”
如果您具有普通的const
指针,则可以将其初始化一次。你可以说
const str1[] = "hello";
和
const *str2 = "world";
即使以后尝试更新类似的字符串
*str1 = 'x'; /* WRONG */
或
*str2 = 'y'; /* WRONG */
将会失败。 (您可以将其视为const
限定数据的“初始化例外”。
但是,正如您所发现的,通过调用const
初始化的malloc
指针没有这种例外。如果要从malloc
获取一些内存,并对其进行一次初始化,然后保证以后不再修改它,并且如果要使编译器为您强制执行此承诺,则不能直接执行,除非您使用注释和klutt's answer中所述的两点解决方法。
答案 1 :(得分:1)
堆中没有只读位置。有关详细说明,请参见Steve Summits的答案。
无论您做什么来实现这一目标,都将取决于您,以确保您不在这些位置中书写。您可以很好地保护自己,但不能100%保护自己。
您可以执行以下操作:
char * x = malloc(1);
*x = 'a';
const char * c = x;
// Promise to not use the pointer x again
printf("%c\n",*c);
或者,如果您希望将其封装一些:
const char * init()
{
char* c = malloc(1);
if(c == NULL) { // Always check if allocation is successful
// Code for error handling
}
*c = 'a';
return c;
}
int main()
{
const char * p = init();
printf("%c\n",*p);
}
请注意,仍然可以不使用char * p = init()
来写const
,这确实可以写*p = 'a'
,但是您至少会触发以下警告:
warning: initialization discards ‘const’ qualifier from pointer target
type [-Wdiscarded-qualifiers]
char * p = init();
这里要注意的另一件事是,如果调用free(p)
,则会收到警告,因为p
是const
指针。如果您仅将p
声明为char *
,那么您将摆脱此警告,但会收到我在上一段中提到的警告。 (而且,当然,通过将其声明为const也将失去您试图获得的所有写保护)
要解决此问题,您可以使用free((char*)p)
作为旁注,如果要确保您永远不会更改p
指向的位置,则可以像这样const char * const p = init()
进行声明。这将使得不可能在代码的后面使p
指向其他内容。