【问题标题】:Difference in C# and C++ Byte array conversion for object对象的 C# 和 C++ 字节数组转换的区别
【发布时间】:2021-10-22 07:32:14
【问题描述】:

我正在尝试将一些 C++ 代码转换为 C# 以用于应用程序。我试图转换的函数计算对象的校验和,其中包括 MAC 地址以及其他详细信息。 C++中的校验和函数定义为:

unsigned short CalculateCheckSum(unsigned char* p, int n)
{
    unsigned short x, checksum = 0;

    for (unsigned long i = 0; i < n; ++i)
    {
        x = p[i];
        x <<= i % 8;
        checksum += x;
    }

    return checksum != 0 ? checksum : 51;

我写的C#中定义的函数是:

 public static ushort CalculateCheckSum(byte[] p, int n)
        {
            ushort x, checksum = 0;

            for (int i = 0; i < n; ++i)
            {
                x = p[i];
                x <<= i % 8;
                checksum += x;
            }

            return (ushort)(checksum != 0 ? checksum : 51);
        }

下面是用 C++ 计算校验和的代码:

    PCInfoClass pcInfo;
    char nicIDStr[1024];
    strcpy_s(nicIDStr, "34-29-8f-93-16-61");
    NICAddressStrToBinary(nicIDStr, pcInfo.nicID);
    char        outbuf[1000];
    pcInfo.timeStamp = 1234;
    pcInfo.expDate = 0;
    I32 pcInfoSz = 20;
    pcInfo.checksum = 0;
    unsigned char* byteStr;
    byteStr = (unsigned char*)&pcInfo;
    pcInfo.checksum = CalculateCheckSum(byteStr, pcInfoSz);

由于 CalculateCheckSum 方法采用 Byte 数组作为参数,因此我使用了 System.Runtime 附带的 BinaryFormatter 类。我尝试使用以下几行在 C# 中复制相同的功能:

            PCInfoClass pcInfo =  new PCInfoClass();
            char[] nicIDStr = new char[1024];
            string str = "34-29-8f-93-16-61";
            for (int i = 0; i < str.Length; i++)
            {
                nicIDStr[i] = str[i];
            }
            NICAddressStrToBinary(nicIDStr, pcInfo.nicID);
            pcInfo.timeStamp = 1234;
            pcInfo.expDate = 0;
            int pcInfoSz = 20;
            pcInfo.checksum = 0;
            pcInfo.checksum = CalculateCheckSum(ObjectToByteArray1(pcInfo), pcInfoSz);



public static  byte[] ObjectToByteArray1(Object obj)
        {
            if (obj == null)
                return null;

            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, obj);

            return ms.ToArray();
        }

不幸的是,两种方法的校验和值不同,因此转换在这一点上卡住了。

此代码中使用的另一个方法是 NICAddressStrToBinary,在 C++ 中其定义为:

bool NICAddressStrToBinary(const char* nicIDStr, unsigned char* outbuf)
{
    int     c, i, dgt;

    if (nicIDStr == NULL) return false;

    //converted char to integer as ascii number.
    for (dgt = 0, i = 0; (c = nicIDStr[i]) != '\0'; ++i)
    {
        //if it is 45 '-' then the loop will continue;
        if (c == '-') continue;

         //if the ascii value is between 48 to 57 then we will decrrease with 48 of given integer
        if ('0' <= c && c <= '9')
        {
            c -= '0';
        }
        else
            if ('a' <= c && c <= 'f')
            {
                c -= 'a' - 10;
            }
            else
                if ('A' <= c && c <= 'F')
                {
                    c -= 'A' - 10;
                }
                else
                {
                    return false;
                }

        if (dgt >= 6 * 2)
        {
            return false;
        }

        if (outbuf != NULL)
        {
            if ((dgt & 1) == 0)
            {
                //// it means c<<4 is c*2power4
                outbuf[dgt / 2] = c << 4;
            }
            else
            {
                outbuf[dgt / 2] |= c;
            }
        }

        dgt++;
    }


    if (dgt < 6  * 2)
    {
        return false;
    }

    return true;
}

