#c #string #null #strcpy
#c #строка #null #strcpy
Вопрос:
По сути, я маркирую строку и strncpy
добавляю найденную строку к элементу структуры, т.Е. stringid. Он, конечно, страдает от проблемы отсутствия завершения, я добавил для него дополнительное пространство массива, я понятия не имею, как его правильно добавить.
Я сделал это следующим образом:
my_struct[iteration].stringID[ID_SIZE-1] = '' //updated
Я не уверен, действительно ли это работает, это выглядит ужасно, IMO.
Str (n) использование нулевого символа, или 0, приводит к предупреждению, генерируемому GCC и MinGW:
warning: null argument where non-null required (arg 2)
Я слепой в том, как сделать это чистым способом? Я думал о том, чтобы установить в массиве элементов все нули, а затем скопировать строку, чтобы она хорошо соответствовала нулевому завершению. У вас есть какие-либо предложения или практики?
Комментарии:
1. Каков размер
stringID
? Если это такID_SIZE
, то вы находитесь на единицу дальше конца строки с этим назначением.2. Я исправил пример, я действительно имел в виду ID_SIZE-1 для доступа к последнему массиву. Это был просто пример того, насколько «уродливо» выглядело решение. Значение -1 делает это еще более важным.
Ответ №1:
Две вещи:
- Остерегайтесь, что
strncpy()
имеет очень неожиданную семантику, она всегда будет заполнять буфер на 0, если строка не заполнена полностью, и она не завершит строку, если она полностью заполнит буфер. Оба они достаточно странные, поэтому я не рекомендую их использовать. - Никогда не индексируйте массив с указанием его размера, как
stringID[ID_SIZE]
кажется, делается; это выходит за рамки.
Лучшее решение — написать пользовательскую версию strncpy()
, которая менее странная, или (если вы знаете длину входных данных) просто используйте strcpy()
.
ОБНОВЛЕНИЕ: Если длина ваших входных токенов статична, но они не заканчиваются 0 в исходном буфере из-за вашего процесса токенизации, тогда просто используйте memcpy()
и ручное завершение:
const char * token = ...; /* Extract from tokenization somehow. Not 0-terminated. */
const size_t token_length = ... /* Perhaps from tokenization step. */
memcpy(my_struct[iteration].stringID, token, token_length);
my_struct[iteration].stringID[token_length] = '';
Я не вижу необходимости «оборачивать» вышесказанное в макрос.
Комментарии:
1. Входная длина каждого токена статична, поэтому она слишком мала, и ее автоматическое завершение не является частью моего требования, извините за неясность. Пользовательское решение кажется хорошим (strlcpy, о котором я слышал, часто используется как замена)
Ответ №2:
На самом деле, нулевое завершение, как вы предложили, совсем не ужасно, и лично мне это очень нравится.
Лучшим способом, на мой взгляд, было бы определить его как макрос аналогичным образом:
// for char* blah;
#define TERMINATE_DYNAMIC_STRING(str, len) str[len] = '';
// for char mytext[] = "hello";
#define TERMINATE_STRING(str) str[sizeof(str)/sizeof(str[0]) - 1] = '';
Затем вы можете использовать его во всем своем коде столько, сколько захотите.
В Windows Microsoft предоставляет вам следующие функции, которые завершаются нулем при копировании строки: StringCchCopy
Комментарии:
1. Я нахожу этот ответ вдохновляющим, поскольку я считал его уродливым только потому, что он выглядел ненужным по сравнению с strcpy, если бы он работал. Написание макроса, оболочки или того, что я выбрал (оставьте как есть), является моим решением.
2. Обратите внимание, что значение
sizeof
указывается в единицахchar
, поэтому для строк вам никогда не нужно включатьsizeof
фактические элементы. Для этого подхода я бы переписалTERMINATE_STRING
просто как str[sizeof str — 1] = ‘ 0’.3. sizeof() возвращает размер в байтах. Предложенный мной код будет корректно работать как для WCHAR, так и для CHARs
Ответ №3:
Как отмечали другие, strncpy
имеет странную семантику. Идиоматический способ копирования ограниченной строки — это strncat
в пустую строку:
my_struct[iteration].stringID[0] = '';
strncat(my_struct[iteration].stringID, src, ID_SIZE-1);
При этом всегда добавляется завершающий NUL (и заполняется не более символами ID_SIZE, включая NUL).
Ответ №4:
В итоге я написал функцию strncpyz(char * pszTo, char * pszTo, size_t LSize), которая принудительно завершает работу с нулевым значением. Это работает довольно хорошо, если у вас есть библиотека для его размещения. Его использование также требует минимальных изменений кода.
Я не в восторге от подхода с макросами, потому что кто-нибудь передаст указатель на неправильный макрос.