1. 程式人生 > >大端模式、小端模式和C#反轉

大端模式、小端模式和C#反轉

iou name 通過 緩存 .net new mark 要求 boa

A、C#大端模式和小端模式。

小端(little-endian)模式:低地址上存放低字節,高地址上存放高字節。

如0x11223344→ byte[] numBytes = new byte[]{ 0x44,0x33,0x22,0x11};

numBytes[0] = 0x44; //低地址存放低字節

numBytes[3] = 0x11; //高地址存放高字節

反之,高字節在前,低字節在後,則為大端模式。

反轉示例:

short num = 12;

byte[] bytes = BitConverter.GetBytes(s);

Array.Reverse(bytes); //bytes轉換為倒序(反轉),可實現大端小端的轉換

B、大端模式和小端模式。原文地址:https://blog.csdn.net/qqliyunpeng/article/details/68484497

作者: 李雲鵬([email protected])

版本號: 20170330
更新時間: <2017-04-06>
原創時間: <2017-03-30>
版權: 本文采用以下協議進行授權,自由轉載 - 非商用 - 非衍生 - 保持署名 | Creative Commons BY-NC-ND 3.0,轉載請註明作者及出處.

1. 概念簡介


不同的系統在存儲數據時是分大端(bit-endian)小端(little-endian)存儲的,比如,Inter x86、ARM核采用的是小端模式,Power PC、MIPS UNIX和HP-PA UNIX采用大端模式

小端模式用文字描述是,低地址上存放低字節,高地址上存放高字節。

假如有一個32位的數據 0x11223344,則在小端模式上的機器上存儲為如下的形式:

技術分享圖片

【1】0x11223344這個數中 0x11 是高字節(MSB),0x44是地字節(LSB)

【2】討論大小端的時候最小單位是字節

【3】內存的畫法中采用的是向上增長的

【3】可以將數據比作方向盤,順時鐘旋轉得到的在內存中的布局是小端存儲

至於大端模式用文字描述是,低地址上存放高字節,高地址上存放低字節。

2. 如何判斷


判斷的方法有很多種,下面將簡單的列舉幾種:

第一種方法:

[cpp] view plain copy
  1. /*
  2. * 1: little-endian
  3. * 0: big-endian
  4. */
  5. int checkEndian()
  6. {
  7. int a = 1;
  8. char *p = (char *)&a;
  9. return (*p == 1);
  10. }

【1】如果是大端,*p的結果是0

第二種方法:

[cpp] view plain copy
  1. /*
  2. * 1: little-endian
  3. * 0: big-endian
  4. */
  5. int checkEndian()
  6. {
  7. union w
  8. {
  9. int a;
  10. char b;
  11. } c;
  12. c.a = 1;
  13. return (c.b == 1);
  14. }

函數中打印方法:

[cpp] view plain copy
  1. printf("%s\n", checkEndian() ? "little-endian" : "big-endian");

3. 大端和小端的轉換


[cpp] view plain copy
  1. int big_litle_endian(int x)
  2. {
  3. int tmp;
  4. tmp = (((x)&0xff)<<24) + (((x>>8)&0xff)<<16) + (((x>>16)&0xff)<<8) + (((x>>24)&0xff));
  5. return tmp;
  6. }

4. 其他


1. 在通信的場合經常會遇到大端和小端的轉換的問題,比如tcp/ip 中,tcp/ip 中規定了自己傳輸的時候采用大端模式,當然相應的它也提供了很多函數來做支持。

如果主機是小端的模式,在跟網絡進行交互的時候經常要用到如下的函數

  • htons —— 把unsigned short類型從 主機序 轉成 網絡字節序
  • ntohs —— 把unsigned short類型從 網絡字節序 轉成 主機序
  • htonl —— 把unsigned long類型從 主機序 轉成 網絡字節序
  • ntohl —— 把unsigned long類型從 網絡字節序 轉成 主機序
需要包含頭文件 [cpp] view plain copy
  1. #if defined(_LINUX) || defined(_DARWIN)
  2. #include <netinet/in.h>
  3. #endif
  4. #ifdef WIN32
  5. #include <WINSOCK2.H>
  6. #endif

當一個系統要發送的數據是 0x12345678,以大端模式發送,則會先發送0x12.

2. 如何在64位ubuntu下同下編譯32位的程序?

需要先安裝32位的庫:sudo apt-get install libc6-dev-i386

然後在編譯的時候加上-m32選項。

