C#與C/C++的交互zz

C#與C++交互,總體來說可以有兩種方法:

  • 利用C++/CLI作為代理中間層

  • 利用PInvoke實現直接調用

第一種方法:實現起來比較簡單直觀,并且可以實現C#調用C++所寫的類,但是問題是MONO構架不支持C++/CLI功能,因此無法實現脫離Microsoft .NET Framework跨平臺運行。

第二種方法:簡單的實現并不麻煩,只要添加DllImportAttribute特性即可以導入C/C++的函數,但是問題是PInvoke不能簡單的實現對C++類的調用。在Warensoft3D中為了可以使用MONO實現跨平臺(當然DirectX是不能跨平臺的),所以使用了本方法,下面將對本方法展開詳細的說明。

測試平臺:

Windows7 64位,VS2010,.NET4.0

注意事項:

PInvoke從功能上來說,只支持函數調用,在被導出的函數前面一定要添加extern "C"來指明導出函數的時候使用C語言方式編譯和連接,這樣保證函數定義的名字和導出的名字相同,否則如果默認按C++方式導出,那個函數的名字就會變得亂七八糟,我們的程序就無法找到入口點了。

本文將說明以下幾點:

  • 互調的基本原理

  • 基本數據類型的傳遞

  • 指針的傳遞

  • 函數指針的傳遞

  • 結構體的傳遞

  1. 互調的基本原理

    首先,我們來看一個再常規不過的概念—"數據類型"

    我們知道在大多數的靜態語言中定義變量的時候都要先指定其數據類型,所謂數據類型,都是人們強加的一個便于記憶的名稱,究其本質就是指明了這個數據在內存里到底是占用了幾個字節,程序在運行的時候,首先找到這個數據的地址,然后再按著該類型的長度,讀取相對應的內存,然后再處理。

    了解了前面這個事兒,所有編程語言之間進行互調就有點門道兒了。對于不同語言之間的互調,只要將該數據的指針(內存地址)傳遞給另一個語言,在另一個語言中根據通信協議將指針所指向的數據存儲入長度對應的數據類型即可,當然要滿足以下幾點:

    1. 對于像Java,.NET這樣有運行時虛擬機編程語言來講,由于虛擬機會讓堆內存來回轉移,因此,在進行互調的時候,要保證正在被互調的數據所在的內存一定要固定,不能被轉移。

    2. 有一些編程語言支持指針,有一些語言不支持指針(如Java),這個問題并不重要,所謂指針,其實就是一個內存地址,對于32位OS的指針是一個32位整數,而對于64位機OS的指針是一個64位整數。因為大多數語言中都有整型數,所以可以利用整型來接收指針。

  2. 基本數據類型的傳遞

互調過程中,最基本要傳遞的無非是數值和字符,即:int,long,float,char等等,但是此類型非彼類型,C/C++與C#中有一些數據類型長度是不一樣的,下表中列出常見數據類型的異同:

C/C++

C#

長度

short

short

2Bytes

int

int

4Bytes

long(該類型在傳遞的時候常常會弄混)

int

4Bytes

bool

bool

1Byte

char(Ascii碼字符)

byte

1Byte

