я использую TCHAR
в проекте Visual C ++, над которым я работаю, определение которого показано ниже:
#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
Мне нужно положить некоторые данные в буфер buff
:
char buff[size] = {0}; // how to declare the buffer size - what should be its value ?
sprintf(buff, "%s (ID: %i)", res->name(), res->id());
где:
name()
возвращается TCHAR*
id()
возвращается int
Как рассчитать стоимость size
— Точная емкость буфера для реальных потребностей (меньше, если не определен юникод, больше, если определен юникод)? Кроме того, я хотел бы защитить себя от возможности переполнения буфера, какую защиту я должен использовать?
Более того, я объявил здесь буфер как char
, Если я объявлю буфер как int
Будет ли какая-то разница для значения размера (т.е. в 4 раза меньше по сравнению с объявленным как char)?
ОБНОВИТЬ
Что я придумаю частично, основываясь на ответе Матса Петерссона:
size_t len;
const char *FORMAT;
#ifndef _UNICODE
len = strlen((char*)res->name());
FORMAT = "%s (ID: %i)";
#else
len = wcslen(res->name());
FORMAT = "%S (ID: %i)";
#endif
int size = 7 * sizeof(TCHAR) + /* place for characters inside format string */
len * sizeof(TCHAR) + /* place for "name" characters */
strlen(_itoa(id, ioatmp, 10)) * sizeof(TCHAR) + /* place for "id" digits */
1 * sizeof(TCHAR); /* zero byte(s) string terminator */
char *buff = new char[size]; /* buffer has to be declared dynamically on the heap,
* because its exact size is not known at compilation time */
sprintf(buff, FORMAT, name, id);
delete[] buff;
Это правильное мышление или я что-то упустил?
Чтобы начать со спины, buff
всегда должен быть char
потому что это то, что хранится sprintf
,
Во-вторых, если ваш res->name()
возвращает строку с широким символом (Unicode), ваша строка формата должна использовать "%S"
для регулярного ASCII вы должны использовать "%s"
,
Теперь, чтобы вычислить длину, необходимую для буфера, и избежать переполнения. Это не так сложно сделать что-то вроде
const TCHAR *nm = res->name();
size_t len;
#ifndef UNICODE
len = strlen(nm);
#else
... see below.
#endif
а затем угадать длину числа (целое число не может занимать более 12 мест) вместе с точным количеством символов, полученных как константы в строке формата.
Это прекрасно работает для стандартного варианта ASCII.
Однако с широким вариантом символа становится интереснее, поскольку он может занимать несколько байтов в выходной строке (например, писать китайские символы, которые всегда требуют многобайтового кодирования). Одним из решений является:
len = snprintf(0, NULL, "%S", nm);
который должен дать вам правильный номер [я думаю]. Это довольно громоздкий метод, но он будет работать. Я не уверен, что есть простой способ преобразовать широкую строку в «количество байтов, необходимое для хранения этой строки» другим способом.
Редактировать: я бы серьезно подумал, стоит ли поддерживать вариант не-UNICOD, а затем просто перевести все на использование swprintf(...)
вместо. Вам по-прежнему нужна длина, но она должна быть просто результатом wcslen(res->name())
вместо того, чтобы требовать некоторого сложного вычисления конверсии.
char buff[size] = {0};
Вы пишете вне буфера. ОБНОВЛЕНИЕ: я возьму это назад — это просто объявление с инициализацией, если размер постоянно."%s (ID: %i)"
должны быть изменены на это: "%s (ID: %d)"
если последний параметр int
,