#c #c
#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(amp;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.
extern char buffer[];
Законно?2. @Ctx верно, но в любом случае в любом случае не имеет особого смысла.
sizeof buffer
3. Я пробовал как C, так и C : результат тот же. Изначально это был C-код. Подробнее: проблема может быть воспроизведена в MSVC 2005 и в современных компиляторах (некоторые из них вы можете проверить на godbolt.org ).
4. Ну, C или C имеет значение, потому что C имеет стандартный заголовок с именем
memory
, но C не имеет такого понятия, кроме, я полагаю, какого-то нестандартного заголовка Linux. Может быть, есть также какой-то мусор MS, называемый memory.h? Я не могу найти его в MSDN. Я имею в виду, что вы не включилиstring.h
то, что требуется для memcpy. VS, будучи в лучшем случае едва совместимым с C90, может затем сойти с ума и предположить, что прототип являетсяint memcpy(int, int, int);
, потому что они, вероятно, также не соответствуют C99. Действительно трудно угадать, что этот так называемый «компилятор» делает, а что нет.5.
extern char buffer[];
это законно . Кроме того, это ничего подобногоextern char *buffer;
.
Ответ №1:
Я думаю, что это ошибка в MSVC, поскольку то, что вы делаете, является законным.
Обратите внимание, что уже была обнаружена аналогичная ошибка под названием: выпуск сборки с оптимизацией скорости оставляет массив неинициализированным.
Код, приведенный для воспроизведения проблемы в отчете об ошибке, также использует extern type array[];
Согласно команде, эта проблема исправлена в предстоящем выпуске (который не упоминается).
Комментарии:
1. … «приятно»… и «промежуточное исправление» заключается в изменении всего исходного кода C.
2. Кажется маловероятным, что у них была эта ошибка в течение 12 лет, и никто этого не заметил?
3. @Lundin Или .. им все равно?
4. Да, это похоже на ту же ошибку, но они написали, что VS2015 выдал правильный вывод. На сайте godbolt вы можете попробовать оба из них, и результат будет одинаковым (вызов memcpy отсутствует).
5. исправлено в предстоящем выпуске Не уверен, означает ли это VS2019 или один из выпусков VS2017. Я бы не ожидал, что какой-либо старый компилятор будет исправлен.
Ответ №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.