如何让GCC将 strcpy内联(字符串拷贝)

后端开发 2026-07-12

我正在使用 www.godbolt.org 网站,选择x86-64 gcc 15.2编译器以及 -O2优化选项,但它仍然在libc中对 strcpy 发出调用。

我希望这个编译器将strcpy操作内联实现,而不是使用内建库API。我该如何使用GCC编译器来实现这一点?

在Godbolt上的示例源代码与编译器输出 on Godbolt,仅在buffer为局部 char buffer[500] 数组时调用 strcpy(buffer, argv[1])

解决方案

对于长度未知的字符串,对strcpy进行内联可能会很复杂,不同的策略可以针对不同情况进行优化(长字符串、短字符串、不同架构等)

让memcpy实现内联更容易,因此如果可以对显式 strlen 进行内联,并使用 -O2 -minline-all-stringops,如上文评论中的Peter Cordes所建议。下面的做法将把拷贝内联执行。

int main(int argc, char** argv)
{
    int age = 100;
    char buffer[500];

    strcpy(buffer, argv[1]);
    printf("strcpy gives %s and age gives %d\n", buffer, age);
    int n = strlen(argv[2]);
    memcpy(buffer, argv[2], n+1);
    printf("strcpy gives %s and age gives %d\n", buffer, age);

    return 0;
}

请注意,代码中包含内联、展开等——程序大小会增大,并且可能抵消内联带来的收益。

        mov     rsi, QWORD PTR [rbx+16]
        mov     rdx, rsi
        mov     rax, rsi
        and     edx, 3
        je      .L20
        cmp     rdx, 2
        je      .L5
        ja      .L4
        cmp     BYTE PTR [rsi], 0
        je      .L3
        lea     rax, [rsi+1]
.L5:
        cmp     BYTE PTR [rax], 0
        je      .L3
        add     rax, 1
.L4:
        cmp     BYTE PTR [rax], 0
        je      .L3
        add     rax, 1
.L20:
        mov     ecx, DWORD PTR [rax]
        add     rax, 4
        lea     edx, [rcx-16843009]
        not     ecx
        and     edx, ecx
        and     edx, -2139062144
        je      .L20
        mov     ecx, edx
        shr     ecx, 16
        test    edx, 32896
        cmove   edx, ecx
        lea     rcx, [rax+2]
        cmove   rax, rcx
        add     dl, dl
        sbb     rax, 3
.L3:
        sub     rax, rsi
        mov     rdi, rsp
        add     eax, 1
        cdqe
        cmp     rax, 8
        jb      .L6
        mov     rcx, rax
        shr     rcx, 3
        rep movsq
.L6:
        xor     edx, edx
        test    al, 4
        je      .L7
        mov     edx, DWORD PTR [rsi]
        mov     DWORD PTR [rdi], edx
        mov     edx, 4
.L7:
        test    al, 2
        je      .L8
        movzx   ecx, WORD PTR [rsi+rdx]
        mov     WORD PTR [rdi+rdx], cx
        add     rdx, 2
.L8:
        test    al, 1
        je      .L9
        movzx   eax, BYTE PTR [rsi+rdx]
        mov     BYTE PTR [rdi+rdx], al

备选方案

另一种选项——在实际的 strcpy 针对短字符串、避免调用带来显著收益的情况下——是将“轻量级”的 strcpy 实现为static inline函数:

#define strcpy inline_strcpy

static inline char *inline_strcpy(char *dest, char *src)
{
    for (char *d = dest ; (*d++ = *src++) ; ) ; 
    return dest ;    
}

或者类似做法——GCC将几乎总是遵循内联,并生成一个相对高效的逐字节循环。

main:
        sub     rsp, 520
        xor     eax, eax
        mov     rcx, QWORD PTR [rsi+8]
        mov     rsi, rsp
.L2:
        movzx   edx, BYTE PTR [rcx+rax]
        mov     BYTE PTR [rsi+rax], dl
        add     rax, 1
        test    dl, dl
        jne     .L2
        mov     edx, 100
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 520
        ret

为了更高性能的解决方案,将利用(英特尔)推荐的lodsb/stosb。免责声明:我不是汇编专家——这段汇编是用ChatGPT生成的。

static inline char *strcpy_asm(char *dst, const char *src)
{
    char *ret = dst;

#if defined(__x86_64__) || defined(__i386__)
    __asm__ volatile (
        "1:\n\t"
        "lodsb\n\t"
        "stosb\n\t"
        "testb %%al, %%al\n\t"
        "jne 1b"
        : "+D"(dst), "+S"(src)
        :
        : "rax", "memory"
    );
#else
    while ((*dst++ = *src++) != '\0')
        ;
#endif

    return ret;
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章