跳转至

gcc 会优化很多内存相关的调用

他们通常在'string.h'里,比如memcpy, 当编译器通过上下文能推断出传入参数类型,libc的mempcy就会替换为gcc的memcpy, 这可能导致问题

举例

知乎评论的,release版本的memset被替换, 导致跟debug版本不同行为bug

https://www.zhihu.com/question/435258463/answer/1969561682

内存地址对齐

如果内存地址对齐的,那么'取值地址被取值长度求模为0',例如int i, 则要求(void*)&i % sizeof(int) == 0

原因

当同时定义多个变量时,不考虑编译器优化,则他们在内存的位置相邻。按照不同的对齐长度,有不同的占用大小
这里以结构体举例

struct bar{
    int interger;
        char charactor;
        long long longlong;
};
使用#pragma pack(4) 设置按4字节对齐,默认也是4字节,那么sizeof(struct abc) == 16 使用gdb打印内存地址,可见地址都对齐的: '0x7fffffffe430&4==0' '0x7fffffffe438%8==0'
(gdb) p sizeof(struct abc)
$5 = 16
(gdb) p &a.interger 
$1 = (int *) 0x7fffffffe430
(gdb) p &a.charactor 
$2 = 0x7fffffffe434 "\232\177"
(gdb) p &a.longlong 
$3 = (long long *) 0x7fffffffe438
(gdb) p &a
$4 = (abc *) 0x7fffffffe430

顺便提一下,指针指向的是内存段的低地址,所以有个面试题是判断系统是大端小端的方法如下
通过如下强转int为char, 如果c==0x12则为大端,否则为小端系统

int i=0x12345678;
char c=(char)i;

使用#pragma pack(1) 设置为按1字节对齐,则上面的结构体大小为'13',显然更省内存了
但通过取地址会发现地址未对齐, 求模不为0。 但我自己使用gcc测试地址依旧对齐的,原因是编译器会进行自然 对齐

自然对齐

上面的#pragma pack(1)虽然使得longlong的地址对于bar来说偏移了5字节,但编译器始终能调整bar的起始地址使得longlong的地址对齐

595fac88 
c3c5a2d8
105d3ab8

不对齐的坏处

虽然上面说了编译器能自然对齐,但还是会真的出现不对齐访问。由于对内存的访问涉及到具体硬件的实现,所以一般会有几种影响 1. 硬件平台支持非对齐访问,但性能有损耗 2. 抛出异常给cpu,由cpu来校准,性能有更大损耗 3. 平台不支持,但也不抛异常,获取的是错误的值,导致难定位的异常

引用

https://www.kernel.org/doc/html/latest/core-api/unaligned-memory-access.html