Это неопределенное поведение, поскольку входные аргументы не могут быть частью выходного буфера (как в случае snprintf
, так и sprintf
):
snprintf(result->value,VALUE_BUFFER,"the quick...%s%s",result->value,pointer);
Это указано телеграфно в стандарте C:
& sect; 7.21.6.5/para 2: & hellip; Если копирование происходит между перекрывающимися объектами, поведение не определено. (то же самое предложение встречается в разделе 7.21.6.6/2 в отношении sprintf
)
, а также в man sprintf
:
& hellip; результаты не определены, если вызов sprintf()
, snprintf()
, vsprintf()
или vsnprintf()
приведет к копированию между перекрывающимися объектами (например, если целевой строковый массив и один из предоставленные входные аргументы ссылаются на один и тот же буфер). (версия для Linux)
Если в некоторых обстоятельствах получится ожидаемый результат, вам повезло (или не повезло, поскольку случайность может привести вас к неверному выводу).
Хотя это не так, эта строка до смешного усложняется тем, что она делает:
sprintf(result->name,"%s","\0");
"\0"
обрабатывается как строка нулевой длины, поскольку строки заканчиваются первым символом NUL, поэтому он отличается от ""
только тем, что использует два байта вместо одного. Но в любом случае вы могли бы просто написать:
result->name[0] = 0; /* Or ... `\0` if you like typing */
Стандартная библиотека включает strcat
и strncat
для объединения строк, но «безопасная» версия strncat
позволяет указать только ограничение на количество добавляемых символов, но не ограничение на общую длину строка. Таким образом, вам нужно следить за количеством доступных символов самостоятельно, и если вы собираетесь это сделать, вы можете вместо этого отслеживать положение конца строки, куда вы хотите скопировать добавленный Строка, а не поиск конца каждый раз, когда вы делаете конкатенацию. По этой причине str(n)cat
вряд ли когда-либо будет правильным решением для конкатенации строк.
Вот простая схема для объединения нескольких кусков в выходной буфер:
size_t used = 0;
result->value = malloc(MAX_VALUE_LEN + 1);
for (...) { /* loop which produces the strings to append */
...
/* append a chunk */
size_t chunk_len = strlen(chunk);
if (MAX_VALUE_LEN - used >= chunk_len) {
memcpy(result->value + used, chunk, chunk_len);
used += chunk_len;
}
else {
/* Value is too long; return an error */
}
}
result->value[used] = 0;
Не все согласятся с моим использованием memcpy, а не strcpy; Я сделал это, потому что я уже знал длину строки для копирования (которую я должен был выяснить, чтобы проверить, достаточно ли места), и обычно более эффективно копировать известное число байтов, чем копировать байты до вы нажали NUL.
Использование memcpy
вынуждает меня явно завершать NUL-результат, но в противном случае мне пришлось бы вставлять NUL в начале, если в цикле не удалось добавить что-либо. Чтобы освободить место для NUL, я изначально выделил MAX_VALUE_LEN + 1
байта. Однако на практике я, вероятно, начал бы с небольшого распределения и экспоненциально realloc
, если это необходимо, вместо наложения искусственного предела и траты памяти в общем случае, когда искусственный предел был намного больше, чем фактически необходимая память.
Если ограничение по размеру не является искусственным, то есть если существует некоторая внешность, которая ограничивает длину добавляемой строки, например размер выходного окна отображения, - тогда можно было бы просто урезать строку чем выдача ошибки для результатов с завышенным размером:
size_t used = 0;
result->value = malloc(MAX_VALUE_LEN + 1);
for (...) { /* loop which produces the strings to append */
...
/* append a chunk */
size_t chunk_len = strlen(chunk);
if (MAX_VALUE_LEN - used < chunk_len) {
chunk_len = MAX_VALUE_LEN - used;
}
memcpy(result->value + used, chunk, chunk_len);
used += chunk_len;
}
result->value[used] = 0;