none
memcmpの挙動がreleaseとdebugで異なる RRS feed

  • 質問

  • /clr:pureを指定したプロジェクトにおいて以下のコードを実行した結果が

    リリースビルドとデバッグビルドで異なります。

    typedef unsigned char u8;

    u8 buf0[] = { 0x7F };

    u8 buf1[] = { 0x80 };

    int test = memcmp( buf0, buf1,1 );

    Debug::WriteLine( test.ToString() );

    //debug: -1

    //release: 1

    どちらが正しいのでしょう?

    2006年12月1日 0:44

回答

  • ILを覗いて調べてみました:

    ----- debug -----
    public static unsafe void doit()  {
      $ArrayType$$$BY00E e$$$bye1;
      $ArrayType$$$BY00E e$$$bye2;
      *((sbyte*) &e$$$bye2) = 0x7f;
      *((sbyte*) &e$$$bye1) = 0x80;
      int num1 = memcmp((void modopt(IsConst)*) &e$$$bye2, (void modopt(IsConst)*) &e$$$bye1, 1);
      Console.WriteLine(num1.ToString());
    }

    -----release -----
    public static unsafe void doit() {
      int num5;
      $ArrayType$$$BY00E e$$$bye1;
      $ArrayType$$$BY00E e$$$bye2;
      *((sbyte*) &e$$$bye2) = 0x7f;
      *((sbyte*) &e$$$bye1) = 0x80;
      uint num4 = 1;
      $ArrayType$$$BY00E* e$$$byePtr2 = &e$$$bye1;
      $ArrayType$$$BY00E* e$$$byePtr1 = &e$$$bye2;
      int num3 = 0;
      sbyte num2 = 0x7f;
      sbyte num1 = -128;
      do  {
        if (num2 > num1)  { //


  •       num3 = 1;
          goto Label_0049;
        }
        if (num4 == 1)  {
          goto Label_0049;
        }
        num4 -= 1;
        e$$$byePtr1++;
        e$$$byePtr2++;
        num2 = *((sbyte*) e$$$byePtr1);
        num1 = *((sbyte*) e$$$byePtr2);
      } while (num2 >= num1);
      num3 = -1;
    Label_0049:
      num5 = num3;
      Console.WriteLine(num5.ToString());
    }

  • signed byte の比較をやらかしてます。 すっごく怪しいです。
    素直にmemcmpを呼んでるdebug版の方が正解でしょう。