C、C#實現反轉總結。原文地址:https://blog.csdn.net/chenfujun818/article/details/78654956

下面是C#版 反轉數組的幾種總結。
解決其他同行的轉換字符串而來。覺得很實用就整理了一下。
字符串版地址:http://m.blog.csdn.net/superit401/article/details/51318880

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//unity 必須引用 system.text 命名空間
using System.Text;

public class ArrayReverseTest : MonoBehaviour {

    private List<int> _arrList = new List<int>{12,58,36,46,78,463,588,999};
    void Start () {
        PrintList();
        Debug.Log(" ================  before ==============");
        // _arrList = ArrayReverse();
        // _arrList = BufferReverse();
        // _arrList = StackReverse();
        // _arrList = XORReverse();
        RecursiveReverse(_arrList,0,_arrList.Count -1);
        PrintList();
        Debug.Log(" ================  after ==============");
    }

    //方法一  使用.net 自帶的Array.Reverse() 面試時不建議用
     List<int> ArrayReverse()
     {
         _arrList.Reverse();
        return _arrList;
     }

    // 方法二 使用緩存數組方式 取1/2 進行交換
     List<int> BufferReverse()
     {
         int countList = _arrList.Count;
         for(int i = 0;i < countList / 2 ;i++)
         {
             int temp = _arrList[i];
             _arrList[i] = _arrList[countList - i - 1];
             _arrList[countList - i - 1] = temp;
         }
         return _arrList;
     }

     //方法三 使用棧
     List<int> StackReverse()
     {
         List<int> tempList = new List<int>();
         tempList.Clear();
         Stack stack = new Stack();
         foreach(int value in _arrList)
         {
            stack.Push(value);//入棧 
         }
        for(int i = 0;i < _arrList.Count;i++)
         {
            tempList.Add((int)stack.Pop());//出棧             
         }
         return tempList;
     }

    //方法四 使用異或運算進行反轉
    List<int> XORReverse()
    {
        int countList = _arrList.Count - 1;
        for(int i = 0;i < countList;i++,countList--)
        {
            _arrList[i] ^= _arrList[countList];
            _arrList[countList] ^= _arrList[i];
            _arrList[i] ^= _arrList[countList];
        }
        return _arrList;
    }

    //方法五 使用遞歸 進行反轉
    void RecursiveReverse(List<int> list,int left,int right)
    {
        if(left >= right)
            return;
        //轉換方式一
        // int temp = list[left];
        // list[left] = list[right];
        // list[right] = temp;

        //轉換方式二
        list[left] ^= list[right];
        list[right] ^= list[left];
        list[left] ^= list[right];

        RecursiveReverse(list,++left,--right); 
    }
     void PrintList()
     {
         //使用stringBuilder 的好處不言自明了 反轉字符串的時候 它也是一種方式
         StringBuilder strBuilder = new StringBuilder(); 
         for(int i = 0,Max = System.Math.Min(_arrList.Count,_arrList.Count);i< _arrList.Count;i++)
         {
             strBuilder.Append(_arrList[i]);
             strBuilder.Append("  ");
         }
         Debug.Log(" _arrlist = " + strBuilder);
     }
}

字符串反轉的9種方法
1. 使用Array.Reverse方法

對於字符串反轉,我們可以使用.NET類庫自帶的Array.Reverse方法

public static string ReverseByArray(string original)
{
char[] c = original.ToCharArray();
Array.Reverse(c);
return new string(c);
}

2. 使用字符緩存

在面試或筆試中,往往要求不用任何類庫方法,那麽有朋友大概會使用類似下面這樣的循環方法

public static string ReverseByCharBuffer(this string original)
{
char[] c = original.ToCharArray();
int l = original.Length;
char[] o = new char[l];
for (int i = 0; i < l ; i++)
{
o[i] = c[l - i - 1];
}
return new string(o);
}

當然,聰明的同學們一定會發現不必對這個字符數組進行完全遍歷,通常情況下我們會只遍歷一半

public static string ReverseByCharBuffer2(string original)
{
char[] c = original.ToCharArray();
int l = original.Length;
for (int i = 0; i < l / 2; i++)
{
char t = c[i];
c[i] = c[l - i - 1];
c[l - i - 1] = t;
}
return new string(c);
}

ReverseByCharBuffer使用了一個新的數組,而且遍歷了字符數組的所有元素,因此時間和空間的開銷都要大於ReverseByCharBuffer2。

