Какие функции манипуляции со строками я должен использовать? - PullRequest
16 голосов
/ 15 ноября 2010

В моей среде Windows / Visual C существует множество альтернатив для выполнения одних и тех же базовых задач по работе со строками.

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

  • strcpy, функция стандартной библиотеки ANSI C (CRT)
  • lstrcpy, версия, включенная в kernel32.dll
  • StrCpy, из библиотеки Shell Lightweight Utility
  • StringCchCopy / StringCbCopy, из библиотеки «безопасной строки»
  • strcpy_s, усиленная версия CRT

Пока я понимаю, что все эти альтернативыУ меня есть историческая причина, могу ли я просто выбрать согласованный набор функций для нового кода?А какой?Или я должен выбрать наиболее подходящую функцию в каждом конкретном случае?

Ответы [ 6 ]

13 голосов
/ 17 ноября 2010

Прежде всего, давайте рассмотрим плюсы и минусы каждого набора функций:

Функция стандартной библиотеки ANSI C (CRT)

Такие функции, как strcpy, являются единственным выбором, если вы разрабатываете переносимый C-код. Даже в проекте только для Windows разумно было бы разделить переносимый код и код, зависящий от ОС.
Эти функции часто оптимизируются на уровне сборки и поэтому работают очень быстро.
Есть некоторые недостатки:

  • они имеют много ограничений, и поэтому вам часто приходится вызывать функции из других библиотек или предоставлять свои собственные версии
  • Есть некоторые архаизмы, такие как печально известный strncpy

Строковые функции Kernel32

Функции, подобные lstrcpy, экспортируются ядром32 и должны использоваться только при попытке избежать какой-либо зависимости от CRT. Возможно, вы захотите сделать это по двум причинам:

  • отказ от полезной нагрузки CRT для сверхлегкого исполняемого файла (необычно в наши дни, но не 10 лет назад!)
  • избегая проблем с инициализацией (если вы запускаете поток с CreateThread вместо _beginthread).

Более того, функция kernel32 может быть более оптимизированной, чем версия CRT: когда ваш исполняемый файл будет работать на Windows 9, оптимизированной для Core i13, kernel32 может использовать оптимизированную сборку версия.

Оболочка Облегченные служебные функции

Здесь действительны те же соображения, что и для функций kernel32, с добавленной стоимостью некоторых более сложных функций. Однако я сомневаюсь, что они активно поддерживаются, и я бы просто их пропустил.

Функция StrSafe

Функции StringCchCopy / StringCbCopy обычно являются моим личным выбором: они очень хорошо спроектированы, мощны и удивительно быстры (я также помню документ, в котором сравнивали производительность этих функций с эквивалентами CRT).

Функции CRT с улучшенной безопасностью

Эти функции имеют несомненное преимущество, так как они очень похожи на эквиваленты ANSI C, поэтому перенос устаревшего кода - это просто. Мне особенно нравится основанная на шаблонах версия (конечно, доступная только при компиляции как C ++). Я очень надеюсь, что они будут в конечном итоге стандартизированы. К сожалению, у них есть ряд недостатков:

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

Выводы

Хотя моим личным фаворитом для разработки Windows является библиотека StrSafe, я советую использовать функции ANSI C, когда это возможно, поскольку переносимый код всегда полезен.

В реальной жизни я разработал персонализированную портативную библиотеку с прототипами, похожими на функции CRT с расширенными функциями безопасности (включая мощную технологию на основе шаблонов), которая опирается на библиотеку StrSafe в Windows и на функции ANSI C в других платформы.

4 голосов
/ 15 ноября 2010

Мои личные предпочтения, как для новых, так и для существующих проектов, это версии StringCchCopy/StringCbCopy из библиотеки безопасных строк. Я нахожу эти функции в целом очень последовательными и гибкими. И они были разработаны с нуля с учетом безопасности.

3 голосов
/ 15 ноября 2010

