是否可以在C中更改const结构成员?

时间:2017-06-14 01:04:06

标签: c

我必须在C中更改const结构实例的const成员。

我已经知道可以按如下方式更改const基本类型:

const int a = 2;
*(int*)&a = 3;

我也可以按如下方式更改结构实例的const成员:

typedef struct ST {
  const int a;
  const int b;
}ST;
ST st = {.a = 1,.b =2};

int main() {
    *(int *)(&((*(ST*)(&st)).a)) = 5; //works fine , no problem
}

但是,如果实例是常量,我没有成功尝试更改struct实例的const成员:

typedef struct ST {
  const int a;
  const int b;
}ST;

const ST st = {.a = 2,.b=3}; //const instance

int main() {
    *(int *)(&((*(ST*)(&st)).a)) = 5; //does not work(Seg fault)!!
}

那么,是否可以改变" a"最后一个场景中的成员价值? 如果没有,为什么?

3 个答案:

答案 0 :(得分:10)

  

我已经知道可以按如下方式更改const基本类型:

const int a = 2;
*(int*)&a = 3;

您的编译器允许您这样做的事实并不合法。此代码调用未定义的行为,因此如果您在其他平台上运行该程序或使用其他编译器,则同一程序可能会失败或崩溃。

*(int *)(&((*(ST*)(&st)).a)) = 5; //works fine , no problem

此代码具有相同的问题:它调用未定义的行为。

您尝试的最后一段代码(您猜对了!)未定义的行为。但是,这次程序崩溃而不是运行完成。

答案 1 :(得分:1)

C编译器通常将常量放在只读内存段中,通常称为.TEXT或.CODE。该段(存储器块)受到操作系统的写入保护,或者对于小型嵌入式CPU而言,SoC常量通常与代码一起放在ROM /或闪存中。不建议您尝试做什么,因为它会影响使用常量的所有模块。

如果您对内存分段感兴趣并尝试了编译器和链接器如何管理内存,请尝试更改链接选项以生成映射文件。

[编辑]但事实上,如果常量位于可写数据段中,您可以轻松更改const结构中的const成员。你可以试试这个:

int someFunc()
{
    const ST CONST_ONSTACK = { .a = 10, .b = 20 }; // the constant is on the stack...
    *(int*)&(CONST_ONSTACK.a) = 3}
    return CONST_ONSTACK.a;
}

如果您的编译器有任何好处,您应该收到警告。

答案 2 :(得分:0)

这是另一种方法,如何使结构体对调用者只读,并使其保持读/写以供内部使用:

例如这里是一个虚构的 HTTP 解析器:

typedef const struct ___http_message {
    struct {
        char* name;
        char* value;
    } *headers;
    char* body;
} http_message_t;

http_message_t* http_parse(char const* data) {
    struct ___http_message* msg = calloc(1, sizeof(struct ___http_message));
    msg->headers = calloc(3, sizeof(*msg->headers));
    msg->headers[0].name = strdup("Content-Type");
    msg->headers[0].value = strdup("application/json");
    msg->headers[1].name = strdup("Content-Length");
    msg->headers[1].value = strdup("20");
    msg->body = strdup("{ \"hello\": \"world\" }");
    return msg;
}

void http_free(http_message_t* message) {
    struct ___http_message* msg = (struct ___http_message*)message;
    free(msg->headers[0].name);
    free(msg->headers[0].value);
    free(msg->headers[1].name);
    free(msg->headers[1].value);
    free(msg->headers);
    free(msg->body);
    free(msg);
}

int main(int argc, char const *argv[]) {
    http_message_t* msg = http_parse("...");
    printf("%s: %s\n", msg->headers[0].name, msg->headers[0].value);
    printf("%s: %s\n", msg->headers[1].name, msg->headers[1].value);
    printf("%s\n", msg->body);

    msg->body = "123"; // cannot assign to variable 'msg' with const-qualified type 'http_message_t *' (aka 'const struct http_message_tag *')
}
// output
Content-Type: application/json
Content-Length: 20
{ "hello": "world" }

您的 http_message_t* 对调用方始终是只读的,因此处理起来更安全。

这种做法是合法的,而且不臭!