代码之家  ›  专栏  ›  技术社区  ›  Some Name

在结构或联合成员中可以存储哪些值?

  •  0
  • Some Name  · 技术社区  · 6 年前

    我试图理解类型双关语在将值存储到结构或联合的成员中时是如何工作的。

    6.2.6.1(p6)

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

    sizeof(declared_type_of_the_member) + padding 与填充相关的字节将具有未指定的值(即使我们在原始对象中定义了字节)。下面是一个例子:

    struct first_member_padded_t{
        int a;
        long b;
    };
    
    int a = 10;
    struct first_member_padded_t s;
    char repr[offsetof(struct first_member_padded_t, b)] = //some value
    memcpy(repr, &a, sizeof(a));
    memcpy(&(s.a), repr, sizeof(repr));
    s.b = 100;
    printf("%d%ld\n", s.a, s.b); //prints 10100
    

    在我的机器上 sizeof(int) = 4, offsetof(struct first_member_padded_t, b) = 8 .

    是印刷的行为 10100 为这样一个程序定义良好?我觉得是这样。

    0 回复  |  直到 6 年前
        1
  •  3
  •   Eric Postpischil    6 年前

    这个问题提出得不好。让我们先看看代码:

    char repr[offsetof(struct first_member_padded_t, b)] = //some value
    memcpy(repr, &a, sizeof(a));
    memcpy(&(s.a), repr, sizeof(repr));
    

    首先要注意的是 repr 初始化,因此其中的所有元素都是给定值。

    第一次 memcpy a 代表 .

    记忆 memcpy(&s, repr, sizeof repr); 代表 进入之内 s . 这将把字节写入 s.a 而且,由于 ,填充到 s、 一个 s.b . 根据C 2018 6.5 7和标准的其他部分,允许访问对象的字节(根据3.11,访问意味着读和写)。所以这个拷贝 s公司 s、 一个 具有相同的价值

    记忆 &(s.a) &s . 它使用的地址是 s、 一个 . 我们知道转换 指向字符类型的指针将允许我们访问 s、 一个 记忆 被指定具有复制字节的效果),但不清楚它是否允许我们访问 s公司 &s.a 访问除 s、 一个 .

    &s、 一个 struct first_member_padding_t ,它将指向 ,我们当然可以使用指针 访问中的所有字节 s公司

    memcpy((struct first_member_padding t *) &s.a, repr, sizeof repr);
    

    然而, memcpy(&s.a, repr, sizeof repr); 只有皈依者 &s、 一个 void * (因为 记忆 被宣布采取 空的* &s、 一个 在函数调用期间自动转换),而不是指向结构类型的指针。这是一个合适的转换吗?注意如果我们做了 ,它将转换 &s公司 . 6.2.5 28告诉我们 void

    memcpy(&s.a, repr, sizeof repr);
    memcpy(&s,   repr, sizeof repr);
    

    这两个语句都通过了 空的* 记忆 ,那两个 空的* s公司

    记忆 (void *) &s.a (void *) &s 不同的行为意味着具有相同值和类型的两个事物的行为可能不同,这意味着一个值包含的内容超出了它的值和类型,这似乎不是6.2或标准的目的。

    双关语

    问题是:

    我试图理解类型双关语在将值存储到结构或联合的成员中时是如何工作的。

    s、 一个 使用与其定义不同类型的左值(因为它使用 int 内景 unsigned int 和阅读 float

    无论如何,双关语并不是问题的真正主题。

    成员中的值

    标题问:

    这个题目似乎与问题的内容不符。标题问题很容易回答:我们可以存储的值 在里面

    填充采用未指定的值

    这个问题引用了标准:

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

    所以我把它解释为我们有一个对象要存储到一个成员中,这样对象的大小等于 izeof(declared_type_of_the_member) + padding 与填充相关的字节将具有未指定的值

    标准中引用的文本意味着,如果 s公司 已设置为某些值,如 ,然后我们会 s.a = something; ,则不再需要填充字节来保存它们以前的值。

    memcpy(&(s.a), repr, sizeof(repr)); 不在6.2.6.16中所指的结构成员中存储值。它不会存储到任何一个成员中 s、 乙

    char repr[sizeof s] = { 0 };
    memcpy(&s, repr, sizeof s); // Set all the bytes of s to known values.
    s.a = 0; // Store a value in a member.
    memcpy(repr, &s, sizeof s); // Get all the bytes of s to examine them.
    for (size_t i = sizeof s.a; i < offsetof(struct first_member_padding_t, b); ++i)
        printf("Byte %zu = %d.\n", i, repr[i]);
    

        2
  •  1
  •   supercat    6 年前

    在许多C标准用来描述的语言实现中,试图在结构或联合中写入N字节对象将影响结构或联合中最多N字节的值。另一方面,在支持8位和32位存储但不支持16位存储的平台上,如果有人声明了如下类型:

    struct S { uint32_t x; uint16_t y;} *s;
    

    然后执行 s->y = 23; 不管接下来的两个字节发生了什么 y ,盲目重写后面的两个字节,而不是执行一对8位写操作来更新 是的 . 该标准的作者不想禁止这种治疗。

    如果该标准包含了一种方法,通过这种方法,实现可以指示对结构或联合成员的写入是否会干扰到它们以外的存储,并且被这种干扰破坏的程序可以拒绝在可能发生这种情况的实现上运行,那将是很有帮助的。然而,标准的作者很可能期望那些对这些细节感兴趣的程序员知道他们的程序应该运行在什么样的硬件上,从而知道这种内存干扰是否会对这些硬件造成问题。

    不幸的是,现代的编译器编写者似乎将那些旨在帮助实现不寻常硬件的自由解释为一种开放的邀请,即使是针对那些可以在没有这种让步的情况下高效处理代码的平台,也能获得“创造性”。

        3
  •  0
  •   alx - recommends codidact    6 年前

    s.a s.b , memcpy() &a :

    int a = 1;
    int b;
    b = *((char *)&a + sizeof(int));
    

    memcpy()

    推荐文章