Я из Python и недавно изучаю C++. Я изучал функцию C/C++ под названием memset и следовал онлайн-примеру с веб-сайта https://www.geeksforgeeks.org/memset-in-cpp/, где я получил несколько ошибок компиляции:
/**
* @author : Bhishan Poudel
* @file : a02_memset_geeks.cpp
* @created : Wednesday Jun 05, 2019 11:07:03 EDT
*
* Ref:
*/
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int main(int argc, char *argv[]){
char str[] = "geeksforgeeks";
//memset(str, "t", sizeof(str));
memset(str, 't', sizeof(str));
cout << str << endl;
return 0;
}
Ошибка при использовании одинарных кавычек 't'
Это печатает дополнительные символы.
tttttttttttttt!R@`
Ошибка при использовании "t" с двойными кавычками
$ g++ -std=c++11 a02_memset_geeks.cpp
a02_memset_geeks.cpp:17:5: error: no matching function for call to 'memset'
memset(str, "t", sizeof(str));
^~~~~~
/usr/include/string.h:74:7: note: candidate function not viable: no known
conversion from 'const char [2]' to 'int' for 2nd argument
void *memset(void *, int, size_t);
^
1 error generated.
Как использовать memset в C++?
Дальнейшее изучение
Отличный туториал с недостатками memset приведен здесь:
https://web.archive.org/web/20170702122030/https:/augias.org/paercebal/tech_doc/doc.en/cp.memset_is_evil.html
большинство онлайн-ресурсов для обучения С++ - дерьмо, и, на самом деле, этот сайт не является исключением, попробуйте вместо этого: stackoverflow.com/questions/388242/…
Я использовал одинарную кавычку 't', но все равно получаю дополнительные символы на выходе.
После того, как вы это сделаете, это больше не будет корректно завершённой нулем C-строкой. Вы потеряли 0. Попробуйте вместо этого передать sizeof(str)-1.
@HansPassant Тогда как его использовать? Является ли он устаревшим и не используется в настоящее время?
Зачем вообще использовать memset в C++? Причина, по которой существуют старые функции C, заключается в обратной совместимости.
Это заряженный пистолет, вы навели его на левую ногу и нажали на курок. Вы должны целиться правильно.
это весьма актуально: stackoverflow.com/a/8590379/4117728
Также используйте std::string вместо char[]
@HansPassant Значит, он должен прострелить себе правую ногу? ;)
@dbush: Или, возможно, хуже ...
Вы не должны менять вопрос под людьми, которые на него отвечают. Если вы берете комментарий или ответ, и он все еще не работает, вы можете задать другой вопрос, но такого рода редактирование, которое заменяет один вопрос другим, разрушительно.
Не используйте std::memset, используйте станд::заполнить, это так же быстро и безопасно.
Вопросы, относящиеся к данным ответам или содержащие редактирование чего-то, что относится к ответу, всегда выглядят немного странно, имхо. Вы можете ответить на свой вопрос, хотя
Если вы внимательно посмотрите на страницу, которую вы цитируете, и посчитаете, вы заметите, что "geeksforgeeks" имеет 13 символов, а строка t, которая представляет вывод, имеет 14. Таким образом, пример кода также создает дополнительный вывод. Как видно из ответов, это не неожиданность — код просто неверный.
Вам действительно следует избегать using namespace std - это плохая привычка, и может молча изменить смысл вашей программы, когда вы этого не ожидаете. Привыкайте использовать префикс пространства имен (std намеренно очень короткий) или импортировать только нужные имена в наименьший разумный объем. Неужели так сложно написать std::memset?
@HansPassant Стрелять в правую ногу лучше? =П
@SergeyA Я не уверен, почему этот вопрос отложен, он имеет полный MWE, правильно решает вопрос и не содержит вульгарных слов или каких-либо плохих комментариев.
В исходном вопросе @astro123 "t" был заключен в двойные кавычки, и я проголосовал за то, чтобы закрыть его как опечатку (очевидно, что memset не принимает указатели в качестве второго аргумента). И затем вы отредактировали вопрос, полностью изменив его значение - он не стал допустимым вопросом, на который можно ответить, но тем самым вы аннулируете предыдущие ответы, что на самом деле не так уж и здорово.
IMO единственный правильный ответ на этот вопрос: «Вы этого не сделаете» - по крайней мере, пока вы изучаете С++. Mybe, когда вы эксперт. И даже тогда, наверное, нет.





Error when using single quotes 't' This prints extra characters.
Это потому, что вы перезаписали нулевой терминатор.
Терминатор является частью размера массива (массив не волшебный), хотя это не часть размера строки логический.
Итак, я думаю, вы имели в виду:
memset(str, 't', strlen(str));
// ^^^^^^
Error when using "t" with double quotes
Совершенно другая вещь. Вы сказали компьютеру установить каждый символ в строке как строку. Не имеет смысла; не будет компилироваться.
How to use memset in C++?
Не надо.
Либо используйте безопасный тип std::fill в сочетании с std::begin и std::end:
std::fill(std::begin(str), std::end(str)-1, 't');
(Если вы беспокоитесь о производительности, не беспокойтесь: это просто делегирует memset, где это возможно, через специализацию шаблона, оптимизация не требуется, без ущерба для безопасности типов; пример здесь в libstdС++.)
Или просто std::string для начала. ?
I was learning the fuction memset in C++ from https://www.geeksforgeeks.org/memset-in-cpp/ where the example is given as below
Не пытайтесь изучать C++ на случайных веб-сайтах. Вместо этого получите себе Хорошая книга.
к сожалению, это действительно sizeof в исходном примере. Жаль, что такой код используется для "обучения" c++ :(
Я изучаю C++ и учусь онлайн от geeksforgeeks.org/memset-in-cpp, пример взят оттуда, никаких предупреждений там не было. Спасибо за информацию об использовании.
Обновлено с учетом обоих комментариев.
Кроме того, я из фона Python, где одинарные кавычки и двойные кавычки одинаковы, поэтому я также получил еще одну ошибку.
@astro123 Еще одна причина работать с хорошей книгой. В C++ есть разные виды литералов, которые полностью отличаются от Python.
Этот сайт (geeksforgeeks) должен быть навсегда забанен.
@astro123: обучение онлайн на сайте geeksforgeeks.org/memset-in-cpp Первая проблема. Этот учебник имеет серьезную ошибку в его крошечном примере. Это не редкость на geeksforgeeks.org. Есть хорошие вещи немного, но они часто смешиваются с плохими вещами, и пока вы не станете экспертом вы не будете знать, как отличить. В отличие от Stack Overflow, у geeksforgeeks нет механизма голосования, чтобы люди могли просматривать сообщения и указывать их качество, поэтому у вас нет возможности узнать, каким из них доверять.
@PeterCordes это позор, ТАК Документация пошла так, как это было ... явно есть спрос на кураторские руководства, за которые проголосовали. Я уверен, что со временем кто-нибудь придумает правильный дизайн.
Почему "не"? Разве реализация memset не часто значительно быстрее? fill не всегда может быть реализован для использования директивы сборки (даже в -O4), в то время как memset всегда должен использовать ее, если она доступна. Также никогда не следует использовать strlen. Просто никогда. если вы знаете размер строки во время компиляции (и sizeof действительно), используйте его. Если вы не знаете этого во время компиляции, strlen небезопасно.
@grovkin Нет, основная реализация будет делегировать memset через специализацию, т. Е. Когда аргументы шаблона предполагают, что это требуется (уровень оптимизации не требуется) - например libstdС++. Не нужно пытаться «превзойти тулчейн», потому что он лучше нас. С другой стороны, вы жертвуете своей безопасностью типов, и я имеют видел давние скрытые ошибки, когда кто-то менял тип и не сканировал все его варианты использования, одним из которых был непослушный memset на том, что раньше было массивом C.
@LightnessRacesinOrbit вы ожидаете, что шаблоны будут специализированы для char *, но я видел, как компиляторы, используемые в производственном коде (я думаю, это был Sun), работали в 10 раз медленнее при использовании copy() вместо memcpy для векторов. Тот факт, что в языке есть механизмы для обработки этого, не означает, что используемый вами компилятор справится с этим. И когда проблема имеет практическое, а не теоретическое значение, вы должны использовать те инструменты, которые у вас есть, а не те, которые, по вашему мнению, у вас должны быть.
@grovkin Такую откровенно нестандартную реализацию не следует использовать в первую очередь.
@LightnessRacesinOrbit еще раз, когда проблема имеет практическое, а не теоретическое значение, вы должны использовать инструменты, которые у вас есть, а не те, которые, по вашему мнению, у вас должны быть. Я вижу, что у вас все еще есть strlen в ответе, кстати. Эта функция никогда не должна использоваться. В любом коде.... когда-либо.
Эта декларация
char str[] = "geeksforgeeks";
объявляет массив символов, содержащий строку, представляющую собой последовательность символов, включая завершающий нулевой символ '\0'.
Вы можете представить объявление следующим эквивалентным способом
char str[] =
{
'g', 'e', 'e', 'k', 's', 'f', 'o', 'r', 'g', 'e', 'e', 'k', 's', '\0'
};
Этот вызов функции memset
memset(str, 't', sizeof(str));
переопределяет все символы массива, включая завершающий ноль.
Итак, следующее утверждение
cout << str << endl;
приводит к неопределенному поведению, поскольку он выводит символы до тех пор, пока не встретится завершающий ноль.
Вместо этого вы могли бы написать
#include <iostream>
#include <cstring>
int main()
{
char str[] = "geeksforgeeks";
std::memset( str, 't', sizeof( str ) - 1 );
std::cout << str << '\n';
}
Или следующим образом
#include <iostream>
#include <cstring>
int main()
{
char str[] = "geeksforgeeks";
std::memset( str, 't', std::strlen( str ) );
std::cout << str << '\n';
}
Это сохраняет конечный ноль неизменным в массиве.
Если вы хотите переопределить все символы массива, включая завершающий ноль, вам следует заменить этот оператор
std::cout << str << '\n';
для этого заявления
std::cout.write( str, sizeof( str ) ) << '\n';
как это показано в программе ниже, потому что массив теперь не содержит строки.
#include <iostream>
#include <cstring>
int main()
{
char str[] = "geeksforgeeks";
std::memset( str, 't', sizeof( str ) );
std::cout.write( str, sizeof( str ) ) << '\n';
}
Что касается этого звонка
memset(str, "t", sizeof(str));
то тип второго аргумента (то есть тип const char *) не соответствует типу второго параметра функции, имеющего тип int. См. объявление функции
void * memset ( void * ptr, int value, size_t num );
Таким образом, компилятор выдает сообщение об ошибке.
Помимо массивов символов (которые очень часто используются даже в C++) вы также можете использовать стандартный класс std::string (или std::basic_string), который имитирует строки.
В этом случае нет необходимости использовать стандартную функцию C memset для заполнения строки одним символом. Самый простой способ сделать это следующий
#include <iostream>
#include <string>
int main()
{
std::string s( "geeksforgeeks" );
s.assign( s.length(), 't' );
std::cout << s << '\n';
}
Другой способ — использовать стандартный алгоритм std::fill или std::fill_n, заявленный в шапке <algorithm>. Например
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
std::string s( "geeksforgeeks" );
std::fill( std::begin( s ), std::end( s ), 't' );
std::cout << s << '\n';
}
или
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
std::string s( "geeksforgeeks" );
std::fill_n( std::begin( s ), s.length(), 't' );
std::cout << s << '\n';
}
Вы даже можете использовать метод replace класса std::string одним из следующих способов:
#include <iostream>
#include <string>
int main()
{
std::string s( "geeksforgeeks" );
s.replace( 0, s.length(), s.length(), 't' );
std::cout << s << '\n';
}
Или
#include <iostream>
#include <string>
int main()
{
std::string s( "geeksforgeeks" );
s.replace( std::begin( s ), std::end( s ), s.length(), 't' );
std::cout << s << '\n';
}
Исходный пост ясно указывает на то, что пользователь пытается изучить C++. Упомяните, по крайней мере, что ничего из этого не имеет значения, если вы используете std::string, который следует использовать здесь вместо того, чтобы использовать этот сложный C материал. (Возможно, это будет полезно знать, но не в начале курса)
@JVApen Исходное сообщение ясно указывает на то, что пользователь пытается узнать, как использовать memset с массивами символов. :)
Хороший ответ. Если вы хотите, чтобы это было лучше для OP: обратите внимание на разницу в системе типов. C++ имеет статическую систему типов, в которой переменные имеют фиксированный статический тип. Python имеет полностью динамическую систему типов, в которой значения имеют тип, а переменные — нет. Вероятно, это источник его путаницы с 't' и "t".
Что вы подразумеваете под «строками имитирует»?
@ Рэй Ты ошибаешься. Для начала правильное объявление будет выглядеть как const char *str = "geeksforgeeks"; И в любом случае строковые литералы в C и C++ неизменяемы. Любая попытка изменить строковый литерал приводит к неопределенному поведению.
@VladfromMoscow Ты прав, конечно. Я не уделял должного внимания тому, что именно делалось после звонка strlen. Я пересматриваю свое предложение: вы также можете объяснить, что если строка была объявлена как const char *str = "geeksforgeeks";, sizeof больше не будет сообщать длину строки, а скорее размер указателя. (Даже если объявление его как указателя на строковый литерал в этом конкретном примере приводит к дальнейшим проблемам, я видел достаточно людей, делающих ошибку, делая sizeof указателя на строку, и я думаю, что стоит объяснить, почему это не работает. )
@ Рэй Спасибо. Но это будет слишком широкий ответ на простой вопрос. :)
Это правильный синтаксис для memset...
void* memset( void* dest, int ch, std::size_t count );
Converts the value ch to unsigned char and copies it into each of the first count characters of the object pointed to by dest. If the object is a potentially-overlapping subobject or is not TriviallyCopyable (e.g., scalar, C-compatible struct, or an array of trivially copyable type), the behaviour is undefined. If count is greater than the size of the object pointed to by dest, the behaviour is undefined.
(источник)
Для первого синтаксиса memset(str, 't', sizeof(str));. Компилятор пожаловался на лишний размер. Он печатает 18 раз tttttttttttttt!R@. Я предлагаю попробовать sizeof(str) -1 для массива символов.
Для второго синтаксиса memset(str, "t", sizeof(str)); вы предоставляете второй параметр как строку. По этой причине компилятор жалуется на ошибку: неверное преобразование из ‘const char*’ в ‘int’
uint32_t имеет полностью определенное представление объекта (за исключением порядка следования байтов). Так что неясно, о каком перекрытии вы говорите, потому что memset принимает только один аргумент указателя; другие аргументы по значению. Эта формулировка имеет смысл для memcpy, которая запрещает перекрытие, в отличие от memmove.
@PeterCordes Честно говоря, эта фраза была заимствована с сайта cppreference.com. Поэтому, если это неправильно, cppreference.com необходимо исправить.
@LightnessRacesinOrbit: на cppref эта фраза является гиперссылкой на определение, что имеет смысл. Вполне правдоподобно, что это UB, если memset также может изменять байты другого объекта (поскольку указатель является подобъектом структуры, объявленной с помощью [[no_unique_address]], что позволяет компилятору делать все, что он хочет, включая создание битовых полей для узкого или логические типы, я думаю). Я менее ясен в части «подобъект базового класса»; возможно, это UB, потому что он может перезаписать указатель vtable?
@PeterCordes - речь идет о что-то вроде этого. Здесь base легко копируется, но это небезопасно для memset (или memmove) или потому, что это потенциально перекрывающийся подобъект. Обратите внимание, что sizeof(base) == 8, но когда он используется в качестве основы derived (который сам имеет член char), sizeof(derived) == 8! Таким образом, члены производного хранятся в дополнении base. Следовательно, небезопасно перезаписывать произвольный base& на memset, так как в этом случае вы также уничтожите производный член.
Обратите также внимание на то, как это отражается в генерации кода на gcc для обнуления base в b = base{}: он выполняет запись qword и byte, поскольку не может безопасно расширить это до одной записи qword, потому что отступы могут использоваться повторно. Затем см. base2 и derived2: они идентичны, за исключением того, что base2 — это struct, а не class. Затем он становится агрегатом, и, я думаю, перекрытие запрещено (обратите внимание, как меняется b = base2{} codegen).
@BeeOnRope: Вы имеете в виду dword + byte до нуля base (вы сказали qword дважды). Интересно. Единственная разница между классом и структурой заключается в том, что класс по умолчанию имеет значение private:, а структура — public:. Похоже, что размещение производных членов в дополнении базы зависит только от видимости и переключается, если вы используете эти теги, чтобы иметь частные члены в структуре base2 и общедоступные члены базового класса. godbolt.org/z/3VLeiS
@Peter Да, это связано с видимостью, хотя я не знал, почему. Как и выше, я думал, что это ключ к тому, является ли base Aggregate или нет (в основном, самая POD-подобная вещь, которую предлагает C++). Это не отключение стандартной раскладки, что я проверял.
@BeeOnRope: я думаю, что это может быть дизайнерское решение, которое могло пойти в любом случае. А может и нет: согласно itanium-cxx-abi.github.io/cxx-abi/abi.html#PODЭтот ABI использует определение POD только для определения размещать ли объекты в хвостовой части подобъекта базового класса. Хотя стандарты со временем расширили определение POD, они также запретили программисту напрямую читать или записывать базовые байты подобъекта базового класса, например, с помощью memcpy. (x86-64 использует тот же C++ ABI). С некоторыми вещами о том, как развивался POD в C++.
@PeterCordes - верно, разработанное решение должно быть в контексте ABI платформы, а не только на уровне компилятора, поскольку все должны с этим согласиться, верно? В любом случае, единственное свойство, которое я обнаружил, которое не противоречило практике в отношении того, может ли дополнение использоваться производным классом, было «агрегатным». См. здесь. base это POD, тривиальная и стандартная раскладка, но все равно небезопасная. Однако это не совокупность. Конечно, это не доказательство :).
@BeeOnRope: Ах, я не знал, что «совокупность» имеет особое техническое значение, которое включает в себя отсутствие частных/защищенных членов. Что такое агрегаты и POD и в чем их особенность?. Я не проверял это, но я думаю из примечаний C++ ABI к "POD", что (некоторый черновик) ISO C++ должен сказать, что вы можете наступать на заполнение агрегата, но не обязательно вообще для любого POD/тривиально -копируемый тип. Таким образом, вы можете поместить производные члены в это заполнение, когда база не является агрегатом. Это то, с чем соглашается этот C++ ABI.
@PeterCordes - да, я только что закончил читать (пролистывать) и этот FAQ :). Я не нахожу слова «агрегат» в связанном с вами Itanium ABI. ABI был написан задолго до многих изменений в стандарте C++, упомянутых в FAQ, и до того, как некоторые термины вообще появились. В частности, в более позднем стандарте были введены более тонкие различия, о которых не знал документ ABI. 1/х
Я не читал ABI, но, основываясь на поиске, я не могу найти язык, охватывающий этот случай. Он упоминает «подобъект базового класса» в разделе, который вы связали как «потенциально перекрывающийся подобъект» одного типа (другой является членами данных с no_unique_address), но все дальнейшие ссылки на «подобъект базового класса» не кажутся уместными (они о vtables), и дальнейшие ссылки на «подобъект po», похоже, касаются случая члена данных, а не случая базового класса. Я делаю конкретный вопрос по теме, свяжу его здесь.
@PeterCordes — к вашему сведению.
Влад услужливо ответил на первую часть вашего вопроса, но я чувствую, что вторую часть можно было бы объяснить немного более интуитивно:
Как уже упоминалось, 't' — это персонаж, тогда как "t" — это нить, а строки имеют нулевой терминатор в конце. Это делает "t" массив не из одного, а из два символов — ['t', '\0']! Это делает ошибку memset более интуитивно понятной — она может достаточно легко преобразовать один char в int, но задыхается, когда получает массив char. Как и в Python, int(['t', '\0']) (или ord(['t', '\0'])) не вычисляется.
А если быть еще более точным, то при передаче «t» адрес «t» передается в «t». Поэтому, если бы он был преобразован в параметр int в memset, это был бы указатель на 't', преобразованный в int, а не значение строки, преобразованное в int.
"t"и't'не одно и то же.