wchar_t(Unicode字符,該類型與C#中的Char兼容)

char

2Bytes

float

float

4Bytes

double

double

8Bytes

最容易弄混的是就是long,char兩個類型,在C/C++中long和int都是4個字節,都對應著C#中的int類型,而C/C++中的char類型占一個字節,用來表示一個ASCII碼字符,在C#中能夠表示一個字節的是byte類型。與C#中char類型對應的應該是C/C++中的wchar_t類型,對應的是一個2字節的Unicode字符。

下面通過實例來說明調用過程:

第一步:

建立一個C++的Win32DLL,如下圖所示:

這里要注意選擇"Export symbols"導出符號。點擊完成。

第二步:

由于項目的名稱是"TestCPPDLL",因此,會自動生成TestCPPDLL.h和TestCPPDLL.cpp兩個文件,.h文件是要導出內容的聲明文件,為了能清楚的說明問題,我們將TestCPPDLL.h和TestCPPDLL.cpp兩個文件中的所有內容都刪除,然后在TestCPPDLL.h中添加如下內容:

第一行代碼中定義了一個名為"TESTCPPDLL_API"的宏,該宏對應的內容是"__declspec(dllexport)"意思是將后面修飾的內容定義為DLL中要導出的內容。當然你也可以不使用這個宏,可以直接將"__declspec(dllexport)"寫在要導出的函數前面。

第二行中的"EXTERN_C",是在"winnt.h"中定義的宏,在函數前面添加"EXTERN_C"等同于在函數前面添加extern "C",意思是該函數在編譯和連接時使用C語言的方式,以保證函數名字不變。

第二行的代碼是一個函數的聲明,說明該函數可以被模塊外部調用,其定義實現在TestCPPDLL.cpp中,TestCPPDLL.cpp的代碼如下所示:

第三步:

在編譯C++DLL之前,需要做以下配置,在項目屬性對話框中選擇"C/C++"|"Advanced",將Compile AS 選項的值改為"C++"。然后確定,并編譯。

生成的DLL文件如下圖所示:

第四步:

首先,添加一個C#的應用程序,如果要在C#中調用C++的DLL文件,先要在C#的類中添加一個靜態方法,并且使用DllImportAttribute對該方法進行修飾,代碼如下所示:

DllImport中的第一個參數是指明DLL文件的位置,第二個參數"EntryPoint"用來指明對應的C/C++中的函數名稱是什么。"extern"關鍵字表明該處聲明的這個Add方法是一個外部調用。

該方法聲明完畢之后,就可以像調用一個普通的靜態方法一樣去使用了。

下面是示例程序:

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

static void Main(string[] args)

{

int c = Add(1,2);

Console.WriteLine(c);

Console.Read();

}

}

在運行C#程序之前,先要修改C#的項目屬性,如下圖所示:

將platform target設置為x86,并且允許非安全代碼(后面有用)。

然后運行該C#程序,其結果如下圖所示:

第五步:

前面的Add方法中傳遞的是數值類型(int),其他的數據類型,如float,double,和bool類型的傳遞方式是一樣的,下面演示如何傳遞字符串。

在TestCPPDLL.h中添加一個新的函數聲明,代碼如下:

EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);

這里的參數是wchar_t類型的指針,對應著C#中的char類型。TestCPPDLL.cpp中添加如下代碼:

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

??? cout<<content;

}

該代碼的功能就是將輸入的字符串通過C++在控制臺上輸出。下面是在C#中的聲明:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "WriteString")]

extern unsafe static void WriteString(char*c);

調用過程如下所示:

//因為使用指針,因為要聲明非安全域

unsafe

{

//在傳遞字符串時,將字符所在的內存固化,

//并取出字符數組的指針

fixed (char* p = &("hello".ToCharArray()[0]))

{

//調用方法

WriteString(p);

}

}

其運行效果如下圖所示:

3. 指針的傳遞

根據前面介紹的數據類型對照表,我們可以直接在方法中傳遞指針,但是要注意的是我們常常需要將數組的指針(數據入口地址,第一個元素的地址),數據從C/C++到C#時問題不大,但是如果從C#到C/C++時一定要將數組先固化,然后再傳遞處理。

下面演示如何傳遞指針,首先在TestCPPDLL.h中添加下列聲明:

//傳入一個整型指針,將其所指向的內容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//傳入一個整型數組的指針以及數組長度,遍歷每一個元素并且輸出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一個整型數組,并且數組指針返回給C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();

其實現寫在TestCPPDLL.cpp中,代碼如下所示:

TESTCPPDLL_API void __stdcall AddInt(int *i)

{

??? (*i)++;

}

TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

int*currentPointer=firstElement;

for (int i = 0; i < arrayLength; i++)

??? {

??????? cout<<*currentPointer;

??????? currentPointer++;

??? }

