__attribute __((__ align__))不使用静态变量

这让我疯了好几天。 如果我将它声明为static我无法将数组对齐到16。

任何帮助非常感谢。

经过修改的版本:

 #include  #include  #define MAX_INPUTS 250 int main() { float input[MAX_INPUTS] __attribute__ ((__aligned__(16))); printf("Address of input: %p\n", input); printf("Assert1: %x\n", ( ((int) (input)) ) ); printf("Assert2: %x\n", ( ((int) (input)) % 16 ) ); printf("Assert3: %x\n", ( ((int) (input)) % 16 ) == 0 ); assert ( ( ((int) (input)) ) ); assert ( ( ((int) (input)) % 16 ) ); /* Fails */ assert ( ( ((int) (input)) % 16 ) == 0 ); /* Passes */ return 0; } 

输出是:

 Address of input: 0022FB70 Assert1: 22fb70 Assert2: 0 Assert3: 1 Assertion failed: ( ((int) (input)) % 16 ), file aligntest.c, line 16 

正如人们所料,Assert 2失败,因为地址以0结尾。但是,有:

 static float input[MAX_INPUTS] __attribute__ ((__aligned__(16))); 

输出是:

 Address of input: 00404028 Assert1: 404028 Assert2: 8 Assert3: 1 Assertion failed: ( ((int) (input)) % 16 ), file aligntest.c, line 16 

虽然结果不为零,但断言2仍然失败。 当Assert2被注释掉时,Assert3传递(有或没有静态声明)并且程序正常终止。

我在英特尔酷睿2双核处理器上使用MinGw gcc 4.4.0,运行XP专业版。

在我的工作机器上(Windows Vista,MinGW gcc 4.3.2),您的代码在任何优化级别都没有为断言生成任何汇编程序!

为了生成断言,我必须提出一个volatile int变量并使用-O0标志进行编译。

 int main(void) { float input[MAX_INPUTS] __attribute__ ((__aligned__(16))); static float input_static[MAX_INPUTS] __attribute__ ((__aligned__(16))); volatile int addr_as_int; printf("Address of input: %p\n", &input); addr_as_int = (int)input; print_pointer(input); print_int(addr_as_int); printf("normal int: %08x; int%%16: %02x\n", addr_as_int, addr_as_int%16); printf("Assert: %d\n", (addr_as_int % 16) == 0); assert((addr_as_int % 16) == 0); /* Passes */ printf("Address of input_static: %p\n", &input_static); addr_as_int = (int)input_static; print_pointer(input_static); print_int(addr_as_int); printf("static int: %08x; int%%16: %02x\n", addr_as_int, (addr_as_int)%16); printf("Assert: %d\n", (addr_as_int % 16) == 0); assert((addr_as_int % 16) == 0); /* Does not Pass */ return 0; } 

我不知道为什么编译器选择从目标文件中删除断言。 我快速谷歌搜索没有透露任何有趣的东西。

更新1(在@Falaina的建议下由@Pax添加 – 我们都建议你接受这个,如果事实certificate是这样):

实际上我认为@Falaina已经在评论@ Pax的回答中钉了它:

只是一个建议。 您是否正在进行优化编译? 有可能编译器试图变得聪明并且“嘿,这个变量对齐到16个字节,显然地址%16是0”并用1替换所有的检查。只是一个想法。

这是解释。 GCC正在从源代码中搞清楚输入确实(应该是)与16个字节对齐。 它足够聪明,可以完全删除assert并为printf打印出1。

但是,在链接阶段,链接器无法保证对齐到16个字节,而是选择8,因为(来自@Pax):

请注意,对齐属性的有效性可能受链接器固有限制的限制。 在许多系统上,链接器只能安排变量对齐到某个最大对齐。 (对于某些链接器,支持的最大对齐可能非常小。)如果链接器只能将变量对齐到最大8字节对齐,那么在__attribute__中指定aligned(16)仍然只能为您提供8字节对齐。 有关详细信息,请参阅链接器文档。

到那时,将assert和非优化的printf恢复到代码中为时已晚。 所以实际的可执行文件不会断言(因为它们已被取出)并且它将打印优化的1而不是计算它的运行时。

volatile在我的回答中修复它的原因是因为GCC不会优化包含volatile组件的表达式。 它保留assert并在运行时正确计算printf参数。