Я бы ответил на этот вопрос немного по-другому. Хотите иметь переносимый код или нет? Если вы хотите быть переносимым, вы не можете полагаться ни на что иное, кроме strcpy, strncpy или на стандартные функции обработки строковых символов широких символов.

Тогда, если ваш код просто должен работать под Windows, вы можете использовать варианты «безопасной строки».

Если вы хотите быть переносимым и при этом иметь дополнительную безопасность, вам следует проверить кросс-платформенные библиотеки, например, например, бойкий или libapr или другие «безопасные строковые библиотеки», например: SafeStrLibrary

1 голос
/ 15 ноября 2010

Я бы предложил использовать функции из стандартной библиотеки или функции из кроссплатформенных библиотек .

0 голосов
/ 15 ноября 2010

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

Если вы используете strcpy, вам нужно быть увереннымваша строка помещается в буфер назначения.Если вы только что выделили его размером не менее strlen(source)+1, все будет в порядке, если исходная строка не может быть одновременно изменена другим потоком.В противном случае вам нужно проверить, помещается ли он в буфер.Вы можете использовать такие интерфейсы, как snprintf или strlcpy (нестандартная функция BSD, но легко скопировать реализацию), которые будут обрезать строки, которые не помещаются в целевой буфер, но тогда вам действительно нужно оценить, может ли обрезание строки привести куязвимости в себе.Я думаю, что гораздо лучший подход при тестировании, подходит ли исходная строка, состоит в том, чтобы создать новое распределение или вернуть состояние ошибки, а не выполнять слепое усечение.

Если вы будете выполнять много операций конкатенации / сборки строк,Вы действительно должны написать весь свой код, чтобы управлять длиной и текущей позицией на ходу.Вместо:

strcpy(out, str1);
strcat(out, str2);
strcat(out, str3);
...

Вы должны сделать что-то вроде:

size_t l, n = outsize;
char *s = out;

l = strlen(str1);
if (l>=outsize) goto error;
strcpy(s, str1);
s += l;
n -= l;

l = strlen(str2);
if (l>=outsize) goto error;
strcpy(s, str2);
s += l;
n -= l;

...

В качестве альтернативы вы можете избежать изменения указателя, сохраняя текущий индекс i типа size_t и используяout+i, или вы можете избежать использования переменных размера, сохраняя указатель на конец буфера и выполняя такие действия, как if (l>=end-s) goto error;.

Обратите внимание, что при любом подходе избыточность может быть сокращенанаписание собственных (простых) функций, которые принимают указатели на переменную position / size и вызывают стандартную библиотеку, например что-то вроде:

if (!my_strcpy(&s, &n, str1)) goto error;

Избегание strcat также имеет преимущества в производительности;см. Алгоритм Шлемеля-рисовальщика .

Наконец, вы должны заметить, что хорошие 75% людей, выполняющих копирование и сборку строк в C, совершенно бесполезны.Моя теория состоит в том, что люди, делающие это, происходят из языков сценариев, где сборка строк - это то, что вы делаете все время, но в Си это часто бывает бесполезно.Во многих случаях вы можете обойтись без копирования строк, используя вместо этого оригинальные копии, и в то же время получить гораздо лучшую производительность и более простой код.Мне вспоминается недавний вопрос SO, в котором OP использовал regexec для сопоставления регулярному выражению, а затем копировал результат просто для его печати, что-то вроде:

char *tmp = malloc(match.end-match.start+1);
memcpy(tmp, src+match.start, match.end-match.start);
tmp[match.end-match.start] = 0;
printf("%s\n", tmp);
free(tmp);

То же самое можно сделатьс:

printf("%.*s\m", match.end-match.start, src+match.start);

Без выделения ресурсов, без очистки, без ошибок (исходный код вылетал при сбое malloc).

0 голосов
/ 15 ноября 2010

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

Но это всего лишь советы, это субъективный вопрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...