这让我疯了好几天。如果我将其声明为static
,我无法将数组与16对齐。
非常感谢任何帮助。
修订版:
#include <stdio.h>
#include <assert.h>
#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传递(带或不带静态声明),程序正常终止。
我在运行XP专业版的英特尔酷睿2双核处理器上使用MinGw gcc 4.4.0。
答案 0 :(得分:2)
来自here:
请注意,对齐属性的有效性可能会受到链接器固有限制的限制。在许多系统上,链接器只能安排变量对齐到某个最大对齐。 (对于某些链接器,支持的最大对齐可能非常小。)如果链接器只能将变量对齐到最多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及更早版本似乎有problem对齐。 Cygwin似乎有gcc3和gcc4包,假设你正在使用它 - 如果没有,仍然检查版本。
更新1:实际上我认为@Falaina已将其钉在下面的评论中。这是一个看似合理的解释。
GCC正在从源代码中确定输入确实(应该是)与16个字节对齐。它足够智能,可以完全删除断言,只打印出1的printf。
但是,在链接阶段,链接器(不像GCC那样)不能保证与16字节对齐,而是选择8(参见上面的引用)。到那时,将断言和非优化的printfs放回到代码中为时已晚。所以实际的可执行文件不会断言(因为它们已被取出)并且它将打印优化的1而不是计算它为运行时。
volatile在@pmg的答案中修复它的原因是因为GCC将不优化包含易失性组件的表达式。它将断言留在并在运行时正确计算打印参数。
如果情况确实如此,毫无疑问这是我见过的更为狡猾的问题之一。我毫不犹豫地把它称为一个错误,因为gcc和ld都是广告宣传的 - 这是因素的组合,搞砸了。
答案 1 :(得分:2)
在我的工作机器上(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添加 - 我们都建议你接受这个,如果结果是这样的话):
实际上我认为@Falaina在对@ Pax的回答发表评论时将其钉在了一起:
只是一个建议。您是否正在进行优化编译?有可能编译器试图变得聪明并且“嘿,这个变量与16个字节对齐,显然地址%16为0”并用1替换所有支票。只是想一想。
这是解释。 GCC正在从源代码中搞清楚输入确实(应该是)与16个字节对齐。它足够聪明,可以完全删除assert
,只打印出printf
的1。
但是,在链接阶段,链接器无法保证对齐到16个字节,而是选择8,因为(来自@Pax):
请注意,对齐属性的有效性可能会受到链接器固有限制的限制。在许多系统上,链接器只能安排变量对齐到某个最大对齐。 (对于某些链接器,支持的最大对齐可能非常小。)如果链接器只能将变量对齐到最多8个字节对齐,那么在__attribute__中指定aligned(16)仍然只能为您提供8字节对齐。有关详细信息,请参阅链接器文档。
到那时,将assert
和非优化printf
恢复到代码中为时已晚。所以实际的可执行文件不会断言(因为它们已被取出)并且它将打印优化的1而不是计算它为运行时。
volatile
在我的回答中修复它的原因是因为GCC不会优化包含易失性组件的表达式。它会保留assert
并在运行时正确计算printf
个参数。
如果您不介意将其声明为比严格要求更大一点,您可以手动对齐数组:
#include <assert.h>
#include <stdio.h>
#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;
}
答案 2 :(得分:0)
我想不出有什么好的理由,但是在半小时内你也没有得到任何其他答案,所以我会采用风格投诉和一些猜测和猜疑......
请从第一个&
中的地址表达式中取出printf()
。虽然(&array)
和(array)
是C中具有相同值的相同表达式,但在两行之间以两种方式写入它似乎是完全错误的。总有一天只关心程序中的小东西会让你免于一个坏错误,或者有人会将操作数的类型更改为一个额外的&amp;制作一个双间接指针。无论如何,风格很重要。我无法证明这一点,但我确信它确实如此。
让事情变得非常简单。只关注传递不可能断言的static
存储类的失败案例。创建一个绝对没有任何内容的新目录,只将故障案例程序复制到该目录,然后根据CLI尝试使用./whatever
或.\whatever
运行它。在编译之前验证没有任何内容运行。确保你正在运行你认为自己的东西。
让我们准确了解您在XP上使用的gnu环境,有几种。
答案 3 :(得分:0)
从pointer to array of float
到int
的转换不一定有意义。
如果pointer to array of floats
大于int
,则会丢失一些信息,这可能是指针的“低位”。
试试这个:
#include <stdio.h>
#include <string.h>
void print_pointer(void *ptr) {
unsigned char data[sizeof (void*)];
size_t k;
memmove(data, &ptr, sizeof (void*));
printf("ptr: ");
for (k=0; k<sizeof (void*); k++) {
printf(" %02x", data[k]);
}
puts("");
}
对int
执行相同操作void print_int(int value) { /* ... */ }
并比较你的发现。