??? cout<<endl;

}

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

??? arrPtr=new int[10];

for (int i = 0; i < 10; i++)

??? {

??????? arrPtr[i]=i;

??? }

return arrPtr;

}

對應調用的C#代碼如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();

調用過程如下所示:

unsafe

{

// 調用C++中的AddInt方法

int i = 10;

AddInt(&i);

Console.WriteLine(i);

//調用C++中的AddIntArray方法將C#中的數據傳遞到C++中,并在C++中輸出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//調用C++中的GetArrayFromCPP方法獲取一個C++中建立的數組

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}

4. 函數指針的傳遞

前面說明的都是簡單數據類型的及其指針的傳遞,利用PInvoke我們也可以實現函數指針的傳遞,C#中并沒有函數指針的概念,但是可以使用委托(delegate)來代替函數指針,關于C#中委托的說明,可以參考筆者前面的一個文章:《C#委托及事件》

大家可能會問,為什么要傳遞函數指針呢?利用PInvoke可以實現C#對C/C++函數的調用,反過來,我們能不能在C/C++程序運行的某一時刻,來調用一個C#對應的函數呢?(例如在C++中存在一個獨立線程,該線程可能在任意時刻觸發一個事件,并且需要通知C#)。這個時候,我們就有必要將一個C#中已經指向某一個函數的函數指針(委托)傳遞給C++。

想要傳遞函數指針,首先要在C#中定義一個委托,并且在C++中定義一個函數指針,同時要保證委托和函數指針具備相同的函數原型,我們首先編寫C#的代碼,如下所示:

//定義一個委托,返回值為空,存在一個整型參數

public delegate void CSCallback(int tick);

//定義一個用于回調的方法,與前面定義的委托的原型一樣

//該方法會被C++所調用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString ());

}

//定義一個委托類型的實例,

//在主程序中該委托實例將指向前面定義的CSCallbackFunction方法

static CSCallback callback;

在CS的主程序中讓callback指向CSCallbackFunction方法,代碼如下所示:

//調用委托所指向的方法

callback = CSCallbackFunction;

然后在C/C++中定義一個函數指針,并且添加一個用于設置函數指針的函數,TestCPPDLL.h中的代碼如下所示:

//定義一個函數指針

typedef void (__stdcall *CPPCallback)(int tick);

//定義一個用于設置函數指針的方法,

//并在該函數中調用C#中傳遞過來的委托

EXTERN_C TESTCPPDLL_API void SetCallback(CPPCallback callback);

SetCallback函數的實現在TestCPPDLL.cpp中,代碼如下所示:

TESTCPPDLL_API void SetCallback(CPPCallback callback)

{

int tick=rand();

//下面的代碼是對C#中委托進行調用

callback(tick);

}

在C#中添加SetCallback函數的聲明,代碼如下所示:

//這里使用CSCallback委托類型來兼容C++里的CPPCallback函數指針

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);

在C#中的調用過程如下所示:

//讓委托指向將被回調的方法

callback = CSCallbackFunction;

//將委托傳遞給C++

SetCallback(callback);

SetCallback方法被執行后,在C#中定義的CSCallbackFunction就會被C++所調用。

5. 結構體的傳遞

傳遞結構體的想法和傳遞一個int類型數據類似,struct中的數據是在內存中順序排列的,只要保證保證以下幾點,就可以直接傳遞結構體,甚至是結構體的指針:

  • 要傳遞的成員為公有的值類型字段

  • C#中結構體字段類型與C++結構體中的字段類型相兼容

  • C#結構中的字段順序與C++結構體中的字段順序相同,要保證該功能,需要將C#結構體標記為[StructLayout( LayoutKind.Sequential)]

下面通過代碼進行說明,首先在C#中添加一個結構體,代碼如下所示:

[StructLayout( LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}

該結構體表示一個3D向量,包括X,Y,Z三個float類型的分量。

然后在TestCPPDLL.h中也定義一個相同結構的結構體,代碼如下所示:

struct Vector3

{

float X,Y,Z;

};

在TestCPPDLL.h中聲明一個用于傳遞Vector3結構體的一個函數,代碼如下所示:

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);

