Язык программирования C имеет в своей стандартной библиотеке набор функций, реализующих операции со строками (символьными и байтовыми строками) . Поддерживаются различные операции, такие как копирование, конкатенация , токенизация и поиск. Для символьных строк стандартная библиотека использует соглашение о том, что строки завершаются нулем : строка из n символов представляется как массив из n + 1 элементов, последний из которых представляет собой « NUL- символ» с числовым значением 0.
Единственная поддержка строк в самом языке программирования заключается в том, что компилятор преобразует строковые константы в кавычках в строки с нулевым завершением.
Строка определяется как непрерывная последовательность кодовых единиц, оканчивающаяся первой нулевой кодовой единицей (часто называемой нулевой кодовой единицей). [1] Это означает, что строка не может содержать нулевую кодовую единицу, поскольку первая увиденная единица обозначает конец строки. Длина строки — это количество кодовых единиц до нулевой кодовой единицы. [1] Память, занимаемая строкой, всегда на одну кодовую единицу больше ее длины, поскольку для хранения нулевого терминатора требуется пространство.
Обычно термин « строка» означает строку, кодовая единица которой имеет тип char
, который на всех современных машинах составляет ровно 8 бит. C90 определяет широкие строки [1] , в которых используется кодовая единица типа wchar_t
, которая на современных машинах имеет длину 16 или 32 бита. Это было предназначено для Unicode , но вместо этого все чаще используется UTF-8 в обычных строках для Unicode.
Строки передаются функциям путем передачи указателя на первую единицу кода. Поскольку char *
и wchar_t *
являются разными типами, функции, обрабатывающие широкие строки, отличаются от функций, обрабатывающих обычные строки, и имеют разные имена.
Строковые литералы ( "text"
в исходном коде C) преобразуются в массивы во время компиляции. [2] Результатом является массив кодовых единиц, содержащий все символы плюс завершающую нулевую кодовую единицу. В С90 L"text"
получается широкая струна. Строковый литерал может содержать нулевую единицу кода (один из способов — поместить ее \0
в исходный код), но это приведет к тому, что строка закончится в этой точке. Остальная часть литерала будет помещена в память (с добавлением еще одной нулевой кодовой единицы в конце), но невозможно узнать, что эти кодовые единицы были переведены из строкового литерала, поэтому такой исходный код не является строковым литералом. [3]
Каждая строка заканчивается при первом появлении нулевой кодовой единицы соответствующего типа ( char
или wchar_t
). Следовательно, строка байтов ( char*
) может содержать символы, отличные от NUL, в ASCII или любом расширении ASCII , но не может содержать символы в таких кодировках, как UTF-16 (даже если 16-битная кодовая единица может быть ненулевой, ее старший или младший байт может быть нуль). Кодировки, которые можно хранить в широких строках, определяются шириной wchar_t
. В большинстве реализаций wchar_t
это не менее 16 бит, поэтому можно хранить все 16-битные кодировки, такие как UCS-2 . Если 32-бит, то можно хранить wchar_t
32-битные кодировки, например UTF-32 . (Стандарт требует «типа, который содержит любой широкий символ», который в Windows больше не действует после перехода от UCS-2 к UTF-16. Это было признано дефектом стандарта и исправлено в C ++.) [4] В C++11 и C11 добавлены два типа с явной шириной char16_t
и char32_t
. [5]
Кодировки переменной ширины можно использовать как в байтовых, так и в широких строках. Длина строки и смещения измеряются в байтах или wchar_t
, а не в «символах», что может сбить с толку начинающих программистов. UTF-8 и Shift JIS часто используются в строках байтов C, тогда как UTF-16 часто используется в строках шириной C, если их длина wchar_t
составляет 16 бит. Усечение строк с символами переменной ширины с помощью таких функций, как strncpy
может привести к созданию недопустимых последовательностей в конце строки. Это может быть небезопасно, если усеченные части интерпретируются кодом, предполагающим, что входные данные действительны.
Поддержка литералов Unicode, таких как (UTF-8) или (UTF-16 или UTF-32, зависит от ), определяется реализацией [6] и может потребовать, чтобы исходный код был в одной и той же кодировке, особенно в тех случаях, когда компиляторы могут просто скопируйте все, что находится между кавычками. Некоторые компиляторы или редакторы потребуют ввода всех символов, отличных от ASCII, в виде последовательностей для каждого байта UTF-8 и/или каждого слова UTF-16. Начиная с C11 (и C++11), доступен новый префикс литерала , который гарантирует UTF-8 для литерала байтовой строки, как в . [7] Начиная с C++20 и C23 , был добавлен тип, предназначенный для хранения символов UTF-8, а типы символьных и строковых литералов с префиксом u8 были изменены на и соответственно.char foo[512] = "φωωβαρ";
wchar_t foo[512] = L"φωωβαρ";
wchar_t
char
\xNN
\uNNNN
u8
char foo[512] = u8"φωωβαρ";
char8_t
char8_t
char8_t[]
В исторической документации термин «символ» часто использовался вместо «байта» для строк C, что приводит к тому, что многие [ who? ] полагать, что эти функции как-то не работают для UTF-8 . Фактически, все длины определяются как байты, и это верно для всех реализаций, и эти функции работают как с UTF-8, так и с однобайтовыми кодировками. Документация BSD была исправлена, чтобы прояснить это, но документация POSIX, Linux и Windows по-прежнему использует «символ» во многих местах, где правильным термином является «байт» или «wchar_t».
Функции обработки буферов памяти могут обрабатывать последовательности байтов, которые включают в себя нулевые байты как часть данных. Имена этих функций обычно начинаются с mem
, в отличие от str
префикса.
Большинство функций, работающих со строками C, объявляются в string.h
заголовке ( cstring
в C++), тогда как функции, работающие со строками C, объявляются в wchar.h
заголовке ( cwchar
в C++). Эти заголовки также содержат объявления функций, используемых для работы с буферами памяти; Таким образом, это название является неправильным.
Функции, объявленные в, string.h
чрезвычайно популярны, поскольку, как часть стандартной библиотеки C , они гарантированно работают на любой платформе, поддерживающей C. Однако с этими функциями существуют некоторые проблемы безопасности, такие как потенциальное переполнение буфера при неосторожном и правильном использовании. , в результате чего программисты отдают предпочтение более безопасным и, возможно, менее переносимым вариантам, из которых некоторые популярные перечислены ниже. Некоторые из этих функций также нарушают константную корректность, принимая const
указатель на строку и возвращая неуказатель const
внутри строки. Чтобы исправить это, в версии стандартной библиотеки C++ некоторые из них были разделены на две перегруженные функции .
Все эти функции требуютmbstate_tобъект, первоначально находившийся в статической памяти (что делает функции небезопасными для потоков), а в более поздних дополнениях должен поддерживать вызывающий объект. Первоначально это было предназначено для отслеживания состояний переключения вмбкодировки, но современные, такие как UTF-8, в этом не нуждаются. Однако эти функции были разработаны исходя из предположения, чтоТуалеткодирование не является кодированием переменной ширины и поэтому предназначено для работы ровно с однимwchar_tза раз, передавая его по значению, а не используя строковый указатель. Поскольку UTF-16 является кодировкой переменной ширины,mbstate_tбыл повторно использован для отслеживания суррогатных пар в широкой кодировке, хотя вызывающий объект все равно должен обнаружить и вызватьmbtowcдважды для одного символа. [80] [81] [82] Более поздние дополнения к стандарту признают, что единственное, что интересует программистов преобразования, — это преобразование между UTF-8 и UTF-16, и прямо обеспечивают это.
Стандартная библиотека C содержит несколько функций для числовых преобразований. Функции, работающие со строками байтов, определены в stdlib.h
заголовке ( cstdlib
header в C++). Функции, работающие с широкими строками, определены в wchar.h
заголовке ( cwchar
header в C++).
Функции strchr
, bsearch
, strpbrk
, strrchr
, и их широкие аналоги не являются константно-корректнымиstrstr
, поскольку они принимают указатель на строку и возвращают неуказатель внутри строки. Это было исправлено в C23 . [95]memchr
const
const
Кроме того, начиная с Нормативной поправки 1 (C95), atoxx
функции считаются включенными в состав strtoxxx
функций, по этой причине ни C95, ни какой-либо более поздний стандарт не предоставляет версии этих функций с расширенными символами. Аргументом против atoxx
является то, что они не делают различия между ошибкой и 0
. [96]
Несмотря на общепризнанную необходимость замены strcat
[22] и strcpy
[18] функциями, не допускающими переполнения буфера, общепринятого стандарта не возникло. Частично это связано с ошибочным убеждением многих программистов на C, что strncat
и strncpy
имеют желаемое поведение; однако ни одна из функций не была предназначена для этого (они предназначались для управления строковыми буферами фиксированного размера с заполненными нулями форматами данных, менее часто используемыми в современном программном обеспечении), а поведение и аргументы неинтуитивны и часто написаны неправильно даже экспертами. программисты. [108]
Наиболее популярной заменой [a]strlcat
являются функции и strlcpy
, которые появились в OpenBSD 2.4 в декабре 1998 года. [108] Эти функции всегда записывают один NUL в целевой буфер, усекая результат при необходимости и возвращая размер буфера, который будет Это необходимо, что позволяет обнаружить усечение и предоставляет размер для создания нового буфера, который не будет усекаться. Их критиковали за то, что они якобы неэффективны, [111] поощряют использование строк C (вместо какой-то более совершенной альтернативной формы строк), [112] [113] и сокрывают другие потенциальные ошибки. [114] [115] Следовательно, в течение многих лет они не были включены в библиотеку GNU C (используемую программным обеспечением в Linux), хотя это и изменилось. В FAQ по glibc Wiki о включении strlc{py|at} отмечается, что начиная с glibc 2.38 код был зафиксирован [116] и поэтому добавлен. [117] В объявлении о доступности glibc 2.38 говорилось, что эти функции «ожидается, будут добавлены в будущую версию POSIX». (Отслеживание дефектов Austin Group, ID 986, отслеживало некоторые дискуссии о таких планах для POSIX.) Даже несмотря на то, что в glibc не была добавлена поддержка, strlcat и strlcpy были реализованы в ряде других библиотек C, включая библиотеки для OpenBSD, FreeBSD , NetBSD , Solaris , OS X и QNX , а также в альтернативных библиотеках C для Linux, таких как libbsd, представленная в 2008 году, [118] и musl , представленная в 2011 году. [119] [120] Отсутствие поддержки библиотеки C GNU Это не помешало различным авторам программного обеспечения использовать его и включать замену, среди прочего, в SDL , GLib , ffmpeg , rsync и даже внутри ядра Linux . Доступны реализации этих функций с открытым исходным кодом. [121] [122]
Иногда используются memcpy
[53] или memmove
[55]strcpy
, поскольку они могут быть более эффективными, чем отсутствие повторной проверки NUL (это менее верно для современных процессоров). Поскольку в качестве параметра им требуется длина буфера, правильная установка этого параметра может избежать переполнения буфера.
В рамках своего жизненного цикла разработки безопасности в 2004 году Microsoft представила семейство «безопасных» функций, включая strcpy_s
и strcat_s
(наряду со многими другими). [123] Эти функции были стандартизированы с некоторыми незначительными изменениями как часть дополнительного C11 (Приложение K), предложенного стандартом ISO/IEC WDTR 24731. Эти функции выполняют различные проверки, включая проверку того, не является ли строка слишком длинной для размещения в буфере. Если проверки не пройдены, вызывается определяемая пользователем функция «обработчик ограничений времени выполнения», [124] которая обычно прерывает выполнение программы. [125] [126] Некоторые функции выполняют разрушительные операции перед вызовом обработчика ограничений времени выполнения; например, strcat_s
устанавливает в качестве места назначения пустую строку, [127] что может затруднить восстановление после ошибок или их отладку. Эти функции вызвали серьезную критику, поскольку изначально они были реализованы только в Windows, и в то же время Microsoft Visual C++ начал выдавать предупреждающие сообщения, предлагающие использовать эти функции вместо стандартных. Некоторые полагают, что это попытка Microsoft привязать разработчиков к своей платформе. [128] Хотя реализации этих функций с открытым исходным кодом доступны, эти функции не присутствуют в обычных библиотеках C для Unix. [129] Опыт работы с этими функциями показал значительные проблемы с их принятием и ошибки в использовании, поэтому предлагается удалить Приложение K для следующей версии стандарта C. [130] Использование memset_s
также было предложено как способ избежать нежелательной оптимизации компилятора. [131] [132]
strlcpy
, против 38 644 использований strcpy_s
(и 15 286 150 использований strcpy
). [ нужна цитата ]{{cite web}}
: CS1 maint: местоположение ( ссылка ){{cite web}}
: CS1 maint: местоположение ( ссылка ){{cite web}}
: CS1 maint: местоположение ( ссылка ){{cite web}}
: CS1 maint: местоположение ( ссылка )Этот API [strlcpy и strlcat] принят большинством современных операционных систем и многими автономными программными пакетами [...]. Заметным исключением является стандартная библиотека C GNU, glibc, чей сопровождающий упорно отказывается включать эти улучшенные API, называя их «ужасно неэффективной чушью BSD», несмотря на предварительные доказательства того, что в большинстве случаев они быстрее, чем API, которые они заменяют.
Правильная обработка строк означает, что вы всегда знаете длину ваших строк и, следовательно, можете использовать memcpy (вместо strcpy).
{{cite web}}
: CS1 maint: местоположение ( ссылка ){{cite web}}
: CS1 maint: местоположение ( ссылка ){{cite web}}
: CS1 maint: местоположение ( ссылка )