为什么MSVC优化了此memcpy调用?

时间:2019-03-28 13:23:08

标签: c++ c

我有以下C代码(我删除了一些其他调用和检查后将其缩短了):

#include <stdint.h>
#include <memory.h>

extern char buffer[];

extern void getstr1(char *buff, int buflen);
extern void getstr2(char **s);
extern void dosomething(char *s);

void myfn()
{
    char *s, *s1;
    int len;

    getstr1(buffer, 128);
    getstr2(&s);

    len = *s + *buffer;
    memcpy(buffer + *buffer + 1, s + 1, (*s) * sizeof(char));
    *buffer = len;

    dosomething(buffer);
}

带有/ O2优化选项的MSVC会产生以下输出:

_s$ = -4                                                ; size = 4
void myfn(void) PROC                                 ; myfn, COMDAT
        push    ecx
        push    128                           ; 00000080H
        push    OFFSET char * buffer             ; buffer
        call    void getstr1(char *,int)           ; getstr1
        lea     eax, DWORD PTR _s$[esp+12]
        push    eax
        call    void getstr2(char * *)                    ; getstr2
        mov     eax, DWORD PTR _s$[esp+16]
        push    OFFSET char * buffer             ; buffer
        mov     al, BYTE PTR [eax]
        add     BYTE PTR char * buffer, al
        call    void dosomething(char *)              ; dosomething
        add     esp, 20                             ; 00000014H
        ret     0
void myfn(void) ENDP                                 ; myfn

您可以选中此on Godbolt

为什么编译器忽略了memcpy调用?将外部变量声明为“ extern char buffer [N];”很有趣。其中N> = 2或作为“ extern char * buffer;”使编译器使用memcpy。同样用memmove代替memcpy也可以做到这一点。我知道当源区域和目标区域重叠时可能的UB,但是在这里编译器对此一无所知。

2 个答案:

答案 0 :(得分:11)

我认为这是MSVC中的错误,因为您所做的是合法的。

请注意,已经归档了一个类似的错误,标题为Release build with speed optimize leaves an array uninitialized

在错误报告中用于重现该问题的代码也使用了extern type array[];

根据团队,此问题已在即将发布的版本中解决(未提及)。

答案 1 :(得分:2)

您所做的完全合法,这绝对是MSVC中的错误。

以下是精简版本以提交错误报告:

#include <string.h>

extern unsigned char buffer[], *s;

void myfn() {
    memcpy(buffer + *buffer + 1, s + 1, *s);
    *buffer = 1;
}

编译为:

void myfn(void) PROC                                 ; myfn, COMDAT
        mov     BYTE PTR unsigned char * buffer, 1
        ret     0
void myfn(void) ENDP                                 ; myfn

删除语句*buffer = 1;可防止代码生成错误。
Godbolt's Compiler Explorer上进行检查。