在TestCPPDLL.cpp中將其實現,代碼如下所示:

TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

??? cout<<"got vector3 in cpp,x:";

??? cout<<vector.X;

??? cout<<",Y:";

??? cout<<vector.Y;

??? cout<<",Z:";

??? cout<<vector.Z;

}

在C#中添加對SendStructFromCSToCPP函數的聲明,代碼如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);

C#中的調用過程如下所示:

//建立一個Vector3的實例

Vector3 vector = new Vector3() { X =10,Y=20,Z=30 };

//將vector傳遞給C++并在C++中輸出

SendStructFromCSToCPP(vector);

基輸出效果如下所示:

完整的TestCPPDLL.h代碼如下所示:

復制代碼

#define TESTCPPDLL_API __declspec(dllexport)

EXTERN_C TESTCPPDLL_API int __stdcall Add(int a,int b);

EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);

//傳入一個整型指針,將其所指向的內容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//傳入一個整型數組的指針以及數組長度,遍歷每一個元素并且輸出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一個整型數組,并且數組指針返回給C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();



//定義一個函數指針

typedef void (__stdcall *CPPCallback)(int tick);

//定義一個用于設置函數指針的方法,

//并在該函數中調用C#中傳遞過來的委托

EXTERN_C TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback);



struct Vector3

{

float X,Y,Z;

};

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);

復制代碼

完整的TestCPPDLL.CPP代碼如下所示:

復制代碼

#include "stdafx.h"

#include <iostream>

#include "TestCPPDLL.h"

using namespace std;

TESTCPPDLL_API int __stdcall Add(int a,int b)

{

return a+b;

}

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

wprintf(content);

printf("\n");

}



TESTCPPDLL_API void __stdcall AddInt(int *i)

{

(*i)++;

}



TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

int*currentPointer=firstElement;

for (int i = 0; i < arrayLength; i++)

{

cout<<*currentPointer;

currentPointer++;

}

cout<<endl;

}

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

arrPtr=new int[10];



for (int i = 0; i < 10; i++)

{

arrPtr[i]=i;

}



return arrPtr;

}



TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback)

{

int tick=100;

//下面的代碼是對C#中委托進行調用

callback(tick);

}



TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

cout<<"got vector3 in cpp,x:";

cout<<vector.X;

cout<<",Y:";

cout<<vector.Y;

cout<<",Z:";

cout<<vector.Z;

}

復制代碼

完整的C#代碼如下所示:

復制代碼

using System;

using System.Collections.Generic;

using System.Runtime.InteropServices;

using System.Text;



namespace ConsoleApplication1

{

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "WriteString")]

extern unsafe static void WriteString(char* c);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();







//定義一個委托,返回值為空,存在一個整型參數

public delegate void CSCallback(int tick);

//定義一個用于回調的方法,與前面定義的委托的原型一樣

//該方法會被C++所調用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString());



}

//定義一個委托類型的實例,

//在主程序中該委托實例將指向前面定義的CSCallbackFunction方法

static CSCallback callback;





//這里使用CSCallback委托類型來兼容C++里的CPPCallback函數指針

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);



[StructLayout(LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}



[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);



static void Main(string[] args)

{

int c = Add(1, 2);

Console.WriteLine(c);

//因為使用指針,因為要聲明非安全域

unsafe

{

//在傳遞字符串時,將字符所在的內存固化,

//并取出字符數組的指針

fixed (char* p = &("hello".ToCharArray()[0]))

{

//調用方法

WriteString(p);

}



}

unsafe

{

// 調用C++中的AddInt方法

int i = 10;



AddInt(&i);

Console.WriteLine(i);



//調用C++中的AddIntArray方法將C#中的數據傳遞到C++中,并在C++中輸出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//調用C++中的GetArrayFromCPP方法獲取一個C++中建立的數組

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}









//讓委托指向將被回調的方法

callback = CSCallbackFunction;

//將委托傳遞給C++

SetCallback(callback);



//建立一個Vector3的實例

Vector3 vector = new Vector3() { X = 10, Y = 20, Z = 30 };

//將vector傳遞給C++并在C++中輸出

SendStructFromCSToCPP(vector);





Console.Read();

}

}

}

