hmemcpyに関して

[戻る]
2005/02/01(火) 00:38:19
こんにちは

まず環境を書いておきます
WinXP、VC++6.0、MFC、SDI
です

現在とあるサイトを参考にファイル操作のプログラムを作っているのですが、
そのサイトで解説されている関数で使われているhmemcpyについて質問があります

まずその関数を載せておきます
与えられた2つのItemIDListを結合し、新しく確保した領域に書き込むものです
なお関数の中の以下のものは独自に定義されたものです
GetSize … ItemIDListのサイズを取得する
Create … 指定されたサイズの、ItemIDListを格納するための領域を確保する

LPITEMIDLIST ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    LPITEMIDLIST pidlNew;
    UINT cb1;
    UINT cb2;

    if (pidl1)  //May be NULL
       cb1 = GetSize(pidl1) - sizeof(pidl1->mkid.cb);
    else
       cb1 = 0;

    cb2 = GetSize(pidl2);

    pidlNew = Create(cb1 + cb2);
    if (pidlNew)
    {
        if (pidl1)
           hmemcpy(pidlNew, pidl1, cb1);
        hmemcpy(((LPSTR)pidlNew) + cb1, pidl2, cb2);
    }
    return pidlNew;
}

この関数の最後のほうでhmemcopyという関数が使用されていますよね
見慣れないので何物かと思い調べてみると、ヘルプにもMSDNonlineにもない、
MSDNonlineの英語版にも無い・・・
ふと思いついてファイルから検索をかけたらWINDOWSX.Hで

#define hmemcpy MoveMemory

と定義されているのを見つけました

前置きが長くなりましたが、ここで2つ疑問があります

まず1つ目です

MoveMemoryはWin32APIだと思いますが、なぜわざわざマクロ定義してあるのでしょうか
同じファイル内でいくつかのC言語の関数が同じようにマクロ定義してありますが、
APIはこれだけみたいです
あと、少し趣旨からはずれるのですが、同じ場所でマクロ定義されている
#define _fmalloc    malloc
#define _nmalloc    malloc
などといったものは、どのような意図があってのものなのでしょうか?
他にもstr系関数などいくつかありました

それと2つ目です

つまりはhmemcpyはMoveMemoryだということはわかりましたが、
MoveMemoryの動作に関して一つわからない点があります
コピー元となった領域のデータが元のまま残ることは保証されているのでしょうか?

int    a = 10;
int    b = 20;
CopyMemory(&b,&a,sizeof(int));

とした場合、bは当然10となりますが、aは10のままという結果になりました
これはたまたまではなく、保証されているのかどうかが気になります
Move(移動)という言葉から、元のデータが消えてしまうような印象を受けるのですが
思い過ごしでしょうか

CopyMemoryでも問題ない気がしますが、これは領域が重なっていた場合を考慮しての
安全策なんでしょうかね?
それとも作者の好みの問題でしょうか?

以上が質問内容です
1つ目などはあまり気にすることではないのかもしれませんが・・・
どなたか答えられる方がいましたらよろしくお願いします

wood 2005/02/01(火) 05:00:44
1.従来製品との互換性の為
  と聞きます

  そのほか、アンダースコアが先頭についている関数はMSの拡張
  またはMS独自規格のものって聞いたことがあります
  ** 間違っていたら 誰かフォロー お願いします **
  
2.名前の付け方は好みだけの問題じゃないと思います
  それなりに意味あると思うけど
  「好み」で済ましてしまうと 「A」でも「B」でも良くなってしまいます
  ましてや「意味」が無いのであれば、巨大な引数をもって
  すべて「オーバーロード」させてしまえば良いことになっていしまいます

なおき 2005/02/01(火) 09:49:44
>CopyMemoryでも問題ない気がしますが、これは領域が重なっていた場合を
>考慮しての安全策なんでしょうかね?


「転送元と転送先のメモリブロックが重なる場合の結果は未定義です。重なる場合は、
MoveMemory 関数を使ってください。」
と、ある以上そうなんでしょう。

るーた 2005/02/01(火) 10:11:19
hmemcpy、_fmalloc、_nmallocは、heap、far、nearの先頭文字+関数名で、
heap、far、nearは、16bit環境下で作成されたものとの互換のため、
今も生き残っているだけです。現在は、必要のないものとなっています。

