Почему MSVC оптимизирует этот вызов memcpy?
У меня есть следующий код 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);
}
MSVC с опцией оптимизации /O2 выдает следующий вывод:
_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
Вы можете проверить это на Godbolt
Почему компилятор пропустил вызов memcpy? Интересно, что объявление внешней переменной как "extern char buffer [N];" где N> = 2 или как "extern char * buffer;" заставляет компилятор использовать memcpy. Также замена memcpy на memmove делает то же самое. Я знаю о возможном UB, когда области источника и назначения перекрываются, но здесь компилятор не знает об этом.
Ответы
Ответ 1
Я думаю, что это ошибка в MSVC, поскольку то, что вы делаете, является законным.
Обратите внимание, что аналогичная ошибка уже подана: " Выпуск сборки со скоростью оптимизации оставляет массив неинициализированным.
Код, приведенный для воспроизведения проблемы в отчете об ошибке, также использует extern type array[];
По словам команды, эта проблема исправлена в следующем выпуске (который не упоминается).
Ответ 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.