我有以下测试程序:
#include <string.h>
int q(int *p) {
int x;
memcpy(&x,p,sizeof(int));
x+=12;
memcpy(p,&x,sizeof(int));
return p[0];
}
当我使用GCC 4.7.2为arm-linux-gnueabihf编译它时,编译器怀疑指针访问可能是未对齐的,注释装配输出中的加载和存储,例如:
ldr r0, [r0, #0] @ unaligned
如果我使用-mno-unaligned-access
进行编译,编译器根本不会发出直接加载和存储,而是调用库memcpy
。但事实上,在这种情况下,指针永远不应该是不对齐的。这是gcc中的忽视,还是我错了?
答案 0 :(得分:2)
我认为gcc
确实被int*
在void*
调用中强制转换为memcpy
而感到困惑,并假设这种指针最差。它可能试图查看底层指针是否正确对齐。您是否尝试过更高的优化级别?可能是在更高级别gcc
变得更聪明。
gcc
也可能不保证int
指针在所有代码中的对齐,但这是不明智的,也不太可能。
由于第6.2.3.2条第7款规定,允许编译器假定int*p
正确对齐:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果 结果指针没有正确对齐68)对于引用的类型,行为是 未定义。
注68)是关于正确对齐的传递性。
答案 1 :(得分:2)
在C编译器中优化的内容比int
值的加载和存储更好,这些设计是机器的自然尺寸。
将函数写为
int q(int *p) {
return *p += 12;
}
它避免了对库例程的两次调用,否则你会指望优化器内联并简化为简单的加载和存储,并表达了就地修改整数值参数并返回结果的意图。
使用memcpy
分配整数会模糊意图。
如果这个问题是将一个较大的问题减少到混乱的最小范围的例子的结果,那么我的实现可能没有直接帮助。但即使p
的类型为some_complex_struct *
而不是int *
,该建议仍然适用。赋值运算符有效。在有意义的情况下使用它优先于memcpy
。
答案 2 :(得分:1)
如果你的linux内核版本在2.6.28之前。 GCC将抛出此Warning
。
-munaligned-access
支持未对齐地址上的访问内存。这要求这些系统的内核启用此类访问。或者,不支持未对齐的访问,所有代码都必须使用-mno-unaligned-access进行编译。上游Linux内核版本自动且无条件地支持GCC发出的未对齐访问,因为此选项自版本2.6.28开始处于活动状态。
答案 3 :(得分:1)
这是我提出的解决方案,为数据字段访问实现了几种替代方案:
// #define USE_MEMCPY
// #define USE_PACKED
#ifdef __cplusplus
template <typename T> void SET(T *__attribute__((may_alias)) p, T val) {
*p=val;
}
template <typename T> T GET(T *__attribute__((may_alias)) p) {
return *p;
}
#else
#ifdef USE_MEMCPY
#include <string.h>
#define _SET(p,val,line) \
({ typeof(val) _temp_##line = (val); \
memcpy((void*)(p),(void*)&_temp_##line,sizeof(_temp_##line)); })
#define _GET(p,line) \
({ typeof(*(p)) _temp_##line; \
memcpy((void*)&_temp_##line,(void*)(p),sizeof(_temp_##line)); \
_temp_##line; })
#define SET(p,val) _SET(p,val,__LINE__)
#define GET(p) _GET(p,__LINE__)
#else /* no memcpy */
#ifdef USE_PACKED
#define SET(p,val) (((struct { typeof(val) x __attribute__((packed)); } __attribute__((may_alias))*)p)->x=(val))
#define GET(p) (((struct { typeof(*p) x __attribute__((packed)); } __attribute__((may_alias))*)p)->x)
#else
#define SET(p,val) (*((typeof(val) __attribute__((may_alias))*)p)=(val))
#define GET(p) (*((typeof(*p) __attribute__((may_alias))*)p))
#endif
#endif
#endif
然后我可以写这样的函数:
int q(int *p) {
SET(p,GET(p)+12);
return p[0];
}