const关键字的使用是否符合其意图?

const关键字的使用是否符合其意图?,c,struct,constants,C,Struct,Constants,我正在设计一个API,并正在考虑使用“不可变”结构或“只读结构”。使用一个简化的示例,这可能类似于: struct vector { const float x; const float y; }; struct vector getCurrentPosition(void); struct vector getCurrentVelocity(void); const struct vector * const VECTOR_UNINITIALIZED; 只要我在堆栈上返回

我正在设计一个API,并正在考虑使用“不可变”结构或“只读结构”。使用一个简化的示例,这可能类似于:

struct vector {
    const float x;
    const float y;
};

struct vector getCurrentPosition(void);
struct vector getCurrentVelocity(void);
const struct vector * const VECTOR_UNINITIALIZED;
只要我在堆栈上返回不可变结构,一切正常。但是,我在实现以下函数时遇到问题:

void getCurrentPositionAndVelocity(
    struct vector *position,
    struct vector *velocity);
此时,我不想引入包含两个向量的新不可变结构。我也不能这样做:

void
getCurrentPositionAndVelocity(
    struct vector *position,
    struct vector *velocity)
{
    *position = getCurrentPosition();
    *velocity = getCurrentVelocity();
}
因为
position
velocity
是只读的(尽管我安装的
clang

我可以使用
memcpy
s来解决这个问题,比如:

void
getCurrentPositionAndVelocity(
    struct vector *position,
    struct vector *velocity)
{
    struct vector p = getCurrentPosition();
    struct vector v = getCurrentVelocity();

    memcpy(position, &p, sizeof *position);
    memcpy(velocity, &v, sizeof *velocity);
}
这看起来很糟糕,但我相信它不会误导API的用户,对他们来说,
向量
结构仍然是不可变的。为此,我还可以添加一个必需的初始值设定项,这样的函数调用只有在
向量
结构具有特殊值时才会成功。类似于:

struct vector {
    const float x;
    const float y;
};

struct vector getCurrentPosition(void);
struct vector getCurrentVelocity(void);
const struct vector * const VECTOR_UNINITIALIZED;
用户应该做什么

struct vector position = *VECTOR_UNINITIALIZED;
struct vector velocity = *VECTOR_UNINITIALIZED;
在调用
getCurrentPositionAndVelocity()
之前,在
memcpy
-ing之前,实现将使用
memcmp
断言向量确实具有“未初始化的哨兵”值。(键入此项,我意识到只有当存在某些真正未使用的值可以充当“未初始化的哨兵”时,此操作才会起作用“价值观,但我认为这是我的情况。)

我的问题是,从API用户的角度来看,
const
关键字的使用是否符合其意图?从编译器的角度来看,这段代码会不会有任何风险,因为严格来说,它可能会违反用
const
关键字表示的只读语义?作为一名API用户,您对这种方法有何看法,或者您有什么更好的建议

memcpy(position, &p, sizeof *position);
memcpy(velocity, &v, sizeof *velocity);

你滥用了与编译器的合同。您声明了一些
const
,然后尝试解决它非常糟糕的做法


如果您想违反此协议,请不要将结构成员声明为const。

TL;博士

const关键字的使用是否符合其意图

没有


我的问题是
const
关键字的这种用法是否符合 从API用户的角度来看,它的意图是什么

我不确定API用户的观点是什么。事实上,像您建议的那样的设计可能会产生比通常更大的用户视角多样性,因为明显的预期行为与C语言要求不一致

会有吗 从编译器的角度来看所涉及的风险,因为此代码可能, 严格地说,违反只读语义,如 const关键字

对。具体来说,


如果试图修改使用 通过将左值与非const-qualified一起使用来限定const-qualified类型 类型,则行为未定义

()


当UB发生时,编译器可能会做各种有趣的事情。其中更合理的是,他们可能假设某些未定义的行为不会发生。因此 例如,当编译一个调用
getCurrentPositionAndVelocity()
函数的程序时,编译器可能会假定该函数不会修改提供给它的结构的
const
成员。或者修改它们的尝试实际上可能会失败。或者别的什么,真的,这就是“未定义”的意思

作为一名API用户,您对此有何看法 或者你有更好的建议吗

memcpy(position, &p, sizeof *position);
memcpy(velocity, &v, sizeof *velocity);
我认为我的API提供者应该尽最大努力提供符合语言标准的实现

此外,我想知道您认为通过使结构成员
const
来保护我不受谁的伤害,以及为什么您认为您比我更清楚是否应该修改结构成员。我还想知道,为什么有人会认为这样的保护足够重要,足以保证出现
const
结构成员的所有问题

至于更好的建议,不让成员们保持一致怎么样?这将为你和你的用户节省悲伤


关于添加参考的编辑,就标准而言,此处讨论的案例与此处考虑的案例有很大不同。动态分配的内存没有声明的类型,但根据写入的内容和/或访问方式,可能具有有效的类型。这方面的规则在本标准的附录中给出,您可以在其他地方的一些答案中找到关于这方面的讨论,例如您在评论中链接的答案

底线是,具有已分配生存期的对象从写入其中的第一个数据获取其有效类型,并且第一次写入可以被视为其有效初始化(尽管标准中未使用后一个术语)。后续操作必须尊重有效类型,包括根据俗称的“严格别名规则”(strict aliasing rule)递归地由类型本身或其成员的
常量产生的约束


具有静态或自动生存期的对象将其声明的类型作为其有效类型,并具有由其初始值设定项指定的初始值(如果有)。它们也必须以与其有效类型一致的方式进行操作。

tl;“剑鱼博士”不适用。问题是明确的(虽然很长),这将是非常容易在C++中解决的,但是我在C中没有看到一个解决方案。我也不确定如果代码< > MycPyIf()/Cube >在C中被定义(这当然是C++中的未定义的)。让弗兰?Cou-OsFabRe首先是初始化,而不是赋值。@ Jeon弗兰CouoOsFabRe作为来自C++的人,我最喜欢。