从函数和未定义的行为返回局部初始化的结构 [英] Returning a local partially initialized struct from a function and undefined behavior

查看:42
本文介绍了从函数和未定义的行为返回局部初始化的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(部分初始化我的意思是定义为未初始化,并且它的一个成员被设置为一些有效值,但不是全部.本地我的意思是定义为自动存储持续时间.这个问题只讨论那些.)

(By partially initialized I mean defined as uninitialized and one of its members is set to some valid value, but not all of them. And by local I mean defined with automatic storage duration. This question only talks about those.)

使用可以用寄存器定义的自动未初始化变量,因为右值是未定义的行为.可以使用寄存器存储类说明符定义结构.

Using an automatic uninitialized variable that could be defined with register, as an rvalue is undefined behavior. Structs can be defined with register storage class specifier.

6.3.2.1

  1. 如果左值指定一个自动存储持续时间的对象,该对象可能已经使用 register 存储类声明(从未获取其地址),并且该对象未初始化(未使用初始化程序声明且未对其进行赋值)在使用之前执行),行为未定义.

请注意,它明确表示并且没有对其进行任何分配.

另外我们知道结构体不能是陷阱值:

Additionally we know that a struct cannot be a trap value:

6.2.6.1.

  1. 结构或联合对象的值永远不是陷阱表示,即使结构或联合对象的成员的值可能是一个陷阱表示

因此返回一个未初始化的结构显然是未定义的行为.

Thus returning an uninitialized struct is clearly undefined behavior.

声明:返回一个未初始化的结构体,该结构体的一个成员被分配了一个有效值.

更容易理解的示例:

struct test
{
    int a;
    int b;
};

struct test Get( void )
{
    struct test g;
    g.a = 123;
    return g;
}

{
    struct test t = Get();
}

我只是碰巧专注于返回,但我相信这也适用于简单的任务,没有任何区别.

I just happened to focus on returning, but I believe this should apply to a simple assignment as well, without any difference.

我的声明正确吗?

推荐答案

除了从函数返回值的细节之外,这正是 缺陷报告 222,由 Clive Feather 于 2000 年提交,该 DR 的解决方案似乎很清楚地回答了这个问题:返回部分-未初始化的 struct 是明确定义的(尽管可能不会使用未初始化成员的值.)

Aside from the detail of returning the value from a function, this is precisely the subject of Defect Report 222, submitted in 2000 by Clive Feather, and the resolution of that DR seems to pretty clearly answer the question: returning a partially-uninitialized struct is well-defined (although the values of the uninitialized members may not be used.)

DR 的决议阐明了 structunion 对象没有陷阱表示(已明确添加到 §6.2.6.1/6).因此,不能在单个成员可能陷入困境的体系结构上使用逐个成员复制.虽然,大概是为了简洁,没有在标准中添加对此效果的明确声明,但之前提到逐个成员复制的可能性的脚注 42(现在是脚注 51)被更弱的声明所取代,该声明表明不需要填充位被复制.

The resolution to the DR clarified that struct and union objects do not have trap representations (which was explicitly added to §6.2.6.1/6). Consequently member-by-member copying cannot be used on an architecture in which the individual members might trap. Although, presumably for parsimony, no explicit statement to this effect was added to the standard, footnote 42 (now footnote 51) which previously mentioned the possibility of member-by-member copying was replaced by a much weaker statement indicating that padding bits need not be copied.

WG14 会议纪要(多伦多,2000 年 10 月) 清楚(强调):

DR222 - 部分初始化的结构

DR222 - Partially-initialized structures

这个DR问的是struct赋值好不好当赋值源是 struct 时定义,其中一些成员没有被赋予价值.大家一致认为这应该很好地定义,因为通常使用,包括标准指定的结构struct tm.也有共识认为,如果一些成员未初始化的赋值(因此可能有一个陷阱值)被很好地定义,几乎没有价值要求至少有一个成员被正确赋值.
因此,structunion 的值作为一个整体可以有一个陷阱值正在被删除.

