Альтернатива strncpy() - какие преимущества у каждой версии?

Я думал о создании альтернативы strncpy с завершением '\ 0' для собственного использования в файле заголовка, и мне интересно, какой из следующих подходов будет лучше.

int copystring(char *dest,char *source,int elements)
{
    int run;
    for(run=0;run<elements-1;run++)//last reserved for'\0'
    {
       dest[run]=source [run];
       if (dest[run]=='\0')
       {
           break;
       }

    }
    dest[elements-1]='\0';//could make conditional but not neccesary.
    return 0;
}

OR

int copystring(char *dest,char *source,int elements)
 {
        strncpy(dest,source,elements-1);
        dest[elements-1]='\0';
        return 0;
  }

Очевидная разница для меня в том, что я вызываю на одну функцию меньше с первой версией, но мне было интересно, есть ли у второй версии какие-либо преимущества, поскольку я не знаю внутренней работы strncpy().

Кстати, почему strncpy() принимает size_t в качестве последнего аргумента? если это для передачи значений sizeof, которые будут полезны только в очень ограниченных обстоятельствах, разве int не подойдет так же?


person Community    schedule 27.04.2016    source источник
comment
Что это за достижение, чего еще нет в strcopy? Кроме того, почему вам нужно знать длину char *source с нулевым завершением, чтобы скопировать его?   -  person CollinD    schedule 27.04.2016
comment
Он имеет защиту от переполнения буфера для адресата. то есть я буду использовать «элементы» для передачи максимальной длины назначения.   -  person    schedule 27.04.2016
comment
Кстати: dest[run+1]='\0'; --›› dest[run]='\0'; (запуск уже увеличивается после цикла)   -  person wildplasser    schedule 27.04.2016
comment
Первая функция просто сломана, так как она безоговорочно копирует elements-1 символов, игнорируя тот факт, что source может закончиться задолго до этого.   -  person AnT    schedule 27.04.2016
comment
@Orangesandlemons Тогда похоже, что вы повторно реализуете strncpy, а не strcpy, как вы сказали.   -  person CollinD    schedule 27.04.2016
comment
@CollinD: Написание strncpy и strcpy, а не strncopy и strcopy.   -  person Keith Thompson    schedule 27.04.2016
comment
@KeithThompson ах, конечно, печатаю слишком быстро для моего же блага. s/copy/cpy/g пожалуйста. #define strcopy strcpy #define strncopy strncpy   -  person CollinD    schedule 27.04.2016
comment
@муравей, да. Я упустил это из виду и буду адаптироваться.   -  person    schedule 27.04.2016
comment
strncpy на самом деле вообще не является строковой функцией, потому что она может не создавать строку (с нулевым завершением) и заполнять назначение нулями, если есть место, в этом случае запись за концом строки. Он предназначен для заполнения массивов символов фиксированного размера, обычно полей структуры.   -  person hyde    schedule 27.04.2016
comment
Угловой случай: copystring(char *dest,char *source,int elements) (2-я версия) резко выходит из строя, когда elements <= 0   -  person chux - Reinstate Monica    schedule 27.04.2016
comment
@CollinD извините, это была опечатка - у меня было это прямо в заголовке, и теперь я исправил это в теле   -  person    schedule 27.04.2016
comment
@Orangesandlemons Упс; извините, если я вышел слишком педантичным. Теперь имеет больше смысла.   -  person CollinD    schedule 27.04.2016
comment
@chucx да, но это будет означать, что я создал целевой массив из нулевых элементов, так как я планирую его использовать именно так.   -  person    schedule 27.04.2016
comment
@CollinD совсем нет, и спасибо, что указали на это.   -  person    schedule 27.04.2016
comment
@Orangesandlemons Для ясности: copystring(dest, source, 0) вызывает strncpy(dest,source,0-1);, что является strncpy(dest,source,some_very_large_number);, а не strncpy(dest,source, -1);   -  person chux - Reinstate Monica    schedule 27.04.2016


Ответы (1)


Самая простая замена для strncpy() (если вы чувствуете, что она вам нужна), вероятно, такова:

dest[0] = '\0';
strncat(dest, source, size);

strncat(), в отличие от strncpy(), гарантирует, что цель правильно завершается нулем, и не копирует лишние нулевые байты, когда цель больше, чем источник.

Но будьте уверены, что такое поведение действительно то, что вам нужно. Если вы используете strcpy(), а целевой массив недостаточно велик, вы получите неопределенное поведение. С strncpy или strncat вы получаете определенное поведение: значение незаметно усекается. Если это то, что вы хотите, отлично, но это бывает редко. Чаще всего усечение данных просто дает вам неверные данные, и вы должны обнаруживать их и обрабатывать как ошибку, а не прокастово отбрасывать то, что не подходит. (Изображение усечения "rm -rf $HOME/unimportant_directory" до "rm -rf $HOME" перед передачей в system().)

Кстати, почему strncpy() принимает size_t в качестве последнего аргумента? если это для передачи значений sizeof, которые будут полезны только в очень ограниченных обстоятельствах, разве int не подойдет так же?

size_t просто имеет больше смысла, так как этот тип используется для представления размеров и длин. Использование int потребует определить (или оставить неопределенным) поведение для отрицательных аргументов и может ограничить размер строки, которую вы можете обработать (если, скажем, int составляет 16 бит, а size_t — 32 бита). Вы всегда можете передать значение int, и оно будет неявно преобразовано в size_t.

Другой вариант — нестандартная функция strlcpy(). Он доступен не во всех системах, но его можно установить (например, с помощью пакета libbsd-dev для систем Debian/Ubuntu/...) или собрать из исходного кода.

Кстати, вот моя тирада на тему strncpy():
http://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safe-strcpy.html

person Keith Thompson    schedule 27.04.2016
comment
Мой план состоит в том, чтобы продолжить чтение строки до тех пор, пока не будет достигнут конец (но не в массив), а затем вернуть это число в качестве возврата функции, что позволяет настраивать обработку ошибок в зависимости от необходимости. Конечно, если мне нужны все персонажи, мне придется выделять их динамически. - person ; 27.04.2016
comment
Примечание: важно, чтобы size не превышала размер пространства, на которое указывает dest. 2) Кстати: хороший выбор между двумя вариантами ОП: ни один - person chux - Reinstate Monica; 27.04.2016
comment
Одним из недостатков этого является то, что потребуется инициализировать dest [0] с помощью '\ 0', в то время как в идеале у меня было бы как можно меньше кода. Я все еще пытаюсь найти способ избавиться от необходимости сообщать моей функции размер целевого массива в случаях, когда массив распался на указатель и т.д. - person ; 27.04.2016
comment
@Orangesandlemons: вы не можете не сообщать своей функции размер целевого массива, если только вы не хотите разрешить переполнение буфера. - person Keith Thompson; 27.04.2016
comment
@Keith Thompson Я знаю, но если бы массивы не распадались, я мог бы сказать это внутренне, а не передавать в качестве аргумента, тем самым сделав функцию намного «чище». - person ; 27.04.2016