2009-11-20 5 views
1

Возможно ли глобальное отключение строк с нулевым завершением в 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, чтобы сэкономить несколько байт ?

+0

Что должно произойти с пустыми строками? Как вы предлагаете справиться с ними? –

+4

Я уверен, что 80 байт не будет проблемой для вашей программы. Особенно на x86. –

+0

В Linux и я предполагаю, что все остальные ОС 'wchar_t' имеют одинаковую длину независимо от архитектуры.Linux, в частности, использует 4 байта как для x86, так и для x86_64. – intgr

ответ

1

Это довольно ужасно, но вы можете явно указать длину каждой константы массива символов:

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)); 
+0

Как насчет пустой строки? –

+3

Вы можете сделать это макросом: '#define NEW_STRING (var, val) char var [sizeof (val) -1] = val' –

+1

Если ваш собственный тип строки определяется как struct' {length; pointer} ', то вы могли бы также использовать length = 0, а указатель = NULL - указатель доступа был бы недействительным в любом случае, потому что нет символов для чтения. – intgr

1

Я понимаю, вы только дело со строками, заявленных в программе:

.... 
char str1[10]; 
char str2[12]; 
.... 

а не с текстовыми буферами, которые вы выделяете malloc() и друзьями в противном случае sizeof не собирается вам помогать.

В любом случае, я бы просто подумал дважды об удалении \ 0 в конце: вы потеряете совместимость со стандартными библиотечными функциями C.

Если вы не собираетесь переписывать какую-либо одну строковую функцию для вашей библиотеки (например, sprintf), вы уверены, что хотите это сделать?

+0

Я не использую выделенные строки, потому что это именно то, для чего предназначена моя строковая библиотека. Ручное распределение памяти слишком опасно, так как всегда существует риск переполнения буфера. Как указано выше, переписывание строковых функций было не очень сложным. Благодаря C99 я могу использовать простой хак, чтобы поддерживать совместимость с функциями Glibc: char tmp [string-> len + 1]; tmp [string-> len + 1] = '\ 0'; printf ("% s", tmp); – user206268

+0

Я уверен, что у вас есть веские причины для этого, но если я понимаю, как работают эксплойты переполнения буфера, наличие строки в стеке более опасно, чем выделение в куче. О добавлении '\ 0' в конец, я не вижу, как содержимое строки копируется в temp, и я думаю, что вы используете расширение gcc, а не C99. –

0

Я не помню подробностей, но когда я

char my_constant[5] 

вполне возможно, что он будет резервировать 8 байт в любом случае, потому что некоторые машины не могут обращаться к середине слова.

Почти всегда лучше оставить эту штуку компилятору и позволить ей обрабатывать optmisation для вас, если нет действительно действительно веских оснований для этого.

+1

Это называется «выравнивание» (также «внутренняя фрагментация»). Это правда, что выброс байт NUL не уменьшает большинство строк, но когда он это делает, он уменьшает размер выравнивания. Таким образом, в среднем каждая строка будет потреблять на 1 байт меньше памяти. – intgr

0

Действительно, это только в том случае, если вы действительно низко в памяти. В противном случае я не рекомендую это делать.

кажется самый правильный способ сделать, что вы говорите о том:

  • подготовить некоторый минимальный файл «листинг» в виде:
 
    string1_constant_name "str1" 
    string2_constant_name "str2" 
    ... 
  • Для построения которая обрабатывает ваш файл и генерирует декларации, такие как
 
    const char string1_constant[4] = "str1"; 

Конечно, я бы не рекомендовал это делать руками, потому что в противном случае вы можете столкнуться с проблемой после любого изменения строки.

Итак, у вас есть обе строки без конца из-за фиксированных автоматически сгенерированных массивов, а также у вас есть sizeof() для каждой переменной. Это решение представляется приемлемым.

Преимущества - это легкая локализация, возможность добавить некоторый уровень проверок, чтобы снизить риск этого решения и сократить сегмент данных R/O.

Недостаток должен включать в себя все такие строковые константы в каждом модуле (в том числе, чтобы сохранить значение sizeof()). Таким образом, это имеет смысл только в том случае, если ваш компоновщик объединяет такие символы (некоторые нет).

+0

Хорошая идея! К сожалению, он требует, чтобы я всегда вытаскивал весь код через «препроцессор» перед компиляцией. Если в GCC нет такого варианта, чтобы отключить завершение NUL, я буду придерживаться своего текущего подхода. – user206268

0

Если вы не используете любую функцию стандартной библиотеки, которая имеет дело со строками, вы можете забыть о завершающем байте 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" */ 
+0

В основном это более или менее то, что я сейчас делаю. Возможно, лучше включить NUL-завершение вообще, но, возможно, есть что-то вроде прагмы, позволяющей включать/отключать завершающий байт NUL для заданной части кода, то есть функции моей строки? – user206268

+1

Просто не используйте его. Вы используете массивы 'int' каждый день, много времени в день ... и вы даже никогда не использовали прерывание' int'. Сделайте то же самое с массивами 'char' (когда я делаю это, я специально« подписываю »свой' char ':' signed char' или 'unsigned char': для меня только простой символ' char' может быть строками 'C'). – pmg

0

Не похожи ли они на строки в стиле Паскаля или на Холлеритные струны? Я думаю, что это полезно, только если вы действительно хотите, чтобы данные String сохраняли NULL, в которых вы действительно нажимаете произвольную память, а не «строки» как таковые.

+0

Да, я использую их так же, как строки Холлерита. В моем коде он определяется следующим образом: typedef struct {unsigned int len; unsigned int maxLen; char buf [0]; } Существуют и другие преимущества. Википедия (взятая из «Строковый литерал») говорит: * исключает поиск текста (для символа разделителя) и, следовательно, требует значительно меньших накладных расходов * избегает проблемы с разделителем-ограничителем на 100% -ном вызове * позволяет включить метасимволы, которые в противном случае могут быть ошибочно приняты, поскольку команды * могут использоваться для довольно эффективного сжатия данных текстовых строк – user206268

0

Вопрос использует ложные предположения - предполагается, что сохранение длины (например, неявно, передавая ее как число функции) не наносит накладных расходов, но это неверно.

Хотя можно сэкономить место, не сохраняя 0-байтовую (или wchar), размер должен быть где-то сохранен, а пример намекает, что он передается как постоянный аргумент функции где-то, что почти наверняка требует больше пространство, в коде. Если одна и та же строка используется несколько раз, накладные расходы на использование, а не на строку.

Наличие обертки, использующей strlen для определения длины строки и не встроенной, почти наверняка сэкономит больше места.

 Смежные вопросы

  • Нет связанных вопросов^_^