在 C# 中它被重写为:

 public static void NICAddressStrToBinary(char[] nicIDStr, byte[] outbuf)
        {
            int c, i, dgt;
            if (nicIDStr == null) return ;
            for (dgt = 0, i = 0; i<=nicIDStr.Length-1; ++i)
            {
                c = nicIDStr[i];
                if (c == '-') continue;
                if ('0' <= c && c <= '9')
                {
                    c -= '0';
                }
                else if ('a' <= c && c <= 'f')
                {
                    c -= 'a' - 10;
                }
                else if ('A' <= c && c <= 'F')
                {
                    c -= 'A' - 10;
                }
                else
                {
                    return;
                }


                /* make sure there aren't too many digits
                 */
                if (dgt >= 6 * 2)
                {
                    return ;
                }

                /* accumulate the binary NIC ID
                 *  remembering that we're starting
                 *  with the most significant digits first
                 */
                if (outbuf != null)
                {
                    if ((dgt & 1) == 0)
                    {
                        //// it means c<<4 is c*2power4
                        outbuf[dgt / 2] = (byte)(c << 4);
                    }
                    else
                    {
                        outbuf[dgt / 2] |= (byte)c;

                    }
                }

                /* advance the digit index
                 */
                dgt++;
            }


            /* make sure I have enough digits
             */
            if (dgt < 6 * 2)
            {
                return ;
            }
            return ;
        }

谁能告诉我在 C++ 和 C# 中计算不同值的原因是什么?

【问题讨论】:

  • 代码太多了!很高兴您发布了所有相关代码,但您可以尝试减少可能存在问题的代码量(通过测试较小的部分,以确定它们在两个程序中是否相同)。要么你会以这种方式找到它,要么你会有一个较小的问题,因此即使开始回答也不会那么令人生畏。还是只有第一个 sn-p 错了?
  • 嗨,很抱歉我在这里倾倒的代码量!我不想遗漏任何细节,但我确实认为问题可能与将对象转换为字节数组有关。在 C++ 中,旧代码只执行 byteStr = (unsigned char*)&amp;pcInfo;,但在 C# 中,我使用了相同的库。我不确定如何确保它们是相同的。
  • 不相关的;当你最终得到像i&lt;=nicIDStr.Length-1; 这样的东西时,可以为自己和编译器节省一些精力并将其重写为i&lt;nicIDStr.Length;。为像我这样的人节省了一些精力,因为当我在 for 循环退出条件中看到 &lt;= 时,我开始寻找它通常会导致的错误。这段代码使用 -1 修复了错误,但是如果长度为零怎么办?如果出现无符号下溢,i &lt;= -1 可能需要很长时间才能变为真。
  • 谢谢,我会记住的。

标签: c# c++


【解决方案1】:

由于 CalculateCheckSum 方法采用 Byte 数组作为参数,因此我使用了 System.Runtime 附带的 BinaryFormatter 类。

这本来是一个合理的选择,但 BinaryFormatter 使用了一种复杂的格式,而不仅仅是“对象的字节”。这些字节可能在某处,但还有很多其他的东西。所以在这种情况下它行不通。

即使在 C# 中,也有一些方法可以获取给定对象的原始字节,但您必须为此目的专门设计 PCInfoClass:使其成为结构,使用 fixed-size 数组作为 nicID (参考是不行的)。然后您可以使用一些技巧(您可以使用的技巧取决于您所针对的 .NET 版本)来获取该结构的原始字节。

我的建议是使用BinaryWriter 手动将每个字段写入MemoryStream,然后像您一样使用ToArray。要非常小心地调用Write 的正确重载,并明确写入填充字节。如果不知道 C++ 中的类定义是什么样的,我无法为您编写该代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 2017-01-27
    • 2010-12-17
    • 2011-05-19
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    相关资源
    最近更新 更多