在8位平台上,我由4个8位整数组成一个无符号32位整数,如下所示:

uint8_t buf[4];
uint32_t large = 0;
large |= ((uint32_t)buf[0]) << 24;
large |= ((uint32_t)buf[1]) << 16;
large |= buf[2] << 8;
large |= buf[3] << 0;

如果没有强制转换,编译器抱怨是可以理解的:

bmp.c:100:23: warning: left shift count >= width of type [-Wshift-count-overflow]
  100 |     large |= (buf[1]) << 16;
      |                       ^~

这些施法费用高吗(我猜是的),这能更有效地完成吗?

以下是我认为的avr-gcc (GCC) 13.2.0个代码的相关拆解:

000060ee <.L29>:
        large |= ((uint32_t)buf[1]) << 16;
    60ee:       91 2c           mov     r9, r1
    60f0:       a1 2c           mov     r10, r1         
    60f2:       b1 2c           mov     r11, r1

000060f4 <.Loc.91>:
        large |= buf[3] << 0;   
    60f4:       a9 2a           or      r10, r25
    
000060f6 <.Loc.92>:
        large |= buf[2] << 8;
    60f6:       50 e0           ldi     r21, 0x00       ; 0
    
000060f8 <.Loc.93>:
    60f8:       54 2f           mov     r21, r20
    60fa:       44 27           eor     r20, r20
    60fc:       05 2e           mov     r0, r21 
    60fe:       00 0c           add     r0, r0
    6100:       66 0b           sbc     r22, r22
    6102:       77 0b           sbc     r23, r23

00006104 <.Loc.94>:
        large |= buf[3] << 0;
    6104:       84 2a           or      r8, r20 
    6106:       95 2a           or      r9, r21 
    6108:       a6 2a           or      r10, r22
    610a:       b7 2a           or      r11, r23
    610c:       b8 2a           or      r11, r24
    610e:       80 92 04 01     sts     0x0104, r8      ; 0x800104 <large>
    6112:       90 92 05 01     sts     0x0105, r9      ; 0x800105 <large+0x1>
    6116:       a0 92 06 01     sts     0x0106, r10     ; 0x800106 <large+0x2>
    611a:       b0 92 07 01     sts     0x0107, r11     ; 0x800107 <large+0x3>

推荐答案

您建议的单个表达式产生15条指令,而不是20条指令 如果我对反汇编的解释是正确的-很好!

不-这是代码中未定义/实现定义的行为的问题,如果编写正确,则无关紧要.我还建议在参数中使用指针表示法(因为C将数组作为指针传递),如果函数不更改它们,则将参数声明为const.它帮助编译器进行优化(甚至从const correctness中提取)

uint32_t foo(const uint8_t *buf)
{
    uint32_t large = 0;
    large |= ((uint32_t)buf[0]) << 24;
    large |= ((uint32_t)buf[1]) << 16;
    large |= (uint32_t)buf[2] << 8;
    large |= buf[3] << 0;
    return large;
}


uint32_t bar(const uint8_t *buf)
{
    return (uint32_t) buf[0] << 24 | (uint32_t) buf[1] << 16 | (uint32_t) buf[2] << 8 | buf[3];
}

两者都会生成相同的机器码:

foo:
.L__stack_usage = 0
        mov r30,r24
        mov r31,r25
        ld r22,Z
        ldd r23,Z+1
        ldd r24,Z+2
        ldd r25,Z+3
        rcall __bswapsi2
        ret
bar:
.L__stack_usage = 0
        mov r30,r24
        mov r31,r25
        ld r22,Z
        ldd r23,Z+1
        ldd r24,Z+2
        ldd r25,Z+3
        rcall __bswapsi2
        ret

https://godbolt.org/z/b7o4114EP

另外,AVR编译器假定为little endian,您可以从大端表示法"合成"uint32_t数字.

如果字符顺序匹配,我会建议使用memcpy

memcpy(&large, buff, sizeof(large));

优化编译器不会调用memcpy

uint32_t bar(uint8_t buf[4])
{
    uint32_t large;
    memcpy(&large, buf, sizeof(large));
    return large
}

bar:
.L__stack_usage = 0
        mov r30,r24
        mov r31,r25
        ld r22,Z
        ldd r23,Z+1
        ldd r24,Z+2
        ldd r25,Z+3
        ret

但更有趣的是,如果buf数据是大端字节序,则使用100s可以使代码更高效

uint32_t foo(const uint8_t *buf)
{
    union 
    {
        uint32_t large;
        uint8_t small[4];
    }d = {.small = {[0] = buf[0], [1] = buf[1], [2] = buf[2], [3] = buf[3]}};
    return d.large;
}

uint32_t bar(const uint8_t *buf)
{
    union 
    {
        uint32_t large;
        uint8_t small[4];
    }d = {.small = {[0] = buf[3], [1] = buf[2], [2] = buf[1], [3] = buf[0]}};
    return d.large;
}

生成的代码如下:

foo:
.L__stack_usage = 0
        mov r30,r24
        mov r31,r25
        ldd r23,Z+1
        ld r22,Z
        ldd r24,Z+2
        ldd r25,Z+3
ret
bar:
.L__stack_usage = 0
        mov r30,r24
        mov r31,r25
        ldd r23,Z+2
        ldd r22,Z+3
        ldd r24,Z+1
        ld r25,Z
ret

C++相关问答推荐

问关于C中的指针和数组

我可以动态分配具有空类型函数的矩阵吗?

为什么GCC可以调用未定义的函数?

将常量转换为指针会增加.数据大小增加1000字节

C是否用0填充多维数组的其余部分?

每个 struct 变量在C中都有自己的命名空间吗?

是否可以使用指针算法在不对齐的情况下在 struct 中相同类型的字段的连续序列之间移动?

在创建动态泛型数组时,通过realloc对故障进行分段

为什么数组的最后一个元素丢失了?

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

#定义SSL_CONNECTION_NO_CONST

正数之和是负数

我在C中运行和调试时得到了不同的输出

条件跳转或移动取决于未初始化值(S)/未初始化值由堆分配创建(Realloc)

在下面的C程序中,.Ap0是如何解释的?

程序如何解释变量中的值

如何在C中处理流水线中的a、n命令?

Makefile无法将代码刷新到ATmega328p

C 程序不显示任何输出,但它接受 CS50 Lab1 的输入问题

int 与 size_t 与 long