如果您不介意将其声明为比严格必要的大一点,则可以手动对齐arrays:

 #include  #include  #define MAX_INPUTS 250 void *force_align(void *base, size_t s, int align) { size_t x; int k = 0; x = (size_t)base; while ((k < align / (int)s) && (x % align)) { k++; x += s; } if (k == align) return NULL; #if 0 printf("%d elements 'discarded'\n", k); #endif return (void*)((size_t)base + k*s); } int main(void) { #define ALIGNMENT_REQ 16 #define EXTRA_ALIGN_REQ (ALIGNMENT_REQ / sizeof (float)) static float misaligned_input[MAX_INPUTS + EXTRA_ALIGN_REQ] __attribute__ ((__aligned__(ALIGNMENT_REQ))); float *input; /* manual alignment, check for NULL */ assert( (input = force_align(misaligned_input, sizeof *input, ALIGNMENT_REQ)) ); printf("Address of misaligned input: %p\n", misaligned_input); printf("Address of input: %p\n", input); printf("Assert1: %x\n", ( ((int) (input)) ) ); printf("Assert2: %x\n", ( ((int) (input)) % ALIGNMENT_REQ ) ); printf("Assert3: %x\n", ( ((int) (input)) % ALIGNMENT_REQ ) == 0 ); assert ( ( ((int) (input)) ) ); #if 0 assert ( ( ((int) (input)) % ALIGNMENT_REQ ) ); /* Fails */ #endif assert ( ( ((int) (input)) % ALIGNMENT_REQ ) == 0 ); /* Passes */ return 0; } 

从这里 :

请注意,对齐属性的有效性可能受链接器固有限制的限制。 在许多系统上,链接器只能安排变量对齐到某个最大对齐。 (对于某些链接器,支持的最大对齐可能非常小。)如果链接器只能将变量对齐到最大8字节对齐,那么在__attribute__中指定aligned(16)仍然只能为您提供8字节对齐。 有关详细信息,请参阅链接器文档。

我知道为什么断言没有发生,这是因为表达式是真的 – 不确定为什么表达式是真的,但你应该在这样的情况下将其分解。 将这些添加到您的调试语句中:

 printf("Assert1: %x\n", ( ((int) (input)))); printf("Assert2: %x\n", ( ((int) (input)) % 16 )); printf("Assert3: %x\n", ( ((int) (input)) % 16 ) == 0); 

并告诉我们结果。

还要检查你正在运行的gcc版本 – 4.3.1及更早版本似乎有对齐问题 。 Cygwin似乎同时拥有gcc3和gcc4包,假设你正在使用它 – 如果没有, 仍然检查版本。

更新1:实际上我认为@Falaina已在下面的评论中将其钉住。 这是一个似是而非的解释。

GCC正在从源代码中搞清楚输入确实(应该是)与16个字节对齐。 它足够聪明,完全放弃断言,只打印出1的printf。

但是,在链接阶段,链接器(不像GCC那样)不能保证对齐到16个字节,而是选择8(参见上面的引用)。 到那时,将断言和非优化的printfs放回代码中为时已晚。 所以实际的可执行文件不会断言(因为它们已被取出)并且它将打印优化的1而不是计算它的运行时。

volatile在@pmg的答案中修复它的原因是因为GCC 不会优化包含volatile组件的表达式。 它将断言留在并在运行时正确计算打印参数。

如果情况确实如此,毫无疑问这是我见过的更狡猾的问题之一。 我毫不犹豫地把它称之为一个bug,因为gcc和ld都是广告宣传的 – 这是因素的结合。

我想不出有什么好的理由,但是在半小时内你也没有得到任何其他答案,所以我会采取风格投诉和一些猜测和怀疑……

  • 请从第一个printf()取出地址表达式。 虽然(&array)(array)是C中具有相同值的相同表达式,但在两行之间将它写入两行之间似乎完全错误。 总有一天只关心程序中的小东西会让你免于一个坏错误,或者有人会将操作数的类型更改为额外的并且产生双间接指针的东西。 无论如何,风格很重要。 我不能certificate这一点,但我确信它确实如此。

  • 让事情变得非常简单。 只关注通过不可能的断言的static存储类的故障情况。 创建一个绝对没有任何内容的新目录,只将故障案例程序复制到该目录,然后根据CLI尝试使用./whatever.\whatever运行它。 在编译之前validation没有任何内容运行。 确保你正在运行你认为自己的东西。

  • 让我们确切地知道你在XP上使用哪个gnu环境,有几个。

pointer to array of floatint的转换不一定有意义。 如果pointer to array of floats大于int则会丢失一些信息,这可能是指针的“低位”。

试试这个:

 #include  #include  void print_pointer(void *ptr) { unsigned char data[sizeof (void*)]; size_t k; memmove(data, &ptr, sizeof (void*)); printf("ptr: "); for (k=0; k 

为int做同样的事情

 void print_int(int value) { /* ... */ } 

并比较你的发现。