gpt4 book ai didi

c - 允许 struct 字段溢出到下一个字段

转载 作者:行者123 更新时间:2023-12-03 17:06:09 28 4
gpt4 key购买 nike

考虑以下简单示例:

struct __attribute__ ((__packed__)) {
int code[1];
int place_holder[100];
} s;

void test(int n)
{
int i;

for (i = 0; i < n; i++) {
s.code[i] = 1;
}
}
for 循环正在写入字段 code , 大小为 1。 code 之后的下一个字段是 place_holder .
我希望在 n > 1 的情况下, 写信给 code数组会溢出并且 1将被写入 place_holder .
但是,当用 -O2 编译时(在 gcc 4.9.4 上,但也可能在其他版本上)发生了一些有趣的事情。
编译器识别出代码可能溢出数组 code , 和 将循环展开限制为 1 次迭代 .
-fdump-tree-all编译时很容易看出并查看最后一次树传递(“t.optimized”):

;; Function test (test, funcdef_no=0, decl_uid=1366, symbol_order=1)

Removing basic block 5
test (int n)
{
<bb 2>:
# DEBUG i => 0
# DEBUG i => 0
if (n_4(D) > 0)
goto <bb 3>;
else
goto <bb 4>;

<bb 3>:
s.code[0] = 1;
# DEBUG i => 1
# DEBUG i => 1

<bb 4>:
return;

}
因此,在这种情况下,编译器将循环完全展开为一次迭代。
我的问题是:
  • 从 C 规范的角度来看,从一个结构成员到下一个结构成员(故意)溢出是非法或未定义的行为吗?
    让我们假设我知道内存中的结构布局并且知道我在故意溢出 code 时我在做什么。大批。
  • 在这种情况下,有没有办法防止 gcc 展开循环?我知道我可以完全防止循环展开,但是我仍然对其他情况下的循环展开感兴趣。我还怀疑编译器正在进行的分析可能会影响循环展开以外的传递。
    gcc 假设我在访问我的数组时不会溢出,所以我真正想要的是告诉编译器不要接受这个假设(通过提供一些编译器选项)。

  • 我知道编写从一个字段溢出到另一个字段的代码是一种不好的做法,而且我不打算编写这样的代码。
    我也知道将数组(可能为零大小)作为 的做法。最后 struct 字段允许它溢出,编译器很好地支持这一点,而在这种情况下,数组 code不是最后一个字段。
    所以这不是“如何修复代码”的问题,而是理解编译器假设并影响它们的问题。
    当我观察已经以这种方式编写的现有代码并对其进行调试以找出为什么它的行为不像原始开发人员所期望的那样时,这些问题就出现了。
    风险在于代码中还有其他地方存在此类问题。静态分析工具可以帮助找出答案,但我也想知道是否有办法让编译器容忍此类代码并仍然生成我们期望的结果。
    更新
    我得到了上面问题(1)的明确答案,但问题(2)没有。
  • 通过一些编译选项,gcc 可以允许它作为扩展吗?
  • 有没有办法至少在 gcc 识别出警告时得到警告? (并且它通过优化事物清楚地识别它)。
    这对于在大型现有代码库中识别此类情况很重要。
  • 最佳答案

    From C specification viewpoint, is overflowing (deliberately) from one struct member to the next is illegal or undefined behavior?


    这是未定义的行为。 arr[i]运算符是 *(arr + i) 附近的语法糖.所以数组访问归结为二进制 +指针算术运算符,C17 6.5.6 加法运算符,来自 §7 和 §8:

    For the purposes of these operators, a pointer to an object that is not an element of anarray behaves the same as a pointer to the first element of an array of length one with thetype of the object as its element type.

    When an expression that has integer type is added to or subtracted from a pointer, theresult has the type of the pointer operand. /--/
    If both the pointeroperand and the result point to elements of the same array object, or one past the lastelement of the array object, the evaluation shall not produce an overflow; otherwise, thebehavior is undefined.If the result points one past the last element of the array object, itshall not be used as the operand of a unary * operator that is evaluated.


    正如您所注意到的,优化编译器可能会利用这些规则来生成更快的代码。

    Is there a way to prevent gcc from unrolling the loop in such case?


    有一个特殊的异常(exception)规则可以使用,C17 6.3.2.3/7:

    When a pointer to an object is converted to a pointer to a character type,the result points to the lowest addressed byte of the object. Successive increments of theresult, up to the size of the object, yield pointers to the remaining bytes of the object.


    此外,严格别名不适用于字符类型,因为 C17 6.5 §7 中的另一条特殊规则

    An object shall have its stored value accessed only by an lvalue expression that has one ofthe following types: ... a character type.


    这两个特殊规则和谐共存。所以假设我们在指针转换期间没有搞乱对齐等,这意味着我们可以这样做:
    unsigned char* i;
    for(i = (unsigned char*)&mystruct; i < (unsigned char*)(&mystruct + 1); i++)
    {
    do_something(*i);
    }
    然而,这可能会读取填充字节等,因此它是“实现定义的”。但理论上您可以按字节访问结构体字节,并且只要结构体偏移量是按字节计算的,您就可以以这种方式遍历结构体(或任何其他对象)的多个成员。

    据我所知,这个看起来很可疑的代码应该是明确定义的:
    #include <stdint.h>
    #include <string.h>
    #include <stdio.h>

    struct __attribute__ ((__packed__)) {
    int code[1];
    int place_holder[100];
    } s;

    void test(int val, int n)
    {
    for (unsigned char* i = (unsigned char*)&s;
    i < (unsigned char*)&s + n*sizeof(int);
    i += _Alignof(int))
    {
    if((uintptr_t)i % _Alignof(int) == 0) // not really necessary, just defensive prog.
    {
    memcpy(i, &val, sizeof(int));
    printf("Writing %d to address %p\n", val, (void*)i);
    }
    }
    }

    int main (void)
    {
    test(42, 3);
    printf("%d %d %d\n", s.code[0], s.place_holder[0], s.place_holder[1]);
    }
    这在 gcc 和 clang (x86) 上运行良好。它的效率如何,那是另一回事了。不过,请不要写这样的代码。

    关于c - 允许 struct 字段溢出到下一个字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62692609/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com