Отключение NUL-завершения строк в GCC
Можно ли глобально отключить строки с NUL-окончанием в GCC?
Я использую свою собственную библиотеку строк, и у меня нет абсолютно никакой потребности в конечных символах NUL, поскольку она уже хранит надлежащую длину внутри структуры.
Тем не менее, если бы я хотел добавить 10 строк, это означало бы, что 10 байтов излишне выделяются в стеке. С широкими строками это еще хуже: что касается x86, то теряется 40 байт; а для x86_64 80 байт!
Я определил макрос для добавления этих выделенных стеком строк в мою структуру:
#define AppendString(ppDest, pSource) \
AppendSubString(ppDest, (*ppDest)->len + 1, pSource, 0, sizeof(pSource) - 1)
С помощью sizeof(...) - 1
работает довольно хорошо, но мне интересно, смогу ли я избавиться от завершения NUL, чтобы сэкономить несколько байтов?
7 ответов
Это довольно ужасно, но вы можете явно указать длину каждой константы массива символов:
char my_constant[6] = "foobar";
assert(sizeof my_constant == 6);
wchar_t wide_constant[6] = L"foobar";
assert(sizeof wide_constant == 6*sizeof(wchar_t));
Я понимаю, что вы имеете дело только со строками, объявленными в вашей программе:
....
char str1[10];
char str2[12];
....
а не с текстовыми буферами, которые вы выделяете с malloc()
а друзья иначе sizeof
не поможет тебе.
В любом случае, я бы дважды подумал об удалении \0 в конце: вы потеряете совместимость со стандартными функциями библиотеки C.
Если вы не собираетесь переписывать какую-либо одностроковую функцию для вашей библиотеки (например, sprintf), вы уверены, что хотите это сделать?
В этом вопросе используются ложные предположения - предполагается, что сохранение длины (например, неявно путем передачи ее в виде числа в функцию) не требует дополнительных затрат, но это не так.
Хотя можно сэкономить место, не сохраняя 0-байт (или wchar), размер должен храниться где-то, и пример намекает на то, что он передается как постоянный аргумент функции где-то, что почти наверняка занимает больше места в коде, Если одна и та же строка используется несколько раз, накладные расходы относятся к использованию, а не к строке.
Наличие оболочки, которая использует strlen для определения длины строки и не встроена, почти наверняка сэкономит больше места.
Разве они не похожи на струны в стиле Паскаля или на Холлерит? Я думаю, что это полезно только в том случае, если вы действительно хотите, чтобы данные String сохраняли значения NULL, в которых вы действительно перемещаете произвольную память, а не "строки" как таковые.
Я не могу вспомнить детали, но когда я делаю
char my_constant[5]
Вполне возможно, что он все равно зарезервирует 8 байтов, потому что некоторые машины не могут адресовать середину слова.
Почти всегда лучше оставить этот тип компилятору и позволить ему обработать выбор за вами, если только для этого нет действительно веской причины.
На самом деле это только в том случае, если у вас действительно мало памяти. В противном случае я не рекомендую это делать.
Кажется, самый правильный способ сделать то, о чем вы говорите, это:
- Чтобы подготовить минимальный файл листинга в виде:
string1_constant_name "str1" string2_constant_name "str2"...
- Для создания утилиты, которая обрабатывает ваш файл и генерирует объявления, такие как
const char string1_constant[4] = "str1";
Конечно, я бы не советовал делать это руками, потому что в противном случае у вас могут возникнуть проблемы после любого изменения строки.
Так что теперь у вас есть обе строки без конца из-за фиксированных автоматически сгенерированных массивов, а также у вас есть sizeof() для каждой переменной. Это решение кажется приемлемым.
Преимуществами являются легкая локализация, возможность добавить некоторый уровень проверок, чтобы снизить риск этого решения, и экономия сегмента данных R/O.
Недостатком является необходимость включения всех таких строковых констант в каждый модуль (например, include, чтобы держать sizeof() известным). Так что это имеет смысл, только если ваш компоновщик объединяет такие символы (некоторые этого не делают).
Если вы не используете какую-либо функцию стандартной библиотеки, которая работает со строками, вы можете забыть о завершающем байте NUL.
нет strlen()
нет fgets()
нет atoi()
нет strtoul()
нет fopen()
нет printf()
с %s
спецификатор преобразования...
Объявите ваши "не совсем строки C" только с необходимым пространством;
struct NotQuiteCString { /* ... */ };
struct NotQuiteCString variable;
variable.data = malloc(5);
data[0] = 'H'; /* ... */ data[4] = 'o'; /* "hello" */