復制代碼

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458024.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458024.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458024.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

curl查看swift狀態命令_HTTP 請求與響應包括哪些,如何用Chrome查看 HTTP 請求與響應內容和curl 命令的使用...

1.HTTP的請求和響應其實就是通過電腦上的軟件來進行的&#xff0c;客戶端請求的內容發送到服務器上&#xff0c;服務器收到請求后就會響應客戶端的請求&#xff0c;如圖&#xff1a;HTTP請求的內容及格式&#xff1a;請求最多包含四部分&#xff0c;最少包含三部分。&#xff0…

Pytorch的BatchNorm層使用中容易出現的問題

前言 本文主要介紹在pytorch中的Batch Normalization的使用以及在其中容易出現的各種小問題&#xff0c;本來此文應該歸屬于[1]中的&#xff0c;但是考慮到此文的篇幅可能會比較大&#xff0c;因此獨立成篇&#xff0c;希望能夠幫助到各位讀者。如有謬誤&#xff0c;請聯系指出…

android 比較靠譜的圖片壓縮

2019獨角獸企業重金招聘Python工程師標準>>> 第一&#xff1a;我們先看下質量壓縮方法&#xff1a; private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, …

jetty上手

jetty簡介&#xff1a;維基百科 Jetty是一個純粹的基于Java的網頁服務器和Java Servlet容器。盡管網頁服務器通常用來為人們呈現文檔&#xff0c;但是Jetty通常在較大的軟件框架中用于計算機與計算機之間的通信。Jetty支持最新的Java Servlet API&#xff08;帶JSP的支持&#…

常用公差配合表圖_ER彈簧夾頭配套BT刀柄常用規格型號表

ER彈簧夾頭具有定心精度高&#xff0c;夾緊力均勻的特點&#xff0c;廣泛用于機械類零件的精加工和半精加工&#xff0c;通常與BT刀柄匹配使用。BT刀柄是是機械主軸與刀具和其它附件工具連接件&#xff0c;BT為日本標準(MAS403)&#xff0c;現在也是普遍使用的一種標準。傳統刀…

Spatial Transformer Networks(STN)

詳細解讀Spatial Transformer Networks&#xff08;STN&#xff09;-一篇文章讓你完全理解STN了_多元思考力-CSDN博客_stn

Linux下python安裝升級詳細步驟 | Python2 升級 Python3

Linux下python升級步驟 Python2 ->Python3 多數情況下&#xff0c;系統自動的Python版本是2.x 或者yum直接安裝的也是2.x 但是&#xff0c;現在多數情況下建議使用3.x 那么如何升級呢&#xff1f; 下面老徐詳細講解升級步驟&#xff1b; 首先下載源tar包 可利用linux自帶下…

華為手機連電腦_手機、電腦無網高速互傳!華為神技逆天

Huawei Share是華為的一項自研多終端傳輸技術&#xff0c;可以在沒有網絡狀態下實現手機與手機、電腦等多終端設備間快速穩定的文件分享&#xff0c;尤其是在辦公場景下&#xff0c;可以極大提升辦公效率。華為表示&#xff0c;未來Huawei Share將應用于更多全場景跨設備無縫分…

【無標題】移動端深度學習開源框架及部署(對比)

移動端深度學習開源框架及部署 - 凌逆戰 - 博客園

Github基本操作的學習與溫習

GitHub是最先進的分布式版本控制工具&#xff0c;下面是我學習中總結的操作流程&#xff0c;僅供參考 -----------------------------------------------------------------------------------------------------------------------------------------------------------------…

excel統計行數_值得收藏的6個Excel函數公式(有講解)

