C 中访问不对齐的数据有什么优雅的方案? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iqoo
V2EX    C

C 中访问不对齐的数据有什么优雅的方案?

  •  
  •   iqoo 2023-03-26 12:42:06 +08:00 2946 次点击
    这是一个创建于 996 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如从数据流当前位置读取一个 u64 ,内存大概率是不对齐的,一般做法是用 memcpy 把数据复制到变量上。

    但有些 CPU 允许访问不对齐的内存,只是效率较低。如果程序只运行在这类 CPU 上,并且该变量读取次数极少(可能就读取一次),那么直接用指针类型转换代替 memcpy ,少一行复制代码,是不是更简洁一些?

    至于运行效率,用 memcpy 复制 u64 编译时会自动展开。但内存对齐不可知的访问,编译时是否也会自动展开?如果是的话,最终效率应该都一样。

    /div>
    12 条回复    2023-03-26 15:57:37 +08:00
    wudicgi
        1
    wudicgi  
       2023-03-26 13:39:09 +08:00
    如果字节序和本机字节序一样的话,用 memcpy() 就挺好
    要是还要转换字节序的话,写个函数按字节读取再位运算合并成 uint64_t 数值也挺好,看着很清晰

    优先考虑优雅的话,效率就往后放放
    HaroldFinchNYC
        2
    HaroldFinchNYC  
       2023-03-26 13:43:19 +08:00
    define "优雅"
    xuanbg
        3
    xuanbg  
       2023-03-26 13:46:09 +08:00
    为什么不对齐呢?都 2023 年了,还缺这么点内存?
    leonshaw
        4
    leonshaw  
       2023-03-26 13:52:41 +08:00 via Android
    用位运算
    duke807
        5
    duke807  
       2023-03-26 13:54:01 +08:00 via Android   4
    参考 linux 内核代码,用 put_unaligned 和 get_unaligned 函数

    我自己经常把 linux 代码搬到 mcu 用,看这个文件最后面:
    https://github.com/dukelec/cdnet/blob/master/utils/cd_utils.h

    使用举例:
    https://github.com/dukelec/msgpackel/blob/master/msgpackel_c/msgpackel.c
    favourstreet
        6
    favourstreet  
       2023-03-26 14:19:08 +08:00 via Android
    我建议直接用对应数据类型的指针,别管其他的。怎么解引用没对齐的指针那是编译器的问题,只有给我实现了 c 语言标准定义的行为,编译器爱怎么优化怎么优化。
    wudicgi
        7
    wudcgi  
       2023-03-26 14:28:05 +08:00
    @favourstreet 这个还真不是编译器能完全负责的,如果所有地址都视为未对齐的,生成机器码的效率会非常低
    其实一般只有处理外来的数据流时可能会遇到这种情况,特殊处理一下就好了
    leonshaw
        8
    leonshaw  
       2023-03-26 14:29:17 +08:00 via Android
    @favourstreet 不对齐的指针是 UB
    duke807
        9
    duke807  
       2023-03-26 14:29:22 +08:00 via Android
    @favourstreet
    no no no ,在不支持非对齐访问的 cpu 上,你这样搞 cpu 会进异常的
    icyalala
        10
    icyalala  
       2023-03-26 15:15:10 +08:00
    在支持非对齐内存访问的架构上,无论用 memcpy 还是自己按字节复制,
    大部分编译器都会直接优化成 mov 之类的单个指令:
    https://godbolt.org/z/355GzW1eb (clang/x64)
    https://godbolt.org/z/ahhbWrGGb (gcc/x64)
    https://godbolt.org/z/97bav5YMz (clang/arm64)

    在不支持的架构上,memcpy 有的会转为一次函数调用 call memcpy ,有的会展开:
    https://godbolt.org/z/3TxG7eonY (clang/arm32) 展开了
    https://godbolt.org/z/hafqdcqTr (gcc/arm32) 没展开

    所以如果你实际 benchmark 下来 call memcpy 性能比较差,那直接都按字节复制就行
    favourstreet
        11
    favourstreet  
       2023-03-26 15:22:00 +08:00 via Android
    @wudicgi
    @duke807
    @duke807
    对不起我看了一下还真是 UB ,让各位大佬见笑了
    DeltaC
        12
    DeltaC  
       2023-03-26 15:57:37 +08:00
    “用指针类型转换代替 memcpy ”,可能违反 strict aliasing rule ,用的时候要注意。
    这里不止存在 unaligned access 问题,你还进行了 type punning 。
    符合标准的 type punning 方式有 4 个:1.符合 strict aliasing rule 的指针访问 2.union 3.memcpy 4.std::bit_cast
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1070 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 18:16 PVG 02:16 LAX 10:16 JFK 13:16
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86