C文字列は常にヌルで終了しますか、それともプラットフォームに依存しますか?


13

現在、私は組み込みシステムで作業しており、オペレーティングシステムなしでマイクロプロセッサに文字列を実装する方法を考えています。これまでのところ、私がやっていることは、NULLで終了する文字ポインタを持つという考えを使用し、NULLが終了を示す文字列として扱うことです。私はこれがかなり一般的であることを知っていますが、これが当てはまることを常に期待できますか?

私が尋ねる理由は、ある時点でリアルタイムオペレーティングシステムを使用することを考えていたため、現在のコードを可能な限り再利用したいからです。そこにあるさまざまな選択肢について、文字列が同じように機能することをほとんど期待できますか?

私の場合はもっと具体的にしましょう。私は、シリアルポート経由でコマンドを受け取って処理するシステムを実装しています。コマンド処理コードを同じにして、RTOS(コマンドを含む)で作成された文字列オブジェクトがすべてNULLで終了することを期待できますか?または、OSに基づいて異なりますか?

更新

この質問を見るようにアドバイスされた後、私はそれが私が尋ねていることを正確に答えていないことを決定しました。質問自体は、文字列の長さを常に渡す必要があるかどうかを尋ねています。これは私が尋ねているものとはまったく異なり、答えの一部には有用な情報が含まれていましたが、私が探しているものではありません。なぜか理由を与えるためにそこに見えた答えではないヌル文字で文字列を終了させます。私が尋ねているものとの違いは、異なるプラットフォームの生まれた文字列がnullで独自の文字列を終了することを多かれ少なかれ期待できるかどうか、それが理にかなっている場合は、すべてのプラットフォームを試してみる必要はありません。


3
私は長い間Cを使用していませんが、NULLで終わる文字列を使用しない実装に遭遇したときのことは考えられません。私が正しく覚えていれば、それは標準Cの一部です(私が言ったように、それはしばらくの間...)
MetalMikester

1
私はCの専門家ではありませんが、私が知る限り、Cのすべての文字列はnullで終わるcharの配列です。ただし、独自の文字列型を作成できますが、すべての文字列操作関数を自分で実装する必要があります。
マチャド


1
@MetalMikesterこの情報は標準のC仕様にあると思いますか?
スヌープ

3
@Snoopyほとんどの場合、はい。しかし、実際には、Cで文字列について話すとき、それらはNULLで終わる文字の配列にすぎません。これは、何らかの非標準の文字列ライブラリを使用している場合を除き、それはここで話していることではありません。特にCの強みの1つである移植性を考慮して、それを尊重しないプラットフォームを見つけることはできないと思います。
-MetalMikester

回答:


42

「C文字列」と呼ばれるものは、どのプラットフォームでもヌルで終了します。これが、標準Cライブラリ関数が文字列の終わりを決定する方法です。

C言語内では、nullで終わらない文字の配列を持つことを妨げるものは何もありません。ただし、文字列の終わりを超えないようにするには、他の方法を使用する必要があります。


4
追加するだけです。通常、あなたは文字列の長さを追跡して、あなたは、右のような何かそれを行うには、カスタムデータ構造で終わるために、整数のどこかに持っているのQtでのQStringクラスを
ルドルフ・オラー

8
点におけるケース:CプログラムとI作業その用途少なくとも5つの異なる文字列形式:NULLで終了するchar配列を、char(一般に「パスカル文字列」として知られている)最初のバイトで符号化された長さの配列、wchar_tの両方のベースバージョン上記、およびchar両方の方法を組み合わせた配列:最初のバイトでエンコードされた長さ、および文字列を終了するヌル文字。
マーク

4
@Markは、多くのサードパーティのコンポーネント/アプリケーションまたはレガシーコードの混乱とのインターフェースですか?
ダンは

2
@DanNeely、上記のすべて。古典的なMacOSとインターフェースするためのPascal文字列、内部使用およびWindows用のC文字列、Unicodeサポートを追加するためのワイド文字列、および誰かが賢くてMacOSとWindowsの両方と同時にインターフェースできる文字列を作成しようとしたためのろくでなし文字列。
マーク

1
@Mark ...そしてもちろん、古典的なMacOSは長い間使われていなかったので、誰も技術的な負債を返済するためにお金を使う気はありませんでした。私の同情。
ダンは

22

