C strcpy () - зло?

Насколько мне известно, нет единственного варианта, который надежно гарантирует это. Существуют комбинации оптимизирующих опций, которые (молча) деактивируют это, поэтому никак ... /INCLUDE не работает, но для этого вам нужно извлечь и перекодировать искаженное имя символа. У вас есть два варианта: (1) обеспечить, чтобы все регистраторы содержались (включены) в блок перевода, содержащий main, и обеспечивали их использование. (2) Откажитесь от этой «идиомы» и используйте явную регистрацию.

Внимание: этот ответ сейчас почти 7 лет, и утверждения относительно доступности опций в инструментальной цепочке MSVC ++ устарели. Тем не менее я по-прежнему рекомендую не полагаться на шаблон регистратора и смотреть на альтернативы. Из-за этой рекомендации, пожалуйста, не стесняйтесь голосовать, но я думаю, что это немного несправедливо, потому что этот вариант был добавлен в компоновщик Microsoft. Тем временем

22
задан Chris Lutz 4 March 2009 в 11:54
поделиться

15 ответов

memcpy может быть быстрее, чем strcpy и strncpy, потому что это не должно сравнивать каждый скопированный байт с '\0', и потому что это уже знает длину скопированного объекта. Это может быть реализовано похожим способом с устройство Вареного пудинга , или инструкции по ассемблеру использования, которые копируют несколько байтов за один раз, как movsw и movsd

26
ответ дан dmityugov 29 November 2019 в 03:19
поделиться

Ваш код ужасно неэффективен, потому что он пробегает строку дважды для копирования его.

Однажды в strlen ().

С другой стороны в strcpy ().

И Вы не проверяете s1 на ПУСТОЙ УКАЗАТЕЛЬ.

Хранение длины в некоторых дополнительных переменных затратах Вы ни о чем, при пробежке каждой строки дважды для копирования его являетесь кардинальным грехом.

-1
ответ дан Thorsten79 29 November 2019 в 03:19
поделиться

В ситуации Вы описываете, strcpy является хорошим выбором. Этот strdup только попадет в беду, если s1 не был закончен '\0'.

я добавил бы комментарий, указывающий, почему нет никаких проблем с strcpy, для предотвращения других (и Вы один год с этого времени) задающийся вопросом о его правильности слишком долго.

strncpy часто кажется безопасным, но может получить Вас в проблему. Если источник "строка" короче, чем количество, это дополняет цель '\0', пока это не достигает количества. Это может быть плохо для производительности. Если исходная строка дольше, чем количество, strncpy не добавляет '\0' к цели. Это обязано получить Вас в проблему позже, когда Вы ожидаете '\0' завершенных "строк". Таким образом, strncpy должен также использоваться с осторожностью!

я только использовал бы memcpy, если бы я не работал с '\0' завершенными строками, но это, кажется, вопрос вкуса.

0
ответ дан Renze de Waal 29 November 2019 в 03:19
поделиться

Ну, strcpy () не является столь же злым, как strdup () - по крайней мере, strcpy () часть Стандарта C.

0
ответ дан 29 November 2019 в 03:19
поделиться

strlen находит до последнего пустого места завершения.

, Но в действительности буферизует, не пустой завершенный.

вот почему люди используют различные функции.

1
ответ дан lakshmanaraj 29 November 2019 в 03:19
поделиться

Зло прибывает, когда люди используют его как это (хотя ниже супер упрощен):

void BadFunction(char *input)
{
    char buffer[1024]; //surely this will **always** be enough

    strcpy(buffer, input);

    ...
}

, Который является ситуацией, которая происходит, удивляя часто.

, Но да, strcpy так же хорош как strncpy в любой ситуации, где Вы выделяете память для целевого буфера и уже использовали strlen для нахождения длины.

4
ответ дан Graeme Perrow 29 November 2019 в 03:19
поделиться

Я думаю, что strncpy является злым также.

, Чтобы действительно защитить себя от программных ошибок этого вида необходимо лишить возможности писать код, который (a) смотрит хорошо и (b) превышает буфер.

Это означает необходимость в реальной строковой абстракции которая хранит буфер и способность непрозрачно, связывает их, навсегда, и проверяет границы. Иначе Вы заканчиваете тем, что передали строки и их мощности на всем протяжении магазина. Как только Вы добираетесь до реальной строковой операции в секунду, как изменение середины строки, почти столь же легко передать неправильную длину в strncpy (и особенно strncat), как это должно назвать strcpy с также маленьким местом назначения.

