代码之家  ›  专栏  ›  技术社区  ›  Jay

与对齐相关的问题

  •  2
  • Jay  · 技术社区  · 14 年前

    在函数的局部范围内声明时,将相同类型的所有变量组合在一起是一个好的实践吗?如果是,为什么?它能解决内存对齐问题吗?

    5 回复  |  直到 14 年前
        1
  •  6
  •   John Knoeller    14 年前

    我认为它与我20年前使用的VAX C编译器有关,但与任何现代编译器无关。它是 可以安全地假设局部变量将以任何特定的顺序出现,当然不能安全地假设它们将以您声明的顺序出现。我有 一定地 已看到MSVC编译器对其重新排序。

    对同一类型的变量分组 当它们是结构的字段时提供帮助,因为结构的字段顺序保证与声明的顺序匹配。

        2
  •  3
  •   Mitch Wheat    14 年前

    它取决于编译器;也就是说,编译器将按其认为合适的方式来布局内存。所以除了风格好之外,它没有任何效果(至少在我使用过的任何现代编译器中)。

        3
  •  1
  •   Alexey Kalmykov    14 年前

    一般来说,它对局部变量没有帮助。有一些优化规则可以由编译器应用,还有一些附加的“pragma”指令可以用来操纵对齐。

        4
  •  1
  •   Michael Burr    14 年前

    它不会解决对齐问题,因为不应该存在对齐问题-编译器将正确地排列局部变量,因此不应该存在对齐问题。

    唯一的问题是像对齐类型那样分组 可以 has是为了减少对堆栈的使用,但是编译器可以自由地重新排序堆栈上变量的布局(甚至可以在不同的时间重复使用不同局部变量的位置,或者将局部变量保存在寄存器中,而不将它们保存在堆栈上),因此通常不会为优化的编译购买任何东西。

    如果您要成为堆栈上的“类型punning”项,则需要使用与用于堆栈外数据的对齐安全相同的方法-可能更多,因为内存由 malloc() new 保证对任何类型的存储都进行适当的调整,即保证不会对分配给自动变量的存储进行调整。

    “类型punning”是指绕过类型系统时。例如通过访问 char 数组作为 int 铸造A char* int* :

    int x;
    char data[4];
    
    fill_data( data, sizeof(data));
    
    int x = *(int*) data;
    

    因为校准要求 char[] 可能与 利息 ,上述访问 data 通过 利息* 可能不是“对齐安全”。然而,由于 马尔洛() 指定返回指针以适合任何类型的对齐方式,以下内容不应有任何对齐问题:

    int x;
    char* pData = malloc( 4);
    
    if (!pData) exit(-1);
    
    fill_data( pData, 4);
    
    x = *(int*) pData;
    

    但是,请注意 sizeof(int) 可能不是4岁 int 类型可能是小的或大的endian,所以上面的代码仍然存在可移植性问题,而不是对齐问题。还有其他执行类型punning的方法,包括通过 union 但它们可能有自己的可移植性问题,特别是访问不是最后一个写入成员的成员是未指明的行为。

        5
  •  0
  •   jimrandomh    14 年前

    填充和对齐问题只对结构重要,而不是局部变量,因为编译器可以按任意顺序放置局部变量。为什么它在结构中很重要-

    许多C编译器将通过在结构成员之间插入填充字节来对齐它们。例如,如果您有一个结构s int a;char b;int c;char d;int e;,并且目标硬件要求在4字节边界上对齐int,那么编译器将在b和c之间以及d和e之间插入3个字节的填充,每个实例浪费6个字节的内存。另一方面,如果成员的顺序是a c、e、b、d,那么它将在末尾插入两个字节的填充(这样,s作为一个整体的大小是4的倍数,因此在数组中成员将正确对齐),每个实例只浪费2个字节。这些规则是非常特定于平台和编译器的;有些编译器可能会重新排列成员以避免填充,而有些则具有扩展来控制填充和对齐规则,以防需要二进制兼容性。一般来说,如果您直接读/写结构,并且依赖于它们具有相同的布局(这通常是一个坏主意),或者您希望拥有大量实例,并且内存是昂贵的,那么您应该只关心对齐。