「バイト数」をゼロに設定してmemcpy()およびmemmove()を呼び出すことはできますか?


102

私はactully移動することは何もないとき/治療の例に必要でコピーしていますmemmove()/ memcpy()エッジケースなど

int numberOfBytes = ...
if( numberOfBytes != 0 ) {
    memmove( dest, source, numberOfBytes );
}

または私はチェックせずに関数を呼び出す必要があります

int numberOfBytes = ...
memmove( dest, source, numberOfBytes );

前のスニペットのチェックは必要ですか?


6
質問は私にfreeのような関数のnullポインタをチェックすることを少し思い出させます。必要ではありませんが、コメントを書いて、あなたがそれについて考えたことを示します。
ヒキガエル

12
@Toad:コードを散らかす以外に、それはどのような目的に役立ちますか?誰かのコードを読むとき、私は元のプログラマーが「実際には必要ではないこの操作をすることを考えましたが、それは不必要なのでそれをしませんでした」ということを知る必要はありません。ポインターが解放されているのを見つけたら、nullが許可されていることを知っているので、「nullをチェックするべきか」についての元のプログラマーの考えを知る必要はありません。同じことが0バイトのコピーにもmemcpy
当てはまり

8
@jalf:それがスタックオーバーフローに関する質問であるという事実は、人々に疑わしいものにします。コメントを追加しても役に立たないかもしれませんが、知識の少ない人には役立つかもしれません
Toad

2
@Toadええ、コメントは、なぜ必要と思われるチェックが実際には価値がないのかを明確に指摘しているので、原則として価値があります。コインのもう一方の側面は、この特定の例は、各プログラマーが一度だけ答えを学ぶ必要がある標準ライブラリー関数を含む一般的なケースであるということです。そうすれば、読んだプログラムで、これらのチェックが不要であることを認識できます。その理由は、私はコメントを省略すると思います。このような複数の呼び出しがあるコードベースは、コメントをそれぞれにコピーして貼り付けるか、どちらかが醜いいくつかの呼び出しでのみ任意に使用する必要があります。
Mark Amery

回答:


145

C99標準(7.21.1 / 2)から:

として宣言さsize_t nれた引数が関数の配列の長さを指定しnている場合、その関数への呼び出しで値0を持つことができます。7.1.4で説明されているように、この副次句の特定の関数の説明で他に明示的に述べられていない限り、そのような呼び出しのポインター引数は、有効な値を保持するものとします。このような呼び出しでは、文字を見つける関数は出現を検出せず、2つの文字シーケンスを比較する関数はゼロを返し、文字をコピーする関数はゼロ文字をコピーします。

だから答えはノーです。チェックは必要ありません(またははい、ゼロを渡すことができます)。


1
配列の最後の要素に続く位置を指している場合、そのような関数の目的でポインターは「有効」と見なされますか?そのようなポインターは正当に逆らうことはできませんでしたが、そこから1を引くなど、他のポインターのようなことを安全に行うことができました。
スーパーキャット

1
@supercat:はい、配列の最後を1つ超えたポインターは、その配列内(または最後を過ぎた1つ)の他のポインターとのポインター演算に有効ですが、逆参照できません。
Mike Seymour、

@MikeSeymour:引用は反対の答えを意味するべきではありません:チェックが必要であり、ヌルポインターでゼロを渡すことはできませんか?
neverhoodboy 2014年

7
@neverhoodboy:いいえ、引用文には「n値がゼロになる可能性がある」と明記されています。nullポインターを渡すことはできないことは正しいですが、それは質問が尋ねていることではありません。
マイクシーモア

1
@MikeSeymour:私のせい。本当に申し訳ありません。問題は、ポインタではなくサイズについてです。
neverhoodboy 2014年

5

@Youが言ったように、この規格ではmemcpyとmemmoveがこのケースを問題なく処理するように規定しています。彼らは通常何とかして実装されているので

void *memcpy(void *_dst, const void *_src, size_t len)
{
    unsigned char *dst = _dst;
    const unsigned char *src = _src;
    while(len-- > 0)
        *dst++ = *src++;
    return _dst;
}

関数呼び出し以外のパフォーマンス上のペナルティもありません。コンパイラーがそのような関数の組み込み/インライン化をサポートしている場合、追加のチェックにより、コードのマイクロビットが遅くなることがあります。


1
この関数はおそらく、アセンブリよりもメモリ転送を最適化できるアセンブリで作成されると思います
Toad

"なんとなく" :)実際、私が見たほとんどすべての実装はアセンブリにあり、ネイティブのワードサイズを使用してほとんどのビットをコピーしようとします(たとえば、x86のuint32_t)が、答えの内容は変わりません:これは開始前に大きな計算を必要としないwhileループなので、チェックはすでに行われています。
Matteo Italia

9
-1、典型的な実装は、これらの関数(C関数としても実装されていない場合もある)を引数なしで呼び出すことが有効なCであるかどうかには関係ありません。
R .. GitHubのSTOP手助けICE

4
「としては@You、で言った:私は非常に私の答えの冒頭で述べたように、有効なCだという事実はすでに、他の回答でカバーされている標準のmemcpyことを指定してMEMMOVEは問題なくこのケースを処理する必要があります」。パフォーマンス上の理由から、len = 0でmemcpyを呼び出すことを恐れるべきではないという事実についての私の意見を追加しました。その場合、それはほとんどコストがかからない呼び出しであるためです。
Matteo Italia
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.