, Конечно, Вы могли бы все еще спросить, использовать ли strncpy или strcpy в реализации той абстракции: strncpy более безопасен, там предоставил Вам полностью grok, что он делает. Но в обрабатывающем строку коде приложения, полагаясь strncpy для предотвращения переполнения буфера похож на износ половины презерватива.

Так, Ваша strdup-замена могла бы выглядеть примерно так (порядок определений, измененных для держания Вас в напряжении):

string *string_dup(const string *s1) {
    string *s2 = string_alloc(string_len(s1));
    if (s2 != NULL) {
        string_set(s2,s1);
    }
    return s2;
}

static inline size_t string_len(const string *s) {
    return strlen(s->data);
}

static inline void string_set(string *dest, const string *src) {
    // potential (but unlikely) performance issue: strncpy 0-fills dest,
    // even if the src is very short. We may wish to optimise
    // by switching to memcpy later. But strncpy is better here than
    // strcpy, because it means we can use string_set even when
    // the length of src is unknown.
    strncpy(dest->data, src->data, dest->capacity);
}

string *string_alloc(size_t maxlen) {
    if (maxlen > SIZE_MAX - sizeof(string) - 1) return NULL;
    string *self = malloc(sizeof(string) + maxlen + 1);
    if (self != NULL) {
        // empty string
        self->data[0] = '\0';
        // strncpy doesn't NUL-terminate if it prevents overflow, 
        // so exclude the NUL-terminator from the capacity, set it now,
        // and it can never be overwritten.
        self->capacity = maxlen;
        self->data[maxlen] = '\0';
    }
    return self;
}

typedef struct string {
    size_t capacity;
    char data[0];
} string;

проблема с этими строковыми абстракциями состоит в том, что никто никогда не может договариваться об одном (например, хороши ли особенности strncpy, упомянутые в комментариях выше, или плохи, нужно ли Вам неизменный и/или строки копии на записи, которые совместно используют буферы, когда Вы создаете подстроку, и т.д.). Таким образом, хотя в теории необходимо просто взять один с полки, можно закончить с одним на проект.

5
ответ дан Steve Jessop 29 November 2019 в 03:19
поделиться

Я лично имею мышление, что, если код, как могут доказывать, является valid— и сделан так quickly—, это совершенно приемлемо. Таким образом, если код прост, и таким образом, очевидно, исправьте, то он прекрасен.

Однако Ваше предположение, кажется, что, в то время как Ваша функция выполняется, никакой другой поток не изменит строку, на которую указывают s1. Что происходит, если эта функция прервана после успешного выделения памяти (и таким образом вызов к strlen), строка растет, и обман , у Вас есть условие переполнения буфера с тех пор strcpy копии к ПУСТОМУ байту.

следующее могло бы быть лучше:

char *
strdup(const char *s1) {
  int s1_len = strlen(s1);
  char *s2 = malloc(s1_len+1);
  if(s2 == NULL) {
    return NULL;
  }

  strncpy(s2, s1, s1_len);
  return s2;
}

Теперь, строка не может вырасти ни через какой собственный отказ, и Вы в безопасности. Результатом не будет дубликат, но это не будет никакое сумасшедшее переполнение, также.

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

ETA: Вот немного лучшая реализация:

char *
strdup(const char *s1, int *retnum) {
  int s1_len = strlen(s1);
  char *s2 = malloc(s1_len+1);
  if(s2 == NULL) {
    return NULL;
  }

  strncpy(s2, s1, s1_len);
  retnum = s1_len;
  return s2;
}

Там количество символов возвращается. Вы можете также:

char *
strdup(const char *s1) {
  int s1_len = strlen(s1);
  char *s2 = malloc(s1_len+1);
  if(s2 == NULL) {
    return NULL;
  }

  strncpy(s2, s1, s1_len);
  s2[s1_len+1] = '\0';
  return s2;
}

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

8
ответ дан Michael Trausch 29 November 2019 в 03:19
поделиться

Я соглашаюсь. Я рекомендовал бы против strncpy(), хотя, так как это будет всегда дополнять Ваш вывод к обозначенной длине. Это - некоторое историческое решение, которое я думаю, было действительно неудачно, поскольку оно серьезно ухудшает производительность.

