Сейчас я работаю со встроенными системами и выясняю способы реализации строк в микропроцессоре без операционной системы. Пока что я просто использую идею использования NULL-концевых символьных указателей и обрабатываю их как строки, где NULL обозначает конец. Я знаю, что это довольно часто, но всегда ли вы можете рассчитывать на это?
Причина, по которой я спрашиваю, заключается в том, что в какой-то момент я думал об использовании операционной системы реального времени, и я хотел бы использовать как можно больше моего текущего кода. Итак, можно ли ожидать, что для различных вариантов, которые существуют, строки будут работать одинаково?
Позвольте мне быть более конкретным, хотя для моего случая. Я внедряю систему, которая принимает и обрабатывает команды через последовательный порт. Могу ли я сохранить код обработки моей команды таким же, а затем ожидать, что все строковые объекты, созданные в ОСРВ (которая содержит команды), будут все завершены с нулевым значением? Или все будет иначе в зависимости от ОС?
Обновить
После того, как мне посоветовали взглянуть на этот вопрос, я решил, что он не совсем отвечает на мой вопрос. Сам вопрос заключается в том, следует ли всегда указывать длину строки, которая полностью отличается от того, что я спрашиваю, и хотя некоторые ответы содержат полезную информацию, они не совсем то, что я ищу. Ответы там, кажется, дают причины, почему или почему бы не завершить строку нулевым символом. Разница с тем, что я спрашиваю, заключается в том, могу ли я более или менее ожидать, что врожденные строки разных платформ завершат свои собственные строки с нулем, без необходимости выходить и пробовать каждую платформу, если это имеет смысл.
Ответы:
Вещи, которые называются «C-строками», будут оканчиваться нулем на любой платформе. Вот как стандартные функции библиотеки C определяют конец строки.
В языке C ничто не мешает иметь массив символов, который не заканчивается на ноль. Однако вам придется использовать какой-то другой метод, чтобы избежать запуска строки.
источник
char
конце,char
массивы с длиной, закодированной в первом байте (обычно называемые «строками Паскаля»),wchar_t
основанные на обоих версиях выше, иchar
массивы, которые объединяют оба метода: длина, закодированная в первом байте, и нулевой символ, заканчивающий строку.Определение завершающего символа зависит от компилятора для литералов и реализации стандартной библиотеки для строк в целом. Это не определяется операционной системой.
Соглашение об
NUL
увольнении восходит к предстандартному С, и через 30 с лишним лет я не могу сказать, что столкнулся со средой, которая делает что-то еще. Это поведение было кодифицировано в C89 и продолжает оставаться частью стандарта языка C (ссылка на черновик C99):NUL
строк, определяемых как окончание , требуяNUL
добавления к строковым литералам.Нет причин, по которым кто-то не мог бы написать функции, которые обрабатывают строки, оканчивающиеся каким-либо другим символом, но в большинстве случаев также нет причин нарушать установленный стандарт, если только ваша цель не дать программистам соответствия. :-)
источник
printf("string: \"%s\"\n", "my cool string")
. Единственный способ обойти в этом случае четыре параметра (кроме завершающего байта) - это определить строку, похожуюstd::string
на C ++, которая имеет свои проблемы и ограничения.NUL
другу - определяйте их независимо от того, что: «На этапе 7 перевода байт или код значение ноль добавляется к каждой многобайтовой последовательности символов, полученной из строкового литерала или литералов. " Библиотека функций с помощью определения стоп 7.1.1 в на первомNUL
они находят и не будут знать , или ухода , что дополнительные символы существуют за ее пределами.В языке C нет строкового типа данных, но есть строковые литералы .
Если вы поместите строковый литерал в вашу программу, он обычно завершается NUL (но смотрите специальный случай, обсуждаемый в комментариях ниже). То есть, если вы поместите
"foobar"
в место, гдеconst char *
ожидается значение, компилятор выдастfoobar⊘
на const / кодовый сегмент / раздел вашей программы, а значением выражения будет указатель на адрес, где он хранилf
символ. (Примечание: я использую⊘
для обозначения байта NUL.)Единственный другой смысл, в котором язык C имеет строки, состоит в том, что он имеет некоторые стандартные библиотечные подпрограммы, которые работают с NUL-символами. Эти библиотечные процедуры не будут существовать в чистой металлической среде, если вы сами не перенесете их.
Они просто код - ничем не отличается от кода, который вы сами пишете. Если вы не сломаете их при портировании, они будут делать то, что всегда делают (например, останавливаются на NUL.)
источник
char foo[4] = "abcd";
является допустимым способом создания ненулевого массива из четырех символов.char const *
выражение . Я забыл, что инициализаторы C иногда могут подчиняться другим правилам.char[4]
. Это не строка, но она была инициализирована с одногоstatic
к примеру Ruakh, тогда компилятор может излучать не завершенный нуль «абвг» к инициализированному сегменту данных таким образом , чтобы переменный инициализируются загрузчиком программы. Итак, Руах был прав: есть, по крайней мере, один случай, когда появление строкового литерала в программе не требует, чтобы компилятор выдавал строку с NUL-символами в конце. (ps, я фактически скомпилировал пример с gcc 5.4.0, и компилятор неКак уже упоминалось, нулевое завершение строк является соглашением Стандартной библиотеки Си. Вы можете работать со строками так, как пожелаете, если не собираетесь использовать стандартную библиотеку.
Это справедливо для любой операционной системы с компилятором 'C', а также вы можете писать программы на 'C', которые не работают под реальной операционной системой, как вы упоминаете в своем вопросе. Примером может служить контроллер для струйного принтера, который я разработал однажды. Во встроенных системах накладные расходы памяти операционной системы могут не требоваться.
В ситуациях с нехваткой памяти, например, я смотрю на характеристики моего компилятора в отношении набора команд процессора. В приложении, где строки обрабатываются много, может быть желательно использовать дескрипторы, такие как длина строки. Я имею в виду случай, когда процессор особенно эффективен при работе с короткими смещениями и / или относительными смещениями с адресными регистрами.
Итак, что важнее в вашем приложении: размер и эффективность кода или совместимость с ОС или библиотекой? Еще одним соображением может быть ремонтопригодность. Чем дальше вы отклоняетесь от соглашения, тем труднее будет поддерживать кого-то еще.
источник
Другие обращались к вопросу о том, что в C строки - это в основном то, что вы из них делаете. Но в вашем вопросе, похоже, есть некоторая путаница с самим терминатором, и, с одной стороны, это может быть тем, о чем беспокоится кто-то из вас.
Строки C заканчиваются нулем. То есть они заканчиваются нулевым символом
NUL
. Они не заканчиваются нулевым указателемNULL
, который представляет собой совершенно другой тип значения с совершенно другой целью.NUL
гарантированно имеет целочисленное значение ноль. Внутри строки он также будет иметь размер основного типа символов, который обычно равен 1.NULL
вовсе не обязательно иметь целочисленный тип.NULL
предназначен для использования в контексте указателя, и обычно ожидается, что он будет иметь тип указателя, который не должен преобразовываться в символ или целое число, если ваш компилятор хорош. Несмотря на то, что определениеNULL
включает в себя глиф0
, оно не обязательно будет иметь это значение [1], и если ваш компилятор не реализует константу как односимвольный#define
(многие этого не делают, потому что наNULL
самом деле не должно быть значимым в указатель контекста), поэтому расширенный код, как гарантируют, не будет фактически включать нулевое значение (даже при том, что это вводит в заблуждение нулевой глиф).Если
NULL
напечатано, он также вряд ли будет иметь размер 1 (или другой размер символа). Это может вызвать дополнительные проблемы, хотя фактические символьные константы по большей части не имеют размера символов.Теперь большинство людей увидят это и подумают: «Нулевой указатель - это что-то иное, чем ноль-бит? Что за вздор?», Но подобные предположения безопасны только на распространенных платформах, таких как x86. Поскольку вы явно упомянули интерес к нацеливанию на другие платформы, вам необходимо принять во внимание эту проблему, поскольку вы явно отделили свой код от предположений о природе отношений между указателями и целыми числами.
Следовательно, хотя строки C заканчиваются нулем, они не заканчиваются
NULL
, аNUL
(обычно пишутся'\0'
). Код, который явно используетсяNULL
в качестве ограничителя строки, будет работать на платформах с простой структурой адресов и даже будет компилироваться со многими компиляторами, но это абсолютно не правильно.[1] фактическое значение нулевого указателя вставляется компилятором, когда он читает
0
токен в контексте, где он будет преобразован в тип указателя. Это не преобразование из целочисленного значения 0, и оно не гарантированно сохраняется, если используется что-либо, кроме самого токена0
, например, динамическое значение из переменной; преобразование также необратимо, и нулевой указатель не должен давать значение 0 при преобразовании в целое число.источник
NUL
msgstr " гарантированно иметь целочисленное значение ноль." -> C не определяетNUL
. Вместо этого C определяет, что строки имеют окончательный нулевой символ , байт со всеми битами, установленными в 0.Я использовал строку в C, это означает, что символы с нулевым окончанием называется Strings.
Это не будет иметь никаких проблем при использовании в baremetal или в любых операционных системах, таких как Windows, Linux, RTOS: (FreeRTO, OSE).
Во встроенном мире нулевое окончание на самом деле помогает больше символизировать символ в виде строки.
Я использовал такие строки в Си во многих критических системах безопасности.
Вам может быть интересно, что такое строка на самом деле в C?
Строки в стиле C, которые являются массивами, также есть строковые литералы, такие как «this». В действительности оба этих типа строк представляют собой просто наборы символов, сидящих в памяти рядом друг с другом.
Например, вы можете объявить и определить массив символов и инициализировать его строковой константой:
Простой ответ: вам не нужно беспокоиться об использовании символов с нулевым завершением, эта работа не зависит от какой-либо платформы.
источник
NUL
автоматически добавляется.Как уже говорили другие, нулевое завершение в значительной степени универсально для стандартного C. Но (как уже отмечали другие) не 100%. Для (другого) примера операционная система VMS обычно использовала так называемые «строковые дескрипторы» http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html, доступ к которым осуществляется в C с помощью #include <descrip.h. >
На уровне приложения может использоваться нулевое завершение или нет, однако разработчик считает это целесообразным. Но для низкоуровневого VMS абсолютно необходимы дескрипторы, которые вообще не используют нулевое завершение (подробности см. Выше). Это в основном так, что все языки (C, ассемблер и т. Д.), Которые напрямую используют внутренние компоненты VMS, могут иметь общий интерфейс с ними.
Поэтому, если вы ожидаете какой-либо подобной ситуации, вам может потребоваться быть более осторожным, чем это необходимо для «всеобщего нулевого завершения». Я был бы более осторожен, если бы делал то, что ты делаешь, но для моих вещей уровня приложения можно с уверенностью предположить нулевое завершение. Я бы не стал предлагать вам такой же уровень безопасности. Ваш код вполне может взаимодействовать с ассемблером и / или другим языковым кодом в какой-то момент в будущем, что может не всегда соответствовать стандарту C строк с нулевым символом в конце.
источник
По моему опыту встраиваемых систем, критичных для безопасности и систем реального времени, нередки случаи, когда используются соглашения о строках C и PASCAL, т. Е. В качестве первого символа указывается длина строки (которая ограничивает длину 255), а для завершения строка с как минимум одним 0x00, (
NUL
), который уменьшает полезный размер до 254.Одна из причин этого заключается в том, чтобы знать, сколько данных вы ожидаете после получения первого байта, а другая заключается в том, что в таких системах, где это возможно, избегают динамических размеров буфера - выделение фиксированного размера буфера 256 быстрее и безопаснее (нет нужно проверить,
malloc
не удалось ли ). Другое заключается в том, что другие системы, с которыми вы общаетесь, могут быть не написаны в ANSI-C.В любой встроенной работе важно создать и поддерживать документ управления интерфейсом (IDC), который определяет все ваши коммуникационные структуры, включая строковые форматы, порядковый номер, целочисленные размеры и т. Д., Как можно скорее (в идеале до запуска ), и это должна быть ваша и все команды, священная книга при написании системы - если кто-то хочет представить новую структуру или формат, она должна быть сначала задокументирована там, и каждый, кто может быть затронут, проинформирован, возможно, с возможностью наложить вето на изменение ,
источник