在C语言中,是否可以更改const结构体成员?

3
我需要在C语言中更改一个const结构体实例的const成员。
我已经知道可以按照以下方式更改const基本类型:
const int a = 2;
*(int*)&a = 3;

我也可以按照以下方式更改结构实例的常量成员:
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
}

然而,如果结构体实例是常量,我尝试更改其常量成员却没有成功:
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”成员的值是否可能?如果不行,为什么?

5
所有这些都是未定义行为。 - M.M
1
我想修改一个const成员。那是个自相矛盾的说法。你的设计需要改变。 - Ajay Brahmakshatriya
此外,请打开所有的警告。在第一种情况下将const int强制转换为int,你的编译器会抱怨。 - Ajay Brahmakshatriya
将取决于编译器和目标。无论如何,强制转换就像*(int*)&(st.a) = 123;这么简单。如果在函数级别声明了st,则此方法可行。-本网站使用星号表示斜体字,请想象它们在这里。 - Michaël Roy
3个回答

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

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

const int a = 2;
*(int*)&a = 3;
你的编译器允许你这样做并不意味着它是合法的。该代码调用了未定义的行为,所以如果在不同平台上运行该程序或使用不同的编译器,同一程序可能会失败或崩溃。
*(int *)(&((*(ST*)(&st)).a)) = 5; //works fine , no problem

这段代码有同样的问题:它会引发未定义行为。

你尝试的最后一段代码同样存在(你猜对了!)未定义行为。然而,这次程序不是正常运行到结束,而是崩溃了。


1

C编译器通常将常量放置在只读内存段中,通常称为.TEXT或.CODE。这个段(内存块)由操作系统保护,或者对于小型嵌入式CPU和SoC常量通常与代码一起放置在ROM /或Flash存储器中。不建议尝试更改常量,因为它会影响使用该常量的所有模块。

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

[编辑] 但是,实际上,如果常量位于可写数据段中,您可以轻松更改常量结构中的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;
}

如果你的编译器足够好,你应该会收到一个警告。

我认为你在中间行的末尾有一个 },应该是一个 ; - Todd Lehman

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*对于调用者来说始终是只读的,这使得处理更加安全。

这种方法是合法的,而且没有什么不好的味道!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接