終了文字の決定は、リテラルのコンパイラーと、一般的な文字列の標準ライブラリーの実装次第です。オペレーティングシステムによって決定されません。

NUL終了の慣習は標準Cに戻っており、30年以上後には、他のことを行う環境に遭遇したとは言えません。この動作はC89で体系化されており、C言語標準の一部であり続けています(リンクはC99のドラフトです)。

  • セクション6.4.5は、文字列リテラルに追加NULするNULことを要求することにより、- 終了文字列のステージを設定します。
  • セクション7.1.1は、文字列を「最初のヌル文字で終了し、それを含む文字の連続シーケンス」として定義することにより、標準ライブラリの関数にそれをもたらします。

他の文字で終了する文字列を処理する関数を誰かが記述できなかった理由はありませんが、プログラマーに適合させることを目標としない限り、ほとんどの場合、確立された標準に反する理由もありません。:-)


2
理由の1つは、同じ文字列の末尾を何度も見つける必要がないようにすることです。
パエロエベルマン

@PaŭloEbermannそうです。1つではなく2つの値を渡す必要があります。のように文字列リテラルを渡すだけの場合は少し面倒ですprintf("string: \"%s\"\n", "my cool string")。この場合、4つのパラメーターを渡す唯一の方法(ある種の終了バイトを除く)はstd::string、C ++のような文字列を定義することです。これには、独自の問題と制限があります。
cmaster-モニカの復元

1
セクション6.4.5 では文字列リテラルをヌル文字で終了する必要はありません。「文字列リテラルは文字列(7.1.1を参照)である必要はありません。\ 0エスケープシーケンスによってヌル文字が埋め込まれる可能性があるためです。
bzeaman

1
@bzeaman脚注では、7.1.1の文字列の定義を満たさない文字列リテラルを構築できると述べていますが、それを参照する文は準拠コンパイラを示していますNUL-「翻訳フェーズ7では、バイトまたはコード文字列リテラルまたはリテラルから生じる各マルチバイト文字シーケンスに値ゼロが追加されます。」7.1.1の定義を使用するライブラリ関数は、最初NULに見つかった時点で停止し、それを超える追加の文字が存在することを認識または気にしません。
Blrfl

私は訂正します。「null」などのさまざまな用語を検索しましたが、「value zero」に言及している6.4.5.5が見つかりませんでした。
bzeaman

3

私は組み込みシステムで作業しています...オペレーティングシステムなしで...私は... NULLで終了する文字ポインタを持ち、NULLが終了を示す文字列として扱うという考えを使用しています。私はこれがかなり一般的であることを知っていますが、これが当てはまることを常に期待できますか?

C言語には文字列データ型はありませんが、文字列リテラルがあります

プログラムに文字列リテラルを配置すると、通常はNULで終了します(ただし、以下のコメントで説明する特別なケースを参照してください)。つまり"foobar"const char *値が期待される場所に配置すると、コンパイラは出力しますfoobar⊘プログラムのconst / codeセグメント/セクションに追加します。式の値は、f文字を格納したアドレスへのポインターになります。(注:NULバイトを示すために使用しています。)

C言語に文字列があるという他の唯一の意味は、NULで終了する文字シーケンスで動作する標準ライブラリルーチンがあることです。これらのライブラリルーチンは、自分で移植しない限り、ベアメタル環境には存在しません。

それらはただのコードです---あなたが書いたコードと違いはありません。あなたがそれらを移植するときにそれらを壊さないならば、彼らは彼らがいつもすることをするでしょう(例えば、NULで止まる)。


2
Re:「プログラムに文字列リテラルを入れると、それは常にNULで終了します」:それについて確かですか?(たとえば)char foo[4] = "abcd";4文字のnullで終了しない配列を作成する有効な方法であると確信しています。
-ruakh

2
@ruakh、おっと!それは私が考慮しなかったケースです。char const * が期待される場所に現れる文字列リテラルについて考えていました。Cの初期化子が異なる規則に従うことがあることを忘れていました。
ソロモンスロー

@ruakh文字列リテラルはNULで終了します。配列はそうではありません。
ジェームズリン

2
@ruakhがありchar[4]ます。それはないが、文字列が、それはして初期化 1から
Caleth

2
@Caleth、「1つから初期化」は、実行時に発生する必要があるものではありません。私たちは、キーワードを追加する場合staticRuakhの例には、コンパイラがあり発する NULは、変数はプログラムローダによって初期化されるように初期化されたデータ・セグメントに「ABCD」を終了しました。だから、Ruakhは正しかった。プログラム内の文字列リテラルの出現が、コンパイラがNUL終了文字列を出力することを要求しない場合が少なくとも1つある。(ps、私は実際にgcc 5.4.0でサンプルをコンパイルしましたが、コンパイラはNULを出力しませんでした。)
ソロモンスロー

2

他の人が言及したように、文字列のヌル終端はC標準ライブラリの規約です。標準ライブラリを使用しない場合は、任意の方法で文字列を処理できます。

これは、「C」コンパイラを備えたすべてのオペレーティングシステムに当てはまります。また、質問で述べたように、真のオペレーティングシステムで実行されない「C」プログラムを作成することもできます。例は、私がかつて設計したインクジェットプリンターのコントローラーです。組み込みシステムでは、オペレーティングシステムのメモリオーバーヘッドは必要ない場合があります。

メモリ不足の状況では、たとえば、プロセッサの命令セットに対するコンパイラの特性を調べます。文字列が多く処理されるアプリケーションでは、文字列の長さなどの記述子を使用することが望ましい場合があります。CPUがアドレスレジスタを使用した短いオフセットや相対オフセットの処理で特に効率的である場合を考えています。

それでは、アプリケーションでより重要なのは、コードサイズと効率、またはOSまたはライブラリとの互換性ですか?もう1つの考慮事項は、保守性です。コンベンションから遠ざかるほど、他の誰かが維持するのが難しくなります。


1

他の人は、Cでは、文字列は主にあなたが作ったものであるという問題に対処しています。しかし、ターミネーター自体についてのあなたの質問には多少の混乱があるようです。そして、ある観点から、これはあなたの立場の誰かが心配していることです。

C文字列はヌルで終了します。つまり、ヌル文字で終了しますNUL。それらは、nullポインターNULLで終了しません。これは、まったく異なる目的を持つまったく異なる種類の値です。

NUL整数値がゼロであることが保証されています。文字列内では、基本となる文字タイプのサイズも持ちます。通常は1です。

NULL整数型を持つことはまったく保証されていません。NULLは、ポインターコンテキストでの使用を目的としており、通常、ポインター型を持っていることが期待されています。ポインター型は、コンパイラーが適切であれば、文字または整数に変換されるべきではありません。定義はしばらくNULLグリフを含み0、それは実際に保証されていない、その値[1]を持っている、とあなたのコンパイラを実装しない限り、一定の1文字として#define(多くはない、ので、NULL 本当に非で有意義であってはなりませんポインターコンテキスト)、したがって、展開されたコードが実際にゼロ値を含むことは保証されません(混乱してゼログリフを含む場合でも)。

NULLを入力した場合、サイズが1(または別の文字サイズ)になる可能性は低くなります。実際の文字定数はほとんどの部分で文字サイズがありませんが、これはおそらく追加の問題を引き起こす可能性があります。

今ではほとんどの人がこれを見て、「ゼロポインタ以外のヌルポインタ?なんてナンセンス」と思うでしょう-しかし、そのような仮定はx86のような一般的なプラットフォームでのみ安全です。他のプラットフォームをターゲットとすることに明示的に言及しているので、ポインターと整数の関係の性質に関する仮定からコードを明確に分離しているため、この問題を考慮する必要があります。

したがって、C文字列はnullで終了しますが、で終了するのNULLではなくNUL(通常は'\0')で終了します。NULL文字列ターミネーターとして明示的に使用するコードは、単純なアドレス構造を持つプラットフォームで動作し、多くのコンパイラーでコンパイルすることもできますが、それは絶対に正しいCではありません。


[1]実際のNULLポインター値は、ポインター型に変換されるコンテキストでコンパイラーが0 トークンを読み取るときにコンパイラーによって挿入されます。これは整数 0 からの変換ではなく0、変数からの動的な値など、トークン自体以外が使用された場合に保持されることは保証されません。変換も可逆的ではなく、nullポインターは整数に変換されたときに値0を生成する必要はありません。


素晴らしい点。これを解決するために編集を送信しました。
モンティハーダー

NUL整数値がゼロであることが保証されています。」-> Cは定義しませんNUL。代わりに、Cは、文字列が最終的な持っていることを定義ヌルchracter、0に設定されたすべてのビットがバイト
回復モニカ- chux

1

私はCで文字列を使用してきました。つまり、ヌル終了文字は文字列と呼ばれます。

ベアメタルまたはWindows、Linux、RTOSなどのオペレーティングシステムで使用する場合、問題はありません:(FreeRTO、OSE)。

組み込みの世界では、ヌル終端は実際に文字を文字列としてトークン化するのに役立ちます。

私は多くの安全性が重要なシステムでそのようなCの文字列を使用しています。

不思議に思われるかもしれませんが、実際にはCの文字列とは何ですか?

配列であるCスタイルの文字列には、「this」などの文字列リテラルもあります。実際には、これらの文字列タイプは両方とも、メモリ内で隣り合って座っている文字の単なるコレクションです。

二重引用符で囲まれた文字列を記述するたびに、Cは、\ 0文字で終了する文字列を含む文字の配列を自動的に作成します。

たとえば、文字の配列を宣言および定義し、文字列定数で初期化できます。

char string[] = "Hello cruel world!";

簡単な答え:ヌル終端の文字の使用について心配する必要はありません。これはどのプラットフォームにも依存しません。


おかげで、二重引用符で宣言すると、a NULが自動的に追加されることを知りませんでした。
スヌープ

1

他の人が言ったように、null終端は標準Cにとってほぼ普遍的です。しかし(他の人が指摘したように)100%ではありません。(別の)例えば、VMSオペレーティング・システムは、典型的には、それが「文字列記述子」と呼ばれるものを使用http://h41379.www4.hpe.com/commercial/c/docs/5492p012.htmlによりCでアクセスする#include <descrip.h >

アプリケーションレベルのものはnull終了を使用することも使用しないこともできますが、開発者は適切だと考えています。ただし、低レベルのVMSには記述子が絶対に必要であり、記述子はヌル終端をまったく使用しません(詳細については上記のリンクを参照してください)。これは主に、VMS内部を直接使用するすべての言語(C、アセンブリなど)が共通のインターフェースを持つことができるようにするためです。

したがって、同様の状況を予想している場合は、「ユニバーサルNULLターミネーション」が必要であると示唆するよりも少し慎重になりたいかもしれません。私があなたがしていることをしている場合はもっと慎重になりますが、私のアプリケーションレベルの場合は、null終了を想定しても安全です。同じレベルの安全性をあなたに提案するつもりはありません。あなたのコードは、将来のある時点でアセンブリや他の言語コードとインターフェースをとる必要があるかもしれません。これは、ヌルで終了する文字列のC標準に常に準拠するとは限りません。


今日、0の終了は実際には非常に珍しいです。C ++ std :: stringはサポートしません、Java Stringはサポートしません、Objective-C NSStringはサポートしません、Swift Stringはサポートしません-結果として、各言語ライブラリは文字列内に NULコード持つ文字列をサポートします(Cでは不可能です)明らかな理由で文字列)。
gnasher729

@ gnasher729「...ほぼ共通」を「標準Cについてほぼ共通」に変更しました。これにより、あいまいさを排除し、今日でも正しいままであることが期待されます(OPの主題と質問によると、これは私が意図したものです)。
ジョンフォーコシュ

0

組み込みの安全性重視のリアルタイムシステムの私の経験では、CとPASCALの両方の文字列規則を使用すること、つまり、文字列の長さを最初の文字(長さを255に制限)として指定し、NUL使用可能なサイズを254に減らす、少なくとも1つの0x00の文字列()。

この理由の1つは、最初のバイトを受信した後に予想されるデータ量を知ることであり、別の理由は、そのようなシステムでは、可能な場合は動的バッファーサイズが回避されることです-固定256バッファーサイズの割り当てはより高速で安全です(いいえmalloc失敗したかどうかを確認する必要があります)。もう1つは、通信している他のシステムがANSI-Cで記述されていない可能性があることです。

埋め込み作業では、文字列形式、エンディアン、整数サイズなどを含むすべての通信構造をできるだけ早く(理想的には開始する前に)定義するInterface Control Document(IDC)を確立および維持することが重要です。そして、それはあなたと、そしてすべてのチーム、システムを書くときの聖なる本であるべきです-誰かが新しい構造やフォーマットを導入したい場合、まずそこに文書化し、影響を受ける可能性のあるすべての人に、おそらく変更を拒否するオプションを付けて通知する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.