This DR asks the question of whether or not struct assignment is well defined when the source of the assignment is a struct, some of whose members have not been given a value. There was consensus that this should be well defined because of common usage, including the standard-specified structure struct tm. There was also consensus that if assignment with some members uninitialized (and thus possibly having a trap value) was being made well defined, there was little value in requiring that at least one member had been properly given a value.
Therefore the notion that the value of a struct or union as a whole can have a trap value is being removed.

有趣的是,在上述会议记录中,委员会认为甚至没有必要为 struct 的单个成员赋予一个值.但是,后来在某些情况下恢复了该要求,并通过了 DR338 的决议(见下文).

It's interesting to note that in the above minutes, the committee held that it was not even necessary that a single member of the struct had been given a value. However, that requirement was later reinstated in some cases, with the resolution to DR338 (see below).

总结:

  • 如果一个自动聚合对象至少已经部分初始化或者它的地址已经被占用(从而使得它不适合按照 §6.3.2.1/2 的 register 声明),则该对象的左值到右值转换是明确定义的.

  • If an automatic aggregate object has been at least partially initialized or if its address has been taken (thereby rendering it not suitable for a register declaration as per §6.3.2.1/2), then lvalue-to-rvalue conversion of that object is well-defined.

这样的对象可以分配给另一个相同类型的聚合对象,可能是在从函数返回之后,而不会调用未定义的行为.

Such an object can be assigned to another aggregate object of the same type, possibly after having been returned from a function, without invoking undefined behaviour.

读取副本中未初始化的成员要么是未定义的,要么是不确定的,这取决于陷阱表示是否可能.(例如,通过指向无符号窄字符类型的指针读取无法捕获.)但如果您在读取成员之前写入它,则没问题.

Reading the uninitialized members in the copy is either undefined or indeterminate, depending on whether trap representations are possible. (A read through a pointer to an unsigned narrow character type cannot trap, for example.) But if you write the member before reading it, you're fine.

我不认为 unionstruct 对象的分配在理论上有任何区别.显然,unions 不能被逐个成员复制(这意味着什么),并且某个非活动成员碰巧具有陷阱表示这一事实是无关紧要的,即使该成员不是由任何其他元素别名.struct 应该有什么不同没有明显的原因.

I don't believe there is any theoretical difference between assignment of union and struct objects. Obviously unions cannot be copied member by member (what would that even mean), and that the fact that some inactive member happens to have a trap representation is irrelevant, even if that member is not aliased by any other element. There's no obvious reason why a struct should be any different.

最后,关于 §6.3.2.1/2 中的异常:这是作为 DR 338.该 DR 的要点是某些硬件 (IA64) 可以捕获使用寄存器中的未初始化值.C99 不允许无符号字符的陷阱表示.所以在这样的硬件上,如果不不必要地"初始化寄存器,就不可能在寄存器中维护一个自动变量.

Finally, with respect to the exception in §6.3.2.1/2: this was added as a result of the resolution to DR 338. The gist of that DR is that some hardware (IA64) can trap the use of an uninitialized value in a register. C99 does not permit trap representations for unsigned chars. So on such hardware, it might not be possible to maintain an automatic variable in a register without "unnecessarily" initializing the register.

DR 338 的决议特别将自动变量中未初始化值的使用标记为未定义行为,这些值可以想象地存储在寄存器中(即那些地址从未被使用过的,就像声明了register),从而允许编译器在寄存器中自动保存unsigned char,而不必担心该寄存器的先前内容.

The resolution to DR 338 specifically marks as undefined behaviour the use of uninitialized values in automatic variables which could conceivably be stored in registers (i.e., those whose address has never been taken, as though declared register), thus permitting the compiler to keep an automatic unsigned char in a register without worrying about the previous contents of that register.

作为 DR 338 的副作用,似乎地址从未被使用过的完全未初始化的自动 struct 不能进行左值到右值的转换.我不知道在 DR 338 的决议中是否完全考虑了这种副作用,但它不适用于部分初始化的 struct,如本问题所示.

As a side effect of DR 338, it appears that completely uninitialized automatic structs whose address has never been taken cannot undergo lvalue-to-rvalue conversion. I don't know if that side-effect was fully contemplated in the resolution to DR 338, but it does not apply in the case of a partially initialized struct, as in this question.

这篇关于从函数和未定义的行为返回局部初始化的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