在Array.Reverse內部,調用了非托管方法TrySZReverse,如果TrySZReverse不成功,實際上也是調用了類似ReverseByCharBuffer2的方法。

if (!TrySZReverse(array, index, length))
{
int num = index;
int num2 = (index + length) - 1;
object[] objArray = array as object[];
if (objArray == null)
{
while (num < num2)
{
object obj3 = array.GetValue(num);
array.SetValue(array.GetValue(num2), num);
array.SetValue(obj3, num2);
num++;
num2--;
}
}
else
{
while (num < num2)
{
object obj2 = objArray[num];
objArray[num] = objArray[num2];
objArray[num2] = obj2;
num++;
num2--;
}
}
}

大致上我能想到的算法就是這麽多了,但是我無意間發現了StackOverflow上的一篇帖子,才發現這麽一個看似簡單的反轉算法實現起來真可謂花樣繁多。
3. 使用StringBuilder

使用StringBuilder方法大致和ReverseByCharBuffer一樣,只不過不使用字符數組做緩存,而是使用StringBuilder。

public static string ReverseByStringBuilder(this string original)
{
StringBuilder sb = new StringBuilder(original.Length);
for (int i = original.Length - 1; i >= 0; i--)
{
sb.Append(original[i]);
}
return sb.ToString();
}

當然,你可以預見,這種算法的效率不會比ReverseByCharBuffer要高。

我們可以像使用字符緩存那樣,對使用StringBuilder方法進行優化,使其遍歷過程也減少一半

public static string ReverseByStringBuilder2(this string original)
{
StringBuilder sb = new StringBuilder(original);
for (int i = 0, j = original.Length - 1; i <= j; i++, j--)
{
sb[i] = original[j];
sb[j] = original[i];
}
return sb.ToString();
}

以上這幾種方法按算法角度來說,其實可以歸結為一類。然而下面的幾種算法就完全不是同一類型的了。
使用棧

4. 棧是一個很神奇的數據結構。我們可以使用它後進先出的特性來對數組進行反轉。先將數組所有元素壓入棧,然後再取出,順序很自然地就與原先相反了。

public static string ReverseByStack(this string original)
{
Stack<char> stack = new Stack<char>();
foreach (char ch in original)
{
stack.Push(ch);
}
char[] c = new char[original.Length];
for (int i = 0; i < original.Length; i++)
{
c[i] = stack.Pop();
}
return new string(c);
}

兩次循環和棧的開銷無疑使這種方法成為目前為止開銷最大的方法。但使用棧這個數據結構的想法還是非常有價值的。
使用XOR

5. 使用邏輯異或也可以進行反轉

public static string ReverseByXor(string original)
{
char[] charArray = original.ToCharArray();
int l = original.Length - 1;
for (int i = 0; i < l; i++, l--)
{
charArray[i] ^= charArray[l];
charArray[l] ^= charArray[i];
charArray[i] ^= charArray[l];
}
return new string(charArray);
}

在C#中,x ^= y相當於x = x ^ y。通過3次異或操作,可以將兩個字符為止互換。對於算法具體的解釋可以參考這篇文章。
6. 使用指針

使用指針可以達到最快的速度,但是unsafe代碼不是微軟所推薦的,在這裏我們就不多做討論了

public static unsafe string ReverseByPointer(this string original)
{
fixed (char* pText = original)
{
char* pStart = pText;
char* pEnd = pText + original.Length - 1;
for (int i = original.Length / 2; i >= 0; i--)
{
char temp = *pStart;
*pStart++ = *pEnd;
*pEnd-- = temp;
}

return original;
}
}

7. 使用遞歸

對於反轉這類算法,都可以使用遞歸方法

public static string ReverseByRecursive(string original)
{
if (original.Length == 1)
return original;
else
return original.Substring(1).ReverseByRecursive() + original[0];
}

8. 使用委托,還可以使代碼變得更加簡潔

public static string ReverseByRecursive2(this string original)
{
Func<string, string> f = null;
f = s => s.Length > 0 ? f(s.Substring(1)) + s[0] : string.Empty;
return f(original);
}

但是委托開銷大的弊病在這裏一點也沒有減少,以至於我做性能測試的時候導致系統假死甚至內存益處。
使用LINQ

9. System.Enumerable裏提供了默認的Reverse擴展方法,我們可以基於該方法來對String類型進行擴展

public static string ReverseByLinq(this string original)
{
return new string(original.Reverse().ToArray());
}
 

大端模式、小端模式和C#反轉