当指针指向使用malloc()获得的内存位置时,编译器如何处理CONST限定符?

问题描述 投票:2回答:4

我真的不知道如何提出更好的头衔,所以请耐心等待我并怜悯。

我知道的。

当我做这样的事情时:

#include <stdio.h>

int main ( void ){
    const char *arr = "Hello";

    arr[0] = 'B';
    printf( "Arr = %s\n", arr );
}

我不会得到segfault,因为应用const限定符我向编译器做出了承诺,我不会触及arr指向的那个值。

至少在我的系统上(Linux模板18.3与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 != '\0' ){
        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 );
}

在这里,当我打电话给arrstrcpy()时,我被迫施展free()。 但是为什么编译器没有看到(忽略)这样一个事实:即使我做出了“承诺”,我也不会尝试修改该变量,而忽略了const限定符?

而且,当我做以下事情时:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ( void ){
    const char *const arr;

    while ( *arr != '\0' ){
        arr++;
    }

    free ( (char*)arr );
}

编译器看到我正在尝试增加指针:

program.c:9:16: error: increment of read-only variable ‘arr’
             arr++;
                ^~

但忽略了这样一个事实,即我承诺不会修改该值。

这里涉及malloc的事实是否对编译器有某种意义,它需要忽略所有那些const限定符? ... 或者我错过了一些重要的东西?

c gcc malloc linuxmint
4个回答
7
投票

如果它最初不是const,你可以从指针中删除const

calloc返回void *。将它分配给const char *时,添加const。但是C的规则允许你暂时添加const并在以后删除它。您可能希望这样做,例如,当某些例程创建数据时,然后将数据提供给应该只读取数据的其他一些例程,但是,稍后,指针将被传回以释放或进一步更改,在这种情况下必须删除const

简而言之,const对于想要遵守规则的软件来说是一种便利。它要求编译器在您不小心尝试使用const指针时通知您。它必然会阻止你通过不恰当地删除const故意违反规则。

(更准确地说一下开头的句子:C对指针转换非常灵活。只要满足对齐要求,你就可以在很大程度上转换不同类型的指针。它实际上是使用指针来访问在错误地完成时遇到麻烦的内存。)


4
投票

部分原因很简单:

strcpy ( (char*)arr , "Hello" );

看到(char*)?当你这样做时,你会收回承诺。如果你写过

strcpy (arr, "Hello");

如果没有强制转换,编译器就会反对。

另一部分是理解C中指针的const注释只与指向内存位置是否可写弱连接。 malloccalloc返回的堆块总是位于可写的存储区中。当您将const指针设置为指向malloc返回的堆块时,编译器将反对任何尝试通过该指针进行写入,但是如果您丢弃了const(收回承诺),或者您只是忽略警告,写入将在运行时正常工作,因为即使指针是const,内存区域仍然是可写的。

相反,字符串文字通常位于不可写的内存区域中。您仍然可以使用非const指针来引用它们,并且编译器不会反对您通过这些指针编写,但写入将无法在运行时工作,因为内存区域是只读的。用const声明的数据对象(例如const int numbers[] = { 1, 2, 3 };)也放在一个只读存储区中,所以再次你不能写入它们 - 无论你是否通过非const指针执行它。

通常可以更改内存区域是否可写,但您必须使用mprotect等操作系统函数。演员不会为你做那件事 - 事实上他们通常根本不会生成任何代码。


2
投票

直接应用于变量定义的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限制的指针,并且可以使用它来更改最初的非const数据。


1
投票

在这里演员到char *

strcpy ( (char*)arr , "Hello" );

使编译器只在这个地方忽略你的承诺。

如果arr指向的对象最初创建为非const,那么你很好。如果没有,那么它是未定义的行为,您可能会遇到段错误。

© www.soinside.com 2019 - 2024. All rights reserved.