C: Как скопировать через нулевой ограничитель в элемент структуры более чистым способом?

#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:

Две вещи:

  1. Остерегайтесь, что strncpy() имеет очень неожиданную семантику, она всегда будет заполнять буфер на 0, если строка не заполнена полностью, и она не завершит строку, если она полностью заполнит буфер. Оба они достаточно странные, поэтому я не рекомендую их использовать.
  2. Никогда не индексируйте массив с указанием его размера, как 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), которая принудительно завершает работу с нулевым значением. Это работает довольно хорошо, если у вас есть библиотека для его размещения. Его использование также требует минимальных изменений кода.

Я не в восторге от подхода с макросами, потому что кто-нибудь передаст указатель на неправильный макрос.