Рассматривают код как это:

char buf[128];
strncpy(buf, "foo", sizeof buf);

Это не запишет ожидаемые четыре символа в buf, но вместо этого запишет "нечто", сопровождаемое 125 нулевыми символами. Если Вы, например, соберете много коротких строк, то это будет означать, что Ваша фактическая производительность намного хуже, чем ожидалось.

При наличии, я предпочитаю использовать snprintf(), пишущий вышеупомянутое как:

snprintf(buf, sizeof buf, "foo");

, вместо этого копируя непостоянную строку, это сделано как это:

snprintf(buf, sizeof buf, "%s", input);

Это важно, с тех пор, если бы input содержит символы % snprintf(), интерпретировал бы их, открыв целый shelvefuls куч проблем.

5
ответ дан unwind 29 November 2019 в 03:19
поделиться

Никто не упомянул strlcpy , разработанный Todd C. Miller и Theo de Raadt . Поскольку они говорят в их статье:

наиболее распространенное неправильное представление состоит в том, что strncpy() NUL-завершает целевую строку. Это только верно, однако, если длина исходной строки является меньше, чем параметр размера. Это может быть проблематично при копировании ввода данных пользователем, который может иметь произвольную длину в буфер фиксированного размера. Самый безопасный способ использовать strncpy() в этой ситуации состоит в том, чтобы передать его меньше, чем размер целевой строки и затем завершить строку вручную. Тем путем у Вас, как гарантируют, всегда будет NUL-завершенная целевая строка.

существуют контрдоводы для использования strlcpy; страница Wikipedia обращает внимание, что

Drepper утверждает, что strlcpy и strlcat совершают ошибки усечения, легче для программиста проигнорировать, и таким образом может представить больше ошибок, чем они удаляют. *

Однако я полагаю, что это просто вынуждает людей, которые знают то, что они делают для добавления ручного ПУСТОГО завершения, в дополнение к ручной настройке аргумента strncpy. Использование strlcpy делает намного легче избежать переполнения буфера, потому что Вам не удалось АННУЛИРОВАТЬ оконечный Ваш буфер.

Также примечание, что отсутствие strlcpy в glibc или библиотеках Microsoft не должно быть барьером для использования; можно найти источник для strlcpy и друзья в любом распределении BSD, и лицензия является, вероятно, дружественной по отношению к commercial/non-commercial проекту. См. комментарий наверху [1 110].

9
ответ дан Jared Oberhaus 29 November 2019 в 03:19
поделиться

Откровенно говоря, при выполнении большой строковой обработки в C Вы не должны спрашивать себя, необходимо ли использовать strcpy или strncpy или memcpy. Необходимо найти или записать строковую библиотеку, которая обеспечивает высокоуровневую абстракцию. Например, тот, который отслеживает длину каждой строки, выделяет память для Вас и обеспечивает все строковые операции, в которых Вы нуждаетесь.

Это почти наверняка гарантирует создание очень немногих видов ошибок обычно связываемыми с обработкой струны до, таких как переполнение буфера, упущение завершить строку с байтом NUL, и так далее.

библиотека могла бы иметь функции, такие как они:

typedef struct MyString MyString;
MyString *mystring_new(const char *c_str);
MyString *mystring_new_from_buffer(const void *p, size_t len);
void mystring_free(MyString *s);
size_t mystring_len(MyString *s);
int mystring_char_at(MyString *s, size_t offset);
MyString *mystring_cat(MyString *s1, ...); /* NULL terminated list */
MyString *mystring_copy_substring(MyString *s, size_t start, size_t max_chars);
MyString *mystring_find(MyString *s, MyString *pattern);
size_t mystring_find_char(MyString *s, int c);
void mystring_copy_out(void *output, MyString *s, size_t max_chars);
int mystring_write_to_fd(int fd, MyString *s);
int mystring_write_to_file(FILE *f, MyString *s);

я записал один для проект Kannel, посмотрите gwlib/octstr.h файл. Это сделало жизнь намного более простой для нас. С другой стороны, такая библиотека довольно проста записать, таким образом, Вы могли бы записать один для себя, даже если только как осуществление.

11
ответ дан 29 November 2019 в 03:19
поделиться

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

С strcpy это может происходить с strncpy, это будет никогда , происходят. Именно поэтому strcpy считают небезопасным. Зло могло бы быть немного сильным.

