你可以修改一个声称是const的C值吗,如果你确定它实际上不是常数?

huangapple go评论49阅读模式
英文:

Are you allowed to modify a C value that claims to be const if you know for sure that it actually isn't constant?

问题

Question 1: 根据C标准,这样的声明是允许的。struct中的const int i意味着i是一个常量,但它并不会改变struct的内存布局。在大多数系统上,这应该工作正常,因为const仅限制了i的修改,而不会影响struct的布局。

Question 2: 从技术上讲,将struct A *指针强制转换回struct B *是允许的,但要小心。即使const关键字并不真正限制i的修改,这种强制类型转换可能会引入潜在的错误。编译器可能会基于const的声明进行某些优化假设,但这不应该影响在堆内存中的i的可修改性。然而,最好的做法是尽量避免这种类型的强制转换,以减少潜在的错误和不确定性。

Question 3: 为了实现一个在外部代码无法直接更改但在内部代码可以更改的"const"值,您可以使用不完全类型(struct A;)以及访问函数的方法是一个不错的选择。这种封装可以提供更好的控制,确保外部代码无法绕过规定的方式修改值,同时内部代码仍然可以访问和修改该值。这是一种常见的C编程实践,以确保数据封装和代码安全性。

英文:

I came across older code in a project that declares a struct in the H file as

struct A {
    const int i;  
};

Now the function that creates struct A pointers internally looks like this

struct A * newStructA ( int i ) {
    struct B * ptr = malloc(sizeof(struct B));
    ptr->i = i;
    return (struct A *)ptr;
}

and struct B is declared in the C file and looks like this:

struct B {
    int i; 
};

Question 1: Is that even allowed according to C standard? Sure, the data type of i is the same and the struct should have the same memory layout, but is that guaranteed or could the const modifier also change the memory layout on certain systems?

Question 2: Knowing that all struct A * pointers the code deals with are in fact struct B * pointers, would it be allowed to cast the pointers back to struct B * and then modify the int value? The declaration says it is const, but I know for sure that it isn't as it's always located in modifiable heap memory. Or could that have implications as the C compiler may rely on the value being constant, so it assumes it cannot ever change and thus if two lines of code contain ptr->i the compiler may not even fetch the value a second time as how could it have changed if it is const? As that would lead to very hard to trace bugs that may only be seen if a certain optimization level is being used.

Question 3: Is there a better way to achieve a const value that external code cannot directly change (or at least should never try to), yet internal code can change as the value is in fact not const at all? The only way I can think of would be to hide the struct layout altogether (struct A;) but then I need to provide a function like int getI(struct A * ptr) and always access the value using that function.

答案1

得分: 3

> Are you allowed to modify a C value that claims to be const if you know for sure that it actually isn't constant?

Yes.

> Is that even allowed according to C standard?

Yes.

> is that guaranteed or could the const modifier also change the memory layout on certain systems?

It could, when defining the variable as const the variable will typically be placed in a different memory region that is read-only.

> Knowing that all struct A * pointers the code deals with are in fact struct B * pointers, would it be allowed to cast the pointers back to struct B * and then modify the int value?

It is unclear. Stack Overflow Discussion.

> Or could that have implications as the C compiler may rely on the value being constant, so it assumes it cannot ever change and thus if two lines of code contain ptr->i the compiler may not even fetch the value a second time as how could it have changed if it is const?

Yes. Related Stack Overflow Discussion.

> Is there a better way to achieve a const value that external code cannot directly change (or at least should never try to), yet internal code can change as the value is in fact not const at all?

This is C. The spirit of C says C Standards:

(1) Trust the programmer.
(2) Don't prevent the programmer from doing what needs to be done.

In my opinion, a better way is not to hide and just use struct with non-const members. A C programmer will be able to access it anyway.

> The only way I can think of would be to hide the struct layout altogether (struct A;) but then I need to provide a function like int getI(struct A * ptr)

Yes, hiding something will cause runtime overhead. FILE * has been with us since forever, and the members of FILE are visible to user code in many implementations. Yet no one uses them.

To achieve that a value should not be changed by external code, write a specification of your library that external code should not do it.

To hide your proprietary code, use accessors and let users operate only on pointers to your data with PIMPL idiom.

英文:

> Are you allowed to modify a C value that claims to be const if you know for sure that it actually isn't constant?

Yes.

> Is that even allowed according to C standard?

Yes.

> is that guaranteed or could the const modifier also change the memory layout on certain systems?

It could, when defining the variable as const the variable will typically be placed in a different memory region that is read-only.

> Knowing that all struct A * pointers the code deals with are in fact struct B * pointers, would it be allowed to cast the pointers back to struct B * and then modify the int value?

It is unclear. https://stackoverflow.com/questions/52546353/can-you-safely-cast-a-c-structure-with-non-const-members-to-an-equivalent-struct

> Or could that have implications as the C compiler may rely on the value being constant, so it assumes it cannot ever change and thus if two lines of code contain ptr->i the compiler may not even fetch the value a second time as how could it have changed if it is const?

Yes. Related https://stackoverflow.com/a/20707255/9072753 .

> Is there a better way to achieve a const value that external code cannot directly change (or at least should never try to), yet internal code can change as the value is in fact not const at all?

This is C. The spirit of C says https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2086.htm :

(1) Trust the programmer.
(2) Don't prevent the programmer from doing what needs to be done.

In my opinion, a better way is not to hide and just use struct with non-const members. A C programmer will be able to access it anyway.

> The only way I can think of would be to hide the struct layout altogether (struct A;) but then I need to provide a function like int getI(struct A * ptr)

Yes, hiding something will cause runtime overhead. FILE * has been with us since forever, and the members of FILE are visible to user code in many implementations. Yet no one uses them.

To achieve that a value should not be changed by external code, write a specification of your library that external code should not do it.

To hide your proprietary code, use accessors and let users operate only on pointers to your data with PIMPL idiom.

答案2

得分: -1

C 2011 draft

> 6.7.3 Type qualifiers ... 6 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with
> non-const-qualified type, the behavior is undefined. If an attempt is
> made to refer to an object defined with a volatile-qualified type
> through use of an lvalue with non-volatile-qualified type, the behavior
> is undefined.133) 133) This applies to those objects that behave as if
> they were defined with qualified types, even if they are never actually
> defined as objects in the program (such as an object at a memory-mapped
> input/output address).

英文:

C 2011 draft

> 6.7.3 Type qualifiers ... 6 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with
> non-const-qualified type, the behavior is undefined. If an attempt is
> made to refer to an object defined with a volatile-qualified type
> through use of an lvalue with non-volatile-qualified type, the behavior
> is undefined.133) 133) This applies to those objects that behave as if
> they were defined with qualified types, even if they are never actually
> defined as objects in the program (such as an object at a memory-mapped
> input/output address).

huangapple
  • 本文由 发表于 2023年2月16日 18:08:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/75470678.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定