У меня есть следующий пример кода, который имитирует код в приложении.
#include <iostream>
#include <string.h>
#include <cstring>
#include <atlstr.h>
using namespace std;
void test(char *s, int size)
{
//s = "";
int lens = strlen(s);
char *str1 = "(( State:0.000000 Std30c5 = State:T ) OR (( State:0.000000 Std30c6 = State:T ) OR (( State:0.000000 Std30c7 = State:T ) OR (( State:0.000000 Std30c8 = State:T ) OR (( State:0.000000 Std30c9 = State:T ) OR (( State:0.000000 Std30ca = State:T ) OR (( State:0.000000 Std30cb = State:T ) OR (( State:0.000000 Std30cc = State:T ) OR (( State:0.000000 Std30cd = State:T ) OR (( State:0.000000 Std30ce = State:T ) OR (( State:0.000000 Std30cf = State:T ) OR ( ...0.000000 = State:T ))))))))))))";
int len1 = strlen(str1);
strncpy(s, str1, 512);
int len = strlen(s);
}
int main()
{
char strDisplay[512] = "";
test(strDisplay, 512);cout << strDisplay << endl;
system("pause");
return 0;
}
Результат:
lenofstrtest = 523;
lenofstr1 = 512;
strtest = «((Состояние: 0,000000 Std30c5 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c6 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c7 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c8 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30c9 = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30ca = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cb = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cc = Состояние: T) ИЛИ ( (Состояние: 0,000000 Std30cd = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30ce = Состояние: T) ИЛИ ((Состояние: 0,000000 Std30cf = Состояние: T) ИЛИ (… 0,000000 = Состояние: T)))))) ))))))ÌÌÌÌJ ¢ £ Ш. öö)»
Почему strncpy копирует дополнительные символы?
(Это вызывает проблему, так как неправильный strnlen вызывает логику распаковки!)
Я думаю, что это связано с «ошибка 512 байт strncpy» … пожалуйста, помогите мне понять эту ошибку.
strncpy
не добавляет завершающий символ ‘\ 0’ к усеченным строкам, что вызывает проблемы, с которыми вы столкнулись. Если строка не завершена правильно, то выглядит, что она длиннее, но на самом деле вы видите данные, которые помещаются в буфер после вашего буфера. Это может вызвать серьезные проблемы.
Вместо strncpy
ты должен использовать strlcpy
который правильно завершает строку и возвращает длину исходной строки, которую вы можете сравнить с длиной вашего буфера, чтобы узнать, была ли строка обрезана или нет. strncpy
возвращает указатель на указатель на ваш буфер (что не очень полезно, так как вы уже знаете его — вы передали его в качестве первого аргумента) и не сообщает вам, произошло ли какое-либо усечение.
Смотри man strlcpy:
Функции strlcpy () и strlcat () копируют и объединяют строки
с теми же параметрами ввода и вывода, что и snprintf (3). Oни
разработаны, чтобы быть более безопасными, более последовательными и менее подверженными ошибкам
замены для легко неправильно используемых функций strncpy (3) и
strncat (3). strlcpy () и strlcat () принимают полный размер
целевой буфер и гарантированное NUL-завершение, если есть место.
Обратите внимание, что место для NUL должно быть включено в dstsize.
а также Обработка строки C — Замены в Википедии:
Наиболее популярной заменой [a] являются функции strlcat и strlcpy,
которая появилась в OpenBSD 2.4 в декабре 1998 года. [84] Эти функции
всегда записывать один NUL в буфер назначения, обрезая результат
если необходимо, и вернуть размер буфера, который был бы необходим,
который позволяет обнаружить усечение и обеспечивает размер для
создание нового буфера, который не будет усекаться.
К сожалению, он не включен в glibc — см. Безопасная переносимость бумага по
Дэмиен Миллер (PDF):
API-интерфейсы strlcpy и strlcat правильно проверяют границы целевого буфера,
nul-terminate во всех случаях и возвращает длину исходной строки,
позволяя обнаружение усечения. Этот API был принят большинством
современные операционные системы и множество автономных программных пакетов,
включая OpenBSD (там, где он возник), Sun Solaris, FreeBSD, NetBSD,
ядро Linux, rsync и проект GNOME. Заметное исключение
является стандартной библиотекой C GNU, glibc [12], чей сопровождающий
стойко отказывается включать эти улучшенные API, маркируя их
«Ужасно неэффективное дерьмо BSD» [4], несмотря на предварительные доказательства того, что они
в большинстве случаев быстрее, чем API, которые они заменяют [13]. В следствии,
более 100 программных пакетов в дереве портов OpenBSD
поддерживать свои собственные замены strlcpy и / или strlcat или эквивалентные
API — не идеальное положение вещей.
Он доступен для Linux в библиотеке libbsd:
В Debian, Ubuntu и других дистрибутивах есть пакеты:
Даже если вы не хотите зависеть ни от чего, кроме glibc, его очень легко добавить в ваш проект, так как весь источник является кратким и доступным по разрешающей лицензии:
/*
* Copyright (c) 1998 Todd C. Miller <[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0) {
while (--n != 0) {
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}
Источник: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11
Когда вы используете функцию strncpy
тогда вы всегда должны добавлять его с завершающим нулем. Например
strncpy(s, str1, n );
s[n-1] = '\0';
В противном случае код будет небезопасным, как в вашем случае.
Примите во внимание, что нет никакого смысла использовать эти два заголовка вместе
#include <string.h>
#include <cstring>
Удалите первый заголовок и используйте только второй заголовок в C ++.
#include <cstring>
В strDisplay должно быть назначено не менее 513 единиц, поскольку в конце не добавляется ни один завершающий символ strncpy.
char strDisplay[513] = "";
strDisplay[512] = '\0'; //recommended
strncpy
плохая функция, потому что она не генерирует строку. Если вы хотите использовать обработку строк в стиле C, тогда snprintf
проще использовать безопасно:
snprintf(s, size, "%s", str1);
Обратите внимание, что char *str1 = "...
устарела в C ++; ты можешь использовать char const *str1 = "...
вместо.
использование std::string
в C ++. Он автоматически позаботится обо всех этих проблемах для вас.
#include <iostream>
#include <string>
std::string test()
{
return "(( State:0.000000 Std30c5 = State:T ) OR (( State:0.000000 Std30c6 = State:T ) OR (( State:0.000000 Std30c7 = State:T ) OR (( State:0.000000 Std30c8 = State:T ) OR (( State:0.000000 Std30c9 = State:T ) OR (( State:0.000000 Std30ca = State:T ) OR (( State:0.000000 Std30cb = State:T ) OR (( State:0.000000 Std30cc = State:T ) OR (( State:0.000000 Std30cd = State:T ) OR (( State:0.000000 Std30ce = State:T ) OR (( State:0.000000 Std30cf = State:T ) OR ( ...0.000000 = State:T ))))))))))))";
}
int main()
{
std::cout << test() << std::endl;
return 0;
}
Обратите внимание, что не требуется управление памятью, временные буферы магического размера или нулевые терминаторы.