16
ответ дан Salgo 29 November 2019 в 03:19
поделиться

Я следую правилам в [1 118] здесь . Позвольте мне заключить в кавычки из него

strncpy, был первоначально введен в библиотеку C для контакта с полями имени фиксированной длины в структурах, таких как записи каталога. Такие поля не используются таким же образом в качестве строк: запаздывающий пустой указатель является ненужным для поля максимальной длины, и устанавливающий запаздывание байтов для более коротких имен к пустому указателю гарантирует эффективные мудрые полем сравнения. strncpy не источником, ''ограничил strcpy'', и Комитет предпочел распознавать существующую практику, а не изменять функцию для лучше удовлетворения ему такому использованию.

По этой причине, Вы не получите запаздывание '\0' в строке, если Вы совершите нападки n не нахождение '\0' от исходной строки до сих пор. Легко неправильно использовать его (конечно, если Вы знаете о той ловушке, можно избежать его). Как кавычка говорит, она не была разработана как ограниченный strcpy. И я предпочел бы не использовать его если не необходимый. В Вашем случае ясно его использование не необходимо, и Вы доказали его. Почему тогда используют его?

И вообще говоря, код программы также о сокращении дублирования. Если Вы знаете, что у Вас есть строка, содержащая 'n' символы, почему говорят функции копирования копировать максимальный n символы? Вы делаете избыточную проверку. Это мало о производительности, но намного больше о последовательном коде. Читатели спросят себя, что strcpy могло сделать, который мог пересечься эти n символы и который заставляет ограничивать копирование, только читать в руководствах, что этого не может произойти в этом случае. И там беспорядок запускается, происходят среди читателей кода.

Для рационального для использования mem-, str- или strn-, я выбрал среди них как в вышеупомянутом связанном документе:

mem-, когда я хочу скопировать необработанные байты, как байты структуры.

str- при копировании пустого указателя завершил строку - только, когда 100% никакое переполнение могли произойти.

strn- при копировании пустого указателя завершился, представляют некоторую длину в виде строки, заполняя остающиеся байты нулем. Вероятно, не, что я хочу в большинстве случаев. Легко забыть, что факт с запаздыванием заполняет нулями, но это дизайном, как вышеупомянутая кавычка объясняет. Так, я просто кодировал бы свой собственный маленький цикл, который копирует символы, добавляя запаздывание '\0':

char * sstrcpy(char *dst, char const *src, size_t n) {
    char *ret = dst;
    while(n-- > 0) {
        if((*dst++ = *src++) == '\0')
            return ret;
    }
    *dst++ = '\0';
    return ret;
}

Всего несколько строк, которые делают точно, что я хочу. Если я хотел "необработанную скорость", я могу все еще высматривать портативную и оптимизированную реализацию, которая делает точно этот ограниченное задание strcpy . Как всегда, профиль сначала и затем смешивает с ним.

Позже, C получил функции для работы с широкими символами, названными wcs- и wcsn- (для [1 117]). Я использовал бы их аналогично.

18
ответ дан Johannes Schaub - litb 29 November 2019 в 03:19
поделиться

Я был бы склонен использовать memcpy, если я уже вычислил длину, хотя strcpy обычно оптимизируется для работы над машинными словами, это чувствует, что необходимо предоставить библиотеке столько информации, сколько Вы можете, таким образом, это может использовать самый оптимальный механизм копирования.

, Но для примера Вы даете, не имеет значения - если это соберется перестать работать, то это будет в начальной букве strlen, таким образом, strncpy не купит Вас ничто с точки зрения безопасности (и presumbly strncpy медленнее, поскольку это должно оба проверить границы и на nul), и любое различие между memcpy и strcpy не стоит изменять код для теоретически.

4
ответ дан Pete Kirkham 29 November 2019 в 03:19
поделиться
char* dupstr(char* str)
{
   int full_len; // includes null terminator
   char* ret;
   char* s = str;

#ifdef _DEBUG
   if (! str)
      toss("arg 1 null", __WHENCE__);
#endif

   full_len = strlen(s) + 1;
   if (! (ret = (char*) malloc(full_len)))
      toss("out of memory", __WHENCE__);
   memcpy(ret, s, full_len); // already know len, so strcpy() would be slower

   return ret;
}
0
ответ дан 29 November 2019 в 03:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: