「strlen(s1) - strlen(s2)」が0より小さいことはありません。
質問
現在、文字列の長さを頻繁に比較する必要のあるCプログラムを書いているので、以下のヘルパー関数を書きました。
int strlonger(char *s1, char *s2) {
return strlen(s1) - strlen(s2) > 0;
}
この関数が真を返すことに気づいたのは
s1
よりも短い
s2
. どなたかこの奇妙な動作を説明してください。
どのように解決するのですか?
あなたが遭遇したのは、符号付きと符号なしの両方の量を含む式を処理するときにCで発生するいくつかの奇妙な動作です。
一方のオペランドが符号付きで他方が符号なしである演算が行われる場合、C は符号付きの引数を符号なしに暗黙的に変換し、数値が非負であると仮定して演算を実行します。この規約はしばしば、以下のような関係演算子の非直感的な動作につながります。
<
と
>
.
ヘルパー関数について、以下のことに注意してください。
strlen
は型を返します。
size_t
(符号なし量)を返す場合、差と比較は共に符号なし算術で計算される。このとき
s1
よりも短い場合
s2
となり、その差は
strlen(s1) - strlen(s2)
は負になるはずですが、代わりに大きな符号なし数値になり、これは
0
. したがって
return strlen(s1) - strlen(s2) > 0;
戻る
1
であっても
s1
よりも短くても
s2
. 関数を修正するには、代わりに次のコードを使用してください。
return strlen(s1) > strlen(s2);
素晴らしいCの世界へようこそ! :)
追加の例
この質問は最近注目されているので、私が考えを理解していることを確認するために、いくつかの (簡単な) 例を提供したいと思います。ここでは、2の補数表現を使用する 32 ビット マシンで作業していると仮定します。
C 言語で符号なし/符号ありの変数を扱う際に理解すべき重要な概念は、次のとおりです。 1 つの式に符号なしと符号ありの量が混在している場合、符号ありの値は暗黙のうちに符号なしにキャストされます。 .
例1:
次のような式を考えてみましょう。
-1 < 0U
第2オペランドは符号なしなので、第1オペランドは を暗黙のうちにキャストします。 を符号なしにキャストし、したがってこの式は比較と等価になります。
4294967295U < 0U
というのは、もちろん偽です。これはおそらく、あなたが期待していた動作ではありません。
例2:
配列の要素を合計しようとする次のようなコードを考えてみましょう。
a
ここで、要素数はパラメータ
length
:
int sum_array_elements(int a[], unsigned length) {
int i;
int result = 0;
for (i = 0; i <= length-1; i++)
result += a[i];
return result;
}
この関数は、符号付きから符号なしへの暗黙のキャストによるバグがいかに発生しやすいかを示すために設計されています。パラメータ
length
を符号なしとして渡すのはごく自然なことです。結局のところ、誰が負の長さを使いたいと思うでしょうか?停止基準である
i <= length-1
も非常に直感的であるように見えます。しかし、引数
length
に等しい
0
である場合、これらの2つの組み合わせは予期しない結果をもたらします。
パラメータ
length
は符号なしなので、計算
0-1
は符号なし算術で実行され、これはモジュラー加算と等価である。その結果は
UMax
. また
<=
の比較も符号なし比較で行われ、任意の数値が以下であることから
UMax
以下であるため、比較は常に保持されます。したがって、このコードでは配列
a
.
このコードを修正するには
length
を
int
のテストを変更することによって、あるいは
for
ループのテストを
i < length
.
結論 Unsignedはいつ使うべきか?
ここであまり議論を呼ぶようなことは言いたくないのですが、私がCでプログラムを書くときによく守っているルールをいくつか紹介します。
-
禁止事項 は、数字が非負であるという理由だけで使用することはできません。 間違えやすく、しかもそれが非常に微妙な場合もある(例2)。
-
DO は、モジュール演算を行う際に使用します。
-
DO ビットを使って集合を表現する場合に使用します。 符号拡張をせずに論理右シフトができるので便利なことが多い。
もちろん、これらの規則に反することを決定する状況もあるかもしれません。しかし、ほとんどの場合、これらの提案に従うことで、コードがより簡単になり、エラーが発生しにくくなります。
関連
-
initializer element is not constant "というエラーが表示されるのですが?
-
コンパイルエラー:可変長オブジェクトが初期化されていない可能性があります。
-
[解決済み] C言語で%sを正しく使う - 超基本レベル
-
[解決済み] ⑭と⑯は何のためにあるのですか?
-
[解決済み] Linuxカーネルにおけるcontainer_ofマクロの理解
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] C言語では「?」演算子は何をするのですか?
-
[解決済み] C言語における「static」の意味とは?
-
[解決済み] Node.js の console.log() で '[Object]' ではなく、完全なオブジェクトを取得するにはどうすればよいですか?
-
[解決済み] mallocとcallocの違い?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
警告:代入がキャストなしで整数からポインタを作成する場合の修正方法に関する警告
-
C 言語のポインタ配列のポインタ型、ポインタに値を割り当てるために配列名を使用、コンパイル時の警告:互換性のないポインタ型からの初期化
-
[解決済み] char *とchar[]の違い [重複]
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] C言語のコードで「:-!」とは何ですか?
-
[解決済み] mallocとcallocの違い?
-
[解決済み] アセンブリがCより速いのはどんなとき?[クローズド]
-
[解決済み] ストラクチャーとユニオンの違い
-
[解決済み] 符号なし整数の減算は定義された動作ですか?