>コピー元となった領域のデータが元のまま残ることは保証されているのでしょうか?
CopyMemory、MoveMemoryで、領域確保した文字列がどうなるか、
実行後、解放できるかどうかなどを確認してみれば、わかることではないかと思われます。

ku 2005/02/01(火) 11:28:47
> とした場合、bは当然10となりますが、aは10のままという結果になりました
> これはたまたまではなく、保証されているのかどうかが気になります
CopyMemory()のコピー先、MoveMemory()の移動先から、
指定したバイト分の領域が書き換わると考えて良いでしょう
それ以外は書き換わりません

char c[5] = {0, 1, 2, 3, 4};
MoveMemory(&c[2], c, 3);
結果は必ず{0, 1, 0, 1, 2}になる

CopyMemory(&c[2], c, 3);
結果は
{0, 1, 0, 1, 0}
となるかもしれないしMoveMemory()と同じ結果になるかもしれないし
わけのわからない値になるかもしれない
コピー元と先が重なっていたら、その間はどうなるか分からない

いずれの場合もc[0]c[1]は変更されない
しかし、移動元でもあり移動先でもあるc[2]は書き換わる(コピーの場合は動作未定義)

16bit用のソースと兼用である場合は別ですが
hmemcpy()のような互換性のためだけに残されている関数を
今から作るプログラムで呼ばない方がいい

たぶん、CopyMemory()とMoveMemory()の差は、
「アドレスの小さい方からか大きい方からかの、どちらのコピーロジックのみ実装している」と
「重なった領域を保証するために、最適な方を選んでコピーするか」の差だと思います
たまたま、VC6のデバッグバージョン(リリース版では知らない)では、
MoveMemory()もCopyMemory()も同じ実装のようです
CopyMemory()にしてもアドレスの比較の部分が無駄なだけで、あまり変わらないでしょうから
ただし、重なった領域の動作は未定義ですから、
今後どういった実装になるか保証はありませんので用途によって使い分けましょう

ku 2005/02/01(火) 12:08:20
> CopyMemoryでも問題ない気がしますが、これは領域が重なっていた場合を考慮しての
> 安全策なんでしょうかね?
> それとも作者の好みの問題でしょうか?
重なっていた場合を考慮するならMoveMemory()かmemmove()を使うと思います
hmemcpy()を使う理由は16bitを考えてとしか思えないけど
たしかITEMIDLISTは16bitには実装されてませんよね

hmemcpy()っていつの間にか無くなってもおかしくないものですから
MoveMemory()が必要と考えた人がhmemcpy()を使うとも思えません
1つならmemcpy()のタイプミスかもしれませんが2つありますね
「hmemcpy()がMoveMemory()である」というのも覚えにくいし、
ソースを見たときに混乱の元になりそうです

サイトがあるなら、ご本人に質問のメールでもだされてはいかがでしょうか?

2005/02/01(火) 16:33:45
[[解決]]
こんにちは
みなさん、レスどうもありがとうございます

1つ目の質問に関して

なるほど、互換性を維持するためのものだったんですね
わたしがプログラミングを始めたときはすでにWin32になっていたので
16bit時代というものをまったく知らないんですよね

もしかしたら作者の方が16bit時代からプログラミングをしている人で、
何かしらの習慣からhmemcpyを使ったのかもしれません

作者の方にメールを送ってもいいのですが、
hmemcpyの正体もはっきりしましたし、
つまり普通にMoveMemory(場合によってCopyMemory)を使えばいいということもわかり
ましたので、
こちらで独自に修正することにします

2つ目の質問に関して

コピー元はそのまま残るようですね
よく考えてみたら、
現実世界で置いてあるりんごを動かせば当然元の場所からはなくなりますけど、
メモリ上のデータを指定した場所に動かす場合、
元のデータをわざわざ消したりする必要性はないのでしょう
というよりもそのようなことをしたら逆に無駄な処理が増える?
コピー元のメモリ内容を参照できなくなってしまっても不便ですしね

よくわからない例えですが・・・

なにはともあれ、みなさんの非常に丁寧な解説のおかげで疑問も解決しました
どうもありがとうございました

古いスレッドには追加発言できません。