2006年12月6日 1:29
  • char は 符号ありなのか符号なしなのかは実装系によるのでどちらでもよいのでしょうけど、unsigned char って sbyte なんでしょうか。どう考えても、byte な気が。
    しかも inline 展開するのは良いとして、そのまま外の型を使うのも変ですね。何を渡されようが、memcmp 内では unsigned char で比較しないと。
    オプティマイザのチョンボでしょうか…。
    2006年12月6日 3:21
  • ILまで調べていただき、ありがとうございます。

    ILの時点で既にsignedになってしまってるのですね
    x86上の逆アセンブルを見てみてもmovsx eax,[ebx+n]...のようなコードが生成されており
    debug版で呼んでいるnativeのmemcmpと、ILに展開されたmemcmpの実装が違うのでしょう(あるいはコンパイラの何らかのバグ?)

    まあ、どちらにしても自前でmemcmpを書かざるを得ないと思いますが
    unsignedで比較を行うnative memcmpの実装で正解ということでしょうか。

    2006年12月6日 3:33
  • > native memcmpの実装で正解ということでしょうか。

    でしょう。nativeならrelease/debugを問わず正しく動いてるんだから。

    Release版でも小賢しいことせんと素直にmemcmp呼べってオプションがあればいいけれど(あるのかな?)

    話はmemcmpに限らず、strなんたら() や memなんたら() などにも及びそうで心配です。
    # ここってMSのヒトちゃんと見てくれてるのかしら? とっとと直してほしいんだけど。

    2006年12月6日 3:45
  •  επιστημη さんからの引用

    Release版でも小賢しいことせんと素直にmemcmp呼べってオプションがあればいいけれど(あるのかな?)

    コンパイラオプションなら /Oi- あたりじゃないでしょうか。
    http://msdn2.microsoft.com/ja-jp/library/f99tchzc(vs.80).aspx

    あと、 #pragma function(memcpm) で個別に制御できるのかもしれません。(このあたりは使ったことないので C++/CLI でも有効なのかどうかとか詳しいことはわかりませんけど)

    2006年12月6日 4:37
  • すべての返信

    • ILを覗いて調べてみました:

      ----- debug -----
      public static unsafe void doit()  {
        $ArrayType$$$BY00E e$$$bye1;
        $ArrayType$$$BY00E e$$$bye2;
        *((sbyte*) &e$$$bye2) = 0x7f;
        *((sbyte*) &e$$$bye1) = 0x80;
        int num1 = memcmp((void modopt(IsConst)*) &e$$$bye2, (void modopt(IsConst)*) &e$$$bye1, 1);
        Console.WriteLine(num1.ToString());
      }

      -----release -----
      public static unsafe void doit() {
        int num5;
        $ArrayType$$$BY00E e$$$bye1;
        $ArrayType$$$BY00E e$$$bye2;
        *((sbyte*) &e$$$bye2) = 0x7f;
        *((sbyte*) &e$$$bye1) = 0x80;
        uint num4 = 1;
        $ArrayType$$$BY00E* e$$$byePtr2 = &e$$$bye1;
        $ArrayType$$$BY00E* e$$$byePtr1 = &e$$$bye2;
        int num3 = 0;
        sbyte num2 = 0x7f;
        sbyte num1 = -128;
        do  {
          if (num2 > num1)  { //


    •       num3 = 1;
            goto Label_0049;
          }
          if (num4 == 1)  {
            goto Label_0049;
          }
          num4 -= 1;
          e$$$byePtr1++;
          e$$$byePtr2++;
          num2 = *((sbyte*) e$$$byePtr1);
          num1 = *((sbyte*) e$$$byePtr2);
        } while (num2 >= num1);
        num3 = -1;
      Label_0049:
        num5 = num3;
        Console.WriteLine(num5.ToString());
      }

    • signed byte の比較をやらかしてます。 すっごく怪しいです。
      素直にmemcmpを呼んでるdebug版の方が正解でしょう。

    2006年12月6日 1:29
  • char は 符号ありなのか符号なしなのかは実装系によるのでどちらでもよいのでしょうけど、unsigned char って sbyte なんでしょうか。どう考えても、byte な気が。
    しかも inline 展開するのは良いとして、そのまま外の型を使うのも変ですね。何を渡されようが、memcmp 内では unsigned char で比較しないと。
    オプティマイザのチョンボでしょうか…。
    2006年12月6日 3:21
  • ILまで調べていただき、ありがとうございます。

    ILの時点で既にsignedになってしまってるのですね
    x86上の逆アセンブルを見てみてもmovsx eax,[ebx+n]...のようなコードが生成されており
    debug版で呼んでいるnativeのmemcmpと、ILに展開されたmemcmpの実装が違うのでしょう(あるいはコンパイラの何らかのバグ?)

    まあ、どちらにしても自前でmemcmpを書かざるを得ないと思いますが
    unsignedで比較を行うnative memcmpの実装で正解ということでしょうか。

    2006年12月6日 3:33
  • > native memcmpの実装で正解ということでしょうか。

    でしょう。nativeならrelease/debugを問わず正しく動いてるんだから。

    Release版でも小賢しいことせんと素直にmemcmp呼べってオプションがあればいいけれど(あるのかな?)

    話はmemcmpに限らず、strなんたら() や memなんたら() などにも及びそうで心配です。
    # ここってMSのヒトちゃんと見てくれてるのかしら? とっとと直してほしいんだけど。

    2006年12月6日 3:45
  •  επιστημη さんからの引用

    Release版でも小賢しいことせんと素直にmemcmp呼べってオプションがあればいいけれど(あるのかな?)

    コンパイラオプションなら /Oi- あたりじゃないでしょうか。
    http://msdn2.microsoft.com/ja-jp/library/f99tchzc(vs.80).aspx

    あと、 #pragma function(memcpm) で個別に制御できるのかもしれません。(このあたりは使ったことないので C++/CLI でも有効なのかどうかとか詳しいことはわかりませんけど)

    2006年12月6日 4:37
  •  Shinichi Aoyagi さんからの引用
    #pragma function(memcpm) で個別に制御できるのかもしれません。(このあたりは使ったことないので C++/CLI でも有効なのかどうかとか詳しいことはわかりませんけど)

    やってみた。C++/CLIでも有効みたい。ちゃんと-1を返してくれました。

    2006年12月6日 11:59