#c #c 17
#c #c 17
Вопрос:
У меня есть следующий код, в котором есть функция VariadicWrite
, и эта функция принимает указатель, который она может изменять, увеличивая его, чтобы он указывал на другую ячейку памяти в зависимости от того, сколько данных в нее записано:
#include <iostream>
#include <cstring>
template<typename T>
T Read(void* amp;ptr) noexcept
{
T resu<
memcpy(amp;result, ptr, sizeof(result));
ptr = static_cast<T*>(ptr) 1;
return resu<
}
template<typename T>
void Write(void* amp;ptr, T result) noexcept
{
memcpy(ptr, amp;result, sizeof(result));
ptr = static_cast<T*>(ptr) 1;
}
template<typename... Args>
auto VariadicWrite(void*amp; ptr, Argsamp;amp;... args)
{
(Write(ptr, args), ...); //unpack Args
return 0;
}
int main()
{
void* ptr = malloc(1024);
memset(ptr, 0, 1024);
void* temp = ptr;
VariadicWrite(ptr, 1, 2, 3);
std::cout<<Read<int>(ptr)<<"n";
std::cout<<Read<int>(ptr)<<"n";
std::cout<<Read<int>(ptr)<<"n";
free(temp);
return 0;
}
Проблема здесь в том, что код выводится, 0, 0, 0, 0
если я использую: void*amp; ptr
.
Если я это сделаю void* ptr
, он распечатается 0, 1, 2, 3
, но ptr
указатель никогда не увеличивается.
Как я могу изменить ptr
указатель VariadicWrite
на? Я думал, что void*amp;
это сработало бы, но в данном случае это не так: S
Комментарии:
1. Пожалуйста, добавьте
c
тег в сообщения C в дополнение к тегу версии.2. Я добавил тег. Также сделал код компилируемым, чтобы каждый мог его протестировать.
3. Это здорово, спасибо.
4. Я подозреваю, что в вашем коде есть UB, потому что в результате я получил разные результаты. godbolt.org/z/YPWMWK
5. Я не знаю, насколько вы знакомы с c , но не используйте
malloc
andfree
практически при любых обстоятельствах. Кроме того, предпочитайте интеллектуальные указатели необработанным указателям.
Ответ №1:
Проблема в том, что ваш VariadicWrite()
вызов будет изменен ptr
, чтобы указывать на конец ваших записанных данных. Затем вы вызываете Read()
, не сбрасывая указатель обратно в начало, поэтому вы считываете нули из неинициализированной части вашего буфера, которая следует за уже записанными данными.
Вставьте ptr = temp;
между write и read и посмотрите, исправит ли это.
Причина void* ptr
, по которой это не работает, заключается в том, что каждый вызов Write(ptr, ...)
будет увеличивать локальную копию аргумента в области действия Write()
функции. Переменная ptr
in VariadicWrite()
не изменяется после вызова to Write()
, поэтому при следующем вызове будет использоваться то же значение.
Если вы измените значение VariadicWrite(void* ptr, …)
и Write(void*amp; ptr, …)
, вы можете получить желаемое поведение. Но я бы предположил, что это плохая идея.
Как мы можем видеть из ошибки в вашем примере, знание того, изменит ли функция параметр передачи по ссылке или нет, имеет решающее значение, но не очевидно из кода, использующего функцию. Это приводит к появлению ошибок, подобных той, которую вы создали здесь. Непоследовательный интерфейс, в котором VariadicWrite()
не изменяется его аргумент, но Write()
изменяется, только усложнит вдвойне избежать такого рода ошибок.
Как правило, лучше избегать неконстантных ссылок, потому что они часто приводят к подобным ошибкам. Я предлагаю вернуть новый указатель вместо изменения аргумента.
template<typename T>
void* Write(void* ptr, const Tamp; arg)
{
return static_cast<T*>(ptr) 1;
}
template<typename... Args>
void* WriteV(void* ptr, Argsamp;amp;... args)
{
((ptr = Write(ptr, args)), ...);
return ptr;
}
Комментарии:
1. Вы, вероятно, имеете в виду «return static_cast<T*>(ptr) 1;» в функции записи.