灵活的数组成员会导致未定义的行为吗? [英] Flexible array members can lead to undefined behavior?

查看:77
本文介绍了灵活的数组成员会导致未定义的行为吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  1. 通过在结构类型内使用灵活数组成员(FAM),我们是否将程序暴露给未定义行为的可能性?

  1. By using flexible array members (FAMs) within structure types, are we exposing our programs to the possibility of undefined behavior?

程序是否可以使用FAM并仍然是严格符合要求的程序?

Is it possible for a program to use FAMs and still be a strictly conforming program?

柔性数组成员的偏移量是否需要在结构的末尾?

Is the offset of the flexible array member required to be at the end of the struct?

问题同时适用于C99 (TC3)C11 (TC1).

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

int main(void) {
    struct s {
        size_t len;
        char pad;
        int array[];
    };

    struct s *s = malloc(sizeof *s + sizeof *s->array);

    printf("sizeof *s: %zu\n", sizeof *s);
    printf("offsetof(struct s, array): %zu\n", offsetof(struct s, array));

    s->array[0] = 0;
    s->len = 1;

    printf("%d\n", s->array[0]);

    free(s);
    return 0;
}

输出:

sizeof *s: 16
offsetof(struct s, array): 12
0

推荐答案

简短答案

  1. 是的.使用FAM的常见约定使我们的程序容易出现不确定的行为.话虽这么说,但我不知道任何现有的不符合规范的实施方式.

  1. Yes. Common conventions of using FAMs expose our programs to the possibility of undefined behavior. Having said that, I'm unaware of any existing conforming implementation that would misbehave.

可能,但可能性不大.即使我们实际上未达到未定义的行为,我们仍然可能无法严格遵循标准.

Possible, but unlikely. Even if we don't actually reach undefined behavior, we are still likely to fail strict conformance.

否. FAM的偏移量不必位于结构的末尾,它可以覆盖任何尾随填充字节.

No. The offset of the FAM is not required to be at the end of the struct, it may overlay any trailing padding bytes.

答案同时适用于C99 (TC3)C11 (TC1).

FAM最初是在C99(TC0)(1999年12月)中引入的,其原始规范要求FAM的偏移量必须在结构的末尾.原始规范定义明确,因此不会导致不确定的行为,也不会成为严格遵守标准的问题.

FAMs were first introduced in C99 (TC0) (Dec 1999), and their original specification required the offset of the FAM to be at the end of the struct. The original specification was well-defined and as such couldn't lead to undefined behavior or be an issue with regards to strict conformance.

C99 (TC0) §6.7.2.1 p16(1999年12月)

C99 (TC0) §6.7.2.1 p16 (Dec 1999)

[此文档为官方标准,受版权保护,不能免费获得]

[This document is the official standard, it is copyrighted and not freely available]

问题在于常见的C99实现(例如GCC)未遵循标准要求,并允许FAM覆盖任何尾随填充字节.他们的方法被认为是更有效的,并且由于他们遵循标准的要求-会导致向后兼容的破坏,因此委员会选择更改规范,并且从C99 TC2(2004年11月)开始不再需要该标准. FAM的偏移量位于结构的末尾.

The problem was that common C99 implementations, such as GCC, didn't follow the requirement of the standard, and allowed the FAM to overlay any trailing padding bytes. Their approach was considered to be more efficient, and since for them to follow the requirement of the standard- would result with breaking backwards compatibility, the committee chose to change the specification, and as of C99 TC2 (Nov 2004) the standard no longer required the offset of the FAM to be at the end of the struct.

C99 (TC2) §6.7.2.1 p16(2004年11月)

C99 (TC2) §6.7.2.1 p16 (Nov 2004)

[...]结构的大小就像省略了柔性数组部件,只是它的尾随填充可能比省略所暗示的要多.

[...] the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

新规范删除了要求FAM偏移量在结构末尾的语句,并且引入了非常不幸的结果,因为该标准赋予实现自由以不保留任何填充字节的值.在结构或联合中处于一致状态.更具体地说:

The new specification removed the statement that required the offset of the FAM to be at the end of the struct, and it introduced a very unfortunate consequence, because the standard gives the implementation the liberty not to keep the values of any padding bytes within structures or unions in a consistent state. More specifically:

C99 (TC3) §6.2.6.1 p6

当将值存储在结构或联合类型的对象中(包括在成员对象中)时,与任何填充字节对应的对象表示形式的字节将采用未指定的值.

When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.

这意味着,如果我们的FAM元素中的任何一个对应于(或覆盖)任何尾随填充字节,则在存储到该结构的成员时,它们(可以)采用未指定的值.我们甚至不必考虑这是否适用于FAM本身存储的值,即使严格解释为仅适用于FAM以外的成员,也足以造成损害.

This means that if any of our FAM elements correspond to (or overlay) any trailing padding bytes, upon storing to a member of the struct- they (may) take unspecified values. We don't even need to ponder whether this applies to a value stored to the FAM itself, even the strict interpretation that this only applies to members other than the FAM, is damaging enough.

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

int main(void) {
    struct s {
        size_t len;
        char pad;
        int array[];
    };

    struct s *s = malloc(sizeof *s + sizeof *s->array);

    if (sizeof *s > offsetof(struct s, array)) {
        s->array[0] = 123;
        s->len = 1; /* any padding bytes take unspecified values */

        printf("%d\n", s->array[0]); /* indeterminate value */
    }

    free(s);
    return 0;
}

一旦我们存储到该结构的成员,则填充字节占用未指定的字节,因此,对与任何尾随填充字节相对应的FAM元素的值所作的任何假设现在都是错误的.这意味着任何假设都会导致我们无法严格遵守标准.

Once we store to a member of the struct, the padding bytes take unspecified bytes, and therefore any assumption made about the values of the FAM elements that correspond to any trailing padding bytes, is now false. Which means that any assumption leads to us failing strict conformance.

尽管填充字节的值是未指定的值",但是对于受它们影响的类型却不能说相同,因为基于未指定值的对象表示可以生成陷阱表示.因此,描述这两种可能性的唯一标准术语是不确定值".如果FAM的类型恰好具有陷阱表示,则访问它不仅是未指定值的问题,而且是未定义行为的问题.

Although the values of the padding bytes are "unspecified values", the same can't be said about the type being affected by them, because an object representation which is based on unspecified values can generate a trap representation. So the only standard term which describes these two possibilities would be "indeterminate value". If the type of the FAM happens to have trap representations, then accessing it is not just a concern of an unspecified value, but undefined behavior.

但是等等,还有更多.如果我们同意描述该值的唯一标准术语是不确定值",那么即使FAM的类型碰巧没有陷阱表示,由于对C的正式解释,我们也达到了不确定的行为.标准委员会认为,将不确定的值传递给标准库函数是未定义的行为.

But wait, there's more. If we agree that the only standard term to describe such value is as being an "indeterminate value", then even if the type of the FAM happens not to have trap representations, we've reached undefined behavior, since the official interpretation of the C standards committee is that passing indeterminate values to standard library functions is undefined behavior.

这篇关于灵活的数组成员会导致未定义的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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