收藏的Excel函數大全公式再多&#xff0c;幾天不用也會忘記。怎么才能不忘&#xff1f;你需要了解公式的運行原理。小編今天不再推送一大堆函數公式&#xff0c;而是根據提問最多的問題&#xff0c;精選出6個實用的&#xff0c;然后詳細的解釋給大家。1、計算兩個時間差TEXT(B2…

Studio One正版多少錢 Studio One正版怎么購買

隨著版權意識的增強&#xff0c;打擊盜版的力度越來越大&#xff0c;現在網絡上的盜版資源越來越少&#xff0c;資源少很難找是一方面&#xff0c;另一方面使用盜版軟件不僅很多功能不能使用&#xff0c;而且很多盜版軟件都被植入各種木馬病毒&#xff0c;從而帶來各種各樣的風…

DNS簡述

常見DNS記錄SOA&#xff1a;域權威開始NS&#xff1a;權威域名服務器A&#xff1a;主機地址CNAME&#xff1a;別名對應的正規名稱MX&#xff1a;郵件傳遞服務器PTR&#xff1a;域名指針 (用于反向 DNS)查詢過程瀏覽器緩存->hosts->LDNS->LDNS緩存->ISP->ISP緩存…

cuda gpu相關匯總

1.Ubuntu16.04:在anaconda下安裝pytorch-gpu 轉自&#xff1a;Ubuntu16.04:在anaconda下安裝pytorch-gpu_莫等閑996的博客-CSDN博客 1 創建虛擬環境并進入 conda create -n pytorch-gpu python3.6 conda activate pytorch-gpu 2 下載對應的安裝包和配件 方法一(推薦)&#…

普通人學python有意義嗎_學python難嗎

首先&#xff0c;對于初學者來說學習Python是不錯的選擇&#xff0c;一方面Python語言的語法比較簡單易學&#xff0c;另一方面Python的實驗環境也比較容易搭建。學習Python需要的時間取決于三方面因素。(推薦學習&#xff1a;Python視頻教程)其一是學習者是否具有一定的計算機…

karatsuba乘法

karatsuba乘法 Karatsuba乘法是一種快速乘法。此算法在1960年由Anatolii Alexeevitch Karatsuba 提出&#xff0c;并于1962年得以發表。[1]此算法主要用于兩個大數相乘。普通乘法的復雜度是n2&#xff0c;而Karatsuba算法的復雜度僅為3nlog3≈3n1.585&#xff08;log3是以2為底…

在Visual Studio上開發Node.js程序(2)——遠程調試及發布到Azure

【題外話】 上次介紹了VS上開發Node.js的插件Node.js Tools for Visual Studio&#xff08;NTVS&#xff09;&#xff0c;其提供了非常方便的開發和調試功能&#xff0c;當然很多情況下由于平臺限制等原因需要在其他機器上運行程序&#xff0c;進而需要遠程調試功能&#xff0c…

服務器定期監控數據_基礎設施硬件監控探索與實踐

本文選自 《交易技術前沿》總第三十六期文章(2019年9月)陳靖宇深圳證券交易所 系統運行部Email: jingyuchenszse.cn摘要&#xff1a;為了應對基礎設施規模不斷上升&#xff0c;數據中心兩地三中心帶來的運維挑戰&#xff0c;深交所結合現有基礎設施現狀&#xff0c;以通用性、靈…

LeetCode206:Reverse Linked List

Reverse a singly linked list. 分別用迭代和遞歸實現 struct ListNode {int val;struct ListNode *next; }; 迭代實現&#xff1a; struct ListNode* reverseList(struct ListNode* head) {struct ListNode *pre NULL;struct ListNode *cur head;while( cur ! NULL ){struct…

VS2010問題匯總

問題1&#xff1a;error C3872: "0xa0": 此字符不允許在標識符中使用 error C3872: "0xa0": 此字符不允許在標識符中使用 或者 error C3872: 0xa0: this character is not allowed in an identifier 解法&#xff1a;這是因為直接復制代碼的問題。0xa0是…