如何初始化C中堆中的只读内存位置?

时间:2018-06-27 22:43:44

标签: c

考虑以下代码:

#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变量?

2 个答案:

答案 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),则会收到警告,因为pconst指针。如果您仅将p声明为char *,那么您将摆脱此警告,但会收到我在上一段中提到的警告。 (而且,当然,通过将其声明为const也将失去您试图获得的所有写保护)

要解决此问题,您可以使用free((char*)p)

释放内存

作为旁注,如果要确保您永远不会更改p指向的位置,则可以像这样const char * const p = init()进行声明。这将使得不可能在代码的后面使p指向其他内容。